nfs-rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|