diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2003-05-25 01:54:13 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2003-05-25 01:54:13 +0000 |
commit | 0f6c8d50a99997ac7829864b1c93362b50f1bbf3 (patch) | |
tree | d0cefe3a05484e65b7b7e79d8cae4a1d2e6d19fb /etc | |
parent | c1ef3e7103cc652d2004ef1ddc9409b946207f33 (diff) | |
download | luasocket-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.lua | 131 |
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 | |||
9 | local Public, Private = {}, {} | ||
10 | local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace | ||
11 | socket.tftp = Public -- create tftp sub namespace | ||
12 | |||
13 | ----------------------------------------------------------------------------- | ||
14 | -- Program constants | ||
15 | ----------------------------------------------------------------------------- | ||
16 | local char = string.char | ||
17 | local byte = string.byte | ||
18 | |||
19 | Public.PORT = 69 | ||
20 | Private.OP_RRQ = 1 | ||
21 | Private.OP_WRQ = 2 | ||
22 | Private.OP_DATA = 3 | ||
23 | Private.OP_ACK = 4 | ||
24 | Private.OP_ERROR = 5 | ||
25 | Private.OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} | ||
26 | |||
27 | ----------------------------------------------------------------------------- | ||
28 | -- Packet creation functions | ||
29 | ----------------------------------------------------------------------------- | ||
30 | function Private.RRQ(source, mode) | ||
31 | return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0) | ||
32 | end | ||
33 | |||
34 | function Private.WRQ(source, mode) | ||
35 | return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0) | ||
36 | end | ||
37 | |||
38 | function 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) | ||
43 | end | ||
44 | |||
45 | function Private.get_OP(dgram) | ||
46 | local op = byte(dgram, 1)*256 + byte(dgram, 2) | ||
47 | return op | ||
48 | end | ||
49 | |||
50 | ----------------------------------------------------------------------------- | ||
51 | -- Packet analysis functions | ||
52 | ----------------------------------------------------------------------------- | ||
53 | function 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 | ||
57 | end | ||
58 | |||
59 | function 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) | ||
64 | end | ||
65 | |||
66 | ----------------------------------------------------------------------------- | ||
67 | -- Downloads and returns a file pointed to by url | ||
68 | ----------------------------------------------------------------------------- | ||
69 | function 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 | ||
131 | end | ||