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