tftp.lua (21316B)
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 199 local acked 200 local retried = 0 201 local timeout = time() + timeout_secs 202 local timedout = false 203 repeat 204 socket:sendto(self.DATA(data, tid), host, port) 205 --[[ Now check for an ACK. 206 RFC1350 requires that for every packet sent, an ACK is received 207 before the next packet can be sent. 208 ]] 209 yield(true, false) -- we need to wait until the socket is readable again 210 local ack, ackhost, ackport = socket:recvfrom(ACKSIZE) 211 local ack_sequence = self.parse_ACK(ack) 212 213 if ackhost ~= host or ackport ~= port then 214 --[[https://tools.ietf.org/html/rfc1350#page-5 215 "If a source TID does not match, the packet should be 216 discarded as erroneously sent from somewhere else. 217 An error packet should be sent to the source of the 218 incorrect packet, while not disturbing the transfer." 219 ]] 220 socket:sendto(err(ERR_UNKNOWN_ID), ackhost, ackport) 221 yield(true, false) 222 elseif ack_sequence ~= tid then 223 -- this looks confusing, but the local variable 224 -- "tid" here is actually block number (aka 225 -- sequence number), not tid at all. 226 log(("ack received for old block %d (expecting %d)"):format(ack_sequence, tid)) 227 acked = false 228 else 229 acked = true 230 end 231 232 if not acked then log("resending") end 233 retried = retried + 1 234 timedout = time() > timeout 235 until acked or retried > ACK_RETRIES 236 if retried > ACK_RETRIES then 237 --There doesn't seem to be a standard error for timeout. 238 socket:sendto(err("Ack timeout"), host, port) 239 error("Timeout waiting for ACK") 240 end 241 --Okay, we've been acked in reasonable time. 242 tid = tid + 1 243 if done then success() end 244 yield(true, true) 245 end 246 end) 247 end 248 249 function tftp:handle_WRQ(socket, host, port, sink) 250 error"Not Implemented" 251 end 252 253 function tftp:listen(rrq_generator_callback, wrq_generator_callback, hosts, port, logfile) 254 --[[-- 255 Listen for TFTP requests on UDP ```bind``:`port`` (0.0.0.0:69 by default) 256 and get data from / send data to user-generated source/sink functions. 257 Data is generated/received by functions returned by the the user-supplied 258 ``rrq_generator_callback``/``wrq_generator_callback`` factory functions. 259 For each resource requested, the generator function will be called 260 with three arguments: 261 - the requested resource as a C-style string (no embedded NUL chars) 262 - the ip address of the peer, as a dotted-quad string ("1.2.3.4") 263 - the port number of the peer 264 It should return a source or sink function that will be called repeatedly 265 until the data transfer is complete: 266 (SOURCE) will be called once for each block of data : it takes a 267 single argument of the requested data length in bytes 268 and returns the next block of data. It must return as follows 269 `true, data` on success 270 `true, nil` on wouldblock but should retry next round, 271 `false` on finished 272 (SINK) takes two arguments 273 ``data`` to write 274 ``done`` (truthy), whether all data has been received and backends can cleanup. 275 276 The (SOURCE) model therefore supports both blocking and non-blocking behaviour. 277 If the given function blocks, however, it will block the whole process as Lua 278 is single threaded. That may or may not be acceptable depending on your needs. 279 If the requested resource is invalid or other termination conditions are met, 280 (SOURCE) and (SINK) functions should raise an error. 281 @return This method never returns unless interrupted. 282 ]] 283 284 local function create_handler(callbacks, request, requestsocket, host, port) 285 --[[ Given a parsed request, instantiate the generator function from the given callbacks, 286 and create a new coroutine to be called when the state of the handler's 287 new socket changes to available. 288 On success, returns a table of the form: 289 ```{ 290 handler=coroutine to call, 291 socket= new socket on a random port on which all new communication will happen, 292 fd=socket.fd as above fd 293 host=remote host, 294 port = remote port, 295 request = the original parsed request, including accepted options, if any. 296 }``` 297 On error, responds to the client with an ERROR packet, and returns nil. 298 ]] 299 local okay, generator, tsize = pcall(callbacks[request.opcode], request.filename, host, port) 300 if not okay then 301 requestsocket:sendto(self.ERROR(ERR_NOTFOUND), host, port) 302 return nil 303 else 304 if request.options then 305 request.options.tsize = request.options.tsize and tostring(tsize) 306 for k, v in pairs(request.options) do 307 if not SUPPORTED_OPTIONS[k] then request.options[k] = nil end 308 end 309 else 310 --RFC1350 requires WRQ requests to be responded to with a zero TID before transfer commences, 311 --but when responding to an options request, it is dropped. 312 if request.opcode == 'WRQ' then requestsocket:sendto(self.ACK(0), host, port) end 313 end 314 local handlersocket = UDPSocket() 315 handlersocket:bind("*", 0) 316 local handler = self['handle_' .. request.opcode](self, handlersocket, host, port, generator, request.options) 317 return { 318 handler=handler, 319 socket=handlersocket, 320 fd=handlersocket.fd, 321 host=host, 322 port=port, 323 request=request, 324 wantread=false, 325 wantwrite=true, 326 } 327 end 328 end 329 330 local function accept(socket) 331 --[[ Read an incoming request from ``socket``, parse, and ACK as appropriate. 332 If the request is invalid, responds to the client with error and returns `nil` 333 otherwise returns the parsed request. 334 ]] 335 local msg, host, port = socket:recvfrom() 336 if msg ~= false then 337 local okay, xRQ = pcall(self.parse_XRQ, msg) 338 if not okay then 339 return nil 340 else 341 return host, port, xRQ 342 end 343 end 344 end 345 346 local socket = UDPSocket() 347 local user_generator_callbacks = {RRQ=rrq_generator_callback, WRQ=wrq_generator_callback} 348 local port = port or TFTP_PORT 349 local logfile = logfile or io.stderr 350 --listen on all given addresses, default to localhost if not given 351 for i, address in pairs((type(hosts) == 'table' and hosts) or (hosts ~= nil and{hosts}) or {'127.0.0.1'}) do 352 local ok, err = socket:bind(address, port) 353 if not ok then error(err .. " binding to " .. address .. ":" .. port) end 354 end 355 356 --[[The main event loop does two things: 357 1. Accepts new connections. 358 2. Handles events occurring on all sockets by dispatching to a handler coroutine. 359 3. Removes finished requests from the queue and destroys the sockets. 360 ]] 361 local handlers = {[socket.fd]={fd=socket.fd, socket=socket, listener=true, wantread=true}} 362 while true do 363 ready_fds = poll(handlers) 364 do 365 local n = 0 366 for _ in pairs(ready_fds) do 367 n = n + 1 368 end 369 -- log(('There are %d sockets ready'):format(n)) 370 end 371 for fd, status in pairs(ready_fds) do 372 -- pretty.dump(ready_fds) 373 -- log(('There are %d sockets ready'):format(#ready_fds)) 374 ready = handlers[fd] 375 if ready.listener and status.readable then 376 --we've got a listener and should accept a new connection 377 local host, port, request = accept(ready.socket) 378 if host ~= nil then 379 log(("accepted new %s request - %s:%s/%s"):format(request.opcode, host, port, request.filename)) 380 local handler = create_handler( 381 user_generator_callbacks, 382 request, 383 ready.socket, 384 host, 385 port 386 ) 387 if handler then handlers[handler.socket.fd] = handler end 388 end 389 elseif (status.readable or status.writeable) and ready.handler then 390 --We've received an event on a socket associated with an existing handler coroutine. 391 local co_state = coroutine.status(ready.handler) 392 local okay, wantread, wantwrite 393 if co_state ~= 'dead' then 394 if (ready.wantread and status.readable) or (ready.wantwrite and status.writeable) then 395 okay, wantread, wantwrite = coroutine.resume(ready.handler) 396 ready.wantread = wantread 397 ready.wantwrite = wantwrite 398 end 399 end 400 if (not okay) or co_state == 'dead' then 401 --- the handler is finished; cleanup 402 ready.socket:close() 403 handlers[ready.fd] = nil 404 ready.fd = nil 405 ready = nil 406 end 407 end 408 end 409 end 410 end 411 412 413 --[[ RRQ/ZRQ read/write request packets 414 https://tools.ietf.org/html/rfc1350 415 2 bytes string 1 byte string 1 byte 416 ------------------------------------------------ 417 | Opcode | Filename | 0 | Mode | 0 | 418 ------------------------------------------------ 419 Figure 5-1: RRQ/WRQ packet 420 ]] 421 function tftp.RRQ(filename) 422 -- RFC1350:"The mail mode is obsolete and should not be implemented or used." 423 -- We don't support netascii, which leaves 'octet' mode only 424 return table.concat({create_opcode(OP_RRQ), filename, '\0', "octet", '\0'}, '') 425 end 426 427 function tftp.parse_XRQ(request) 428 local opcode = assert(parse_opcode(request), "Invalid opcode") 429 assert(({RRQ=true, XRQ=true})[opcode], "Not an xRQ") 430 assert(request:sub(#request) == '\0', "Invalid request: expected ASCII NUL terminated request") 431 432 local cstrings = {} 433 function zero_iter(s) 434 local pos = 1 435 return function() 436 --This is ugly. Lua 5.2 handles embedded NUL bytes in string.gmatch, 437 --but vanilla Lua5.1 doesn't match correctly and luajit can't seem to parse them 438 for i=pos, #s do 439 if s:byte(i) == 0 then 440 local sub = s:sub(pos, i-1) 441 pos = i+1 442 return sub 443 end 444 end 445 end 446 end 447 for s in zero_iter(request:sub(3)) do 448 cstrings[#cstrings+1] = s 449 end 450 assert(#cstrings >= 2) 451 local filename = assert(is_netascii(cstrings[1]) and cstrings[1], "Requested filename must be netascii") 452 local mode = assert(({netascii='netascii', octet='octet'})[cstrings[2]]) 453 local options 454 if #cstrings > 2 then 455 options = {} 456 assert(#cstrings % 2 == 0) 457 for i=3, #cstrings, 2 do 458 --[[ RFC1782, and 3247 require case insensitive comparisons. 459 We normalize them to lowercase with the consequence that 460 duplicate keys are replaced which are forbidden by the standard anyway. 461 ]] 462 options[cstrings[i]:lower()] = cstrings[i+1]:lower() 463 end 464 end 465 466 return {opcode=opcode, filename=filename, mode=mode, options=options} 467 end 468 469 --[[ ACK functions 470 2 bytes 2 bytes 471 --------------------- 472 | Opcode | Block # | 473 --------------------- 474 Figure 5-3: ACK packet 475 ]] 476 function tftp.parse_ACK(ack) 477 --get the sequence number from an ACK or raise a error if not valid 478 assert(#ack == ACKSIZE, "invalid ack") 479 assert(parse_opcode(ack) == 'ACK', "invalid ack") 480 481 -- extract the low and high order bytes and convert to an integer 482 local high, low = ack:byte(3, 4) 483 return (high * 256) + low 484 end 485 486 --[[ 487 +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 488 | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | 489 +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ 490 ]] 491 function tftp.OACK(options) 492 local stropts = {} 493 for k, v in pairs(options) do 494 assert(is_netascii(k)) 495 stropts[#stropts+1] = k .. '\0' .. v .. '\0' 496 end 497 return create_opcode(OP_OACK) .. table.concat(stropts, '') 498 end 499 500 function tftp.ACK(tid) 501 return table.concat({create_opcode(OP_ACK), create_opcode(tid)}, '') 502 end 503 504 505 --[[ DATA functions 506 2 bytes 2 bytes n bytes 507 ---------------------------------- 508 | Opcode | Block # | Data | 509 ---------------------------------- 510 Figure 5-2: DATA packet 511 ]] 512 function tftp.DATA(data, tid) 513 local opcode = create_opcode(OP_DATA) 514 local block = create_opcode(tid) 515 return table.concat({opcode, block, data}, '') 516 end 517 518 function tftp.parse_DATA(data) 519 assert(#data <= 512, "tftp data packets must be 512 bytes or less") 520 assert(parse_opcode(data) == OP_DATA, "Invalid opcode") 521 return {tid=parse_opcode(data:sub(3, 4)), data=data:sub(5)} 522 end 523 524 --[[ ERROR Functions 525 2 bytes 2 bytes string 1 byte 526 ----------------------------------------- 527 | Opcode | ErrorCode | ErrMsg | 0 | 528 ----------------------------------------- 529 Figure 5-4: ERROR packet 530 ]] 531 function tftp.ERROR(err) 532 local defined_errors = { 533 --https://tools.ietf.org/html/rfc1350#page-10 534 [0] = type(err) == 'string' and err or "Not defined", 535 "File not found.", 536 "Access violation.", 537 "Disk full or allocation exceeded.", 538 "Illegal TFTP operation.", 539 "Unknown transfer ID.", 540 "File already exists.", 541 "No such user.", 542 } 543 544 local errno = type(err) == 'string' and 0 or err 545 return table.concat({ 546 create_opcode(OP_ERR), 547 create_opcode(errno), 548 defined_errors[errno], 549 '\0' 550 }, '') 551 end 552 553 function tftp.parse_ERROR(err) 554 assert(parse_opcode(err) == OP_ERR) 555 local error_code = parse_opcode(err:sub(3, 4)) 556 return {errcode=error_code, errmsg=err:sub(5, #err-1)} 557 end 558 559 return tftp