Mercurial > luasocket
comparison etc/tftp.lua @ 0:4b915342e2a8
LuaSocket 2.0.2 + CMake build description.
author | Eric Wing <ewing . public |-at-| gmail . com> |
---|---|
date | Tue, 26 Aug 2008 18:40:01 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4b915342e2a8 |
---|---|
1 ----------------------------------------------------------------------------- | |
2 -- TFTP support for the Lua language | |
3 -- LuaSocket toolkit. | |
4 -- Author: Diego Nehab | |
5 -- RCS ID: $Id: tftp.lua,v 1.16 2005/11/22 08:33:29 diego Exp $ | |
6 ----------------------------------------------------------------------------- | |
7 | |
8 ----------------------------------------------------------------------------- | |
9 -- Load required files | |
10 ----------------------------------------------------------------------------- | |
11 local base = _G | |
12 local table = require("table") | |
13 local math = require("math") | |
14 local string = require("string") | |
15 local socket = require("socket") | |
16 local ltn12 = require("ltn12") | |
17 local url = require("socket.url") | |
18 module("socket.tftp") | |
19 | |
20 ----------------------------------------------------------------------------- | |
21 -- Program constants | |
22 ----------------------------------------------------------------------------- | |
23 local char = string.char | |
24 local byte = string.byte | |
25 | |
26 PORT = 69 | |
27 local OP_RRQ = 1 | |
28 local OP_WRQ = 2 | |
29 local OP_DATA = 3 | |
30 local OP_ACK = 4 | |
31 local OP_ERROR = 5 | |
32 local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"} | |
33 | |
34 ----------------------------------------------------------------------------- | |
35 -- Packet creation functions | |
36 ----------------------------------------------------------------------------- | |
37 local function RRQ(source, mode) | |
38 return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) | |
39 end | |
40 | |
41 local function WRQ(source, mode) | |
42 return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0) | |
43 end | |
44 | |
45 local function ACK(block) | |
46 local low, high | |
47 low = math.mod(block, 256) | |
48 high = (block - low)/256 | |
49 return char(0, OP_ACK, high, low) | |
50 end | |
51 | |
52 local function get_OP(dgram) | |
53 local op = byte(dgram, 1)*256 + byte(dgram, 2) | |
54 return op | |
55 end | |
56 | |
57 ----------------------------------------------------------------------------- | |
58 -- Packet analysis functions | |
59 ----------------------------------------------------------------------------- | |
60 local function split_DATA(dgram) | |
61 local block = byte(dgram, 3)*256 + byte(dgram, 4) | |
62 local data = string.sub(dgram, 5) | |
63 return block, data | |
64 end | |
65 | |
66 local function get_ERROR(dgram) | |
67 local code = byte(dgram, 3)*256 + byte(dgram, 4) | |
68 local msg | |
69 _,_, msg = string.find(dgram, "(.*)\000", 5) | |
70 return string.format("error code %d: %s", code, msg) | |
71 end | |
72 | |
73 ----------------------------------------------------------------------------- | |
74 -- The real work | |
75 ----------------------------------------------------------------------------- | |
76 local function tget(gett) | |
77 local retries, dgram, sent, datahost, dataport, code | |
78 local last = 0 | |
79 socket.try(gett.host, "missing host") | |
80 local con = socket.try(socket.udp()) | |
81 local try = socket.newtry(function() con:close() end) | |
82 -- convert from name to ip if needed | |
83 gett.host = try(socket.dns.toip(gett.host)) | |
84 con:settimeout(1) | |
85 -- first packet gives data host/port to be used for data transfers | |
86 local path = string.gsub(gett.path or "", "^/", "") | |
87 path = url.unescape(path) | |
88 retries = 0 | |
89 repeat | |
90 sent = try(con:sendto(RRQ(path, "octet"), gett.host, gett.port)) | |
91 dgram, datahost, dataport = con:receivefrom() | |
92 retries = retries + 1 | |
93 until dgram or datahost ~= "timeout" or retries > 5 | |
94 try(dgram, datahost) | |
95 -- associate socket with data host/port | |
96 try(con:setpeername(datahost, dataport)) | |
97 -- default sink | |
98 local sink = gett.sink or ltn12.sink.null() | |
99 -- process all data packets | |
100 while 1 do | |
101 -- decode packet | |
102 code = get_OP(dgram) | |
103 try(code ~= OP_ERROR, get_ERROR(dgram)) | |
104 try(code == OP_DATA, "unhandled opcode " .. code) | |
105 -- get data packet parts | |
106 local block, data = split_DATA(dgram) | |
107 -- if not repeated, write | |
108 if block == last+1 then | |
109 try(sink(data)) | |
110 last = block | |
111 end | |
112 -- last packet brings less than 512 bytes of data | |
113 if string.len(data) < 512 then | |
114 try(con:send(ACK(block))) | |
115 try(con:close()) | |
116 try(sink(nil)) | |
117 return 1 | |
118 end | |
119 -- get the next packet | |
120 retries = 0 | |
121 repeat | |
122 sent = try(con:send(ACK(last))) | |
123 dgram, err = con:receive() | |
124 retries = retries + 1 | |
125 until dgram or err ~= "timeout" or retries > 5 | |
126 try(dgram, err) | |
127 end | |
128 end | |
129 | |
130 local default = { | |
131 port = PORT, | |
132 path ="/", | |
133 scheme = "tftp" | |
134 } | |
135 | |
136 local function parse(u) | |
137 local t = socket.try(url.parse(u, default)) | |
138 socket.try(t.scheme == "tftp", "invalid scheme '" .. t.scheme .. "'") | |
139 socket.try(t.host, "invalid host") | |
140 return t | |
141 end | |
142 | |
143 local function sget(u) | |
144 local gett = parse(u) | |
145 local t = {} | |
146 gett.sink = ltn12.sink.table(t) | |
147 tget(gett) | |
148 return table.concat(t) | |
149 end | |
150 | |
151 get = socket.protect(function(gett) | |
152 if base.type(gett) == "string" then return sget(gett) | |
153 else return tget(gett) end | |
154 end) | |
155 |