nfs-rb 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +12 -0
- data/LICENSE +23 -0
- data/README.md +73 -0
- data/Rakefile +14 -0
- data/bin/nfs-rb +52 -0
- data/lib/nfs.rb +17 -0
- data/lib/nfs/default_logger.rb +13 -0
- data/lib/nfs/file_proxy.rb +117 -0
- data/lib/nfs/filehandle.rb +24 -0
- data/lib/nfs/handler.rb +570 -0
- data/lib/nfs/mount.rb +94 -0
- data/lib/nfs/nfs.rb +259 -0
- data/lib/nfs/server.rb +39 -0
- data/lib/nfs/sunrpc.rb +365 -0
- data/lib/nfs/sunrpc/client.rb +77 -0
- data/lib/nfs/sunrpc/procedure.rb +56 -0
- data/lib/nfs/sunrpc/program.rb +58 -0
- data/lib/nfs/sunrpc/server.rb +119 -0
- data/lib/nfs/sunrpc/tcp_server.rb +41 -0
- data/lib/nfs/sunrpc/udp_server.rb +42 -0
- data/lib/nfs/sunrpc/version.rb +55 -0
- data/lib/nfs/version.rb +3 -0
- data/lib/nfs/xdr.rb +300 -0
- data/nfs-rb.gemspec +17 -0
- data/spec/nfs_spec.rb +29 -0
- data/spec/orig_dir/file1.txt +1 -0
- data/spec/orig_dir/file2.txt +3 -0
- data/spec/run_server.rb +10 -0
- data/spec/spec_helper.rb +44 -0
- metadata +74 -0
data/lib/nfs/mount.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# The XDR representation of the NFS mount protocol. Based on RFC 1094.
|
2
|
+
|
3
|
+
module NFS
|
4
|
+
module Mount
|
5
|
+
include SUNRPC
|
6
|
+
|
7
|
+
MNTPATHLEN = 1024 # maximum bytes in a pathname argument
|
8
|
+
MNTNAMLEN = 255 # maximum bytes in a name argument
|
9
|
+
|
10
|
+
# The fhandle is the file handle that the server passes to the client.
|
11
|
+
# All file operations are done using the file handles to refer to a file
|
12
|
+
# or a directory. The file handle can contain whatever information the
|
13
|
+
# server needs to distinguish an individual file.
|
14
|
+
# -just use nfs_fh from the nfs protocol.
|
15
|
+
|
16
|
+
# If a status of zero is returned, the call completed successfully, and
|
17
|
+
# a file handle for the directory follows. A non-zero status indicates
|
18
|
+
# some sort of error. The status corresponds with UNIX error numbers.
|
19
|
+
FhStatus = Union.new(NFS::NFSStat) do
|
20
|
+
arm :NFS_OK do
|
21
|
+
component :fhs_fhandle, NFS::NFSFh
|
22
|
+
end
|
23
|
+
|
24
|
+
default do
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# The type dirpath is the pathname of a directory
|
29
|
+
DirPath = DynamicString.new(MNTPATHLEN)
|
30
|
+
|
31
|
+
# The type name is used for arbitrary names (hostnames, groupnames)
|
32
|
+
Name = DynamicString.new(MNTNAMLEN)
|
33
|
+
|
34
|
+
# A list of who has what mounted
|
35
|
+
MountBody = Structure.new do
|
36
|
+
component :ml_hostname, Name
|
37
|
+
component :ml_directory, DirPath
|
38
|
+
component :ml_next, Optional.new(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
MountList = Optional.new(MountBody)
|
42
|
+
|
43
|
+
# A list of netgroups
|
44
|
+
GroupNode = Structure.new do
|
45
|
+
component :gr_name, Name
|
46
|
+
component :gr_next, Optional.new(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
Groups = Optional.new(GroupNode)
|
50
|
+
|
51
|
+
# A list of what is exported and to whom
|
52
|
+
ExportNode = Structure.new do
|
53
|
+
component :ex_dir, DirPath
|
54
|
+
component :ex_groups, Groups
|
55
|
+
component :ex_next, Optional.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
Exports = Optional.new(ExportNode)
|
59
|
+
|
60
|
+
MOUNTVERS = 1
|
61
|
+
|
62
|
+
MOUNTPROG = Program.new(100005) do
|
63
|
+
version(MOUNTVERS) do
|
64
|
+
# If fhs_status is 0, then fhs_fhandle contains the
|
65
|
+
# file handle for the directory. This file handle may
|
66
|
+
# be used in the NFS protocol. This procedure also adds
|
67
|
+
# a new entry to the mount list for this client mounting
|
68
|
+
# the directory.
|
69
|
+
# Unix authentication required.
|
70
|
+
procedure FhStatus, :MNT, 1, DirPath
|
71
|
+
|
72
|
+
# Returns the list of remotely mounted filesystems. The
|
73
|
+
# mountlist contains one entry for each hostname and
|
74
|
+
# directory pair.
|
75
|
+
procedure MountList, :DUMP, 2, Void.new
|
76
|
+
|
77
|
+
# Removes the mount list entry for the directory
|
78
|
+
# Unix authentication required.
|
79
|
+
procedure Void.new, :UMNT, 3, DirPath
|
80
|
+
|
81
|
+
# Removes all of the mount list entries for this client
|
82
|
+
# Unix authentication required.
|
83
|
+
procedure Void.new, :UMNTALL, 4, Void.new
|
84
|
+
|
85
|
+
# Returns a list of all the exported filesystems, and which
|
86
|
+
# machines are allowed to import it.
|
87
|
+
procedure Exports, :EXPORT, 5, Void.new
|
88
|
+
|
89
|
+
# Identical to MOUNTPROC_EXPORT above
|
90
|
+
procedure Exports, :EXPORTALL, 6, Void.new
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/nfs/nfs.rb
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
# A port of the NFSv2 XDR specification to Ruby XDR/SUNRPC. Based on RFC 1094.
|
2
|
+
|
3
|
+
module NFS
|
4
|
+
module NFS
|
5
|
+
include SUNRPC
|
6
|
+
|
7
|
+
PORT = 2049
|
8
|
+
MAXDATA = 8192
|
9
|
+
MAXPATHLEN = 1024
|
10
|
+
MAXNAMELEN = 255
|
11
|
+
FHSIZE = 32
|
12
|
+
FIFO_DEV = -1 # size kludge for named pipes
|
13
|
+
|
14
|
+
MODE_FMT = 0170000 # type of file
|
15
|
+
MODE_DIR = 0040000 # directory
|
16
|
+
MODE_CHR = 0020000 # character special
|
17
|
+
MODE_BLK = 0060000 # block special
|
18
|
+
MODE_REG = 0100000 # regular
|
19
|
+
MODE_LNK = 0120000 # symbolic link
|
20
|
+
MODE_SOCK = 0140000 # socket
|
21
|
+
MODE_FIFO = 0010000 # fifo
|
22
|
+
|
23
|
+
NFSStat = Enumeration.new do
|
24
|
+
name :NFS_OK, 0 # no error
|
25
|
+
name :NFSERR_PERM, 1 # Not owner
|
26
|
+
name :NFSERR_NOENT, 2 # No such file or directory
|
27
|
+
name :NFSERR_IO, 5 # I/O error
|
28
|
+
name :NFSERR_NXIO, 6 # No such device or address
|
29
|
+
name :NFSERR_ACCES, 13 # Permission denied
|
30
|
+
name :NFSERR_EXIST, 17 # File exists
|
31
|
+
name :NFSERR_NODEV, 19 # No such device
|
32
|
+
name :NFSERR_NOTDIR, 20 # Not a directory
|
33
|
+
name :NFSERR_ISDIR, 21 # Is a directory
|
34
|
+
name :NFSERR_INVAL, 22 # Invalid argument
|
35
|
+
name :NFSERR_FBIG, 27 # File too large
|
36
|
+
name :NFSERR_NOSPC, 28 # No space left on device
|
37
|
+
name :NFSERR_ROFS, 30 # Read-only file system
|
38
|
+
name :NFSERR_NAMETOOLONG, 63 # File name too long
|
39
|
+
name :NFSERR_NOTEMPTY, 66 # Directory not empty
|
40
|
+
name :NFSERR_DQUOT, 69 # Disc quota exceeded
|
41
|
+
name :NFSERR_STALE, 70 # Stale NFS file handle
|
42
|
+
name :NFSERR_WFLUSH, 99 # Write cache flushed
|
43
|
+
end
|
44
|
+
|
45
|
+
FType = Enumeration.new do
|
46
|
+
name :NFNON, 0 # non-file
|
47
|
+
name :NFREG, 1 # regular file
|
48
|
+
name :NFDIR, 2 # directory
|
49
|
+
name :NFBLK, 3 # block special
|
50
|
+
name :NFCHR, 4 # character special
|
51
|
+
name :NFLNK, 5 # symbolic link
|
52
|
+
name :NFSOCK, 6 # unix domain sockets
|
53
|
+
name :NFBAD, 7 # unused
|
54
|
+
name :NFFIFO, 8 # named pipe
|
55
|
+
end
|
56
|
+
|
57
|
+
NFSFh = Structure.new do
|
58
|
+
component :data, FixedOpaque.new(FHSIZE)
|
59
|
+
end
|
60
|
+
|
61
|
+
NFSTime = Structure.new do
|
62
|
+
component :seconds, UnsignedInteger.new
|
63
|
+
component :useconds, UnsignedInteger.new
|
64
|
+
end
|
65
|
+
|
66
|
+
FAttr = Structure.new do
|
67
|
+
component :type, FType # file type
|
68
|
+
component :mode, UnsignedInteger.new # protection mode bits
|
69
|
+
component :nlink, UnsignedInteger.new # number of hard links
|
70
|
+
component :uid, UnsignedInteger.new # owner user id
|
71
|
+
component :gid, UnsignedInteger.new # owner group id
|
72
|
+
component :size, UnsignedInteger.new # file size in bytes
|
73
|
+
component :blocksize, UnsignedInteger.new # prefered block size
|
74
|
+
component :rdev, UnsignedInteger.new # special device number
|
75
|
+
component :blocks, UnsignedInteger.new # Kb of disk used by file
|
76
|
+
component :fsid, UnsignedInteger.new # device number
|
77
|
+
component :fileid, UnsignedInteger.new # inode number
|
78
|
+
component :atime, NFSTime # time of last access
|
79
|
+
component :mtime, NFSTime # time of last modification
|
80
|
+
component :ctime, NFSTime # time of last change
|
81
|
+
end
|
82
|
+
|
83
|
+
SAttr = Structure.new do
|
84
|
+
component :mode, UnsignedInteger.new # protection mode bits
|
85
|
+
component :uid, UnsignedInteger.new # owner user id
|
86
|
+
component :gid, UnsignedInteger.new # owner group id
|
87
|
+
component :size, UnsignedInteger.new # file size in bytes
|
88
|
+
component :atime, NFSTime # time of last access
|
89
|
+
component :mtime, NFSTime # time of last modification
|
90
|
+
end
|
91
|
+
|
92
|
+
Filename = DynamicString.new(MAXNAMELEN)
|
93
|
+
NFSPath = DynamicString.new(MAXPATHLEN)
|
94
|
+
|
95
|
+
AttrStat = Union.new(NFSStat) do
|
96
|
+
arm :NFS_OK do
|
97
|
+
component :attributes, FAttr
|
98
|
+
end
|
99
|
+
|
100
|
+
default do
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
SAttrArgs = Structure.new do
|
105
|
+
component :file, NFSFh
|
106
|
+
component :attributes, SAttr
|
107
|
+
end
|
108
|
+
|
109
|
+
DirOpArgs = Structure.new do
|
110
|
+
component :dir, NFSFh
|
111
|
+
component :name, Filename
|
112
|
+
end
|
113
|
+
|
114
|
+
DirOpOkRes = Structure.new do
|
115
|
+
component :file, NFSFh
|
116
|
+
component :attributes, FAttr
|
117
|
+
end
|
118
|
+
|
119
|
+
DirOpRes = Union.new(NFSStat) do
|
120
|
+
arm :NFS_OK do
|
121
|
+
component :diropres, DirOpOkRes
|
122
|
+
end
|
123
|
+
|
124
|
+
default do
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
ReadLinkRes = Union.new(NFSStat) do
|
129
|
+
arm :NFS_OK do
|
130
|
+
component :data, NFSPath
|
131
|
+
end
|
132
|
+
|
133
|
+
default do
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Arguments to remote read
|
138
|
+
ReadArgs = Structure.new do
|
139
|
+
component :file, NFSFh # handle for file
|
140
|
+
component :offset, UnsignedInteger.new # byte offset in file
|
141
|
+
component :count, UnsignedInteger.new # immediate read count
|
142
|
+
component :totalcount, UnsignedInteger.new # read count from offset
|
143
|
+
end
|
144
|
+
|
145
|
+
# Status OK portion of remote read reply
|
146
|
+
ReadOkRes = Structure.new do
|
147
|
+
component :attributes, FAttr # Attributes needed for pagin ??
|
148
|
+
component :data, Opaque.new(MAXDATA)
|
149
|
+
end
|
150
|
+
|
151
|
+
ReadRes = Union.new(NFSStat) do
|
152
|
+
arm :NFS_OK do
|
153
|
+
component :reply, ReadOkRes
|
154
|
+
end
|
155
|
+
|
156
|
+
default do
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Arguments to remote write
|
161
|
+
WriteArgs = Structure.new do
|
162
|
+
component :file, NFSFh # handle for file
|
163
|
+
component :beginoffset, UnsignedInteger.new # begin. byte offset in file
|
164
|
+
component :offset, UnsignedInteger.new # curr. byte offset in file
|
165
|
+
component :totalcount, UnsignedInteger.new # write count to this offset
|
166
|
+
component :data, Opaque.new(MAXDATA) # data
|
167
|
+
end
|
168
|
+
|
169
|
+
CreateArgs = Structure.new do
|
170
|
+
component :where, DirOpArgs
|
171
|
+
component :attributes, SAttr
|
172
|
+
end
|
173
|
+
|
174
|
+
RenameArgs = Structure.new do
|
175
|
+
component :from, DirOpArgs
|
176
|
+
component :to, DirOpArgs
|
177
|
+
end
|
178
|
+
|
179
|
+
LinkArgs = Structure.new do
|
180
|
+
component :from, NFSFh
|
181
|
+
component :to, DirOpArgs
|
182
|
+
end
|
183
|
+
|
184
|
+
SymlinkArgs = Structure.new do
|
185
|
+
component :from, DirOpArgs
|
186
|
+
component :to, NFSPath
|
187
|
+
component :attributes, SAttr
|
188
|
+
end
|
189
|
+
|
190
|
+
NFSCookie = UnsignedInteger.new
|
191
|
+
|
192
|
+
# Arguments to readdir
|
193
|
+
ReadDirArgs = Structure.new do
|
194
|
+
component :dir, NFSFh # directory handle
|
195
|
+
component :cookie, NFSCookie # cookie
|
196
|
+
component :count, UnsignedInteger.new # directory bytes to read
|
197
|
+
end
|
198
|
+
|
199
|
+
Entry = Structure.new do
|
200
|
+
component :fileid, UnsignedInteger.new
|
201
|
+
component :name, Filename
|
202
|
+
component :cookie, NFSCookie
|
203
|
+
component :nextentry, Optional.new(self)
|
204
|
+
end
|
205
|
+
|
206
|
+
DirList = Structure.new do
|
207
|
+
component :entries, Optional.new(Entry)
|
208
|
+
component :eof, Boolean.new
|
209
|
+
end
|
210
|
+
|
211
|
+
ReadDirRes = Union.new(NFSStat) do
|
212
|
+
arm :NFS_OK do
|
213
|
+
component :reply, DirList
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
StatFsOkRes = Structure.new do
|
218
|
+
component :tsize, UnsignedInteger.new # preferred xfer size in bytes
|
219
|
+
component :bsize, UnsignedInteger.new # file system block size
|
220
|
+
component :blocks, UnsignedInteger.new # total blocks in file system
|
221
|
+
component :bfree, UnsignedInteger.new # free blocks in fs
|
222
|
+
component :bavail, UnsignedInteger.new # free blocks avail to non-root
|
223
|
+
end
|
224
|
+
|
225
|
+
StatFsRes = Union.new(NFSStat) do
|
226
|
+
arm :NFS_OK do
|
227
|
+
component :reply, StatFsOkRes
|
228
|
+
end
|
229
|
+
|
230
|
+
default do
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Remote file service routines
|
235
|
+
NFS_VERSION = 2
|
236
|
+
|
237
|
+
NFS_PROGRAM = Program.new(100003) do
|
238
|
+
version(NFS_VERSION) do
|
239
|
+
procedure AttrStat, :GETATTR, 1, NFSFh
|
240
|
+
procedure AttrStat, :SETATTR, 2, SAttrArgs
|
241
|
+
procedure Void.new, :ROOT, 3, Void.new
|
242
|
+
procedure DirOpRes, :LOOKUP, 4, DirOpArgs
|
243
|
+
procedure ReadLinkRes, :READLINK, 5, NFSFh
|
244
|
+
procedure ReadRes, :READ, 6, ReadArgs
|
245
|
+
procedure Void.new, :WRITECACHE, 7, Void.new
|
246
|
+
procedure AttrStat, :WRITE, 8, WriteArgs
|
247
|
+
procedure DirOpRes, :CREATE, 9, CreateArgs
|
248
|
+
procedure NFSStat, :REMOVE, 10, DirOpArgs
|
249
|
+
procedure NFSStat, :RENAME, 11, RenameArgs
|
250
|
+
procedure NFSStat, :LINK, 12, LinkArgs
|
251
|
+
procedure NFSStat, :SYMLINK, 13, SymlinkArgs
|
252
|
+
procedure DirOpRes, :MKDIR, 14, CreateArgs
|
253
|
+
procedure NFSStat, :RMDIR, 15, DirOpArgs
|
254
|
+
procedure ReadDirRes, :READDIR, 16, ReadDirArgs
|
255
|
+
procedure StatFsRes, :STATFS, 17, NFSFh
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
data/lib/nfs/server.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module NFS
|
2
|
+
class Server
|
3
|
+
attr_reader :dir, :host, :port, :protocol
|
4
|
+
|
5
|
+
def initialize(dir:, host:, port:, protocol:)
|
6
|
+
@dir = dir
|
7
|
+
@host = host
|
8
|
+
@port = port
|
9
|
+
@protocol = protocol
|
10
|
+
|
11
|
+
@handler = Handler.new(FileProxy.open(dir))
|
12
|
+
@server = server_class.new(@handler.programs, port, host)
|
13
|
+
end
|
14
|
+
|
15
|
+
def join
|
16
|
+
@server.join
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
@server.start
|
21
|
+
end
|
22
|
+
|
23
|
+
def shutdown
|
24
|
+
@server.shutdown
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def server_class
|
30
|
+
if protocol == :tcp
|
31
|
+
SUNRPC::TCPServer
|
32
|
+
elsif protocol == :udp
|
33
|
+
SUNRPC::UDPServer
|
34
|
+
else
|
35
|
+
raise "Unsupported protocol #{protocol}, expected :tcp or :udp"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/nfs/sunrpc.rb
ADDED
@@ -0,0 +1,365 @@
|
|
1
|
+
# Ruby XDR based implementation of SUNRPC. Based on RFC 1057.
|
2
|
+
|
3
|
+
module NFS
|
4
|
+
module SUNRPC
|
5
|
+
autoload :Client, 'nfs/sunrpc/client'
|
6
|
+
autoload :Procedure, 'nfs/sunrpc/procedure'
|
7
|
+
autoload :Program, 'nfs/sunrpc/program'
|
8
|
+
autoload :Server, 'nfs/sunrpc/server'
|
9
|
+
autoload :TCPServer, 'nfs/sunrpc/tcp_server'
|
10
|
+
autoload :UDPClient, 'nfs/sunrpc/udp_client'
|
11
|
+
autoload :UDPServer, 'nfs/sunrpc/udp_server'
|
12
|
+
autoload :Version, 'nfs/sunrpc/version'
|
13
|
+
|
14
|
+
include XDR
|
15
|
+
|
16
|
+
MAXAUTHLEN = 400
|
17
|
+
AUTH_UNIX_MAXMACHINENAMELEN = 255
|
18
|
+
AUTH_UNIX_MAXGIDS = 16
|
19
|
+
|
20
|
+
AuthFlavor = Enumeration.new do
|
21
|
+
name :AUTH_NULL, 0
|
22
|
+
name :AUTH_UNIX, 1
|
23
|
+
name :AUTH_SHORT, 2
|
24
|
+
name :AUTH_DES, 3
|
25
|
+
# and more to be defined?
|
26
|
+
end
|
27
|
+
|
28
|
+
OpaqueAuth = Structure.new do
|
29
|
+
component :flavor, AuthFlavor
|
30
|
+
component :body, Opaque.new(MAXAUTHLEN)
|
31
|
+
end
|
32
|
+
|
33
|
+
AuthUnix = Structure.new do
|
34
|
+
component :stamp, UnsignedInteger.new
|
35
|
+
component :machinename, DynamicString.new(AUTH_UNIX_MAXMACHINENAMELEN)
|
36
|
+
component :uid, UnsignedInteger.new
|
37
|
+
component :gid, UnsignedInteger.new
|
38
|
+
component :gids, DynamicArray.new(UnsignedInteger.new, AUTH_UNIX_MAXGIDS)
|
39
|
+
end
|
40
|
+
|
41
|
+
AuthDESNamekind = Enumeration.new do
|
42
|
+
name :ADN_FULLNAME, 0
|
43
|
+
name :ADN_NICKNAME, 1
|
44
|
+
end
|
45
|
+
|
46
|
+
DESBlock = FixedOpaque.new(8)
|
47
|
+
|
48
|
+
MAXNETNAMELEN = 255
|
49
|
+
|
50
|
+
AuthDESFullname = Structure.new do
|
51
|
+
component :name, DynamicString.new(MAXNETNAMELEN) # name of client
|
52
|
+
component :key, DESBlock # PK encrypted conversation key
|
53
|
+
component :window, FixedOpaque.new(4) # encrypted window
|
54
|
+
end
|
55
|
+
|
56
|
+
AuthDESCred = Union.new(AuthDESNamekind) do
|
57
|
+
arm :ADN_FULLNAME do
|
58
|
+
component :adc_fullname, AuthDESFullname
|
59
|
+
end
|
60
|
+
|
61
|
+
arm :ADN_NICKNAME do
|
62
|
+
component :adc_nickname, SignedInteger.new
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Timestamp = Structure.new do
|
67
|
+
component :seconds, UnsignedInteger.new # seconds
|
68
|
+
component :useconds, UnsignedInteger.new # microseconds
|
69
|
+
end
|
70
|
+
|
71
|
+
AuthDESVerfClnt = Structure.new do
|
72
|
+
component :adv_timestamp, DESBlock # encrypted timestamp
|
73
|
+
component :adv_winverf, FixedOpaque.new(4) # encrypted window verifier
|
74
|
+
end
|
75
|
+
|
76
|
+
AuthDESVerfSvr = Structure.new do
|
77
|
+
component :adv_timeverf, DESBlock # encrypted verifier
|
78
|
+
component :adv_nickname, SignedInteger.new # nickname for client (unencrypted)
|
79
|
+
end
|
80
|
+
|
81
|
+
MsgType = Enumeration.new do
|
82
|
+
name :CALL, 0
|
83
|
+
name :REPLY, 1
|
84
|
+
end
|
85
|
+
|
86
|
+
ReplyStat = Enumeration.new do
|
87
|
+
name :MSG_ACCEPTED, 0
|
88
|
+
name :MSG_DENIED, 1
|
89
|
+
end
|
90
|
+
|
91
|
+
AcceptStat = Enumeration.new do
|
92
|
+
name :SUCCESS, 0 # RPC executed successfully
|
93
|
+
name :PROG_UNAVAIL, 1 # remote hasn't exported program
|
94
|
+
name :PROG_MISMATCH, 2 # remote can't support version number
|
95
|
+
name :PROC_UNAVAIL, 3 # program can't support procedure
|
96
|
+
name :GARBAGE_ARGS, 4 # procedure can't decode params
|
97
|
+
end
|
98
|
+
|
99
|
+
RejectStat = Enumeration.new do
|
100
|
+
name :RPC_MISMATCH, 0 # RPC version number != 2
|
101
|
+
name :AUTH_ERROR, 1 # remote can't authenticate caller
|
102
|
+
end
|
103
|
+
|
104
|
+
AuthStat = Enumeration.new do
|
105
|
+
name :AUTH_BADCRED, 1 # bad credentials (seal broken)
|
106
|
+
name :AUTH_REJECTEDCRED, 2 # client must begin new session
|
107
|
+
name :AUTH_BADVERF, 3 # bad verifier (seal broken)
|
108
|
+
name :AUTH_REJECTEDVERF, 4 # verifier expired or replayed
|
109
|
+
name :AUTH_TOOWEAK, 5 # rejected for security reasons
|
110
|
+
end
|
111
|
+
|
112
|
+
CallBody = Structure.new do
|
113
|
+
component :rpcvers, UnsignedInteger.new # must be equal to two (2)
|
114
|
+
component :prog, UnsignedInteger.new
|
115
|
+
component :vers, UnsignedInteger.new
|
116
|
+
component :proc, UnsignedInteger.new
|
117
|
+
component :cred, OpaqueAuth
|
118
|
+
component :verf, OpaqueAuth
|
119
|
+
# procedure specific parameters start here
|
120
|
+
end
|
121
|
+
|
122
|
+
AcceptedReply = Structure.new do
|
123
|
+
component :verf, OpaqueAuth
|
124
|
+
component :reply_data, Union.new(AcceptStat) do
|
125
|
+
arm :SUCCESS do
|
126
|
+
component :results, FixedOpaque.new(0)
|
127
|
+
# Procedure specific results start here
|
128
|
+
end
|
129
|
+
|
130
|
+
arm :PROG_MISMATCH do
|
131
|
+
component :mismatch_info, Structure.new do
|
132
|
+
component :low, UnsignedInteger.new
|
133
|
+
component :high, UnsignedInteger.new
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
default do
|
138
|
+
# Void. Cases include PROG_UNAVAIL, PROC_UNAVAIL, and GARBAGE_ARGS.
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
RejectedReply = Union.new(RejectStat) do
|
144
|
+
arm :RPC_MISMATCH do
|
145
|
+
component :mismatch_info, Structure.new do
|
146
|
+
component :low, UnsignedInteger.new
|
147
|
+
component :high, UnsignedInteger.new
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
arm :AUTH_ERROR do
|
152
|
+
component :stat, AuthStat
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
ReplyBody = Union.new(ReplyStat) do
|
157
|
+
arm :MSG_ACCEPTED do
|
158
|
+
component :areply, AcceptedReply
|
159
|
+
end
|
160
|
+
|
161
|
+
arm :MSG_DENIED do
|
162
|
+
component :rreply, RejectedReply
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
RpcMsg = Structure.new do
|
167
|
+
component :xid, UnsignedInteger.new
|
168
|
+
component :body, (Union.new(MsgType) do
|
169
|
+
arm :CALL do
|
170
|
+
component :cbody, CallBody
|
171
|
+
end
|
172
|
+
|
173
|
+
arm :REPLY do
|
174
|
+
component :rbody, ReplyBody
|
175
|
+
end
|
176
|
+
end)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Server Exceptions
|
180
|
+
|
181
|
+
class IgnoreRequest < Exception
|
182
|
+
def encode(xid)
|
183
|
+
nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Abstract base of "rejected" errors
|
188
|
+
class RequestDenied < Exception
|
189
|
+
def encode(xid)
|
190
|
+
RpcMsg.encode({
|
191
|
+
xid: xid,
|
192
|
+
body: {
|
193
|
+
_discriminant: :REPLY,
|
194
|
+
rbody: {
|
195
|
+
_discriminant: :MSG_DENIED,
|
196
|
+
rreply: rreply
|
197
|
+
}
|
198
|
+
}
|
199
|
+
})
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class RpcMismatch < RequestDenied
|
204
|
+
# RPC mismatch takes the xid since, it won't actually have one
|
205
|
+
# passed to its encode method.
|
206
|
+
def initialize(low, high, xid)
|
207
|
+
@low, @high, @xid = low, high, xid
|
208
|
+
end
|
209
|
+
|
210
|
+
def encode(xid)
|
211
|
+
RpcMsg.encode({
|
212
|
+
xid: @xid,
|
213
|
+
body: {
|
214
|
+
_discriminant: :REPLY,
|
215
|
+
rbody: {
|
216
|
+
_discriminant: :MSG_DENIED,
|
217
|
+
rreply: rreply
|
218
|
+
}
|
219
|
+
}
|
220
|
+
})
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def rreply
|
226
|
+
{
|
227
|
+
_discriminant: :RPC_MISMATCH,
|
228
|
+
mismatch_info: {
|
229
|
+
low: @low,
|
230
|
+
high: @high
|
231
|
+
}
|
232
|
+
}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Abstract base of authentication errors
|
237
|
+
class AuthenticationError < RequestDenied
|
238
|
+
private
|
239
|
+
|
240
|
+
def rreply
|
241
|
+
{
|
242
|
+
_discriminant: :AUTH_ERROR,
|
243
|
+
stat: AuthStat
|
244
|
+
}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class BadCredentials < AuthenticationError
|
249
|
+
private
|
250
|
+
|
251
|
+
def auth_stat
|
252
|
+
:AUTH_BADCRED
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class RejectedCredentials < AuthenticationError
|
257
|
+
private
|
258
|
+
|
259
|
+
def auth_stat
|
260
|
+
:AUTH_REJECTEDCRED
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class BadVerifier < AuthenticationError
|
265
|
+
private
|
266
|
+
|
267
|
+
def auth_stat
|
268
|
+
:AUTH_BADVERF
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
class RejectedVerifier < AuthenticationError
|
273
|
+
private
|
274
|
+
|
275
|
+
def auth_stat
|
276
|
+
:AUTH_REJECTEDVERF
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class TooWeak < AuthenticationError
|
281
|
+
private
|
282
|
+
|
283
|
+
def auth_stat
|
284
|
+
:AUTH_TOOWEAK
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# Abstract base of errors where the message was "accepted"
|
289
|
+
class AcceptedError < Exception
|
290
|
+
def encode(xid)
|
291
|
+
RpcMsg.encode({
|
292
|
+
xid: xid,
|
293
|
+
body: {
|
294
|
+
_discriminant: :REPLY,
|
295
|
+
rbody: {
|
296
|
+
_discriminant: :MSG_ACCEPTED,
|
297
|
+
areply: areply
|
298
|
+
}
|
299
|
+
}
|
300
|
+
})
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Program not supported
|
305
|
+
class ProgramUnavailable < AcceptedError
|
306
|
+
private
|
307
|
+
|
308
|
+
def areply
|
309
|
+
{
|
310
|
+
verf: { flavor: :AUTH_NULL, body: '' },
|
311
|
+
reply_data: {
|
312
|
+
_discriminant: :PROG_UNAVAIL
|
313
|
+
}
|
314
|
+
}
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# Version not supported
|
319
|
+
class ProgramMismatch < AcceptedError
|
320
|
+
def initialize(low, high)
|
321
|
+
@low, @high = low, high
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
|
326
|
+
def areply
|
327
|
+
{
|
328
|
+
verf: { flavor: :AUTH_NULL, body: '' },
|
329
|
+
reply_data: {
|
330
|
+
_discriminant: :PROG_MISMATCH,
|
331
|
+
low: @low,
|
332
|
+
high: @high
|
333
|
+
}
|
334
|
+
}
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# Procedure not supported
|
339
|
+
class ProcedureUnavailable < AcceptedError
|
340
|
+
private
|
341
|
+
|
342
|
+
def areply
|
343
|
+
{
|
344
|
+
verf: { flavor: :AUTH_NULL, body: '' },
|
345
|
+
reply_data: {
|
346
|
+
_discriminant: :PROC_UNAVAIL
|
347
|
+
}
|
348
|
+
}
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
class GarbageArguments < AcceptedError
|
353
|
+
private
|
354
|
+
|
355
|
+
def areply
|
356
|
+
{
|
357
|
+
verf: { flavor: :AUTH_NULL, body: '' },
|
358
|
+
reply_data: {
|
359
|
+
_discriminant: :GARBAGE_ARGS
|
360
|
+
}
|
361
|
+
}
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|