aboutsummaryrefslogtreecommitdiff
path: root/etc
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-16 01:02:07 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-16 01:02:07 +0000
commitd46f7a09a768b146f2f3cdc9a6a50357832bd1c7 (patch)
treed4f7ca1e677d06446245691d5ece6dab51915f16 /etc
parent843a431ef98fd541d98fd3898463985d9bfcde28 (diff)
downloadluasocket-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.lua134
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
9local Public, Private = {}, {} 8-----------------------------------------------------------------------------
10local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace 9-- Load required files
11socket.tftp = Public -- create tftp sub namespace 10-----------------------------------------------------------------------------
11local socket = require("socket")
12local ltn12 = require("ltn12")
13local 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
16local char = string.char 23local char = string.char
17local byte = string.byte 24local byte = string.byte
18 25
19Public.PORT = 69 26PORT = 69
20Private.OP_RRQ = 1 27local OP_RRQ = 1
21Private.OP_WRQ = 2 28local OP_WRQ = 2
22Private.OP_DATA = 3 29local OP_DATA = 3
23Private.OP_ACK = 4 30local OP_ACK = 4
24Private.OP_ERROR = 5 31local OP_ERROR = 5
25Private.OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} 32local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"}
26 33
27----------------------------------------------------------------------------- 34-----------------------------------------------------------------------------
28-- Packet creation functions 35-- Packet creation functions
29----------------------------------------------------------------------------- 36-----------------------------------------------------------------------------
30function Private.RRQ(source, mode) 37local 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)
32end 39end
33 40
34function Private.WRQ(source, mode) 41local 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)
36end 43end
37 44
38function Private.ACK(block) 45local 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)
43end 50end
44 51
45function Private.get_OP(dgram) 52local 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
48end 55end
@@ -50,13 +57,13 @@ end
50----------------------------------------------------------------------------- 57-----------------------------------------------------------------------------
51-- Packet analysis functions 58-- Packet analysis functions
52----------------------------------------------------------------------------- 59-----------------------------------------------------------------------------
53function Private.split_DATA(dgram) 60local 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
57end 64end
58 65
59function Private.get_ERROR(dgram) 66local 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)
64end 71end
65 72
66----------------------------------------------------------------------------- 73-----------------------------------------------------------------------------
67-- Downloads and returns a file pointed to by url 74-- High level TFTP API
68----------------------------------------------------------------------------- 75-----------------------------------------------------------------------------
69function Public.get(url) 76local 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
131end 125end
126
127local default = {
128 port = PORT,
129 path ="/",
130 scheme = "tftp"
131}
132
133local 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
138end
139
140local 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)
146end
147
148get = socket.protect(function(gett)
149 if type(gett) == "string" then return sget(gett)
150 else return tget(gett) end
151end)