diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-16 01:02:07 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-16 01:02:07 +0000 |
commit | d46f7a09a768b146f2f3cdc9a6a50357832bd1c7 (patch) | |
tree | d4f7ca1e677d06446245691d5ece6dab51915f16 /etc | |
parent | 843a431ef98fd541d98fd3898463985d9bfcde28 (diff) | |
download | luasocket-d46f7a09a768b146f2f3cdc9a6a50357832bd1c7.tar.gz luasocket-d46f7a09a768b146f2f3cdc9a6a50357832bd1c7.tar.bz2 luasocket-d46f7a09a768b146f2f3cdc9a6a50357832bd1c7.zip |
Fixed smtp.lua loading.
Adjusted tftp module.
Added some comments.
Diffstat (limited to 'etc')
-rw-r--r-- | etc/tftp.lua | 134 |
1 files changed, 77 insertions, 57 deletions
diff --git a/etc/tftp.lua b/etc/tftp.lua index 4113034..5238de0 100644 --- a/etc/tftp.lua +++ b/etc/tftp.lua | |||
@@ -2,13 +2,20 @@ | |||
2 | -- TFTP support for the Lua language | 2 | -- TFTP support for the Lua language |
3 | -- LuaSocket toolkit. | 3 | -- LuaSocket toolkit. |
4 | -- Author: Diego Nehab | 4 | -- Author: Diego Nehab |
5 | -- Conforming to: RFC 783, LTN7 | ||
6 | -- RCS ID: $Id$ | 5 | -- RCS ID: $Id$ |
7 | ----------------------------------------------------------------------------- | 6 | ----------------------------------------------------------------------------- |
8 | 7 | ||
9 | local Public, Private = {}, {} | 8 | ----------------------------------------------------------------------------- |
10 | local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace | 9 | -- Load required files |
11 | socket.tftp = Public -- create tftp sub namespace | 10 | ----------------------------------------------------------------------------- |
11 | local socket = require("socket") | ||
12 | local ltn12 = require("ltn12") | ||
13 | local url = require("url") | ||
14 | |||
15 | ----------------------------------------------------------------------------- | ||
16 | -- Setup namespace | ||
17 | ----------------------------------------------------------------------------- | ||
18 | _LOADED["tftp"] = getfenv(1) | ||
12 | 19 | ||
13 | ----------------------------------------------------------------------------- | 20 | ----------------------------------------------------------------------------- |
14 | -- Program constants | 21 | -- Program constants |
@@ -16,33 +23,33 @@ socket.tftp = Public -- create tftp sub namespace | |||
16 | local char = string.char | 23 | local char = string.char |
17 | local byte = string.byte | 24 | local byte = string.byte |
18 | 25 | ||
19 | Public.PORT = 69 | 26 | PORT = 69 |
20 | Private.OP_RRQ = 1 | 27 | local OP_RRQ = 1 |
21 | Private.OP_WRQ = 2 | 28 | local OP_WRQ = 2 |
22 | Private.OP_DATA = 3 | 29 | local OP_DATA = 3 |
23 | Private.OP_ACK = 4 | 30 | local OP_ACK = 4 |
24 | Private.OP_ERROR = 5 | 31 | local OP_ERROR = 5 |
25 | Private.OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} | 32 | local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} |
26 | 33 | ||
27 | ----------------------------------------------------------------------------- | 34 | ----------------------------------------------------------------------------- |
28 | -- Packet creation functions | 35 | -- Packet creation functions |
29 | ----------------------------------------------------------------------------- | 36 | ----------------------------------------------------------------------------- |
30 | function Private.RRQ(source, mode) | 37 | local function RRQ(source, mode) |
31 | return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0) | 38 | return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) |
32 | end | 39 | end |
33 | 40 | ||
34 | function Private.WRQ(source, mode) | 41 | local function WRQ(source, mode) |
35 | return char(0, Private.OP_RRQ) .. source .. char(0) .. mode .. char(0) | 42 | return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) |
36 | end | 43 | end |
37 | 44 | ||
38 | function Private.ACK(block) | 45 | local function ACK(block) |
39 | local low, high | 46 | local low, high |
40 | low = math.mod(block, 256) | 47 | low = math.mod(block, 256) |
41 | high = (block - low)/256 | 48 | high = (block - low)/256 |
42 | return char(0, Private.OP_ACK, high, low) | 49 | return char(0, OP_ACK, high, low) |
43 | end | 50 | end |
44 | 51 | ||
45 | function Private.get_OP(dgram) | 52 | local function get_OP(dgram) |
46 | local op = byte(dgram, 1)*256 + byte(dgram, 2) | 53 | local op = byte(dgram, 1)*256 + byte(dgram, 2) |
47 | return op | 54 | return op |
48 | end | 55 | end |
@@ -50,13 +57,13 @@ end | |||
50 | ----------------------------------------------------------------------------- | 57 | ----------------------------------------------------------------------------- |
51 | -- Packet analysis functions | 58 | -- Packet analysis functions |
52 | ----------------------------------------------------------------------------- | 59 | ----------------------------------------------------------------------------- |
53 | function Private.split_DATA(dgram) | 60 | local function split_DATA(dgram) |
54 | local block = byte(dgram, 3)*256 + byte(dgram, 4) | 61 | local block = byte(dgram, 3)*256 + byte(dgram, 4) |
55 | local data = string.sub(dgram, 5) | 62 | local data = string.sub(dgram, 5) |
56 | return block, data | 63 | return block, data |
57 | end | 64 | end |
58 | 65 | ||
59 | function Private.get_ERROR(dgram) | 66 | local function get_ERROR(dgram) |
60 | local code = byte(dgram, 3)*256 + byte(dgram, 4) | 67 | local code = byte(dgram, 3)*256 + byte(dgram, 4) |
61 | local msg | 68 | local msg |
62 | _,_, msg = string.find(dgram, "(.*)\000", 5) | 69 | _,_, msg = string.find(dgram, "(.*)\000", 5) |
@@ -64,68 +71,81 @@ function Private.get_ERROR(dgram) | |||
64 | end | 71 | end |
65 | 72 | ||
66 | ----------------------------------------------------------------------------- | 73 | ----------------------------------------------------------------------------- |
67 | -- Downloads and returns a file pointed to by url | 74 | -- High level TFTP API |
68 | ----------------------------------------------------------------------------- | 75 | ----------------------------------------------------------------------------- |
69 | function Public.get(url) | 76 | local function tget(gett) |
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 | 77 | local retries, dgram, sent, datahost, dataport, code |
80 | local cat = socket.concat.create() | ||
81 | local last = 0 | 78 | local last = 0 |
82 | local udp, err = socket.udp() | 79 | local con = socket.try(socket.udp()) |
83 | if not udp then return nil, err end | ||
84 | -- convert from name to ip if needed | 80 | -- convert from name to ip if needed |
85 | parsed.host = socket.dns.toip(parsed.host) | 81 | gett.host = socket.try(socket.dns.toip(gett.host)) |
86 | udp:settimeout(1) | 82 | con:settimeout(1) |
87 | -- first packet gives data host/port to be used for data transfers | 83 | -- first packet gives data host/port to be used for data transfers |
88 | retries = 0 | 84 | retries = 0 |
89 | repeat | 85 | repeat |
90 | sent, err = udp:sendto(Private.RRQ(parsed.path, "octet"), | 86 | sent = socket.try(con:sendto(RRQ(gett.path, "octet"), |
91 | parsed.host, parsed.port) | 87 | gett.host, gett.port)) |
92 | if err then return nil, err end | 88 | dgram, datahost, dataport = con:receivefrom() |
93 | dgram, datahost, dataport = udp:receivefrom() | ||
94 | retries = retries + 1 | 89 | retries = retries + 1 |
95 | until dgram or datahost ~= "timeout" or retries > 5 | 90 | until dgram or datahost ~= "timeout" or retries > 5 |
96 | if not dgram then return nil, datahost end | 91 | socket.try(dgram, datahost) |
97 | -- associate socket with data host/port | 92 | -- associate socket with data host/port |
98 | udp:setpeername(datahost, dataport) | 93 | socket.try(con:setpeername(datahost, dataport)) |
94 | -- default sink | ||
95 | local sink = gett.sink or ltn12.sink.null() | ||
99 | -- process all data packets | 96 | -- process all data packets |
100 | while 1 do | 97 | while 1 do |
101 | -- decode packet | 98 | -- decode packet |
102 | code = Private.get_OP(dgram) | 99 | code = get_OP(dgram) |
103 | if code == Private.OP_ERROR then | 100 | socket.try(code ~= OP_ERROR, get_ERROR(dgram)) |
104 | return nil, Private.get_ERROR(dgram) | 101 | socket.try(code == OP_DATA, "unhandled opcode " .. code) |
105 | end | ||
106 | if code ~= Private.OP_DATA then | ||
107 | return nil, "unhandled opcode " .. code | ||
108 | end | ||
109 | -- get data packet parts | 102 | -- get data packet parts |
110 | local block, data = Private.split_DATA(dgram) | 103 | local block, data = split_DATA(dgram) |
111 | -- if not repeated, write | 104 | -- if not repeated, write |
112 | if block == last+1 then | 105 | if block == last+1 then |
113 | cat:addstring(data) | 106 | socket.try(sink(data)) |
114 | last = block | 107 | last = block |
115 | end | 108 | end |
116 | -- last packet brings less than 512 bytes of data | 109 | -- last packet brings less than 512 bytes of data |
117 | if string.len(data) < 512 then | 110 | if string.len(data) < 512 then |
118 | sent, err = udp:send(Private.ACK(block)) | 111 | socket.try(con:send(ACK(block))) |
119 | return cat:getresult() | 112 | socket.try(con:close()) |
113 | socket.try(sink(nil)) | ||
114 | return 1 | ||
120 | end | 115 | end |
121 | -- get the next packet | 116 | -- get the next packet |
122 | retries = 0 | 117 | retries = 0 |
123 | repeat | 118 | repeat |
124 | sent, err = udp:send(Private.ACK(last)) | 119 | sent = socket.try(con:send(ACK(last))) |
125 | if err then return err end | 120 | dgram, err = con:receive() |
126 | dgram, err = udp:receive() | ||
127 | retries = retries + 1 | 121 | retries = retries + 1 |
128 | until dgram or err ~= "timeout" or retries > 5 | 122 | until dgram or err ~= "timeout" or retries > 5 |
129 | if not dgram then return err end | 123 | socket.try(dgram, err) |
130 | end | 124 | end |
131 | end | 125 | end |
126 | |||
127 | local default = { | ||
128 | port = PORT, | ||
129 | path ="/", | ||
130 | scheme = "tftp" | ||
131 | } | ||
132 | |||
133 | local function parse(u) | ||
134 | local t = socket.try(url.parse(u, default)) | ||
135 | socket.try(t.scheme == "tftp", "invalid scheme '" .. t.scheme .. "'") | ||
136 | socket.try(t.host, "invalid host") | ||
137 | return t | ||
138 | end | ||
139 | |||
140 | local function sget(u) | ||
141 | local gett = parse(u) | ||
142 | local t = {} | ||
143 | gett.sink = ltn12.sink.table(t) | ||
144 | tget(gett) | ||
145 | return table.concat(t) | ||
146 | end | ||
147 | |||
148 | get = socket.protect(function(gett) | ||
149 | if type(gett) == "string" then return sget(gett) | ||
150 | else return tget(gett) end | ||
151 | end) | ||