tftp.lua (21012B)
1 -- this code is based on src/tftp.lua from https://github.com/ldrumm/tufty 2 -- which is distributed under the MIT License 3 -- Starting upstream revision 3cb95c869e2fe74cc61ca303d88af6c5daad6734 4 -- 5 -- Changes made since then are mostly to make it work better with 6 -- luasocket 7 8 --[[ 9 This package provides intefaces for handling TFTP requests as specified by 10 rfc1350, rfc1782, rfc2347, rfc2348 and (partial support for rfc2349) 11 It should work on Standard PC servers as well as small home routers running 12 OpenWRT or similar. 13 ]] 14 15 local tftp = {} 16 local TIMEOUT = 5 17 local ACK_RETRIES = 10 18 local TFTP_PORT = 69 19 local BLKSIZE = 512 20 21 local OP_RRQ = 1 22 local OP_WRQ = 2 23 local OP_DATA = 3 24 local OP_ACK = 4 25 local OP_ERR = 5 26 local OP_OACK = 6 27 local ACKSIZE = 4 28 29 local ERR_NOTFOUND = 1 30 local ERR_ACCESS = 2 31 local ERR_ALLOC = 3 32 local ERR_ILLEGAL_OP = 4 33 local ERR_UNKNOWN_ID = 5 34 local ERR_EXISTS = 6 35 local ERR_WHO = 7 36 37 --rfc2347 specifies the options extension. 38 --rfc2348 specifies the blksize extension 39 --rfc2349 specifies the timeout and tsize extensions 40 local SUPPORTED_OPTIONS = {blksize=true, timeout=true, tsize=true} 41 42 local luasocket = require("socket") 43 44 --Use penlight's prettyprinter if available 45 pcall(require, 'pl') 46 local log = pretty and pretty.dump or print 47 48 local time = (function() 49 return require("socket").gettime 50 end)() 51 52 local poll = (function() 53 --[[ 54 ``poll`` is expected to accept a table of sockets keyed by 55 backend file descriptor formatted as follows: { 56 [low level socket lib file descriptor]{ 57 fd=(low level socket lib fd) 58 wantread=(bool) 59 wantwrite=(bool) 60 ...arbitrary ignored extra fields 61 }, 62 ... 63 } 64 it returns a list of fds formatted as follows 65 { 66 [low level socket lib file descriptor]{ 67 fd= [low level socket lib file descriptor], 68 readable=(bool), 69 writable=(bool) 70 } 71 ... 72 } 73 ]] 74 75 return function(fds, timeout) 76 local wantread = {} 77 local wantwrite = {} 78 for _, fd in pairs(fds) do 79 fd.readable=false 80 fd.writeable=false 81 if fd.wantwrite then wantwrite[#wantwrite + 1] = fd.fd end 82 if fd.wantread then wantread[#wantread + 1] = fd.fd end 83 end 84 local readable, writeable, timedout = luasocket.select(wantread, wantwrite, timeout) 85 if timedout then return nil end 86 local ready = {} 87 for _, fd in ipairs(readable) do 88 ready[fd] = ready[fd] or { fd = fd } 89 ready[fd].readable = true 90 end 91 for _, fd in ipairs(writeable) do 92 ready[fd] = ready[fd] or { fd = fd } 93 ready[fd].writeable = true 94 end 95 return ready 96 end 97 end)() 98 99 local function UDPSocket() 100 --[[ We want to support the basic functionality required for TFTP operation over 101 UDP. 102 This wraps only the required functionality and in no way represents a 103 complete UDP socket implementation. 104 see http://w3.impa.br/~diego/software/luasocket/udp.html for the luasocket UDP API 105 ]] 106 107 return { 108 fd = luasocket.udp(), 109 bind = function(self, address, port) 110 return self.fd:setsockname(address, port) 111 end, 112 sendto = function(self, data, address, port) 113 return self.fd:sendto(data, address, port) 114 end, 115 recvfrom = function(self, length) 116 return self.fd:receivefrom(length) 117 end, 118 close = function(self) 119 return self.fd:close() 120 end 121 } 122 end 123 124 local function is_netascii(s) 125 --[[Check whether a string contains only characters from the RFC764 ascii 126 subset. see https://tools.ietf.org/html/rfc764#page-11 127 ]] 128 local ctrls = {[0]=1, [10]=1, [13]=1, [7]=1, [8]=1, [9]=1, [11]=1, [12]=1} 129 for i=1, #s do 130 local byte = s:sub(i, i):byte() 131 if (byte < 31 and ctrls[byte] == nil) or byte > 127 then 132 return false 133 end 134 end 135 return true 136 end 137 138 local function create_opcode(val) 139 if val < 0 or val > 2^16-1 then error("opcodes must fit into a 16bit integer") end 140 local high = math.floor(val / 256) 141 -- RFC1350 doesn't mention byte order. Assume network order (big-endian). 142 return string.char(high, val - (high * 256)) 143 end 144 145 local function parse_opcode(packet) 146 local opcode = string.byte(packet:sub(2, 2)) --assume big endian 147 return ({"RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK"})[opcode] 148 end 149 150 function tftp:handle_RRQ(socket, host, port, source, options) 151 local blksize = options and tonumber(options.blksize) or BLKSIZE 152 local timeout_secs = options and tonumber(options.timeout) or TIMEOUT --rfc2349 timout option 153 local length = options and tonumber(options.length) 154 local tid = 1 155 local time = time 156 local started = time() 157 local err = self.ERROR 158 local done = false 159 local error, success = error, error -- to terminate the coroutine immediately, we raise an error 160 local yield = coroutine.yield 161 162 return coroutine.create(function() 163 if options then 164 --[[The handler coroutine should not start running until we know the client is ready. 165 This depends on whether the client has requested rfc2347 options and responded to an OACK. 166 Without the options extension request, the client can be responded to immediately.]] 167 local acked, timeout = false, time() + timeout_secs 168 assert(socket:sendto(self.OACK(options), host, port)) 169 log(("sent OACK to %s:%d"):format(host, port)) 170 repeat 171 yield(true, false) 172 local msg, port, host = socket:recvfrom(ACKSIZE) 173 if self.parse_ACK(msg) == 0 then acked = true end 174 timedout = time() > timeout 175 until acked or timedout 176 if timedout then error("Request timed out waiting for OACK response") end 177 end 178 log(("coroutine started on %s:%s/"):format(host, port)) 179 while not done do 180 if tid >= 2^16 then 181 socket:sendto(err("File too big."), host, port) 182 error("File too big.") 183 end 184 local okay, continue, data = pcall(source, blksize) 185 if not okay then 186 packet = socket:sendto(err("An unknown error occurred"), host, port) 187 error("generator failure") 188 end 189 if data == nil and not continue then 190 done = true 191 end 192 if data == nil and continue then 193 --[[The generator ``source`` can be async and return `true, nil` 194 if no data is ready, but things are going well. 195 ]] 196 yield(false, true) 197 end 198 socket:sendto(self.DATA(data, tid), host, port) 199 --[[ Now check for an ACK. 200 RFC1350 requires that for every packet sent, an ACK is received 201 before the next packet can be sent. 202 ]] 203 local acked 204 local retried = 0 205 local timeout = time() + timeout_secs 206 local timedout = false 207 repeat 208 yield(true, false) -- we need to wait until the socket is readable again 209 local ack, ackhost, ackport = socket:recvfrom(ACKSIZE) 210 if ackhost ~= host or ackport ~= port or self.parse_ACK(ack) ~= tid then 211 --[[https://tools.ietf.org/html/rfc1350#page-5 212 "If a source TID does not match, the packet should be 213 discarded as erroneously sent from somewhere else. 214 An error packet should be sent to the source of the 215 incorrect packet, while not disturbing the transfer." 216 ]] 217 socket:sendto(err(ERR_UNKNOWN_ID), ackhost, ackport) 218 yield(true, false) 219 else 220 acked = true 221 end 222 retried = retried + 1 223 timedout = time() > timeout 224 until acked or retried > ACK_RETRIES or timedout 225 if timedout or retried > ACK_RETRIES then 226 --There doesn't seem to be a standard error for timeout. 227 socket:sendto(err("Ack timeout"), host, port) 228 error("Timeout waiting for ACK") 229 end 230 --Okay, we've been acked in reasonable time. 231 tid = tid + 1 232 if done then success() end 233 yield(true, true) 234 end 235 end) 236 end 237 238 function tftp:handle_WRQ(socket, host, port, sink) 239 error"Not Implemented" 240 end 241 242 function tftp:listen(rrq_generator_callback, wrq_generator_callback, hosts, port, logfile) 243 --[[-- 244 Listen for TFTP requests on UDP ```bind``:`port`` (0.0.0.0:69 by default) 245 and get data from / send data to user-generated source/sink functions. 246 Data is generated/received by functions returned by the the user-supplied 247 ``rrq_generator_callback``/``wrq_generator_callback`` factory functions. 248 For each resource requested, the generator function will be called 249 with three arguments: 250 - the requested resource as a C-style string (no embedded NUL chars) 251 - the ip address of the peer, as a dotted-quad string ("1.2.3.4") 252 - the port number of the peer 253 It should return a source or sink function that will be called repeatedly 254 until the data transfer is complete: 255 (SOURCE) will be called once for each block of data : it takes a 256 single argument of the requested data length in bytes 257 and returns the next block of data. It must return as follows 258 `true, data` on success 259 `true, nil` on wouldblock but should retry next round, 260 `false` on finished 261 (SINK) takes two arguments 262 ``data`` to write 263 ``done`` (truthy), whether all data has been received and backends can cleanup. 264 265 The (SOURCE) model therefore supports both blocking and non-blocking behaviour. 266 If the given function blocks, however, it will block the whole process as Lua 267 is single threaded. That may or may not be acceptable depending on your needs. 268 If the requested resource is invalid or other termination conditions are met, 269 (SOURCE) and (SINK) functions should raise an error. 270 @return This method never returns unless interrupted. 271 ]] 272 273 local function create_handler(callbacks, request, requestsocket, host, port) 274 --[[ Given a parsed request, instantiate the generator function from the given callbacks, 275 and create a new coroutine to be called when the state of the handler's 276 new socket changes to available. 277 On success, returns a table of the form: 278 ```{ 279 handler=coroutine to call, 280 socket= new socket on a random port on which all new communication will happen, 281 fd=socket.fd as above fd 282 host=remote host, 283 port = remote port, 284 request = the original parsed request, including accepted options, if any. 285 }``` 286 On error, responds to the client with an ERROR packet, and returns nil. 287 ]] 288 local okay, generator, tsize = pcall(callbacks[request.opcode], request.filename, host, port) 289 if not okay then 290 requestsocket:sendto(self.ERROR(ERR_NOTFOUND), host, port) 291 return nil 292 else 293 if request.options then 294 request.options.tsize = request.options.tsize and tostring(tsize) 295 for k, v in pairs(request.options) do 296 if not SUPPORTED_OPTIONS[k] then request.options[k] = nil end 297 end 298 else 299 --RFC1350 requires WRQ requests to be responded to with a zero TID before transfer commences, 300 --but when responding to an options request, it is dropped. 301 if request.opcode == 'WRQ' then requestsocket:sendto(self.ACK(0), host, port) end 302 end 303 local handlersocket = UDPSocket() 304 handlersocket:bind("*", 0) 305 local handler = self['handle_' .. request.opcode](self, handlersocket, host, port, generator, request.options) 306 return { 307 handler=handler, 308 socket=handlersocket, 309 fd=handlersocket.fd, 310 host=host, 311 port=port, 312 request=request, 313 wantread=false, 314 wantwrite=true, 315 } 316 end 317 end 318 319 local function accept(socket) 320 --[[ Read an incoming request from ``socket``, parse, and ACK as appropriate. 321 If the request is invalid, responds to the client with error and returns `nil` 322 otherwise returns the parsed request. 323 ]] 324 local msg, host, port = socket:recvfrom() 325 if msg ~= false then 326 local okay, xRQ = pcall(self.parse_XRQ, msg) 327 if not okay then 328 return nil 329 else 330 return host, port, xRQ 331 end 332 end 333 end 334 335 local socket = UDPSocket() 336 local user_generator_callbacks = {RRQ=rrq_generator_callback, WRQ=wrq_generator_callback} 337 local port = port or TFTP_PORT 338 local logfile = logfile or io.stderr 339 --listen on all given addresses, default to localhost if not given 340 for i, address in pairs((type(hosts) == 'table' and hosts) or (hosts ~= nil and{hosts}) or {'127.0.0.1'}) do 341 local ok, err = socket:bind(address, port) 342 if not ok then error(err .. " binding to " .. address .. ":" .. port) end 343 end 344 345 --[[The main event loop does two things: 346 1. Accepts new connections. 347 2. Handles events occurring on all sockets by dispatching to a handler coroutine. 348 3. Removes finished requests from the queue and destroys the sockets. 349 ]] 350 local handlers = {[socket.fd]={fd=socket.fd, socket=socket, listener=true, wantread=true}} 351 while true do 352 ready_fds = poll(handlers) 353 do 354 local n = 0 355 for _ in pairs(ready_fds) do 356 n = n + 1 357 end 358 -- log(('There are %d sockets ready'):format(n)) 359 end 360 for fd, status in pairs(ready_fds) do 361 -- pretty.dump(ready_fds) 362 -- log(('There are %d sockets ready'):format(#ready_fds)) 363 ready = handlers[fd] 364 if ready.listener and status.readable then 365 --we've got a listener and should accept a new connection 366 local host, port, request = accept(ready.socket) 367 if host ~= nil then 368 log(("accepted new %s request - %s:%s/%s"):format(request.opcode, host, port, request.filename)) 369 local handler = create_handler( 370 user_generator_callbacks, 371 request, 372 ready.socket, 373 host, 374 port 375 ) 376 if handler then handlers[handler.socket.fd] = handler end 377 end 378 elseif (status.readable or status.writeable) and ready.handler then 379 --We've received an event on a socket associated with an existing handler coroutine. 380 local co_state = coroutine.status(ready.handler) 381 local okay, wantread, wantwrite 382 if co_state ~= 'dead' then 383 if (ready.wantread and status.readable) or (ready.wantwrite and status.writeable) then 384 okay, wantread, wantwrite = coroutine.resume(ready.handler) 385 ready.wantread = wantread 386 ready.wantwrite = wantwrite 387 end 388 end 389 if (not okay) or co_state == 'dead' then 390 --- the handler is finished; cleanup 391 ready.socket:close() 392 handlers[ready.fd] = nil 393 ready.fd = nil 394 ready = nil 395 end 396 end 397 end 398 end 399 end 400 401 402 --[[ RRQ/ZRQ read/write request packets 403 https://tools.ietf.org/html/rfc1350 404 2 bytes string 1 byte string 1 byte 405 ------------------------------------------------ 406 | Opcode | Filename | 0 | Mode | 0 | 407 ------------------------------------------------ 408 Figure 5-1: RRQ/WRQ packet 409 ]] 410 function tftp.RRQ(filename) 411 -- RFC1350:"The mail mode is obsolete and should not be implemented or used." 412 -- We don't support netascii, which leaves 'octet' mode only 413 return table.concat({create_opcode(OP_RRQ), filename, '\0', "octet", '\0'}, '') 414 end 415 416 function tftp.parse_XRQ(request) 417 local opcode = assert(parse_opcode(request), "Invalid opcode") 418 assert(({RRQ=true, XRQ=true})[opcode], "Not an xRQ") 419 assert(request:sub(#request) == '\0', "Invalid request: expected ASCII NUL terminated request") 420 421 local cstrings = {} 422 function zero_iter(s) 423 local pos = 1 424 return function() 425 --This is ugly. Lua 5.2 handles embedded NUL bytes in string.gmatch, 426 --but vanilla Lua5.1 doesn't match correctly and luajit can't seem to parse them 427 for i=pos, #s do 428 if s:byte(i) == 0 then 429 local sub = s:sub(pos, i-1) 430 pos = i+1 431 return sub 432 end 433 end 434 end 435 end 436 for s in zero_iter(request:sub(3)) do 437 cstrings[#cstrings+1] = s 438 end 439 assert(#cstrings >= 2) 440 local filename = assert(is_netascii(cstrings[1]) and cstrings[1], "Requested filename must be netascii") 441 local mode = assert(({netascii='netascii', octet='octet'})[cstrings[2]]) 442 local options 443 if #cstrings > 2 then 444 options = {} 445 assert(#cstrings % 2 == 0) 446 for i=3, #cstrings, 2 do 447 --[[ RFC1782, and 3247 require case insensitive comparisons. 448 We normalize them to lowercase with the consequence that 449 duplicate keys are replaced which are forbidden by the standard anyway. 450 ]] 451 options[cstrings[i]:lower()] = cstrings[i+1]:lower() 452 end 453 end 454 455 return {opcode=opcode, filename=filename, mode=mode, options=options} 456 end 457 458 --[[ ACK functions 459 2 bytes 2 bytes 460 --------------------- 461 | Opcode | Block # | 462 --------------------- 463 Figure 5-3: ACK packet 464 ]] 465 function tftp.parse_ACK(ack) 466 --get the sequence number from an ACK or raise a error if not valid 467 assert(#ack == ACKSIZE, "invalid ack") 468 assert(parse_opcode(ack) == 'ACK', "invalid ack") 469 470 -- extract the low and high order bytes and convert to an integer 471 local high, low = ack:byte(3, 4) 472 return (high * 256) + low 473 end 474 475 --[[ 476 +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 477 | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | 478 +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 479 ]] 480 function tftp.OACK(options) 481 local stropts = {} 482 for k, v in pairs(options) do 483 assert(is_netascii(k)) 484 stropts[#stropts+1] = k .. '\0' .. v .. '\0' 485 end 486 return create_opcode(OP_OACK) .. table.concat(stropts, '') 487 end 488 489 function tftp.ACK(tid) 490 return table.concat({create_opcode(OP_ACK), create_opcode(tid)}, '') 491 end 492 493 494 --[[ DATA functions 495 2 bytes 2 bytes n bytes 496 ---------------------------------- 497 | Opcode | Block # | Data | 498 ---------------------------------- 499 Figure 5-2: DATA packet 500 ]] 501 function tftp.DATA(data, tid) 502 local opcode = create_opcode(OP_DATA) 503 local block = create_opcode(tid) 504 return table.concat({opcode, block, data}, '') 505 end 506 507 function tftp.parse_DATA(data) 508 assert(#data <= 512, "tftp data packets must be 512 bytes or less") 509 assert(parse_opcode(data) == OP_DATA, "Invalid opcode") 510 return {tid=parse_opcode(data:sub(3, 4)), data=data:sub(5)} 511 end 512 513 --[[ ERROR Functions 514 2 bytes 2 bytes string 1 byte 515 ----------------------------------------- 516 | Opcode | ErrorCode | ErrMsg | 0 | 517 ----------------------------------------- 518 Figure 5-4: ERROR packet 519 ]] 520 function tftp.ERROR(err) 521 local defined_errors = { 522 --https://tools.ietf.org/html/rfc1350#page-10 523 [0] = type(err) == 'string' and err or "Not defined", 524 "File not found.", 525 "Access violation.", 526 "Disk full or allocation exceeded.", 527 "Illegal TFTP operation.", 528 "Unknown transfer ID.", 529 "File already exists.", 530 "No such user.", 531 } 532 533 local errno = type(err) == 'string' and 0 or err 534 return table.concat({ 535 create_opcode(OP_ERR), 536 create_opcode(errno), 537 defined_errors[errno], 538 '\0' 539 }, '') 540 end 541 542 function tftp.parse_ERROR(err) 543 assert(parse_opcode(err) == OP_ERR) 544 local error_code = parse_opcode(err:sub(3, 4)) 545 return {errcode=error_code, errmsg=err:sub(5, #err-1)} 546 end 547 548 return tftp