aboutsummaryrefslogtreecommitdiff
path: root/etc
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2003-05-25 01:54:13 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2003-05-25 01:54:13 +0000
commit0f6c8d50a99997ac7829864b1c93362b50f1bbf3 (patch)
treed0cefe3a05484e65b7b7e79d8cae4a1d2e6d19fb /etc
parentc1ef3e7103cc652d2004ef1ddc9409b946207f33 (diff)
downloadluasocket-0f6c8d50a99997ac7829864b1c93362b50f1bbf3.tar.gz
luasocket-0f6c8d50a99997ac7829864b1c93362b50f1bbf3.tar.bz2
luasocket-0f6c8d50a99997ac7829864b1c93362b50f1bbf3.zip
Porting to LUA 5.0 final
Diffstat (limited to 'etc')
-rw-r--r--etc/tftp.lua131
1 files changed, 131 insertions, 0 deletions
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 @@
1-----------------------------------------------------------------------------
2-- TFTP support for the Lua language
3-- LuaSocket 1.5 toolkit.
4-- Author: Diego Nehab
5-- Conforming to: RFC 783, LTN7
6-- RCS ID: $Id$
7-----------------------------------------------------------------------------
8
9local Public, Private = {}, {}
10local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace
11socket.tftp = Public -- create tftp sub namespace
12
13-----------------------------------------------------------------------------
14-- Program constants
15-----------------------------------------------------------------------------
16local char = string.char
17local byte = string.byte
18
19Public.PORT = 69
20Private.OP_RRQ = 1
21Private.OP_WRQ = 2
22Private.OP_DATA = 3
23Private.OP_ACK = 4
24Private.OP_ERROR = 5
25Private.OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"}
26
27-----------------------------------------------------------------------------
28-- Packet creation functions
29-----------------------------------------------------------------------------
30function Private.RRQ(source, mode)
31 return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0)
32end
33
34function Private.WRQ(source, mode)
35 return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0)
36end
37
38function Private.ACK(block)
39 local low, high
40 low = math.mod(block, 256)
41 high = (block - low)/256
42 return char(0, Private.OP_ACK, high, low)
43end
44
45function Private.get_OP(dgram)
46 local op = byte(dgram, 1)*256 + byte(dgram, 2)
47 return op
48end
49
50-----------------------------------------------------------------------------
51-- Packet analysis functions
52-----------------------------------------------------------------------------
53function Private.split_DATA(dgram)
54 local block = byte(dgram, 3)*256 + byte(dgram, 4)
55 local data = string.sub(dgram, 5)
56 return block, data
57end
58
59function Private.get_ERROR(dgram)
60 local code = byte(dgram, 3)*256 + byte(dgram, 4)
61 local msg
62 _,_, msg = string.find(dgram, "(.*)\000", 5)
63 return string.format("error code %d: %s", code, msg)
64end
65
66-----------------------------------------------------------------------------
67-- Downloads and returns a file pointed to by url
68-----------------------------------------------------------------------------
69function Public.get(url)
70 local parsed = socket.url.parse(url, {
71 host = "",
72 port = Public.PORT,
73 path ="/",
74 scheme = "tftp"
75 })
76 if parsed.scheme ~= "tftp" then
77 return nil, string.format("unknown scheme '%s'", parsed.scheme)
78 end
79 local retries, dgram, sent, datahost, dataport, code
80 local cat = socket.concat.create()
81 local last = 0
82 local udp, err = socket.udp()
83 if not udp then return nil, err end
84 -- convert from name to ip if needed
85 parsed.host = socket.toip(parsed.host)
86 udp:timeout(1)
87 -- first packet gives data host/port to be used for data transfers
88 retries = 0
89 repeat
90 sent, err = udp:sendto(Private.RRQ(parsed.path, "octet"),
91 parsed.host, parsed.port)
92 if err then return nil, err end
93 dgram, datahost, dataport = udp:receivefrom()
94 retries = retries + 1
95 until dgram or datahost ~= "timeout" or retries > 5
96 if not dgram then return nil, datahost end
97 -- associate socket with data host/port
98 udp:setpeername(datahost, dataport)
99 -- process all data packets
100 while 1 do
101 -- decode packet
102 code = Private.get_OP(dgram)
103 if code == Private.OP_ERROR then
104 return nil, Private.get_ERROR(dgram)
105 end
106 if code ~= Private.OP_DATA then
107 return nil, "unhandled opcode " .. code
108 end
109 -- get data packet parts
110 local block, data = Private.split_DATA(dgram)
111 -- if not repeated, write
112 if block == last+1 then
113 cat:addstring(data)
114 last = block
115 end
116 -- last packet brings less than 512 bytes of data
117 if string.len(data) < 512 then
118 sent, err = udp:send(Private.ACK(block))
119 return cat:getresult()
120 end
121 -- get the next packet
122 retries = 0
123 repeat
124 sent, err = udp:send(Private.ACK(last))
125 if err then return err end
126 dgram, err = udp:receive()
127 retries = retries + 1
128 until dgram or err ~= "timeout" or retries > 5
129 if not dgram then return err end
130 end
131end