From 0f6c8d50a99997ac7829864b1c93362b50f1bbf3 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Sun, 25 May 2003 01:54:13 +0000 Subject: Porting to LUA 5.0 final --- etc/tftp.lua | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 etc/tftp.lua (limited to 'etc') diff --git a/etc/tftp.lua b/etc/tftp.lua new file mode 100644 index 0000000..a0db68e --- /dev/null +++ b/etc/tftp.lua @@ -0,0 +1,131 @@ +----------------------------------------------------------------------------- +-- TFTP support for the Lua language +-- LuaSocket 1.5 toolkit. +-- Author: Diego Nehab +-- Conforming to: RFC 783, LTN7 +-- RCS ID: $Id$ +----------------------------------------------------------------------------- + +local Public, Private = {}, {} +local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace +socket.tftp = Public -- create tftp sub namespace + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +local char = string.char +local byte = string.byte + +Public.PORT = 69 +Private.OP_RRQ = 1 +Private.OP_WRQ = 2 +Private.OP_DATA = 3 +Private.OP_ACK = 4 +Private.OP_ERROR = 5 +Private.OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} + +----------------------------------------------------------------------------- +-- Packet creation functions +----------------------------------------------------------------------------- +function Private.RRQ(source, mode) + return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0) +end + +function Private.WRQ(source, mode) + return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0) +end + +function Private.ACK(block) + local low, high + low = math.mod(block, 256) + high = (block - low)/256 + return char(0, Private.OP_ACK, high, low) +end + +function Private.get_OP(dgram) + local op = byte(dgram, 1)*256 + byte(dgram, 2) + return op +end + +----------------------------------------------------------------------------- +-- Packet analysis functions +----------------------------------------------------------------------------- +function Private.split_DATA(dgram) + local block = byte(dgram, 3)*256 + byte(dgram, 4) + local data = string.sub(dgram, 5) + return block, data +end + +function Private.get_ERROR(dgram) + local code = byte(dgram, 3)*256 + byte(dgram, 4) + local msg + _,_, msg = string.find(dgram, "(.*)\000", 5) + return string.format("error code %d: %s", code, msg) +end + +----------------------------------------------------------------------------- +-- Downloads and returns a file pointed to by url +----------------------------------------------------------------------------- +function Public.get(url) + local parsed = socket.url.parse(url, { + host = "", + port = Public.PORT, + path ="/", + scheme = "tftp" + }) + if parsed.scheme ~= "tftp" then + return nil, string.format("unknown scheme '%s'", parsed.scheme) + end + local retries, dgram, sent, datahost, dataport, code + local cat = socket.concat.create() + local last = 0 + local udp, err = socket.udp() + if not udp then return nil, err end + -- convert from name to ip if needed + parsed.host = socket.toip(parsed.host) + udp:timeout(1) + -- first packet gives data host/port to be used for data transfers + retries = 0 + repeat + sent, err = udp:sendto(Private.RRQ(parsed.path, "octet"), + parsed.host, parsed.port) + if err then return nil, err end + dgram, datahost, dataport = udp:receivefrom() + retries = retries + 1 + until dgram or datahost ~= "timeout" or retries > 5 + if not dgram then return nil, datahost end + -- associate socket with data host/port + udp:setpeername(datahost, dataport) + -- process all data packets + while 1 do + -- decode packet + code = Private.get_OP(dgram) + if code == Private.OP_ERROR then + return nil, Private.get_ERROR(dgram) + end + if code ~= Private.OP_DATA then + return nil, "unhandled opcode " .. code + end + -- get data packet parts + local block, data = Private.split_DATA(dgram) + -- if not repeated, write + if block == last+1 then + cat:addstring(data) + last = block + end + -- last packet brings less than 512 bytes of data + if string.len(data) < 512 then + sent, err = udp:send(Private.ACK(block)) + return cat:getresult() + end + -- get the next packet + retries = 0 + repeat + sent, err = udp:send(Private.ACK(last)) + if err then return err end + dgram, err = udp:receive() + retries = retries + 1 + until dgram or err ~= "timeout" or retries > 5 + if not dgram then return err end + end +end -- cgit v1.2.3-55-g6feb