├── LuaService.exe
├── binstd.dll
├── info.url
├── init.lua
├── install.cmd
├── lua
├── compact_encoding.lua
├── dht.lua
├── encoding.lua
├── g2.lua
├── http.lua
├── ltn12.lua
├── magnet_to_uri_res.lua
├── mime.lua
├── nodes.lua
├── nodes2.lua
├── print_override.lua
├── serialize2.lua
├── serialize3.lua
├── socket.lua
├── socket
│ ├── ftp.lua
│ ├── http.lua
│ ├── smtp.lua
│ ├── tp.lua
│ └── url.lua
├── string2.lua
├── torred.lua
└── util.lua
├── lua5.1.dll
├── lua5.1.exe
├── magnet_to_uri_res.cmd
├── msgs.txt
├── nodes.tbl
├── rhash.exe
├── settings.lua
├── socket
└── core.dll
├── start.cmd
└── uninstall.cmd
/LuaService.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/LuaService.exe
--------------------------------------------------------------------------------
/binstd.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/binstd.dll
--------------------------------------------------------------------------------
/info.url:
--------------------------------------------------------------------------------
1 | [InternetShortcut]
2 | URL=http://localhost:6543/
3 | Modified=C028B49309F9CA01FC
4 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | -- init.lua for the LuaDHT service
2 | return {
3 | tracelevel = 0, -- Framework trace level
4 | name = "LuaDHT", -- Service name for SCM
5 | script = "lua/dht.lua", -- Script that runs the service
6 | }
--------------------------------------------------------------------------------
/install.cmd:
--------------------------------------------------------------------------------
1 | %~d0
2 | cd %~p0
3 | copy nodes.tbl "%SYSTEMROOT%\system32\nodes.tbl"
4 | LuaService -i
5 | info.url
6 | pause
--------------------------------------------------------------------------------
/lua/compact_encoding.lua:
--------------------------------------------------------------------------------
1 | function decode_ip(data, start_index)
2 | local start_index = start_index or 1
3 | return ( data:byte(start_index).."."..data:byte(start_index+1).."."..data:byte(start_index+2).."."..data:byte(start_index+3) )
4 | end
5 |
6 | function decode_port(data, start_index)
7 | local start_index = start_index or 1
8 | return ( data:byte(start_index)*256 + data:byte(start_index+1) )
9 | end
10 |
11 | function decode_peer(data, start_index)
12 | local start_index = start_index or 1
13 |
14 | return decode_ip(data, start_index), decode_port(data, start_index + 4)
15 | end
16 |
17 | function read_ip(ip)
18 | return string.match(ip, "([0-9]+).([0-9]+).([0-9]+).([0-9]+)")
19 | end
20 |
21 | function ipv4_array(address)
22 | local ip = {}
23 | ip[1], ip[2], ip[3], ip[4] = read_ip(address)
24 | if ip[1] then
25 | ip[1], ip[2], ip[3], ip[4] = tonumber(ip[1]), tonumber(ip[2]), tonumber(ip[3]), tonumber(ip[4])
26 | return ip
27 | end
28 | end
29 |
30 | function encode_ipv4(ip, swap)
31 | local a1, a2, a3, a4 = read_ip(ip)
32 | if swap then
33 | return string.char(a4,a3,a2,a1)
34 | else
35 | return string.char(a1,a2,a3,a4)
36 | end
37 | end
38 |
39 | function encode_port(port, le)
40 | local p1 = math.mod(port, 256)
41 | local p2 = (port - p1) / 256
42 | if le then p1, p2 = p2, p1 end
43 | return string.char(p2, p1)
44 | end
45 |
46 |
47 | function encode_peer_le(address, port)
48 | return encode_ipv4(address)..encode_port(port, true)
49 | end
50 |
51 | function encode_peer(address, port, le)
52 | return encode_ipv4(address)..encode_port(port)
53 | end
54 |
55 | function decode_node(data, node_index)
56 | node_index = node_index or 1
57 | local s = (26 * node_index) - 25
58 | if #data < (s + 25) then return nil end
59 | return data:sub(s, s + 19), decode_peer(data, s+20)
60 | end
61 |
62 | function encode_node(node)
63 | return node.id..encode_peer(node.address, node.port)
64 | end
--------------------------------------------------------------------------------
/lua/dht.lua:
--------------------------------------------------------------------------------
1 | require("settings")
2 | require("print_override")
3 |
4 | require("socket")
5 | require("encoding")
6 | require("torred")
7 | require("serialize3")
8 | require("compact_encoding")
9 | require("nodes")
10 | require("http")
11 | require("util")
12 |
13 | function statistic_distance(id1 , id2)
14 | return xor(id1:byte(1), id2:byte(1)) * (256^3) + xor(id1:byte(2), id2:byte(2)) * (256^2) + xor(id1:byte(3), id2:byte(3)) * 256 + xor(id1:byte(4), id2:byte(4))
15 | end
16 |
17 | local help = {
18 | ["a.name"]="name of torrent",
19 | ["q"]=[=[q=[help, ping, find_node, get_peers, announce_peer]
20 | help - Micro help about new parameter of dht packet.
21 | a.path (query packet) - path to new parameter
22 | r.help (responce packet) - help about new parameter
23 |
24 | more: http://forum.bittorrent.org/viewtopic.php?pid=2134
25 |
26 | other see on http://bittorrent.org/beps/bep_0005.html]=],
27 | ["a.path"]=[[if (query packet) and (q="help") then it path to new parameter]],
28 | ["r.help"]=[[if (responce packet) and (q="help") then it help text about new parameter]],
29 | ["r.ip"]=[[your ip]]
30 | }
31 |
32 | function get_help(path)
33 | if type(path)== "table" then
34 | path = table.concat(path, ".")
35 | end
36 | return help[path]
37 | end
38 |
39 | math.randomseed(os.time())
40 |
41 | my_ip = ""
42 | my_id = random_part(9).."<<< ^-^ >>>"
43 |
44 |
45 | --my_id_plus = {my_id, "\0"..my_id:sub(2,20), "\127"..my_id:sub(2,20), "\255"..my_id:sub(2,20)}
46 |
47 | peers = {btih={}}
48 | torrent_info = {}
49 | search_btih = {}
50 | announce_btih = {}
51 |
52 | in_packet = 0
53 | packet_id = 0
54 |
55 |
56 | -----------------------------------------------------------------------------
57 | function main()
58 | local loader, err = loadfile(nodes_file)
59 | if not loader then welcome_msg = welcome_msg.."\n"..err end
60 | nodes = (loader and loader()) or nodes
61 | local timer = new_timer()
62 | local save_timer = new_timer()
63 | local check_timer = new_timer()
64 | local random_timer = new_timer()
65 | local udp_port = socket.udp()
66 | udp_port:setsockname("*", port_number)
67 | udp_port:settimeout(1)
68 |
69 | local tcp_port = socket.bind("127.0.0.1", port_number)
70 | tcp_port:settimeout(0.001)
71 |
72 | function on_error()
73 | if udp_port then udp_port:close() end
74 | if tcp_port then tcp_port:close() end
75 | end
76 |
77 | print(welcome_msg)
78 | local cycle = 0
79 | while not ( service and service.stopping() ) do
80 | cycle = cycle + 1
81 | status_print(cycle)
82 | next_packet(udp_port)
83 | next_client(tcp_port, udp_port)
84 | if not service and save_timer(5*60) then
85 | if mgs_on then mid_print("--- save nodes ---") end
86 | save_nodes()
87 | end
88 | if ( in_packet > 10 ) and not nodes_clear then
89 | if mgs_on then mid_print("--- nodes clear on ---") end
90 | nodes_clear = true
91 | end
92 | if random_timer(30) then
93 | if mgs_on then mid_print("--- random bootstrap ---") end
94 | bootstrap(udp_port, random_part(20), true)
95 | collectgarbage()
96 | end
97 | if timer(12) then
98 | if mgs_on then mid_print("--- bootstrap ---") end
99 | bootstrap(udp_port, my_id)
100 | search(udp_port)
101 | announce(udp_port)
102 | end
103 | end
104 |
105 | mid_print("--- save nodes ---")
106 | save_nodes()
107 | udp_port:close()
108 | tcp_port:close()
109 | end
110 |
111 | local cut_list = {128, 64, 32, 16, 8, 4, 2, 1}
112 | function number_cut(number, bits_len)
113 | for i, n in ipairs(cut_list) do
114 | if (8-i >= bits_len) and (number>=n) then
115 | number = number - n
116 | elseif (8-i < bits_len) then
117 | break
118 | end
119 | end
120 | return number
121 | end
122 |
123 | local r = math.random(0, 7)
124 |
125 | function calc_idv4(ip_raw, r)
126 | local ip_cut = {string.byte(ip_raw, 1, 4)}
127 | ip_cut[1] = "\\"..math.fmod(ip_cut[1], 2)
128 | ip_cut[2] = "\\"..number_cut(ip_cut[2], 3)
129 | ip_cut[3] = "\\"..number_cut(ip_cut[3], 5)
130 | ip_cut[4] = "\\"..number_cut(ip_cut[4], 7)
131 | ip_cut[5] = "\\"..r
132 | ip_cut = table.concat(ip_cut)
133 | local command = "lua5.1 -l binstd -e\"io.write('"..ip_cut.."')\"|rhash -p\"%@h\" -"
134 | local x = io.popen(command, "r")
135 | return x:read(4)
136 | end
137 |
138 | function change_id()
139 | if not service then
140 | my_id = calc_idv4(my_ip, r)..string.sub(my_id, 5, 19)..string.char(r)
141 | end
142 | end
143 |
144 | function by_last_seen(node1, node2)
145 | return (node1.last_seen or node1.added)
146 | < (node2.last_seen or node2.added)
147 | end
148 |
149 |
150 | function skip_value(tbl, key, value)
151 | --print(tostring(key))
152 | return (key == "sended") and (type(value) == "table") or
153 | (key == "bs_send") and (type(value) == "number") or
154 | (key == "last_query") and (type(value) == "table") or
155 | (key == "conected_to") and (type(value) == "string") or
156 | (key == "peer") and (type(value) == "string") or
157 | (key == "confirmed")
158 | end
159 |
160 | function save_nodes()
161 | local fnodes = io.open(nodes_file, "wb")
162 | local writer = function(data)
163 | fnodes:write(data)
164 | end
165 | writer("return ")
166 | serialize(nodes, writer, skip_value)
167 | --fnodes:write(serialize(nodes, "return", nil, nil, skip_value))
168 | fnodes:close()
169 | end
170 |
171 | function is_searched(check_time)
172 | if check_time then
173 | return (os.time() - check_time) < 3*60
174 | else
175 | return false
176 | end
177 | end
178 |
179 | function check_search_node(node)
180 | return not ( is_searched(node.gp_sended) or ( node.sended and node.sended.packet ))
181 | end
182 |
183 | function search(udp_port)
184 | for info_hash, count in pairs(search_btih) do
185 | if count > 0 then
186 | local selected_nodes = find_close_id(info_hash, 3, check_search_node)
187 |
188 | for index, node in ipairs(selected_nodes) do
189 | node.gp_sended = os.time()
190 | send_get_peers(udp_port, node, info_hash)
191 | end
192 |
193 | search_btih[info_hash] = count - 1
194 | else
195 | search_btih[info_hash] = nil
196 | end
197 | end
198 | end
199 |
200 | function check_announce_node(node, info_hash)
201 | return node.token[info_hash] and not ( node.sended and node.sended.packet )
202 | end
203 |
204 | function announce(udp_port)
205 | for info_hash, info in pairs( announce_btih ) do
206 | for node, token in pairs( info.token ) do
207 | send_announce(udp_port, node, info_hash, info.token[node], info.port)
208 | info.token[node] = nil
209 | end
210 | end
211 | end
212 |
213 | function announce_peer(info_hash, port)
214 | if port and port > 0 then
215 | if not announce_btih[info_hash] then
216 | announce_btih[info_hash] = { token = {} }
217 | end
218 | announce_btih[info_hash].port = port
219 | else
220 | print("port nil")
221 | end
222 | end
223 |
224 | function search_peers(info_hash)
225 | search_btih[info_hash] = 15
226 | end
227 |
228 | function get_peers(info_hash, port)
229 | search_peers(info_hash)
230 | announce_peer(info_hash, port)
231 | return peers.btih[info_hash]
232 | end
233 |
234 | function local_packet(krpc)
235 | if krpc.y == "q" then
236 | if krpc.q == "get_peers" then
237 | search_peers(krpc.a.info_hash)
238 | return { t = krpc.t, y = "r", r = {values=get_values(krpc.a.info_hash, true)} }
239 | elseif krpc.q == "announce_peer" then
240 | announce_peer(krpc.a.info_hash, krpc.a.port)
241 | end
242 | end
243 | end
244 |
245 | function next_packet(udp_port)
246 | local data, address, port = udp_port:receivefrom()
247 | if (not data) then
248 | if (address ~= "timeout" and address ~= "closed") then
249 | print("receivefrom error: "..address)
250 | debug.debug()
251 | end
252 | return
253 | end
254 |
255 | packet_id = packet_id + 1
256 | if not is_dictionary(data) then return end
257 | if mgs_on then status_print("from_bencode") end
258 | local krpc = from_bencode(data, true)
259 | if not krpc then return end
260 | if mgs_on then status_print("decoded") end
261 | if address == "127.0.0.1" then
262 | local ret = local_packet(krpc)
263 | if ret then
264 | udp_port:sendto(to_bencode(ret), address, port)
265 | end
266 | return
267 | end
268 |
269 | if packet_analyze(krpc) then
270 | --udp_port:sendto(data, address, port)
271 | end
272 |
273 | if krpc.y == "e" then
274 | local node = nodes.ap[address..":"..port]
275 | local sended = node and node.sended and node.sended.packet
276 | if sended and (sended.t == krpc.t) then
277 | node.sended.packet = nil
278 | node.sended.time = nil
279 | node.error = krpc.e
280 | end
281 | if type(krpc.e) == "table" then
282 | print_msg("node error:", krpc.e[1] or '', krpc.e[2] or '')
283 | end
284 | return
285 | end
286 |
287 | if not krpc.t then return end
288 |
289 | local id = (krpc.a and krpc.a.id) or (krpc.r and krpc.r.id)
290 | if not id then return end
291 |
292 | local node = add_node(id, address, port, add_node_check, true)
293 | if not node then return end
294 | node.last_seen = os.time()
295 |
296 | in_packet = in_packet + 1
297 |
298 | if krpc.y == "q" then -- query
299 | return on_query(udp_port, krpc, node)
300 | end
301 |
302 | local packet = node.sended and node.sended.packet
303 | if packet and (krpc.y == "r") and (packet.t == krpc.t) then -- responce
304 | node.sended.packet = nil
305 | node.sended.time = nil
306 | node.error = nil
307 | node.checked = os.time()
308 | return on_responce(krpc, packet, node)
309 | end
310 | end
311 |
312 | function check_node_bootstrap(node, no_linked)
313 | return (node.id ~= my_id)
314 | and (not (node.sended and node.sended.packet))
315 | and ((node.bs_send or 0) < (os.time() - 600))
316 | and ((not no_linked) or not node.last_seen)
317 | end
318 |
319 | function bootstrap(udp_port, id, no_linked)
320 | local selected_nodes = find_close_id(id, 4, check_node_bootstrap, {no_linked})
321 |
322 | for index, node in ipairs(selected_nodes) do
323 | if (not nodes.count) or (nodes.count < (500*index)) or (index == 1) then
324 | node.bs_send = os.time()
325 | send_find_node(udp_port, node, id)
326 | else
327 | send_ping(udp_port, node)
328 | end
329 | end
330 | end
331 |
332 | function is_checked(check_time)
333 | if check_time then
334 | return (os.time() - check_time) < 15*60
335 | else
336 | return false
337 | end
338 | end
339 |
340 | function get_node_token(node)
341 | return global_token.."("..encode_peer(node.address, node.port)..")"
342 | end
343 |
344 | function check_token(token, node)
345 | if mgs_on then
346 | print(string.format("token:\t%s\n client: %s",
347 | safe_string(token),
348 | (safe_string(node.client) or "")))
349 | end
350 | return token == get_node_token(node)
351 | end
352 |
353 | function check_node_on_query(node, for_node_id)
354 | return (node.id ~= my_id) and (for_node_id ~= node.id) and is_checked( node.last_seen )
355 | end
356 |
357 | packet_analyze = (function()
358 | -- local const
359 | local top_level_keys = {y = "string", e = "table", q = "string", t = "string", a = "table", r = "table", v = "string", ip = "string"}
360 | local tables_keys = {
361 | y = {q = true, r = true, e = true},
362 | q = {ping = true, find_node = true, get_peers = true, announce_peer = true, vote = true},
363 | a = {id = "string", target = "string", info_hash = "string", token = "string", port = "number", want = "table", scrape = "number", name = "string" --[[UTc`]], vote = "number" --[[UTct]], seed = "number" --[[Az]], noseed = "number" --[[LT\0\16]]},
364 | r = {id = "string", nodes = "string", nodes2 = "table", values = "table", token = "string", ip = "string", p = "number"},
365 | e = {[1] = "number", [2] = "string"}
366 | }
367 |
368 | return function(packet)
369 | if not packet_analyze_on then return end
370 | for tlk, v in pairs(packet) do
371 | if top_level_keys[tlk] == type(v) then
372 | local keys_types = tables_keys[tlk]
373 | if keys_types then
374 | if type(v) == "table" then
375 | for k, v in pairs(v) do
376 | if keys_types[k] ~= type(v) then
377 | print2('found key: '..safe_string(tlk)..'.'..safe_string(k))
378 | serialize(packet, nb_out)
379 | flush()
380 | return true
381 | end
382 | end
383 | elseif not keys_types[v] then
384 | print2('found value: '..safe_string(tlk)..' = '..safe_string(v))
385 | serialize(packet, nb_out)
386 | flush()
387 | return true
388 | end
389 | end
390 | else
391 | print2('found top level key: '..safe_string(tlk)..' = '..safe_string(v))
392 | serialize(packet, nb_out)
393 | flush()
394 | return true
395 | end
396 | end
397 | end
398 | end)()
399 |
400 | function get_values(info_hash, not_annonced)
401 | local peers_list = peers.btih[info_hash]
402 | if peers_list and next(peers_list) then
403 | local values = {}
404 | local peers_count = 0
405 | for compact, peer in pairs(peers_list) do
406 | if (os.time() - peer.last_seen) > 15*60 then
407 | peers_list[compact] = nil
408 | elseif not_annonced or peer.announce_node then
409 | peers_count = peers_count + 1
410 | table.insert(values, compact)
411 | end
412 | end
413 |
414 | if not next(values) then
415 | peers.btih[info_hash] = nil
416 | else
417 | return values, peers_count
418 | end
419 | end
420 | end
421 |
422 | function on_query(udp_port, query, node)
423 |
424 | local id = query.a.target or query.a.info_hash or query.a.id
425 | if not id then return end
426 |
427 | local tid = query.t
428 | if not tid then return end
429 |
430 | if not node.last_query then node.last_query = {} end
431 |
432 | if query.q ~= "announce_peer" and (os.time() - (node.last_query.time or 0) < 10) then
433 | return
434 | end
435 |
436 | node.client = query.v or node.client
437 | if mgs_on then
438 | print(string.format("%s\t\t\t> query (%s): %s, %s, %s ",
439 | packet_id,
440 | query.q,
441 | statistic_distance(id, my_id),
442 | safe_string(node.client) or "",
443 | ( (node.last_query.time and (os.time() - node.last_query.time)) or "first" )))
444 | end
445 | node.last_query.query = query.q
446 | node.last_query.id = id
447 | node.last_query.time = os.time()
448 |
449 |
450 | local krpc = { t = tid, y = "r", r = {} }
451 |
452 | if query.q == "ping" then
453 | if node.ping_responce and (os.time() - node.ping_responce < 60) then
454 | if mgs_on then
455 | print("last ping: "..(os.time() - node.ping_responce))
456 | end
457 | return
458 | end
459 | node.ping_responce = os.time()
460 | return send_krpc( udp_port, node, krpc)
461 | elseif query.q == "help" then
462 | krpc.r.help = get_help(query.a.path)
463 | if not krpc.r.help then
464 | return send_error( udp_port, node, 203, tid, "help not found" )
465 | end
466 | return send_krpc( udp_port, node, krpc )
467 | elseif query.q == "announce_peer" then
468 | if mgs_on then print(string.rep("-", 79)) end
469 | if check_token(query.a.token, node) then
470 | node.checked = os.time()
471 |
472 | add_peer(query.a.info_hash, node.address, query.a.port, node)
473 |
474 | add_info(query.a, node)
475 |
476 | if mgs_on then print(string.rep("-", 79)) end
477 | return send_krpc( udp_port, node, krpc )
478 | else
479 | if mgs_on then print(string.rep("-", 79)) end
480 | return send_error( udp_port, node, 203, tid, "wrong token" )
481 | end
482 | end
483 |
484 | -- find_node or get_peers --
485 |
486 | local selected_nodes = find_close_id(id, 8, check_node_on_query, {node.id})
487 |
488 | local compact_nodes = {}
489 | for index, node in ipairs(selected_nodes) do
490 | table.insert(compact_nodes, encode_node(node))
491 | end
492 |
493 | if next(compact_nodes) then
494 | krpc.r.nodes = table.concat(compact_nodes)
495 | else
496 | return send_error( udp_port, node, 201, tid, "no nodes" )
497 | end
498 |
499 | if query.q == "get_peers" then
500 | krpc.r.token = get_node_token(node)
501 | krpc.r.values = get_values(query.a.info_hash)
502 |
503 | if krpc.r.values then
504 | if mgs_on then print_msg("found peers:", #(krpc.r.values), hexenc(id)) end
505 | return send_krpc( udp_port, node, krpc, "responce peers" )
506 | end
507 | end
508 |
509 | return send_krpc( udp_port, node, krpc, "responce nodes" )
510 | end
511 |
512 | function add_node_check(id, address, port, node, self)
513 | if self then
514 | node.confirmed = true
515 | node.need_to_check = nil
516 | if node.id ~= id then
517 | node.ref = nil
518 | node.change_id = (node.change_id or 0) + 1
519 | print_msg(string.format("change id (%s)\n from: %s (%s)\n to: %s (%s)\n client: %s",
520 | node.change_id,
521 | hexenc(node.id),
522 | statistic_distance(node.id, my_id),
523 | hexenc(id),
524 | statistic_distance(id, my_id),
525 | safe_string(node.client) or ""))
526 | remove_node_id_ip(node)
527 | node.id = id
528 | elseif node.port ~= port then
529 | node.ref = nil
530 | node.change_port = (node.change_port or 0) + 1
531 | print_msg(string.format("change port (%s) from %s to %s\n for %s (%s)\n client: %s",
532 | node.change_port,
533 | node.port,
534 | port,
535 | hexenc(node.id),
536 | statistic_distance(node.id, my_id),
537 | safe_string(node.client) or ""))
538 |
539 | remove_node_ip_port(node)
540 | node.port = port
541 | end
542 | elseif node.id ~= id then
543 | local msg = "wrong"
544 | if not node.confirmed then
545 | msg = "found other"
546 | node.need_to_check = true
547 | end
548 |
549 | print_msg(string.format("%s id: %s (%s)\n for %s (%s)",
550 | msg,
551 | hexenc(id),
552 | statistic_distance(id, my_id),
553 | hexenc(node.id),
554 | statistic_distance(node.id, my_id)))
555 | elseif node.port ~= port then
556 | local msg = "wrong"
557 | if not node.confirmed then
558 | local msg = "found other"
559 | node.need_to_check = true
560 | end
561 | print_msg(string.format("%s port: %s for %s (%s)",
562 | msg,
563 | port,
564 | hexenc(node.id),
565 | statistic_distance(node.id, my_id)))
566 | end
567 |
568 | if not self then
569 | node.ref = (node.ref or 0) + 1
570 | end
571 | end
572 |
573 | function on_responce(responce, query, node)
574 | local id = query.a.target or query.a.info_hash
575 |
576 | if responce.v then
577 | node.client = responce.v
578 | end
579 |
580 | if responce.ip then
581 | if my_ip ~= responce.ip:sub(1,4) then
582 | my_ip = responce.ip:sub(1,4)
583 | change_id()
584 | if mgs_on then
585 | print(string.format("my_ip = %s", decode_ip(my_ip)))
586 | end
587 | end
588 | end
589 |
590 | --[[ if responce.r.ip and (my_ip ~= responce.r.ip) then
591 | my_ip = responce.r.ip
592 | change_id()
593 | if mgs_on then
594 | print(string.format("my_ip = %s", decode_ip(my_ip)))
595 | end
596 | end]]
597 |
598 | if mgs_on then
599 | print(string.format("%s\t\t\t> responce (%s): %s, %s",
600 | packet_id,
601 | query.q,
602 | statistic_distance(id or my_id, node.id),
603 | safe_string(node.client) or ""))
604 | end
605 |
606 | if (not id) or query.q == "ping" then
607 | free_krpc(query)
608 | return
609 | elseif query.q == "announce_peer" then
610 | node.announced = os.time()
611 | free_krpc(query)
612 | return
613 | elseif query.q == "get_peers" then
614 | if responce.r.token and announce_btih[id] then
615 | announce_btih[id].token[node] = responce.r.token
616 | end
617 | if responce.r.values then
618 | for i, v in pairs(responce.r.values) do
619 | if #v == 6 then
620 | add_peer(query.a.info_hash, decode_peer(v))
621 | end
622 | end
623 | end
624 | end
625 |
626 | if responce.r.nodes then
627 | count = #responce.r.nodes / 26
628 | for i = 1, count do
629 | local id, address, port = decode_node(responce.r.nodes, i)
630 | if id ~= my_id then
631 | local node1 = add_node(id, address, port, add_node_check)
632 | if node1 then
633 | node1.conected_to = node1.conected_to or node.id
634 | end
635 | end
636 | end
637 | end
638 |
639 | if responce.r.nodes2 then
640 | for i, compact in pairs(responce.r.nodes2) do
641 | if #compact == 26 then
642 | local id, address, port = decode_node(compact)
643 | if id ~= my_id then
644 | local node1 = add_node(id, address, port, add_node_check)
645 | if node1 then
646 | node1.conected_to = node1.conected_to or node.id
647 | end
648 | end
649 | end
650 | end
651 | end
652 |
653 | free_krpc(query)
654 | end
655 |
656 | function add_peer(id, address, port, announce_node)
657 |
658 | local peers_list = peers.btih[id]
659 | if not peers_list then
660 | peers_list = {}
661 | peers.btih[id] = peers_list
662 | end
663 |
664 | local compact = encode_peer(address, port)
665 | local peer = peers_list[compact]
666 | if not peer then
667 | if mgs_on then
668 | print(string.format("new peer:\t%s:%s\t%s", address, port, hexenc(id)))
669 | end
670 | peer = {}
671 | peers_list[compact] = peer
672 | end
673 |
674 | if announce_node then
675 | peer.announce_node = announce_node
676 | announce_node.peer = id
677 | end
678 |
679 | peer.last_seen = os.time()
680 |
681 | return peer
682 | end
683 |
684 | function add_info(info, node)
685 | if info.id then
686 | torrent_info[info.info_hash] = torrent_info[info.info_hash] or {}
687 | if info.name and #(info.name)>0 then
688 | torrent_info[info.info_hash].name = info.name
689 | end
690 | end
691 | end
692 |
693 | --[[ Send ]]--
694 |
695 |
696 | local send_krpc_const = {
697 | query = { t = "d", o = { "t", "y", "q", "a", "v" } },
698 | responce = { t = "d", o = { "t", "y", "r", "v" } },
699 | error = { t = "d", o = { "t", "y", "e", "v" } },
700 |
701 | ping = { t = "d", o = { "id" } },
702 | announce_q = { t = "d", o = { "id", "info_hash", "port", "token" } },
703 | find_node_q = { t = "d", o = { "id", "target", "want" } },
704 | get_peers_q = { t = "d", o = { "id", "info_hash", "want" } },
705 | responce_r = { t = "d" , o = { "id", "nodes", "values", "token" } }
706 | }
707 |
708 | function get_id(node)
709 | local id = my_id
710 | if my_id_plus then
711 | for x = 1, #my_id_plus do
712 | if less(my_id_plus[x], id, node.id) then
713 | id = my_id_plus[x]
714 | end
715 | end
716 | end
717 | return id
718 | end
719 |
720 | function send_krpc(udp_port, node, krpc)
721 |
722 | local const = send_krpc_const
723 |
724 | if krpc.e then
725 |
726 | elseif krpc.r then
727 | if not krpc.r.id then
728 | krpc.r.id = get_id(node)
729 | krpc.r.ip = encode_ipv4(node.address)
730 | end
731 | else
732 |
733 | if not krpc.a.id then
734 | krpc.a.id = get_id(node)
735 | end
736 | end
737 |
738 | if not krpc.v then krpc.v = script_id end
739 |
740 | if mgs_on then
741 | print(string.format("send ( %s )\t<",
742 | (krpc.q
743 | or (krpc.r and ((krpc.r.values and "values") or (krpc.r.nodes and "nodes") or "pong"))
744 | or (krpc.e and "error"))))
745 | end
746 |
747 | --packet_analyze(krpc)
748 |
749 | local data = to_bencode(krpc)
750 |
751 | if krpc.q then
752 | local sended = node.sended
753 | if not sended then
754 | sended = {}
755 | node.sended = sended
756 | end
757 | sended.packet = krpc
758 | sended.time = os.time()
759 | end
760 |
761 | udp_port:sendto( data , node.address, node.port )
762 | socket.sleep(0.03)
763 | end
764 |
765 | function get_random_tid()
766 | return string.char(math.random(0, 255))..string.char(math.random(0, 255))..string.char(math.random(0, 255))
767 | end
768 |
769 | function send_error(udp_port, node, number, tid, description)
770 | local krpc = { t = tid, y = "e", e = { nubmer, description or "unknown error" } }
771 | send_krpc(udp_port, node, krpc, "error")
772 | end
773 |
774 | local want = { "n4" }
775 | local krpc_buff = {find_node = {}, ping={}}
776 |
777 |
778 | function on_node_removed(node)
779 | if node.sended and node.sended.packet then
780 | free_krpc(node.sended.packet)
781 | end
782 | end
783 |
784 | function free_krpc(krpc)
785 | if krpc.q and krpc.q == "find_node" then
786 | krpc.a.target = nil
787 | table.insert(krpc_buff.find_node, krpc)
788 | --print("find_node_free:", #(krpc_buff.find_node))
789 | elseif krpc.q and krpc.q == "ping" then
790 | table.insert(krpc_buff.ping, krpc)
791 | --print("ping_free:", #(krpc_buff.ping))
792 | end
793 | end
794 |
795 | function send_ping(udp_port, node)
796 | local krpc = table.remove(krpc_buff.ping)
797 | if not krpc then
798 | krpc = { t = get_random_tid(), y = "q", q = "ping", a = {} }
799 | end
800 | send_krpc(udp_port, node, krpc)
801 | end
802 |
803 | function send_get_peers(udp_port, node, info_hash)
804 | local krpc = { t = get_random_tid(), y = "q", q = "get_peers", a = { info_hash = info_hash, want = want } }
805 | send_krpc(udp_port, node, krpc)
806 | end
807 |
808 | function send_find_node(udp_port, node, node_id)
809 | local krpc = table.remove(krpc_buff.find_node)
810 | if not krpc then
811 | krpc = { t = get_random_tid(), y = "q", q = "find_node", a = { want = want } }
812 | end
813 | krpc.a.target = node_id
814 | send_krpc(udp_port, node, krpc)
815 | end
816 |
817 | function send_announce(udp_port, node, info_hash, token, port)
818 | local krpc = { t = get_random_tid(), y = "q", q = "announce_peer",
819 | a = { info_hash = info_hash, port = port, token = token }
820 | }
821 | send_krpc(udp_port, node, krpc)
822 | end
823 | -----------------------
824 |
825 | for i = 1, 10 do
826 | local mainco = coroutine.create(main)
827 | local ok, err = coroutine.resume(mainco)
828 | if ok then
829 | break
830 | else
831 | errfile = io.open(error_log, "ab+")
832 | errfile:write("\n"..debug.traceback(mainco, err))
833 | errfile:close()
834 | on_error()
835 | end
836 | end
837 |
--------------------------------------------------------------------------------
/lua/encoding.lua:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/lua/encoding.lua
--------------------------------------------------------------------------------
/lua/g2.lua:
--------------------------------------------------------------------------------
1 | require("socket")
2 | require("serialize2")
3 | require("string2")
4 | require("compact_encoding")
5 |
6 | --bind = socket.bind("127.0.0.1", 6754)
7 |
8 | old_print, print = print, function(...)
9 | local text = table.concat(arg, "\t").."\n"
10 | io.stderr:write(text)
11 | io.stderr:flush()
12 | io.stdout:write(text)
13 | io.stdout:flush()
14 | end
15 |
16 | function bintonumber(binstr, big_endian)
17 | local number = 0
18 | local n = (big_endian and #binstr) or 1
19 | local m = (big_endian and 1) or (-1)
20 | for i = 1, #binstr do
21 | number = number + binstr:byte(i)*(256^(m*(n - i)))
22 | end
23 |
24 | return number
25 | end
26 |
27 | function numbertobin(number, big_endian)
28 | local bin = ""
29 | while (number > 0) do
30 | local last = math.mod(number, 256)
31 | number = (number - last) / 256
32 |
33 | if big_endian then
34 | bin = string.char(last) .. bin
35 | else
36 | bin = bin .. string.char(last)
37 | end
38 | end
39 |
40 | return bin
41 | end
42 |
43 | function readheader(client)
44 | local control_byte = client:receive(1):byte(1)
45 |
46 | if control_byte == 0 then return false, 1 end
47 |
48 | local packet = {
49 | big_endian = false,
50 | compound_packet = false,
51 | name_len = 1,
52 | len_len = 0
53 | }
54 |
55 | local cnt = 0
56 | while (control_byte > 0) do
57 | local bit = math.mod(control_byte, 2)
58 | control_byte = (control_byte - bit)/2
59 |
60 | if bit > 0 then
61 | if cnt == 0 then
62 | return nil, 1
63 | elseif cnt == 1 then
64 | packet.big_endian = true
65 | elseif cnt == 2 then
66 | packet.compound_packet = true
67 | elseif cnt >= 3 and cnt <= 5 then
68 | packet.name_len = packet.name_len + 2^(cnt-3)
69 | elseif cnt >= 6 and cnt <= 7 then
70 | packet.len_len = packet.len_len + 2^(cnt-6)
71 | end
72 | end
73 |
74 | cnt = cnt + 1
75 | end
76 |
77 | packet.len = 0
78 | if packet.len_len > 0 then
79 | local len = client:receive(packet.len_len)
80 | if not len then return end
81 | packet.len = bintonumber(len)
82 | end
83 |
84 | packet.name = client:receive(packet.name_len)
85 |
86 | print(packet.name, packet.len_len, packet.len)
87 |
88 | return packet, 1 + packet.len_len + packet.name_len
89 | end
90 |
91 | function readpacket(client)
92 | local packet, head_len = readheader(client)
93 |
94 | if not packet then return packet, head_len end
95 |
96 | local data_len = packet.len
97 |
98 | if (data_len > 0) and packet.compound_packet then
99 | packet.childs = {}
100 | local child_packet, child_len
101 |
102 | repeat
103 | child_packet, child_len = readpacket(client)
104 | data_len = data_len - child_len
105 | --print(data_len, "data_len")
106 | if child_packet then table.insert(packet.childs, child_packet) end
107 | until not (child_packet and (data_len > 0))
108 |
109 | if child_packet == nil then return end
110 | end
111 |
112 | if data_len > 0 then packet.data = client:receive(data_len) end
113 |
114 | return packet, head_len + packet.len
115 | end
116 |
117 | function encode_packet(packet)
118 | local control_byte = 0
119 | local buffer = new_string_builder()
120 |
121 | if packet.big_endian then
122 | control_byte = control_byte + 2
123 | end
124 |
125 | if packet.childs then
126 | control_byte = control_byte + 4
127 | for index, child in ipairs(packet.childs) do
128 | local data = encode_packet(child)
129 | assert(data)
130 | buffer.add(data)
131 | end
132 | end
133 |
134 | if packet.data then
135 | if buffer.len() > 0 then
136 | buffer.add("\000")
137 | end
138 | buffer.add(packet.data)
139 | end
140 |
141 | local bin_len = numbertobin(buffer.len(), packet.big_endian)
142 | local len_len = #bin_len
143 | assert(len_len <= 3)
144 | control_byte = control_byte + len_len * (2^6)
145 |
146 | assert(packet.name)
147 | local name_len = #(packet.name) - 1
148 | assert(name_len < 8)
149 | control_byte = control_byte + name_len * (2^3)
150 |
151 | buffer.insert(string.char(control_byte), bin_len, packet.name)
152 |
153 | return buffer.get()
154 | end
155 |
156 | function send_packet(client, packet)
157 | local data = encode_packet(packet)
158 | print("responce =", safestring(data))
159 | client:send(data)
160 | end
161 |
162 | function g2_main(client)
163 | local state = 1
164 | local line = ""
165 |
166 | function readline(client)
167 | line = client:receive("*l")
168 | return line
169 | end
170 |
171 | while readline(client) do
172 | print(safestring(line))
173 |
174 | if line == "" then
175 | if state == 1 then
176 | client:send([[
177 | GNUTELLA/0.6 200 OK
178 | Listen-IP: 127.0.0.1:6754
179 | Remote-IP: ]]..client:getpeername().."\n".. [[
180 | User-Agent: g2.lua 0.1
181 | Content-Type: application/x-gnutella2
182 | Accept: application/x-gnutella2
183 | X-Hub: True
184 | X-Hub-Needed: False
185 |
186 | ]])
187 | state = 2
188 | elseif state == 2 then
189 | local packet = readpacket(client)
190 | while packet do
191 | print(serialize(packet, "packet"))
192 | --print (client:getsockname())
193 | --print (decode_peer(encode_peer(client:getsockname())))
194 | if packet.name =="LNI" then
195 | send_packet(client,
196 | {
197 | name = "LNI",
198 | childs = {
199 | {
200 | big_endian = true,
201 | name = "NA",
202 | data = encode_peer_le(client:getsockname())
203 | },
204 | {
205 | name = "V",
206 | data = "G2LA"
207 | },
208 | {
209 | name = "GU",
210 | data = "g2.lua gui test1"
211 | },
212 | {
213 | name = "HS",
214 | data = "\000\000\001\000"
215 | }
216 | }
217 | }
218 | )
219 |
220 | elseif packet.name =="PI" then
221 | send_packet(client, { name = "PO" })
222 | elseif packet.name == "Q2" then
223 | local urn, dn, sz, btih
224 | for index, child in ipairs(packet.childs) do
225 | if child.name == "URN" then
226 | urn = child
227 | if child.data:sub(1,2) = "bt" then
228 | btih = child.data:sub(3, 23)
229 | end
230 | end
231 | if child.name == "DN" then
232 | dn = child
233 | end
234 | if child.name == "SZ" then
235 | sz = child
236 | end
237 | end
238 | if urn then
239 | send_packet(client,
240 | {
241 | name = "QH2",
242 | data = "\001"..packet.data,
243 | childs = {
244 | {
245 | big_endian = true,
246 | name = "NA",
247 | data = encode_peer_le(client:getsockname())
248 | },
249 | {
250 | name = "GU",
251 | data = "g2.lua gui test1"
252 | },
253 | {
254 | name = "H",
255 | childs = {
256 | urn, dn, sz,
257 | {
258 | name = "URL",
259 | --data = "http://downloads.sourceforge.net/shareaza/Shareaza_2.5.3.0_Win32.exe"
260 | }
261 | }
262 | }
263 | }
264 | }
265 | )
266 | end
267 | end
268 | packet = readpacket(client)
269 | -- send responce
270 | end
271 | end
272 | end
273 | end
274 | end
275 |
276 |
--------------------------------------------------------------------------------
/lua/http.lua:
--------------------------------------------------------------------------------
1 | http = {}
2 |
3 | function next_client(tcp_port)
4 | local client, err = tcp_port:accept()
5 |
6 | if not client then
7 | if (err ~= "timeout") then
8 | print("accept error: "..err)
9 | debug.debug()
10 | end
11 | return
12 | end
13 |
14 | local request = client:receive("*l")
15 | if not request then client:close() return end
16 |
17 | if string.find(request, "GNUTELLA CONNECT/0.6") then
18 | return g2_main(client)
19 | end
20 |
21 | -- Brouser --
22 | if string.find(request, " / ") then
23 | return http.main_page(client)
24 | end
25 |
26 | if string.find(request, " /console ") then
27 | to_http(nil, "text/html")
28 | local function print_fnc(request)
29 | if not client:send(request.."\n") then
30 | client:close()
31 | remove_print_fnc(print_fnc)
32 | end
33 | end
34 | add_print_fnc(print_fnc)
35 | return
36 | end
37 |
38 | if string.find(request, " /info ") then
39 | return http.info(client)
40 | end
41 |
42 | if string.find(request, " /map ") then
43 | return http.svg_map(client)
44 | end
45 |
46 |
47 | -- Tracker --
48 | local info_hash = string.match(request, "[?&]info_hash=([^& ]+)")
49 | if not info_hash then
50 | client:close()
51 | return
52 | else
53 | info_hash = unescape(info_hash)
54 | if #info_hash ~= 20 then
55 | print("#info_hash", #info_hash, info_hash)
56 | client:close()
57 | return
58 | end
59 | end
60 |
61 | local port = string.match(request, "[?&]port=([0-9]+)")
62 | port = (port and tonumber(port))
63 | local responce
64 | local table_type
65 |
66 | local header = client:receive("*l")
67 | while header and #header > 0 do
68 | local a, p = string.match(header, "Listen%-IP: ([0-9%.]+):([0-9]+)")
69 | port = port or (p and tonumber(p))
70 | header = client:receive("*l")
71 | end
72 |
73 |
74 |
75 | if string.find(request, " /announce", 1, true) then
76 | print("\t\t\t> http (announce)")
77 | search_peers(info_hash)
78 | announce_peer(info_hash, port)
79 | responce = {interval = 120}
80 | local values = get_values(info_hash, true)
81 | if values then
82 | responce.peers =table.concat(values)
83 | table_type = {[responce] = announce_responce}
84 | end
85 | elseif string.find(request, " /scrape", 1, true) then
86 | print("\t\t\t> http (scrape)")
87 | responce = {[info_hash] = {complete = 0, downloaded = 0, incomplete = 0}}
88 | table_type = {[responce] = scrape_responce, [responce[info_hash]] = scrape_details}
89 | else
90 | if info_hash then
91 | return http.search(client, info_hash, port, request)
92 | else
93 | return http.not_found(client)
94 | end
95 | end
96 |
97 | client:send(to_http(to_bencode(responce, table_type)))
98 | client:close()
99 | end
100 |
101 | announce_responce = { t = "d", o = {"interval", "peers"} }
102 | scrape_responce = { t = "d" }
103 | scrape_details = {t = "d", o = {"complete","downloaded","incomplete"}}
104 |
105 | ferrmsg = function(text)
106 | return "d14:failure reason"..#text..":"..text.."e"
107 | end
108 |
109 | standart_header = [[
110 | HTTP/1.1 200 OK
111 | Server: ]]..script_name.."\n"..[[
112 | Connection: close
113 | ]]
114 |
115 | function to_http(body, content_type)
116 | body = body or ferrmsg("tracker internal error", content_type)
117 | local data = standart_header
118 | data = data.."Content-Type: "..(content_type or "text/plain").."\n"
119 | if body then data = data.."Content-Length: "..string.len(body).."\n" end
120 | data = data.."\n"..(body or '')
121 | return data
122 | end
123 |
124 |
125 | function http.main_page(client)
126 | local buff = new_string_builder()
127 | buff.add("
Lua DHT Tracker Info ")
128 | buff.add(""..welcome_msg.." ")
129 | buff.add(
130 | [[
131 |
132 | node ip: ]]..decode_ip(#my_ip == 4 and my_ip or "\0\0\0\0").." \n"..[[
133 | node id: ]]..hexenc(my_id or "").." \n"..[[
134 | nodes count: ]]..(nodes.count or "unknown").."\n"..[[
135 |
136 |
137 |
138 | Console Out
139 | Hosted torrents
140 | Nodes Map
141 |
142 |
143 |
144 |
145 | input btih and click this link:
146 | magnet:?xt=urn:btih: &tr=http://127.0.0.1:]]..port_number..[[/announce
147 |
148 | ]] )
149 | buff.add("")
150 | client:send(to_http(buff.get("\n"), "text/html"))
151 | client:close()
152 | end
153 |
154 | local maximum_ugol = 255 * 256 * 256 * 256
155 |
156 | function spiral(ugol)
157 | --ugol = maximum_ugol - ugol
158 | local m1 = (ugol/maximum_ugol * 200)
159 | local m2 = (ugol/maximum_ugol * 46)
160 | return (50+math.cos(m1)*(m2)), (50+math.sin(m1)*(m2))
161 | end
162 |
163 | function http.svg_map(client)
164 | print("\t\t\t>http map")
165 | local buff = { new_string_builder(), new_string_builder(), new_string_builder(),
166 | new_string_builder() }
167 | buff[1].add([[
168 |
174 |
175 | ]])
176 |
177 |
178 | buff[1].add([[]])
179 | buff[2].add([[]])
180 | buff[3].add([[]])
181 | buff[4].add([[]])
182 |
183 | for ip, port_list in pairs(nodes.ap) do
184 | for port, node in pairs(port_list) do
185 | local opacity = (1 - (os.time() - (node.last_seen or node.added)) / (15*60) )
186 |
187 | if opacity>0 then
188 | opacity = (svg_opacity and ' opacity="'..opacity..'" ') or ''
189 |
190 | local x, y = spiral(statistic_distance(my_id, node.id))
191 |
192 | local fill = (node.confirmed and ' fill="green" ') or (node.sended and ' fill="lime"') or ( (not node.last_seen) and ' fill="red" ' ) or ""
193 |
194 | buff[2].add([[ ]])
196 |
197 | if node.conected_to then
198 | local stroke = (node.confirmed and ' stroke="green" ') or (node.last_seen and ' stroke="bule" ') or ""
199 | local x2, y2 = spiral(statistic_distance(my_id, node.conected_to))
200 | buff[1].add(' ')
201 | end
202 |
203 |
204 | if node.announced then
205 | buff[3].add([[ ]])
207 | elseif node.peer then
208 | buff[3].add([[ ]])
210 | end
211 | end
212 | end
213 | end
214 | local x, y = spiral(0)
215 |
216 | buff[3].add([[ ]])
217 |
218 | find_close_id(my_id, 50,
219 | function(node, buff)
220 | local x2, y2 = spiral(statistic_distance(my_id, node.id))
221 |
222 | buff.add(' ')
223 |
224 | x = x2
225 | y = y2
226 | return true
227 | end
228 | , {buff[4]}
229 | )
230 |
231 | buff[1].add(" ")
232 | buff[2].add(" ")
233 | buff[3].add(" ")
234 | buff[4].add(" ")
235 | buff[4].add(" ")
236 | client:send(to_http(buff[1].get("\n")..buff[2].get("\n")..buff[3].get("\n")..buff[4].get("\n"), "image/svg+xml"))
237 | client:close()
238 | end
239 |
240 | function sort_by_count(f, s)
241 | if f.count > s.count then
242 | return true
243 | elseif f.count < s.count then
244 | return false
245 | end
246 |
247 | return less(f.hash_raw, s.hash_raw, my_id)
248 | end
249 |
250 | function http.info(client)
251 | print("\t\t\t>http info")
252 | local buff1 = {}
253 | for hash, peer_list in pairs(peers.btih) do
254 | table.insert(buff1, { hash = hexenc(hash), hash_raw = hash, count = count_elements(peer_list), list = peer_list })
255 | end
256 | table.sort(buff1, sort_by_count)
257 | local buff = new_string_builder()
258 | buff.add("Lua DHT Tracker Info ")
259 | buff.add("BitTorrent Info Hash or Name count list ")
260 | for index, value in pairs(buff1) do
261 | local name = (torrent_info[value.hash_raw] and torrent_info[value.hash_raw].name) or value.hash
262 | buff.add(string.format("%s %s ",value.hash, port_number, name, value.hash, name, value.count ))
263 | for compact, peer in pairs(value.list) do
264 | buff.add(string.format("%s:%s ", decode_peer(compact)))
265 | end
266 | buff.add(" ")
267 | end
268 | buff.add("
")
269 | client:send(to_http(buff.get("\n"), "text/html"))
270 | client:close()
271 | return
272 | end
273 |
274 | function http.search(client, info_hash, port, request)
275 | print("\t\t\t> http (search)")
276 |
277 | local buff = new_string_builder()
278 | buff.add([[
279 |
280 |
281 | search by info_hash
282 |
283 |
284 | This page refresh every 120 seconds. Whait plz.
285 |
286 | ]])
287 | local protocol = string.match(request, "[?&]protocol=([^& ]+)") or "bittorrent"
288 | local file_size = string.match(request, "[?&]xl=([^& ]+)") or ""
289 | local file_name = string.match(request, "[?&]dn=([^& ]+)") or ""
290 | local btih = string.match(request, "[?&]urn:btih:([^& ]+)") or hexenc(info_hash)
291 | local file_urn = string.match(request, "[?&](urn:[^& ]+)") or "urn:btih:"..btih
292 | local ed2k_hash = string.match(request, "[?&]urn:ed2k:([^& ]+)") or ""
293 |
294 | buff.add(string.format([[protocol: %s
295 | file_size: %s
296 | file_name: %s
297 | file_urn: %s
298 | btih: %s
299 | info_hash: %s
300 | ed2k_hash: %s
301 |
]], protocol , file_size, file_name,
302 | file_urn, btih, hexenc(info_hash), ed2k_hash))
303 |
304 | local peers = get_peers(info_hash, port)
305 | local x_alt = ""
306 |
307 | if peers then
308 |
309 | if protocol == "ed2k" then
310 |
311 | buff.add(string.format("%s:%s \n", a, p, file_urn, a, p))
325 | elseif protocol == "p2p-radio" then
326 | buff.add(string.format("%s:%s \n", a, p, a, p))
327 | else
328 | buff.add(string.format("%s:%s \n", a, p, btih, a, p))
329 | end
330 | --end
331 | end
332 |
333 | if protocol == "ed2k" then
334 | buff.add("|/")
335 | end
336 |
337 | if not peers_x_alt.empty() then
338 | x_alt = "X-Alt: "..peers_x_alt.get(",").."\n"
339 | end
340 | end
341 |
342 | buff.add([[
343 |
344 |
345 |
346 | ]])
347 | local content = buff.get()
348 | client:send([[
349 | HTTP/1.1 503 Bisy
350 | Server: ]]..script_name.."\n"..[[
351 | X-Queue: position=2,length=10,limit=20,pollMin=10,pollMax=100");
352 | Retry-After: 120
353 | Refresh: 120]]
354 | .."\n"..x_alt.."Content-Length: "..#content.."\n\n"..content)
355 |
356 | client:close()
357 | return
358 | end
359 |
360 | function http.not_found(client)
361 | client:send([[
362 | HTTP/1.1 404 Not Found
363 | Server: ]]..script_name.."\n"..
364 | [[Content-Length: 9
365 |
366 | Not Found]])
367 | end
--------------------------------------------------------------------------------
/lua/ltn12.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- LTN12 - Filters, sources, sinks and pumps.
3 | -- LuaSocket toolkit.
4 | -- Author: Diego Nehab
5 | -- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module
10 | -----------------------------------------------------------------------------
11 | local string = require("string")
12 | local table = require("table")
13 | local base = _G
14 | module("ltn12")
15 |
16 | filter = {}
17 | source = {}
18 | sink = {}
19 | pump = {}
20 |
21 | -- 2048 seems to be better in windows...
22 | BLOCKSIZE = 2048
23 | _VERSION = "LTN12 1.0.1"
24 |
25 | -----------------------------------------------------------------------------
26 | -- Filter stuff
27 | -----------------------------------------------------------------------------
28 | -- returns a high level filter that cycles a low-level filter
29 | function filter.cycle(low, ctx, extra)
30 | base.assert(low)
31 | return function(chunk)
32 | local ret
33 | ret, ctx = low(ctx, chunk, extra)
34 | return ret
35 | end
36 | end
37 |
38 | -- chains a bunch of filters together
39 | -- (thanks to Wim Couwenberg)
40 | function filter.chain(...)
41 | local n = table.getn(arg)
42 | local top, index = 1, 1
43 | local retry = ""
44 | return function(chunk)
45 | retry = chunk and retry
46 | while true do
47 | if index == top then
48 | chunk = arg[index](chunk)
49 | if chunk == "" or top == n then return chunk
50 | elseif chunk then index = index + 1
51 | else
52 | top = top+1
53 | index = top
54 | end
55 | else
56 | chunk = arg[index](chunk or "")
57 | if chunk == "" then
58 | index = index - 1
59 | chunk = retry
60 | elseif chunk then
61 | if index == n then return chunk
62 | else index = index + 1 end
63 | else base.error("filter returned inappropriate nil") end
64 | end
65 | end
66 | end
67 | end
68 |
69 | -----------------------------------------------------------------------------
70 | -- Source stuff
71 | -----------------------------------------------------------------------------
72 | -- create an empty source
73 | local function empty()
74 | return nil
75 | end
76 |
77 | function source.empty()
78 | return empty
79 | end
80 |
81 | -- returns a source that just outputs an error
82 | function source.error(err)
83 | return function()
84 | return nil, err
85 | end
86 | end
87 |
88 | -- creates a file source
89 | function source.file(handle, io_err)
90 | if handle then
91 | return function()
92 | local chunk = handle:read(BLOCKSIZE)
93 | if not chunk then handle:close() end
94 | return chunk
95 | end
96 | else return source.error(io_err or "unable to open file") end
97 | end
98 |
99 | -- turns a fancy source into a simple source
100 | function source.simplify(src)
101 | base.assert(src)
102 | return function()
103 | local chunk, err_or_new = src()
104 | src = err_or_new or src
105 | if not chunk then return nil, err_or_new
106 | else return chunk end
107 | end
108 | end
109 |
110 | -- creates string source
111 | function source.string(s)
112 | if s then
113 | local i = 1
114 | return function()
115 | local chunk = string.sub(s, i, i+BLOCKSIZE-1)
116 | i = i + BLOCKSIZE
117 | if chunk ~= "" then return chunk
118 | else return nil end
119 | end
120 | else return source.empty() end
121 | end
122 |
123 | -- creates rewindable source
124 | function source.rewind(src)
125 | base.assert(src)
126 | local t = {}
127 | return function(chunk)
128 | if not chunk then
129 | chunk = table.remove(t)
130 | if not chunk then return src()
131 | else return chunk end
132 | else
133 | table.insert(t, chunk)
134 | end
135 | end
136 | end
137 |
138 | function source.chain(src, f)
139 | base.assert(src and f)
140 | local last_in, last_out = "", ""
141 | local state = "feeding"
142 | local err
143 | return function()
144 | if not last_out then
145 | base.error('source is empty!', 2)
146 | end
147 | while true do
148 | if state == "feeding" then
149 | last_in, err = src()
150 | if err then return nil, err end
151 | last_out = f(last_in)
152 | if not last_out then
153 | if last_in then
154 | base.error('filter returned inappropriate nil')
155 | else
156 | return nil
157 | end
158 | elseif last_out ~= "" then
159 | state = "eating"
160 | if last_in then last_in = "" end
161 | return last_out
162 | end
163 | else
164 | last_out = f(last_in)
165 | if last_out == "" then
166 | if last_in == "" then
167 | state = "feeding"
168 | else
169 | base.error('filter returned ""')
170 | end
171 | elseif not last_out then
172 | if last_in then
173 | base.error('filter returned inappropriate nil')
174 | else
175 | return nil
176 | end
177 | else
178 | return last_out
179 | end
180 | end
181 | end
182 | end
183 | end
184 |
185 | -- creates a source that produces contents of several sources, one after the
186 | -- other, as if they were concatenated
187 | -- (thanks to Wim Couwenberg)
188 | function source.cat(...)
189 | local src = table.remove(arg, 1)
190 | return function()
191 | while src do
192 | local chunk, err = src()
193 | if chunk then return chunk end
194 | if err then return nil, err end
195 | src = table.remove(arg, 1)
196 | end
197 | end
198 | end
199 |
200 | -----------------------------------------------------------------------------
201 | -- Sink stuff
202 | -----------------------------------------------------------------------------
203 | -- creates a sink that stores into a table
204 | function sink.table(t)
205 | t = t or {}
206 | local f = function(chunk, err)
207 | if chunk then table.insert(t, chunk) end
208 | return 1
209 | end
210 | return f, t
211 | end
212 |
213 | -- turns a fancy sink into a simple sink
214 | function sink.simplify(snk)
215 | base.assert(snk)
216 | return function(chunk, err)
217 | local ret, err_or_new = snk(chunk, err)
218 | if not ret then return nil, err_or_new end
219 | snk = err_or_new or snk
220 | return 1
221 | end
222 | end
223 |
224 | -- creates a file sink
225 | function sink.file(handle, io_err)
226 | if handle then
227 | return function(chunk, err)
228 | if not chunk then
229 | handle:close()
230 | return 1
231 | else return handle:write(chunk) end
232 | end
233 | else return sink.error(io_err or "unable to open file") end
234 | end
235 |
236 | -- creates a sink that discards data
237 | local function null()
238 | return 1
239 | end
240 |
241 | function sink.null()
242 | return null
243 | end
244 |
245 | -- creates a sink that just returns an error
246 | function sink.error(err)
247 | return function()
248 | return nil, err
249 | end
250 | end
251 |
252 | -- chains a sink with a filter
253 | function sink.chain(f, snk)
254 | base.assert(f and snk)
255 | return function(chunk, err)
256 | if chunk ~= "" then
257 | local filtered = f(chunk)
258 | local done = chunk and ""
259 | while true do
260 | local ret, snkerr = snk(filtered, err)
261 | if not ret then return nil, snkerr end
262 | if filtered == done then return 1 end
263 | filtered = f(done)
264 | end
265 | else return 1 end
266 | end
267 | end
268 |
269 | -----------------------------------------------------------------------------
270 | -- Pump stuff
271 | -----------------------------------------------------------------------------
272 | -- pumps one chunk from the source to the sink
273 | function pump.step(src, snk)
274 | local chunk, src_err = src()
275 | local ret, snk_err = snk(chunk, src_err)
276 | if chunk and ret then return 1
277 | else return nil, src_err or snk_err end
278 | end
279 |
280 | -- pumps all data from a source to a sink, using a step function
281 | function pump.all(src, snk, step)
282 | base.assert(src and snk)
283 | step = step or pump.step
284 | while true do
285 | local ret, err = step(src, snk)
286 | if not ret then
287 | if err then return nil, err
288 | else return 1 end
289 | end
290 | end
291 | end
292 |
293 |
--------------------------------------------------------------------------------
/lua/magnet_to_uri_res.lua:
--------------------------------------------------------------------------------
1 | require("binstd")
2 | require("encoding")
3 | require("settings")
4 | local dbg = false
5 |
6 | function dbgmsg(...)
7 | if dbg then
8 | usermsg(unpack(arg))
9 | end
10 | end
11 | function usermsg(...)
12 | io.stderr:write(table.concat(arg, "\t"))
13 | end
14 | local urn = {}
15 |
16 | local urn_name
17 | local hash
18 |
19 | while not next(urn) do
20 | if not arg[1] then
21 | usermsg("\ninput urn or magnet, or press Enter to exit: \n")
22 | end
23 | magnet = arg[1] or io.stdin:read("*l")
24 | if #magnet <= 1 then os.exit(0) end
25 | dbgmsg("\nmagnet: ", magnet)
26 |
27 | string.gsub(magnet, "urn:([0-9A-Za-z:%./]+)", function(urn_part)
28 | if string.match(urn_part, "^tree:tiger") then
29 | urn["tree:tiger:"] = string.match(urn_part, ":([0-9A-Za-z]+)$")
30 | elseif string.match(urn_part, "^bitprint:") then
31 | urn["sha1:"], urn["tree:tiger:"] = string.match(urn_part, ":([0-9A-Za-z]+).([0-9A-Za-z]+)$")
32 | elseif string.match(urn_part, "^ed2k") then
33 | urn["ed2k:"] = string.match(urn_part, ":([0-9A-Za-z]+)$")
34 | elseif string.match(urn_part, "^sha1:") then
35 | urn["sha1:"] = string.match(urn_part, ":([0-9A-Za-z]+)$")
36 | elseif string.match(urn_part, "^btih:") then
37 | urn["btih:"] = string.match(urn_part, ":([0-9A-Za-z]+)$")
38 | end
39 | end)
40 |
41 | if urn["tree:tiger:"] then
42 | urn_name = "tree:tiger:"
43 | hash = urn[urn_name]
44 | decode = "b32dec"
45 | elseif urn["ed2k:"] then
46 | urn_name = "ed2k:"
47 | hash = urn[urn_name]
48 | decode = "hexdec"
49 | elseif urn["sha1:"] then
50 | urn_name = "sha1:"
51 | hash = urn[urn_name]
52 | decode = "b32dec"
53 | elseif urn["btih:"] then
54 | urn_name = "btih:"
55 | hash = urn[urn_name]
56 | decode = "hexdec"
57 | end
58 |
59 | if not next(urn) then
60 | usermsg("\nurn not found; pattern: (urn:[0-9A-Za-z:%./]+)")
61 | arg[3] = nil
62 | else
63 | usermsg("\nurn found: ", urn_name, hash)
64 | end
65 | end
66 |
67 | if hash then
68 | local cmd = {}
69 | table.insert(cmd, [[lua5.1 -lbinstd -lencoding -e "io.write('d'..bencode(']]..urn_name..[[')..bencode(]]..decode..[[(']]..
70 | hash..[['))..'8:protocol7:uri-rese')"]])
71 | table.insert(cmd, [[rhash -p"io.write('http://127.0.0.1:]]..port_number..[[/uri-res/N2R?urn:]]..urn_name..hash..
72 | [=[&info_hash='..urlhex([[%@h]]))" -]=])
73 | table.insert(cmd, [[lua5.1 -lbinstd -lencoding -]])
74 | dbgmsg(table.concat(cmd, "|"))
75 | os.execute(table.concat(cmd, "|"))
76 | end
77 |
--------------------------------------------------------------------------------
/lua/mime.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- MIME support for the Lua language.
3 | -- Author: Diego Nehab
4 | -- Conforming to RFCs 2045-2049
5 | -- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module and import dependencies
10 | -----------------------------------------------------------------------------
11 | local base = _G
12 | local ltn12 = require("ltn12")
13 | local mime = require("mime.core")
14 | local io = require("io")
15 | local string = require("string")
16 | module("mime")
17 |
18 | -- encode, decode and wrap algorithm tables
19 | encodet = {}
20 | decodet = {}
21 | wrapt = {}
22 |
23 | -- creates a function that chooses a filter by name from a given table
24 | local function choose(table)
25 | return function(name, opt1, opt2)
26 | if base.type(name) ~= "string" then
27 | name, opt1, opt2 = "default", name, opt1
28 | end
29 | local f = table[name or "nil"]
30 | if not f then
31 | base.error("unknown key (" .. base.tostring(name) .. ")", 3)
32 | else return f(opt1, opt2) end
33 | end
34 | end
35 |
36 | -- define the encoding filters
37 | encodet['base64'] = function()
38 | return ltn12.filter.cycle(b64, "")
39 | end
40 |
41 | encodet['quoted-printable'] = function(mode)
42 | return ltn12.filter.cycle(qp, "",
43 | (mode == "binary") and "=0D=0A" or "\r\n")
44 | end
45 |
46 | -- define the decoding filters
47 | decodet['base64'] = function()
48 | return ltn12.filter.cycle(unb64, "")
49 | end
50 |
51 | decodet['quoted-printable'] = function()
52 | return ltn12.filter.cycle(unqp, "")
53 | end
54 |
55 | local function format(chunk)
56 | if chunk then
57 | if chunk == "" then return "''"
58 | else return string.len(chunk) end
59 | else return "nil" end
60 | end
61 |
62 | -- define the line-wrap filters
63 | wrapt['text'] = function(length)
64 | length = length or 76
65 | return ltn12.filter.cycle(wrp, length, length)
66 | end
67 | wrapt['base64'] = wrapt['text']
68 | wrapt['default'] = wrapt['text']
69 |
70 | wrapt['quoted-printable'] = function()
71 | return ltn12.filter.cycle(qpwrp, 76, 76)
72 | end
73 |
74 | -- function that choose the encoding, decoding or wrap algorithm
75 | encode = choose(encodet)
76 | decode = choose(decodet)
77 | wrap = choose(wrapt)
78 |
79 | -- define the end-of-line normalization filter
80 | function normalize(marker)
81 | return ltn12.filter.cycle(eol, 0, marker)
82 | end
83 |
84 | -- high level stuffing filter
85 | function stuff()
86 | return ltn12.filter.cycle(dot, 2)
87 | end
88 |
--------------------------------------------------------------------------------
/lua/nodes.lua:
--------------------------------------------------------------------------------
1 | require("compact_encoding")
2 |
3 | nodes = {ap = {}, by_id={}, id_map={}}
4 |
5 |
6 | function getbit(number)
7 | if number == 0 then
8 | return 0, number
9 | elseif number > 0 then
10 | local bit = math.mod(number,2)
11 | return bit, (number-bit)/2
12 | else
13 | return nil, "number must be >= 0"
14 | end
15 | end
16 |
17 |
18 | function xor(n1, n2, hamming_distance)
19 | local getbit = getbit
20 | local n3 = 0
21 | local cnt = 1
22 | local bit1 = 0
23 | local bit2 = 0
24 | while (n1 > 0) or (n2 > 0) do
25 |
26 | bit1, n1 = getbit(n1)
27 | bit2, n2 = getbit(n2)
28 |
29 | if(bit1 ~= bit2) then
30 | n3 = n3 + cnt
31 | end
32 |
33 | if not hamming_distance then
34 | cnt = cnt * 2
35 | end
36 | end
37 |
38 | return n3
39 | end
40 |
41 | function hamming_distance(n1, n2)
42 | return xor(n1, n2, true)
43 | end
44 |
45 | function table.get_by_keys(tbl, ...)
46 | local keys = {...}
47 | local value = tbl
48 | for i, key in ipairs(keys) do
49 | if type(value) == "table" then
50 | value = value[key]
51 | else
52 | return nil
53 | end
54 | end
55 | return value
56 | end
57 |
58 | function node_by_ip_port(ip, port)
59 | return table.get_by_keys(nodes.ap, ip, port)
60 | end
61 |
62 | function node_by_id_ip(id, ip)
63 | return table.get_by_keys(nodes.by_id, id, ip)
64 | end
65 |
66 | function add_node(id, address, port, check_fnc, ... )
67 | if is_blocked_port(port) then return end
68 | if is_local(address) then return end
69 |
70 | local node = node_by_ip_port(address, port)
71 |
72 | if (not node) then
73 | node = node_by_id_ip(id, address)
74 | end
75 |
76 | if (not node) then
77 | node = { address = address, port = port, id = id, added = os.time() }
78 | elseif check_fnc then
79 | check_fnc(id, address, port, node, unpack(arg))
80 | end
81 |
82 | add_id_ip(node)
83 | add_ip_port(node)
84 | add_id(nodes.id_map, node.id)
85 |
86 | return node
87 | end
88 |
89 | function add_id_ip(node)
90 | local by_ip = nodes.by_id[node.id]
91 | if not by_ip then
92 | by_ip = {}
93 | nodes.by_id[node.id] = by_ip
94 | end
95 |
96 | by_ip[node.address] = node
97 | end
98 |
99 | function add_ip_port(node)
100 | local port_list = nodes.ap[node.address]
101 |
102 | if not port_list then
103 | port_list = {}
104 | nodes.ap[node.address] = port_list
105 | end
106 |
107 | if port_list[node.port] ~= node then
108 | port_list[node.port] = node
109 | nodes.count = (nodes.count or 0) + 1
110 | nodes.counted = false
111 | end
112 | end
113 |
114 |
115 | function add_id(list, id, index)
116 | index = index or 1
117 | local sub_list = list[id:byte(index)]
118 | if not sub_list then
119 | list[id:byte(index)] = id
120 | elseif type(sub_list) == "table" then
121 | add_id(sub_list, id, index + 1)
122 | elseif (type(sub_list) == "string") and not (sub_list == id) then
123 | local id2 = sub_list
124 | local new_list = {}
125 | list[id:byte(index)] = new_list
126 | for i = index + 1, #id2 do
127 | local id_byte = id:byte(i)
128 | local id2_byte = id2:byte(i)
129 | if id_byte == id2_byte then
130 | new_list[id_byte] = {}
131 | new_list = new_list[id_byte]
132 | else
133 | new_list[id_byte] = id
134 | new_list[id2_byte] = id2
135 | break
136 | end
137 | end
138 | end
139 | end
140 |
141 |
142 | function remove_node_id_ip(node)
143 | list = nodes.by_id[node.id]
144 | if list then
145 | list[node.address] = nil
146 | if not next(list) then nodes.by_id[node.id] = nil end
147 | end
148 | end
149 |
150 | function remove_node_ip_port(node)
151 | local port_list = nodes.ap[node.address]
152 | if port_list and (port_list[node.port] == node) then
153 | port_list[node.port] = nil
154 | nodes.count = (nodes.count or 0) - 1
155 | nodes.counted = false
156 | if not next(port_list) then
157 | nodes.ap[node.address] = nil
158 | end
159 | end
160 | end
161 |
162 | function count_nodes()
163 | local count = 0
164 |
165 | for ip, port_list in pairs(nodes.ap) do
166 | for port, node in pairs(port_list) do
167 | count = count + 1
168 | end
169 | end
170 |
171 | nodes.count = count
172 | nodes.counted = true
173 | end
174 |
175 | function check_node(key, node, nodes_list, node_id)
176 | local valid = true
177 | if node.id ~= node_id then
178 | nodes_list[key] = nil
179 | valid = false
180 | end
181 |
182 | if (not nodes.counted) and ((nodes.count or 0) <= 300) then
183 | count_nodes()
184 | end
185 |
186 | if nodes_clear and (nodes.count > 300)
187 | and (((node.last_seen or node.added) < (os.time() - 30*60))
188 | or (node.sended and node.sended.time and (node.sended.time < (os.time() - 3*60))))
189 | then
190 | remove_node_ip_port(node)
191 | remove_node_id_ip(node)
192 | valid = false
193 | if on_node_removed then
194 | on_node_removed(node)
195 | end
196 | end
197 |
198 | return valid
199 | end
200 |
201 | function find_close_id(id, count, check_fnc, args, list, index, k_nodes)
202 | count = count or 8
203 | index = index or 1
204 | k_nodes = k_nodes or {}
205 | list = list or nodes.id_map
206 | args = args or {}
207 |
208 | for i = 0, 255 do
209 | local byte = xor(id:byte(index), i)
210 | local el = list[byte]
211 | if type(el) == "table" then
212 | _, list[byte] = find_close_id( id, count, check_fnc, args, el, index + 1, k_nodes)
213 | elseif el then
214 | local node_id = el
215 | local nodes_list = nodes.by_id[node_id]
216 | if nodes_list and next(nodes_list) then
217 | for key, node in pairs(nodes_list) do
218 | if check_node(key, node, nodes_list, node_id) then
219 | if (not check_fnc) or check_fnc(node, unpack(args)) then
220 | --print(b32enc(node_id), "("..#node_id..")", statistic_distance(id, node_id))
221 | table.insert(k_nodes, node)
222 | end
223 | end
224 | end
225 | end
226 |
227 | if (not nodes_list) or (not next(nodes_list)) then
228 | if nodes_list then nodes.by_id[node_id] = nil end
229 | list[byte] = nil
230 | end
231 |
232 | end
233 |
234 | if #k_nodes >= count then break end
235 | end
236 |
237 | local index, el = next(list)
238 | if not index then
239 | list = nil
240 | elseif not next(list, index) and type(el) == "string" then
241 | list = el
242 | end
243 | return k_nodes, list
244 | end
245 |
246 | function is_local(ip)
247 | local ip = ipv4_array(ip)
248 | if ip[1] == 10 then
249 | return true
250 | elseif (ip[1] == 172) and (ip[2] >= 16) and (ip[2] <= 32) then
251 | return true
252 | elseif (ip[1] == 192) and (ip[2] == 168) then
253 | return true
254 | elseif (ip[1] == 127) then
255 | return true
256 | end
257 | end
258 |
259 | function is_blocked_port(port)
260 | return (port < 1024) or (port > 65535)
261 | end
262 |
--------------------------------------------------------------------------------
/lua/nodes2.lua:
--------------------------------------------------------------------------------
1 | require("compact_encoding")
2 |
3 | nodes = {ap = {}, by_id={}, id_map={}}
4 |
5 | function add_node(id, address, port, check_fnc, ... )
6 | if is_local(address) then return end
7 |
8 | local address_port = address..":"..port
9 |
10 | local node = nodes.ap[address_port]
11 | if (not node) then
12 | local by_address = nodes.by_id[id]
13 | node = by_address and by_address[address]
14 | end
15 |
16 | if (not node) then
17 | node = { address = address, port = port, id = id, added = os.time() }
18 | nodes.ap[address_port] = node
19 | else
20 | check_fnc(id, address, port, node, unpack(arg))
21 | end
22 |
23 | local by_address = nodes.by_id[node.id]
24 | if not by_address then
25 | by_address = {}
26 | nodes.by_id[node.id] = by_address
27 | end
28 |
29 | by_address[address] = node
30 |
31 | add_id(nodes.id_map, node.id)
32 |
33 | return node
34 | end
35 |
36 |
37 | function add_id(list, id, index)
38 | index = index or 1
39 | local sub_list = list[id:byte(index)]
40 | if not sub_list then
41 | list[id:byte(index)] = id:sub(index + 1)
42 | elseif type(sub_list) == "table" then
43 | add_id(sub_list, id, index + 1)
44 | elseif (type(sub_list) == "string") and not (sub_list == id:sub(index + 1)) then
45 | local new_list = {}
46 | list[id:byte(index)] = new_list
47 | local i = 1
48 | while i <= #sub_list do
49 | if id:byte(index + i) == sub_list:byte(i) then
50 | new_list[sub_list:byte(i)] = {}
51 | new_list = new_list[sub_list:byte(i)]
52 | else
53 | break
54 | end
55 | i = i + 1
56 | end
57 |
58 | new_list[sub_list:byte(i)] = (#sub_list < (i + 1)) or sub_list:sub(i + 1)
59 | new_list[id:byte(index + i)] = (#id < (index + i + 1)) or id:sub(index + i + 1)
60 | end
61 | end
62 |
63 | function remove_id(id)
64 |
65 | end
66 |
67 |
68 | function remove_node_id_ip(node)
69 | nodes.by_id[node.id][node.address] = nil
70 | end
71 |
72 | function remove_node_ip_port(node)
73 | nodes.ap[node.address..":"..node.port] = nil
74 | end
75 |
76 |
77 | function find_close_id(id, count, check_fnc, args)
78 | count = count or 8
79 | args = args or {}
80 | local k_nodes = {}
81 | local k_count = 0
82 |
83 | local function check_el(byte, el, list, id_part)
84 | if type(el) == "table" then
85 | _, list[byte] = find_close_id( id, count, check_fnc, args, el, index + 1, id_part..string.char(byte), k_nodes)
86 | elseif el then
87 | local node_id = id_part..string.char(byte)..(((type(el) == "string") and el) or "")
88 | local nodes_list = nodes.by_id[node_id]
89 | if nodes_list and next(nodes_list) then
90 | for key, node in pairs(nodes_list) do
91 | if (node.id == node_id) then
92 | if check_fnc(node, unpack(args)) then
93 | --print(b32enc(node_id), "("..#node_id..")", statistic_distance(id, node_id))
94 | table.insert(k_nodes, node)
95 | end
96 | else
97 | nodes_list[key] = nil
98 | end
99 | end
100 | end
101 |
102 | if (not nodes_list) or (not next(nodes_list)) then
103 | if nodes_list then nodes.by_id[node_id] = nil end
104 | list[byte] = nil
105 | end
106 |
107 | end
108 | end
109 |
110 | local function small_count(index, list, id_part)
111 | local id_byte = id:byte(index)
112 | repeat
113 | local byte_len = 256
114 | local byte_value = nil
115 | local byte_el = nil
116 | local count = 0
117 | for byte, sub_el in pairs(list) do
118 | local len = xor(id_byte, byte)
119 | if (cecked > len) and (len < byte_len) then
120 | byte_len = len
121 | byte_value = byte
122 | byte_el = sub_el
123 | end
124 | if count then
125 | count = count + 1
126 | if count > 15 then
127 | return false
128 | end
129 | end
130 | end
131 | count = nil
132 | cecked = byte_len
133 | if byte_el then
134 | check_el(byte_value, byte_el, list, id_part)
135 | end
136 | until byte_len == 256 or k_count >= count
137 | return true
138 | end
139 | end
140 |
141 |
142 | function find_close_id_old(id, count, check_fnc, args, list, index, id_part, k_nodes)
143 | count = count or 8
144 | index = index or 1
145 | id_part = id_part or ""
146 |
147 | local list = list or nodes.id_map
148 | args = args or {}
149 |
150 |
151 |
152 | function small_count()
153 | repeat
154 | local byte_len = 256
155 | local byte_value = nil
156 | local byte_el = nil
157 | local count = 0
158 | for byte, sub_el in pairs(list[byte]) do
159 | local len = xor(id:byte(index), byte)
160 | if (cecked > len) and (len < byte_len) then
161 | byte_len = len
162 | byte_value = byte
163 | byte_el = sub_el
164 | end
165 | if count then
166 | count = count + 1
167 | if count > 15 then
168 | return false
169 | end
170 | end
171 | end
172 | cecked = byte_len
173 | if byte_el then
174 | check_el(byte_value, byte_el)
175 | end
176 | until byte_len == 256 or #k_nodes >= count
177 |
178 | end
179 |
180 |
181 | if not small_count() then
182 | large_count()
183 | end
184 |
185 |
186 |
187 |
188 | if id_count[list] and (id_count[list] < 15) then
189 | local cecked = -1
190 |
191 | else
192 | for i = 0, 255 do
193 | local byte = xor(id:byte(index), i)
194 | local el = list[byte]
195 | if byte_el then
196 | check_el(byte_value, byte_el)
197 | end
198 |
199 | if #k_nodes >= count then break end
200 | end
201 | end
202 |
203 | if not next(list) then list = nil end
204 | return k_nodes, list
205 | end
206 |
207 | function check_id_list()
208 | for id, nodes_list in pairs(nodes.by_id) do
209 | for key, node in pairs(nodes_list) do
210 | if node.id ~= id then
211 | nodes_list[key] = nil
212 | end
213 | end
214 | if not next(nodes_list) then
215 | nodes.by_id[id] = nil
216 | remove_id(id)
217 | end
218 | end
219 | end
220 |
221 | function is_local(address)
222 | local ip = ipv4_array(address)
223 | if ip[1] == 10 then
224 | return true
225 | elseif (ip[1] == 172) and (ip[2] >= 16) and (ip[2] <= 32) then
226 | return true
227 | elseif (ip[1] == 192) and (ip[2] == 168) then
228 | return true
229 | elseif (ip[1] == 127) then
230 | return true
231 | end
232 | end
--------------------------------------------------------------------------------
/lua/print_override.lua:
--------------------------------------------------------------------------------
1 | print_list = {}
2 |
3 | function add_print_fnc(print_fnc)
4 | print_list[print_fnc] = true
5 | end
6 |
7 | function remove_print_fnc(print_fnc)
8 | print_list[print_fnc] = nil
9 | end
10 |
11 | function nb_print(...)
12 | io.stderr:write(table.concat(arg, "\t"))
13 | end
14 |
15 | function nb_out(...)
16 | io.stdout:write(table.concat(arg, "\t"))
17 | end
18 |
19 | function flush()
20 | io.stderr:flush()
21 | io.stdout:flush()
22 | end
23 |
24 | function status_print(...)
25 | local line = table.concat(arg, " ")
26 | if #line < 79 then
27 | line = string.rep(" ", 79 - #line)..line.."\r"
28 | end
29 | nb_print(line)
30 | end
31 |
32 | old_print = print
33 |
34 | function print(...)
35 | local line = table.concat(arg, "\t")
36 | nb_print(line.."\n")
37 | for print_fnc, _ in pairs( print_list ) do
38 | print_fnc( line )
39 | end
40 | end
41 |
42 |
43 |
44 | function print2(...)
45 | local line = table.concat(arg, "\t")
46 | print(line)
47 | old_print(line)
48 | io.stdout:flush()
49 | end
50 |
51 | function mid_print(...)
52 | local line = table.concat(arg, " ")
53 | if #line < 79 then
54 | local hlf = math.floor((79 - #line) / 2)
55 | line = string.rep(" ", hlf)..line
56 | end
57 | print(line)
58 | end
59 |
60 | function print_msg(...)
61 | print(string.rep("-", 79))
62 | print(unpack(arg))
63 | print(string.rep("-", 79))
64 | end
65 |
--------------------------------------------------------------------------------
/lua/serialize2.lua:
--------------------------------------------------------------------------------
1 |
2 | -- local table1={nil,nil,nil,[{"no linked table as key 1"}]={"nn1"},"test1"}
3 | -- local table2={table1,nil,nil,[{"no linked table as key 2",table1}]={"nn2"},"test2"}
4 | -- local table3={table1,table2,nil,[{"no \\ \n linked table as key 3",table1,table2,[{"no linked table as key 3.1"}]="3.1"}]={"nn3"},"test3"}
5 | -- table1[1]=table1
6 | -- table1[2]=table2
7 | -- table1[3]=table2
8 | -- table2[2]=table2
9 | -- table2[3]=table3
10 | -- table3[3]=table3
11 | -- table1[table1]="table1.1"
12 | -- table1[table2]="table1.2"
13 | -- table1[table3]="table1.3"
14 | -- table2[table1]=table1
15 | -- table2[table2]=table2
16 | -- table2[table3]=table3
17 | -- table3[table1]=table1
18 |
19 |
20 | function safestring(value)
21 | if type(value)=="string" then
22 | local v = string.gsub(value, "([\\\10\13%c%z\128-\255\"])([0-9]?)", function(chr, digit)
23 | local b = string.byte(chr)
24 | if #digit == 1 then
25 | if string.len(b)<2 then b="0"..b end
26 | if string.len(b)<3 then b="0"..b end
27 | end
28 | b="\\"..b..digit
29 | return b
30 | end)
31 | return '"'..v..'"'
32 | end
33 | end
34 |
35 | function serialize(tTable, sTableName,sNewLine ,sTab, fSkipValue)
36 | assert(tTable, "tTable equals nil");
37 | sTableName = sTableName or "";
38 | assert(type(tTable) == "table", "tTable must be a table!");
39 | assert(type(sTableName) == "string", "sTableName must be a string!");
40 | if not sNewLine then sNewLine="\n" end
41 | if not sTab then sTab="\t" end
42 | local tTablesCollector={}
43 | local kidx = 0
44 |
45 | local function next2(tbl, index)
46 | local new_index, new_value = next(tbl, index)
47 | while new_index and fSkipValue(tbl, new_index, new_value) do
48 | new_index, new_value = next(tbl, new_index)
49 | end
50 | return new_index, new_value
51 | end
52 |
53 | local function pairs2(tbl)
54 | return next2, tbl, nil
55 | end
56 |
57 | if not fSkipValue then pairs2 = pairs end
58 |
59 | local function SerializeInternal(tTable, sTableName, sTabs, sLongKey)
60 | local tRepear = {}
61 | local tTmp = {}
62 | sLongKey = sLongKey or sTableName;
63 | sTabs = sTabs or "";
64 | if tTablesCollector[tTable] then
65 | local sKey = tTablesCollector[tTable]
66 | table.insert(tRepear, sNewLine..sTab..sLongKey.."="..sKey..";")
67 | if #sKey > #sLongKey then
68 | tTablesCollector[tTable] = sLongKey;
69 | end
70 | return nil, tRepear
71 | else
72 | tTablesCollector[tTable] = sLongKey
73 | end
74 |
75 | if not next(tTable) then
76 | table.insert(tTmp, sTabs..sTableName.."={}")
77 | return tTmp, tRepear
78 | end
79 |
80 | if sTableName~="" then
81 | table.insert(tTmp, sTabs..sTableName.."={")
82 | end
83 |
84 | local bEmpty = true
85 | for key, value in pairs2(tTable) do
86 | local sKey
87 | local bToRepear = false
88 | if (type(key) == "table") then
89 | if tTablesCollector[key] then
90 | sKey="["..tTablesCollector[key].."]"
91 | else
92 | kidx=kidx+1
93 | sKey="keys["..kidx.."]"
94 | local tTmp2, tRepear2 = SerializeInternal(key, sKey, sTab)
95 | table.insert(tRepear, sNewLine..table.concat(tTmp2, "")..";"..table.concat(tRepear2, ""))
96 | sKey="["..sKey.."]"
97 | end
98 | bToRepear = true
99 | elseif (type(key) == "string") then
100 | local m = string.match(key, "([A-Za-z_]+)")
101 | if m and (#m == #key) then
102 | sKey = m
103 | else
104 | sKey = "["..safestring(key).."]"
105 | end
106 | elseif (type(key) == "number") then
107 | sKey = string.format("[%d]",key);
108 | else
109 | sKey = "["..tostring(key).."]"
110 | end
111 |
112 | local prefix = (bEmpty and sNewLine) or ","..sNewLine
113 |
114 | if(type(value) == "table") then
115 | local tTmp2, tRepear2=SerializeInternal(value, sKey,(bToRepear and sTab) or sTabs..sTab, sLongKey..((sKey:sub(1,1)=="[" and "") or ".")..sKey)
116 |
117 | if tTmp2 and next(tTmp2) then
118 | if bToRepear then
119 | table.insert(tRepear, sNewLine..sTab..sLongKey..table.concat(tTmp2, "")..";")
120 | else
121 | table.insert(tTmp, prefix..table.concat(tTmp2, ""));
122 | bEmpty = false
123 | end
124 | end
125 |
126 | if tRepear2 and next(tRepear2) then
127 | table.insert(tRepear, table.concat(tRepear2, ""))
128 | end
129 | else
130 | local sValue = ((type(value) == "string") and safestring(value)) or tostring(value);
131 | if bToRepear then
132 | table.insert(tRepear, sNewLine..sTab..sLongKey..sKey.."="..sValue..";")
133 | else
134 | table.insert(tTmp, prefix..sTabs..sTab..sKey.."="..sValue)
135 | bEmpty = false
136 | end
137 | end
138 |
139 | end
140 |
141 | if sTableName~="" then
142 | if bEmpty then
143 | table.insert(tTmp, "}")
144 | else
145 | table.insert(tTmp, sNewLine..sTabs.."}")
146 | end
147 | end
148 |
149 | return tTmp, tRepear
150 | end
151 |
152 | local ret=nil
153 | if sTableName=="return" then
154 | ret="temp"
155 | sTableName=ret
156 | end
157 |
158 | local tResult, tRepear = SerializeInternal(tTable,sTableName,sTab)
159 | local prefix, suffix = (ret and "return ") or "", ""
160 |
161 |
162 | if tRepear and next(tRepear) then
163 | if not ret then
164 | prefix = sTableName.."="
165 | end
166 | if tResult[1] and tResult[1]:sub(1,1) == "\t" then
167 | tResult[1] = tResult[1]:sub(2)
168 | end
169 | prefix = prefix.."(function()"..sNewLine..sTab.."local keys={};"..sNewLine..sTab.."local "
170 | suffix = ";"..sNewLine..sTab..table.concat(tRepear, "")..sNewLine..sTab.."return "..sTableName..";"..sNewLine.."end)()"
171 | elseif ret then
172 | local st,ed = string.find(tResult[1],ret.."=",1,true)
173 | if st and ed then
174 | tResult[1] = string.sub(tResult[1], ed+1)
175 | end
176 | end
177 |
178 | collectgarbage()
179 | --print(sResult)
180 | return prefix..table.concat(tResult, "")..suffix
181 | end
182 |
183 |
184 |
185 | -- local seri=serialize(table1,"testtable").."\n return testtable"
186 | -- print(seri)
187 | -- local file = io.open("D:\\xxx.txt","w")
188 | -- file:write(seri)
189 | -- for i=0,1000 do
190 | -- seri=serialize(assert(loadstring(seri))(),"testtable").."\n return testtable"
191 | -- end
192 | -- print(seri)
193 |
--------------------------------------------------------------------------------
/lua/serialize3.lua:
--------------------------------------------------------------------------------
1 | --local table1 = _G
2 |
3 | -- local table0 = {"self key", name_test = 1, }
4 | -- table0.self = table0
5 | -- table0[table0]={"oiewrhter"}
6 | -- local table1={nil,nil,nil, recovery = 1, recovery0 = 2, recovery1 = 5, [{x = 1, "no linked table as key 1"}]={"nn1"},"test1", [table0] = table0}
7 | -- local table2={table1,nil,nil,[{x = 2, "no linked table as key 2",table1}]={"nn2"},"test2"}
8 | -- local table3={table1,table2,nil,[{x = 3, "no \\ \n linked table as key 3",table1,table2,[{"no linked table as key 3.1"}]="3.1"}]={"nn3"},"test3"}
9 | -- table1[1]=table3
10 | -- table1["numbers test"] = {0/0, -1/0, 1/0, 1234567890.1234567890, 1,2,3,4,5,6, 6.2 }
11 | -- table1["boolean test"] = {true, false}
12 | -- table1["string test"] = "\0\1\2\3\4\5\6\7\8\9\10\t\n\\"
13 | -- table1["order test"] = {"1", "2", [5] = "5", "3 or 6?"}
14 | -- table1["lng str"] = "too long string copy test."
15 | -- table1["lng str copy"] = table1["lng str"]
16 | -- table1[table1["lng str"]] = "lng str as key"
17 | -- table1["function test"] = function() return "test" end
18 | -- table1[table1["function test"]] = "function as key"
19 | --table1[table1]={[table1]={[table1]="multi key test"}}
20 | --table1["nodes test"] = dofile("nodes.tbl")
21 | -- table1[2]=table2
22 | -- table1[3]=table2
23 | -- table2[2]=table2
24 | -- table2[3]=table3
25 | -- table3[3]=table1
26 | -- table1[table1]="table1.1"
27 | -- table1[table2]="table1.2"
28 | -- table1[table3]="table1.3"
29 | -- table2[table1]=table1
30 | -- table2[table2]=table2
31 | -- table2[table3]=table3
32 | -- table3[table1]=table1
33 |
34 | --table1._G=_G
35 |
36 |
37 |
38 | -- local seri=serialize(table1,"testtable").."\n return testtable"
39 | -- print(seri)
40 | -- local file = io.open("D:\\xxx.txt","w")
41 | -- file:write(seri)
42 | -- for i=0,1000 do
43 | -- seri=serialize(assert(loadstring(seri))(),"testtable").."\n return testtable"
44 | -- end
45 | -- print(seri)
46 |
47 | --[[
48 | return ({
49 | value1,
50 | value2,
51 | key1 = value3,
52 | key2 = value4,
53 | ...
54 | function recovery(self)
55 |
56 | end
57 | }):recovery()
58 | ]]
59 | function safe_string(value, cache)
60 | if type(value) == "string" then
61 | local c = cache and cache[value]
62 | if c then
63 | return c
64 | end
65 |
66 | local v = '"'..string.gsub(value, "([\\\10\13%c%z\"])([0-9]?)", function(chr, digit)
67 | local b = string.byte(chr)
68 | if #digit == 1 then
69 | if string.len(b) == 1 then return "\\00"..b..digit end
70 | if string.len(b) == 2 then return "\\0"..b..digit end
71 | end
72 | return "\\"..b..digit
73 | end)..'"'
74 |
75 | if cache then
76 | cache[value] = v
77 | end
78 |
79 | return v
80 |
81 | --return string.format('%q', value)
82 | elseif type(value) == "number" then
83 | if not ( (value > 0) or (value < 0) or (value == 0) ) then -- indeterminate form
84 | return "0/0"
85 | elseif value == 1/0 then -- infinity
86 | return "1/0"
87 | elseif value == -1/0 then -- negative infinity
88 | return "-1/0"
89 | else
90 | return tostring(value)
91 | end
92 | elseif type(value) == "function" then
93 | local ok, dump = pcall(string.dump, value)
94 | if ok then
95 | return "loadstring("..safe_string(dump, cache)..")"
96 | end
97 | elseif type(value) == "boolean" then
98 | return tostring(value)
99 | elseif type(value) == "nil" then
100 | --return "nil"
101 | end
102 |
103 | return nil
104 | end
105 |
106 | local function safe_key(key, cache, safe_string_cache)
107 | local c = cache and cache[key]
108 | if c then
109 | return c[1], c[2]
110 | end
111 |
112 | local safe_name = string.match(key, "^([A-Za-z_]+[A-Za-z0-9_]*)$")
113 | local dot = "."
114 |
115 |
116 | if not safe_name then
117 | safe_name = "["..safe_string(key, safe_string_cache).."]"
118 | dot = ""
119 | end
120 |
121 | if cache then
122 | cache[key] = {safe_name, dot}
123 | end
124 | return safe_name, dot
125 | end
126 |
127 | local function testname(name)
128 | --[[assert( (type(name) == "table")
129 | or (name:sub(1,1) == ".")
130 | or (name:sub(1,1) == "[")
131 | or (name:sub(1,4) == "root")
132 | or (name:sub(1,3) == "key")
133 | )]]
134 | end
135 |
136 | function serialize(value, fnc_write, skip)
137 | if type(value) == "table" then
138 | local obj_map = {}
139 | local keys = {order = {}, links = {}}
140 | local recover = {}
141 | local recovery_name = "recovery"
142 | local deep = 0
143 | local return_string = (not fnc_write) and {}
144 | local safe_string_cache = {}
145 | local safe_key_cache = {}
146 |
147 | if return_string then
148 | fnc_write = function(text)
149 | table.insert(return_string, text)
150 | end
151 | end
152 |
153 |
154 | local function add_key_value(tabl, key, value)
155 | local links = keys.links[key]
156 | if not links then
157 | table.insert(keys.order, key)
158 | links = {}
159 | keys.links[key] = links
160 | end
161 | table.insert(links, {tabl = tabl, value = value})
162 | end
163 |
164 | local serialize_table
165 | local function serialize_value(value, name, dot, parent, prefix)
166 | if type(value) == "table" then
167 | local not_empty, deferred_creation = serialize_table(value, name, dot, parent, prefix);
168 | return (not_empty or (not deferred_creation)), deferred_creation
169 | else
170 | local serialized = safe_string(value, safe_string_cache)
171 | if serialized then
172 | if prefix then fnc_write(prefix) end
173 | fnc_write(serialized)
174 |
175 | if (type(value) == "function") then
176 | obj_map[value] = {name = name, dot = dot , parent = parent}
177 | end
178 | return true
179 | end
180 | end
181 | end
182 |
183 | local function tbl_new_line(not_empty, deep)
184 | return string.format((not_empty and ",\n%s") or "\n%s", string.rep("\t", deep))
185 | end
186 |
187 |
188 |
189 |
190 | local function serialize_by_index(tabl, prefix)
191 | local not_empty, last_index
192 |
193 | for index, value in ipairs(tabl) do
194 | if obj_map[value] then
195 | break
196 | end
197 | if (not skip) or not skip(tabl, index, value) then
198 | local value_prefix = ((not_empty and "") or prefix or "") .. tbl_new_line(not_empty, deep)
199 | local writen, deferred_creation = serialize_value(value, string.format("[%i]", index), nil, tabl, value_prefix)
200 | if writen then
201 | last_index = index
202 | not_empty = true
203 | elseif deferred_creation then
204 | return not_empty, index-1, deferred_creation
205 | else
206 | return not_empty, last_index
207 | end
208 | else
209 | break
210 | end
211 | end
212 | return not_empty, last_index
213 | end
214 |
215 | local function check_in_parents(obj, parent)
216 |
217 | if not parent then
218 | return false
219 | elseif obj == parent then
220 | return true
221 | end
222 | local info = obj_map[parent]
223 | return check_in_parents(obj, info.parent)
224 | end
225 |
226 | local function serialize_by_keys(tabl, last_index, parent, not_empty, prefix)
227 | local have_recover, parent_link, obj_as_key
228 | for key, value in pairs(tabl) do
229 | if (not skip) or not skip(tabl, key, value) then
230 |
231 |
232 | if (type(key) == "table")
233 | or (type(key) == "function")
234 | then
235 | obj_as_key = true
236 | add_key_value(tabl, key, value)
237 | elseif (not last_index)
238 | or (type(key) ~= "number")
239 | or (key > last_index)
240 | or (key < 1)
241 | or (key > math.floor(key))
242 | then
243 | local name, dot = safe_key(key, safe_key_cache, safe_string_cache)
244 | local value_prefix = string.format("%s%s%s=", ((not_empty and "") or prefix or ""), tbl_new_line(not_empty, deep), name)
245 | local info = obj_map[value]
246 | if info then
247 | parent_link = check_in_parents(value, parent)
248 | have_recover = true
249 | table.insert(recover, {tabl = tabl, dot = dot, name = name, value = value})
250 | else
251 | local writed, deferred_creation = serialize_value( value,
252 | name,
253 | dot,
254 | tabl,
255 | value_prefix )
256 | if writed then
257 | not_empty = true
258 | elseif deferred_creation then
259 | have_recover = true
260 | end
261 | end
262 | end
263 | end
264 | end
265 | return not_empty, have_recover and not(parent_link or obj_as_key)
266 | end
267 |
268 | function serialize_table(tabl, name, dot, parent, prefix, open)
269 | assert(obj_map[tabl] == nil)
270 | obj_map[tabl] = {name = name, dot = dot , parent = parent}
271 | testname(name)
272 |
273 | if prefix then
274 | prefix = prefix.."{"
275 | else
276 | prefix = "{"
277 | end
278 |
279 | deep = deep + 1
280 |
281 | local not_empty, last_index, deferred_creation = serialize_by_index(tabl, prefix)
282 |
283 |
284 | if not_empty then
285 | serialize_by_keys(tabl, last_index, parent, not_empty)
286 | else
287 | not_empty, deferred_creation = serialize_by_keys(tabl, last_index, parent, not_empty, prefix)
288 | end
289 |
290 | deep = deep - 1
291 | if not open then
292 | if not_empty then
293 | fnc_write(string.format("\n%s}", string.rep("\t", deep)))
294 | elseif deferred_creation and parent then
295 | obj_map[tabl].deferred_creation = deferred_creation
296 | else
297 | fnc_write(prefix)
298 | fnc_write("}")
299 | end
300 | end
301 | return not_empty, deferred_creation
302 | end
303 |
304 | local get_key, format_key
305 |
306 | function format_key(key)
307 | if obj_map[key] then
308 | return string.format("[%s]", get_key(key))
309 | else
310 | return key
311 | end
312 | end
313 |
314 |
315 |
316 | function get_key(obj)
317 | local key = {}
318 | local info = obj_map[obj]
319 | local open_objects = 0
320 | local dot = ""
321 | while info do
322 | if info.deferred_creation then
323 | table.insert(key, 1, "={")
324 | open_objects = open_objects + 1
325 | info.deferred_creation = false;
326 | else
327 | table.insert(key, 1, dot)
328 | end
329 | dot = info.dot or ""
330 | table.insert(key, 1, format_key(info.name))
331 | info = obj_map[info.parent]
332 | end
333 | return table.concat(key), open_objects
334 | end
335 |
336 | fnc_write("(")
337 |
338 | if value[recovery_name] then
339 | local indx = 0
340 | local new_name
341 | repeat
342 | new_name = recovery_name..indx
343 | indx = indx + 1
344 | until not value[new_name]
345 | recovery_name = new_name
346 | end
347 |
348 | local not_empty = serialize_table(value, "root", nil, nil, nil, true)
349 |
350 | -- recover links
351 | if next(keys.order) or next(recover) then
352 | deep = deep + 2
353 | local tabs = string.rep("\t", deep)
354 | fnc_write(string.format("%s%s=function(root)\n\t\troot.%s=nil;\n",
355 | ((not_empty and ",\n\t") or ""), recovery_name, recovery_name))
356 | local idx = 0
357 | if next(keys.order) then
358 |
359 | local local_key = "\t\tlocal key={};\n"
360 | repeat
361 | local keys_old = keys
362 | keys = {order = {}, links = {}}
363 | for _, key in ipairs(keys_old.order) do
364 | if not obj_map[key] then
365 | idx = idx + 1
366 |
367 | local key_name = string.format("key[%i]", idx)
368 | local prefix = string.format("%s\t\t%s=", local_key, key_name)
369 | if serialize_value(key, key_name, nil, nil, prefix) then
370 | fnc_write(";\n")
371 | local_key = ""
372 | end
373 | end
374 | if obj_map[key] then
375 | for _, link in pairs(keys_old.links[key]) do
376 | if obj_map[link.value] then
377 | table.insert(recover, {tabl = link.tabl, name = key, value = link.value})
378 | elseif serialize_value( link.value,
379 | key,
380 | nil,
381 | link.tabl,
382 | string.format("%s%s[%s]=", tabs, get_key(link.tabl), get_key(key)))
383 | then
384 | fnc_write(";\n")
385 | end
386 | end
387 | end
388 | end
389 | until not next(keys.order)
390 | end
391 | for _, rec in ipairs(recover) do
392 | local in_key, open_objects = get_key(rec.tabl)
393 | local dot = ""
394 | if open_objects == 0 and rec.dot then
395 | dot = rec.dot
396 | end
397 |
398 | fnc_write(string.format("%s%s%s%s=%s%s;\n", tabs, in_key, dot, format_key(rec.name), get_key(rec.value), string.rep("}", open_objects)))
399 | end
400 | fnc_write(string.format("\t\treturn root;\n\tend\n}):%s()", recovery_name))
401 | else
402 | fnc_write("})")
403 | end
404 | if return_string then
405 | return table.concat(return_string)
406 | end
407 | else
408 | local serialized = safe_string(value)
409 | if serialized then
410 | if fnc_write then
411 | fnc_write(serialized)
412 | else
413 | return serialized
414 | end
415 | end
416 | end
417 | end
418 | --[[
419 | local buf
420 | function step_by_step(text)
421 | io.write(text)
422 | table.insert(buf, text)
423 | --io.read(1)
424 | end
425 |
426 | for i = 1, 1 do
427 | buf = {}
428 | step_by_step("return ")
429 | serialize(table1, step_by_step)
430 | f = io.open("rex.txt", "wb")
431 | f:write(table.concat(buf))
432 | f:close()
433 | local t, e = loadstring(table.concat(buf))
434 | print(e)
435 | table1 = t()
436 | end]]
437 |
--------------------------------------------------------------------------------
/lua/socket.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- LuaSocket helper module
3 | -- Author: Diego Nehab
4 | -- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
5 | -----------------------------------------------------------------------------
6 |
7 | -----------------------------------------------------------------------------
8 | -- Declare module and import dependencies
9 | -----------------------------------------------------------------------------
10 | local base = _G
11 | local string = require("string")
12 | local math = require("math")
13 | local socket = require("socket.core")
14 | module("socket")
15 |
16 | -----------------------------------------------------------------------------
17 | -- Exported auxiliar functions
18 | -----------------------------------------------------------------------------
19 | function connect(address, port, laddress, lport)
20 | local sock, err = socket.tcp()
21 | if not sock then return nil, err end
22 | if laddress then
23 | local res, err = sock:bind(laddress, lport, -1)
24 | if not res then return nil, err end
25 | end
26 | local res, err = sock:connect(address, port)
27 | if not res then return nil, err end
28 | return sock
29 | end
30 |
31 | function bind(host, port, backlog)
32 | local sock, err = socket.tcp()
33 | if not sock then return nil, err end
34 | sock:setoption("reuseaddr", true)
35 | local res, err = sock:bind(host, port)
36 | if not res then return nil, err end
37 | res, err = sock:listen(backlog)
38 | if not res then return nil, err end
39 | return sock
40 | end
41 |
42 | try = newtry()
43 |
44 | function choose(table)
45 | return function(name, opt1, opt2)
46 | if base.type(name) ~= "string" then
47 | name, opt1, opt2 = "default", name, opt1
48 | end
49 | local f = table[name or "nil"]
50 | if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
51 | else return f(opt1, opt2) end
52 | end
53 | end
54 |
55 | -----------------------------------------------------------------------------
56 | -- Socket sources and sinks, conforming to LTN12
57 | -----------------------------------------------------------------------------
58 | -- create namespaces inside LuaSocket namespace
59 | sourcet = {}
60 | sinkt = {}
61 |
62 | BLOCKSIZE = 2048
63 |
64 | sinkt["close-when-done"] = function(sock)
65 | return base.setmetatable({
66 | getfd = function() return sock:getfd() end,
67 | dirty = function() return sock:dirty() end
68 | }, {
69 | __call = function(self, chunk, err)
70 | if not chunk then
71 | sock:close()
72 | return 1
73 | else return sock:send(chunk) end
74 | end
75 | })
76 | end
77 |
78 | sinkt["keep-open"] = function(sock)
79 | return base.setmetatable({
80 | getfd = function() return sock:getfd() end,
81 | dirty = function() return sock:dirty() end
82 | }, {
83 | __call = function(self, chunk, err)
84 | if chunk then return sock:send(chunk)
85 | else return 1 end
86 | end
87 | })
88 | end
89 |
90 | sinkt["default"] = sinkt["keep-open"]
91 |
92 | sink = choose(sinkt)
93 |
94 | sourcet["by-length"] = function(sock, length)
95 | return base.setmetatable({
96 | getfd = function() return sock:getfd() end,
97 | dirty = function() return sock:dirty() end
98 | }, {
99 | __call = function()
100 | if length <= 0 then return nil end
101 | local size = math.min(socket.BLOCKSIZE, length)
102 | local chunk, err = sock:receive(size)
103 | if err then return nil, err end
104 | length = length - string.len(chunk)
105 | return chunk
106 | end
107 | })
108 | end
109 |
110 | sourcet["until-closed"] = function(sock)
111 | local done
112 | return base.setmetatable({
113 | getfd = function() return sock:getfd() end,
114 | dirty = function() return sock:dirty() end
115 | }, {
116 | __call = function()
117 | if done then return nil end
118 | local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
119 | if not err then return chunk
120 | elseif err == "closed" then
121 | sock:close()
122 | done = 1
123 | return partial
124 | else return nil, err end
125 | end
126 | })
127 | end
128 |
129 |
130 | sourcet["default"] = sourcet["until-closed"]
131 |
132 | source = choose(sourcet)
133 |
134 |
--------------------------------------------------------------------------------
/lua/socket/ftp.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- FTP support for the Lua language
3 | -- LuaSocket toolkit.
4 | -- Author: Diego Nehab
5 | -- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module and import dependencies
10 | -----------------------------------------------------------------------------
11 | local base = _G
12 | local table = require("table")
13 | local string = require("string")
14 | local math = require("math")
15 | local socket = require("socket")
16 | local url = require("socket.url")
17 | local tp = require("socket.tp")
18 | local ltn12 = require("ltn12")
19 | module("socket.ftp")
20 |
21 | -----------------------------------------------------------------------------
22 | -- Program constants
23 | -----------------------------------------------------------------------------
24 | -- timeout in seconds before the program gives up on a connection
25 | TIMEOUT = 60
26 | -- default port for ftp service
27 | PORT = 21
28 | -- this is the default anonymous password. used when no password is
29 | -- provided in url. should be changed to your e-mail.
30 | USER = "ftp"
31 | PASSWORD = "anonymous@anonymous.org"
32 |
33 | -----------------------------------------------------------------------------
34 | -- Low level FTP API
35 | -----------------------------------------------------------------------------
36 | local metat = { __index = {} }
37 |
38 | function open(server, port, create)
39 | local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create))
40 | local f = base.setmetatable({ tp = tp }, metat)
41 | -- make sure everything gets closed in an exception
42 | f.try = socket.newtry(function() f:close() end)
43 | return f
44 | end
45 |
46 | function metat.__index:portconnect()
47 | self.try(self.server:settimeout(TIMEOUT))
48 | self.data = self.try(self.server:accept())
49 | self.try(self.data:settimeout(TIMEOUT))
50 | end
51 |
52 | function metat.__index:pasvconnect()
53 | self.data = self.try(socket.tcp())
54 | self.try(self.data:settimeout(TIMEOUT))
55 | self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
56 | end
57 |
58 | function metat.__index:login(user, password)
59 | self.try(self.tp:command("user", user or USER))
60 | local code, reply = self.try(self.tp:check{"2..", 331})
61 | if code == 331 then
62 | self.try(self.tp:command("pass", password or PASSWORD))
63 | self.try(self.tp:check("2.."))
64 | end
65 | return 1
66 | end
67 |
68 | function metat.__index:pasv()
69 | self.try(self.tp:command("pasv"))
70 | local code, reply = self.try(self.tp:check("2.."))
71 | local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
72 | local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
73 | self.try(a and b and c and d and p1 and p2, reply)
74 | self.pasvt = {
75 | ip = string.format("%d.%d.%d.%d", a, b, c, d),
76 | port = p1*256 + p2
77 | }
78 | if self.server then
79 | self.server:close()
80 | self.server = nil
81 | end
82 | return self.pasvt.ip, self.pasvt.port
83 | end
84 |
85 | function metat.__index:port(ip, port)
86 | self.pasvt = nil
87 | if not ip then
88 | ip, port = self.try(self.tp:getcontrol():getsockname())
89 | self.server = self.try(socket.bind(ip, 0))
90 | ip, port = self.try(self.server:getsockname())
91 | self.try(self.server:settimeout(TIMEOUT))
92 | end
93 | local pl = math.mod(port, 256)
94 | local ph = (port - pl)/256
95 | local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",")
96 | self.try(self.tp:command("port", arg))
97 | self.try(self.tp:check("2.."))
98 | return 1
99 | end
100 |
101 | function metat.__index:send(sendt)
102 | self.try(self.pasvt or self.server, "need port or pasv first")
103 | -- if there is a pasvt table, we already sent a PASV command
104 | -- we just get the data connection into self.data
105 | if self.pasvt then self:pasvconnect() end
106 | -- get the transfer argument and command
107 | local argument = sendt.argument or
108 | url.unescape(string.gsub(sendt.path or "", "^[/\\]", ""))
109 | if argument == "" then argument = nil end
110 | local command = sendt.command or "stor"
111 | -- send the transfer command and check the reply
112 | self.try(self.tp:command(command, argument))
113 | local code, reply = self.try(self.tp:check{"2..", "1.."})
114 | -- if there is not a a pasvt table, then there is a server
115 | -- and we already sent a PORT command
116 | if not self.pasvt then self:portconnect() end
117 | -- get the sink, source and step for the transfer
118 | local step = sendt.step or ltn12.pump.step
119 | local readt = {self.tp.c}
120 | local checkstep = function(src, snk)
121 | -- check status in control connection while downloading
122 | local readyt = socket.select(readt, nil, 0)
123 | if readyt[tp] then code = self.try(self.tp:check("2..")) end
124 | return step(src, snk)
125 | end
126 | local sink = socket.sink("close-when-done", self.data)
127 | -- transfer all data and check error
128 | self.try(ltn12.pump.all(sendt.source, sink, checkstep))
129 | if string.find(code, "1..") then self.try(self.tp:check("2..")) end
130 | -- done with data connection
131 | self.data:close()
132 | -- find out how many bytes were sent
133 | local sent = socket.skip(1, self.data:getstats())
134 | self.data = nil
135 | return sent
136 | end
137 |
138 | function metat.__index:receive(recvt)
139 | self.try(self.pasvt or self.server, "need port or pasv first")
140 | if self.pasvt then self:pasvconnect() end
141 | local argument = recvt.argument or
142 | url.unescape(string.gsub(recvt.path or "", "^[/\\]", ""))
143 | if argument == "" then argument = nil end
144 | local command = recvt.command or "retr"
145 | self.try(self.tp:command(command, argument))
146 | local code = self.try(self.tp:check{"1..", "2.."})
147 | if not self.pasvt then self:portconnect() end
148 | local source = socket.source("until-closed", self.data)
149 | local step = recvt.step or ltn12.pump.step
150 | self.try(ltn12.pump.all(source, recvt.sink, step))
151 | if string.find(code, "1..") then self.try(self.tp:check("2..")) end
152 | self.data:close()
153 | self.data = nil
154 | return 1
155 | end
156 |
157 | function metat.__index:cwd(dir)
158 | self.try(self.tp:command("cwd", dir))
159 | self.try(self.tp:check(250))
160 | return 1
161 | end
162 |
163 | function metat.__index:type(type)
164 | self.try(self.tp:command("type", type))
165 | self.try(self.tp:check(200))
166 | return 1
167 | end
168 |
169 | function metat.__index:greet()
170 | local code = self.try(self.tp:check{"1..", "2.."})
171 | if string.find(code, "1..") then self.try(self.tp:check("2..")) end
172 | return 1
173 | end
174 |
175 | function metat.__index:quit()
176 | self.try(self.tp:command("quit"))
177 | self.try(self.tp:check("2.."))
178 | return 1
179 | end
180 |
181 | function metat.__index:close()
182 | if self.data then self.data:close() end
183 | if self.server then self.server:close() end
184 | return self.tp:close()
185 | end
186 |
187 | -----------------------------------------------------------------------------
188 | -- High level FTP API
189 | -----------------------------------------------------------------------------
190 | local function override(t)
191 | if t.url then
192 | local u = url.parse(t.url)
193 | for i,v in base.pairs(t) do
194 | u[i] = v
195 | end
196 | return u
197 | else return t end
198 | end
199 |
200 | local function tput(putt)
201 | putt = override(putt)
202 | socket.try(putt.host, "missing hostname")
203 | local f = open(putt.host, putt.port, putt.create)
204 | f:greet()
205 | f:login(putt.user, putt.password)
206 | if putt.type then f:type(putt.type) end
207 | f:pasv()
208 | local sent = f:send(putt)
209 | f:quit()
210 | f:close()
211 | return sent
212 | end
213 |
214 | local default = {
215 | path = "/",
216 | scheme = "ftp"
217 | }
218 |
219 | local function parse(u)
220 | local t = socket.try(url.parse(u, default))
221 | socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
222 | socket.try(t.host, "missing hostname")
223 | local pat = "^type=(.)$"
224 | if t.params then
225 | t.type = socket.skip(2, string.find(t.params, pat))
226 | socket.try(t.type == "a" or t.type == "i",
227 | "invalid type '" .. t.type .. "'")
228 | end
229 | return t
230 | end
231 |
232 | local function sput(u, body)
233 | local putt = parse(u)
234 | putt.source = ltn12.source.string(body)
235 | return tput(putt)
236 | end
237 |
238 | put = socket.protect(function(putt, body)
239 | if base.type(putt) == "string" then return sput(putt, body)
240 | else return tput(putt) end
241 | end)
242 |
243 | local function tget(gett)
244 | gett = override(gett)
245 | socket.try(gett.host, "missing hostname")
246 | local f = open(gett.host, gett.port, gett.create)
247 | f:greet()
248 | f:login(gett.user, gett.password)
249 | if gett.type then f:type(gett.type) end
250 | f:pasv()
251 | f:receive(gett)
252 | f:quit()
253 | return f:close()
254 | end
255 |
256 | local function sget(u)
257 | local gett = parse(u)
258 | local t = {}
259 | gett.sink = ltn12.sink.table(t)
260 | tget(gett)
261 | return table.concat(t)
262 | end
263 |
264 | command = socket.protect(function(cmdt)
265 | cmdt = override(cmdt)
266 | socket.try(cmdt.host, "missing hostname")
267 | socket.try(cmdt.command, "missing command")
268 | local f = open(cmdt.host, cmdt.port, cmdt.create)
269 | f:greet()
270 | f:login(cmdt.user, cmdt.password)
271 | f.try(f.tp:command(cmdt.command, cmdt.argument))
272 | if cmdt.check then f.try(f.tp:check(cmdt.check)) end
273 | f:quit()
274 | return f:close()
275 | end)
276 |
277 | get = socket.protect(function(gett)
278 | if base.type(gett) == "string" then return sget(gett)
279 | else return tget(gett) end
280 | end)
281 |
282 |
--------------------------------------------------------------------------------
/lua/socket/http.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- HTTP/1.1 client support for the Lua language.
3 | -- LuaSocket toolkit.
4 | -- Author: Diego Nehab
5 | -- RCS ID: $Id: http.lua,v 1.70 2007/03/12 04:08:40 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module and import dependencies
10 | -------------------------------------------------------------------------------
11 | local socket = require("socket")
12 | local url = require("socket.url")
13 | local ltn12 = require("ltn12")
14 | local mime = require("mime")
15 | local string = require("string")
16 | local base = _G
17 | local table = require("table")
18 | module("socket.http")
19 |
20 | -----------------------------------------------------------------------------
21 | -- Program constants
22 | -----------------------------------------------------------------------------
23 | -- connection timeout in seconds
24 | TIMEOUT = 60
25 | -- default port for document retrieval
26 | PORT = 80
27 | -- user agent field sent in request
28 | USERAGENT = socket._VERSION
29 |
30 | -----------------------------------------------------------------------------
31 | -- Reads MIME headers from a connection, unfolding where needed
32 | -----------------------------------------------------------------------------
33 | local function receiveheaders(sock, headers)
34 | local line, name, value, err
35 | headers = headers or {}
36 | -- get first line
37 | line, err = sock:receive()
38 | if err then return nil, err end
39 | -- headers go until a blank line is found
40 | while line ~= "" do
41 | -- get field-name and value
42 | name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
43 | if not (name and value) then return nil, "malformed reponse headers" end
44 | name = string.lower(name)
45 | -- get next line (value might be folded)
46 | line, err = sock:receive()
47 | if err then return nil, err end
48 | -- unfold any folded values
49 | while string.find(line, "^%s") do
50 | value = value .. line
51 | line = sock:receive()
52 | if err then return nil, err end
53 | end
54 | -- save pair in table
55 | if headers[name] then headers[name] = headers[name] .. ", " .. value
56 | else headers[name] = value end
57 | end
58 | return headers
59 | end
60 |
61 | -----------------------------------------------------------------------------
62 | -- Extra sources and sinks
63 | -----------------------------------------------------------------------------
64 | socket.sourcet["http-chunked"] = function(sock, headers)
65 | return base.setmetatable({
66 | getfd = function() return sock:getfd() end,
67 | dirty = function() return sock:dirty() end
68 | }, {
69 | __call = function()
70 | -- get chunk size, skip extention
71 | local line, err = sock:receive()
72 | if err then return nil, err end
73 | local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
74 | if not size then return nil, "invalid chunk size" end
75 | -- was it the last chunk?
76 | if size > 0 then
77 | -- if not, get chunk and skip terminating CRLF
78 | local chunk, err, part = sock:receive(size)
79 | if chunk then sock:receive() end
80 | return chunk, err
81 | else
82 | -- if it was, read trailers into headers table
83 | headers, err = receiveheaders(sock, headers)
84 | if not headers then return nil, err end
85 | end
86 | end
87 | })
88 | end
89 |
90 | socket.sinkt["http-chunked"] = function(sock)
91 | return base.setmetatable({
92 | getfd = function() return sock:getfd() end,
93 | dirty = function() return sock:dirty() end
94 | }, {
95 | __call = function(self, chunk, err)
96 | if not chunk then return sock:send("0\r\n\r\n") end
97 | local size = string.format("%X\r\n", string.len(chunk))
98 | return sock:send(size .. chunk .. "\r\n")
99 | end
100 | })
101 | end
102 |
103 | -----------------------------------------------------------------------------
104 | -- Low level HTTP API
105 | -----------------------------------------------------------------------------
106 | local metat = { __index = {} }
107 |
108 | function open(host, port, create)
109 | -- create socket with user connect function, or with default
110 | local c = socket.try((create or socket.tcp)())
111 | local h = base.setmetatable({ c = c }, metat)
112 | -- create finalized try
113 | h.try = socket.newtry(function() h:close() end)
114 | -- set timeout before connecting
115 | h.try(c:settimeout(TIMEOUT))
116 | h.try(c:connect(host, port or PORT))
117 | -- here everything worked
118 | return h
119 | end
120 |
121 | function metat.__index:sendrequestline(method, uri)
122 | local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
123 | return self.try(self.c:send(reqline))
124 | end
125 |
126 | function metat.__index:sendheaders(headers)
127 | local h = "\r\n"
128 | for i, v in base.pairs(headers) do
129 | h = i .. ": " .. v .. "\r\n" .. h
130 | end
131 | self.try(self.c:send(h))
132 | return 1
133 | end
134 |
135 | function metat.__index:sendbody(headers, source, step)
136 | source = source or ltn12.source.empty()
137 | step = step or ltn12.pump.step
138 | -- if we don't know the size in advance, send chunked and hope for the best
139 | local mode = "http-chunked"
140 | if headers["content-length"] then mode = "keep-open" end
141 | return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
142 | end
143 |
144 | function metat.__index:receivestatusline()
145 | local status = self.try(self.c:receive(5))
146 | -- identify HTTP/0.9 responses, which do not contain a status line
147 | -- this is just a heuristic, but is what the RFC recommends
148 | if status ~= "HTTP/" then return nil, status end
149 | -- otherwise proceed reading a status line
150 | status = self.try(self.c:receive("*l", status))
151 | local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
152 | return self.try(base.tonumber(code), status)
153 | end
154 |
155 | function metat.__index:receiveheaders()
156 | return self.try(receiveheaders(self.c))
157 | end
158 |
159 | function metat.__index:receivebody(headers, sink, step)
160 | sink = sink or ltn12.sink.null()
161 | step = step or ltn12.pump.step
162 | local length = base.tonumber(headers["content-length"])
163 | local t = headers["transfer-encoding"] -- shortcut
164 | local mode = "default" -- connection close
165 | if t and t ~= "identity" then mode = "http-chunked"
166 | elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
167 | return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
168 | sink, step))
169 | end
170 |
171 | function metat.__index:receive09body(status, sink, step)
172 | local source = ltn12.source.rewind(socket.source("until-closed", self.c))
173 | source(status)
174 | return self.try(ltn12.pump.all(source, sink, step))
175 | end
176 |
177 | function metat.__index:close()
178 | return self.c:close()
179 | end
180 |
181 | -----------------------------------------------------------------------------
182 | -- High level HTTP API
183 | -----------------------------------------------------------------------------
184 | local function adjusturi(reqt)
185 | local u = reqt
186 | -- if there is a proxy, we need the full url. otherwise, just a part.
187 | if not reqt.proxy and not PROXY then
188 | u = {
189 | path = socket.try(reqt.path, "invalid path 'nil'"),
190 | params = reqt.params,
191 | query = reqt.query,
192 | fragment = reqt.fragment
193 | }
194 | end
195 | return url.build(u)
196 | end
197 |
198 | local function adjustproxy(reqt)
199 | local proxy = reqt.proxy or PROXY
200 | if proxy then
201 | proxy = url.parse(proxy)
202 | return proxy.host, proxy.port or 3128
203 | else
204 | return reqt.host, reqt.port
205 | end
206 | end
207 |
208 | local function adjustheaders(reqt)
209 | -- default headers
210 | local lower = {
211 | ["user-agent"] = USERAGENT,
212 | ["host"] = reqt.host,
213 | ["connection"] = "close, TE",
214 | ["te"] = "trailers"
215 | }
216 | -- if we have authentication information, pass it along
217 | if reqt.user and reqt.password then
218 | lower["authorization"] =
219 | "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
220 | end
221 | -- override with user headers
222 | for i,v in base.pairs(reqt.headers or lower) do
223 | lower[string.lower(i)] = v
224 | end
225 | return lower
226 | end
227 |
228 | -- default url parts
229 | local default = {
230 | host = "",
231 | port = PORT,
232 | path ="/",
233 | scheme = "http"
234 | }
235 |
236 | local function adjustrequest(reqt)
237 | -- parse url if provided
238 | local nreqt = reqt.url and url.parse(reqt.url, default) or {}
239 | -- explicit components override url
240 | for i,v in base.pairs(reqt) do nreqt[i] = v end
241 | if nreqt.port == "" then nreqt.port = 80 end
242 | socket.try(nreqt.host and nreqt.host ~= "",
243 | "invalid host '" .. base.tostring(nreqt.host) .. "'")
244 | -- compute uri if user hasn't overriden
245 | nreqt.uri = reqt.uri or adjusturi(nreqt)
246 | -- ajust host and port if there is a proxy
247 | nreqt.host, nreqt.port = adjustproxy(nreqt)
248 | -- adjust headers in request
249 | nreqt.headers = adjustheaders(nreqt)
250 | return nreqt
251 | end
252 |
253 | local function shouldredirect(reqt, code, headers)
254 | return headers.location and
255 | string.gsub(headers.location, "%s", "") ~= "" and
256 | (reqt.redirect ~= false) and
257 | (code == 301 or code == 302) and
258 | (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
259 | and (not reqt.nredirects or reqt.nredirects < 5)
260 | end
261 |
262 | local function shouldreceivebody(reqt, code)
263 | if reqt.method == "HEAD" then return nil end
264 | if code == 204 or code == 304 then return nil end
265 | if code >= 100 and code < 200 then return nil end
266 | return 1
267 | end
268 |
269 | -- forward declarations
270 | local trequest, tredirect
271 |
272 | function tredirect(reqt, location)
273 | local result, code, headers, status = trequest {
274 | -- the RFC says the redirect URL has to be absolute, but some
275 | -- servers do not respect that
276 | url = url.absolute(reqt.url, location),
277 | source = reqt.source,
278 | sink = reqt.sink,
279 | headers = reqt.headers,
280 | proxy = reqt.proxy,
281 | nredirects = (reqt.nredirects or 0) + 1,
282 | create = reqt.create
283 | }
284 | -- pass location header back as a hint we redirected
285 | headers = headers or {}
286 | headers.location = headers.location or location
287 | return result, code, headers, status
288 | end
289 |
290 | function trequest(reqt)
291 | -- we loop until we get what we want, or
292 | -- until we are sure there is no way to get it
293 | local nreqt = adjustrequest(reqt)
294 | local h = open(nreqt.host, nreqt.port, nreqt.create)
295 | -- send request line and headers
296 | h:sendrequestline(nreqt.method, nreqt.uri)
297 | h:sendheaders(nreqt.headers)
298 | -- if there is a body, send it
299 | if nreqt.source then
300 | h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
301 | end
302 | local code, status = h:receivestatusline()
303 | -- if it is an HTTP/0.9 server, simply get the body and we are done
304 | if not code then
305 | h:receive09body(status, nreqt.sink, nreqt.step)
306 | return 1, 200
307 | end
308 | local headers
309 | -- ignore any 100-continue messages
310 | while code == 100 do
311 | headers = h:receiveheaders()
312 | code, status = h:receivestatusline()
313 | end
314 | headers = h:receiveheaders()
315 | -- at this point we should have a honest reply from the server
316 | -- we can't redirect if we already used the source, so we report the error
317 | if shouldredirect(nreqt, code, headers) and not nreqt.source then
318 | h:close()
319 | return tredirect(reqt, headers.location)
320 | end
321 | -- here we are finally done
322 | if shouldreceivebody(nreqt, code) then
323 | h:receivebody(headers, nreqt.sink, nreqt.step)
324 | end
325 | h:close()
326 | return 1, code, headers, status
327 | end
328 |
329 | local function srequest(u, b)
330 | local t = {}
331 | local reqt = {
332 | url = u,
333 | sink = ltn12.sink.table(t)
334 | }
335 | if b then
336 | reqt.source = ltn12.source.string(b)
337 | reqt.headers = {
338 | ["content-length"] = string.len(b),
339 | ["content-type"] = "application/x-www-form-urlencoded"
340 | }
341 | reqt.method = "POST"
342 | end
343 | local code, headers, status = socket.skip(1, trequest(reqt))
344 | return table.concat(t), code, headers, status
345 | end
346 |
347 | request = socket.protect(function(reqt, body)
348 | if base.type(reqt) == "string" then return srequest(reqt, body)
349 | else return trequest(reqt) end
350 | end)
351 |
--------------------------------------------------------------------------------
/lua/socket/smtp.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- SMTP client support for the Lua language.
3 | -- LuaSocket toolkit.
4 | -- Author: Diego Nehab
5 | -- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module and import dependencies
10 | -----------------------------------------------------------------------------
11 | local base = _G
12 | local coroutine = require("coroutine")
13 | local string = require("string")
14 | local math = require("math")
15 | local os = require("os")
16 | local socket = require("socket")
17 | local tp = require("socket.tp")
18 | local ltn12 = require("ltn12")
19 | local mime = require("mime")
20 | module("socket.smtp")
21 |
22 | -----------------------------------------------------------------------------
23 | -- Program constants
24 | -----------------------------------------------------------------------------
25 | -- timeout for connection
26 | TIMEOUT = 60
27 | -- default server used to send e-mails
28 | SERVER = "localhost"
29 | -- default port
30 | PORT = 25
31 | -- domain used in HELO command and default sendmail
32 | -- If we are under a CGI, try to get from environment
33 | DOMAIN = os.getenv("SERVER_NAME") or "localhost"
34 | -- default time zone (means we don't know)
35 | ZONE = "-0000"
36 |
37 | ---------------------------------------------------------------------------
38 | -- Low level SMTP API
39 | -----------------------------------------------------------------------------
40 | local metat = { __index = {} }
41 |
42 | function metat.__index:greet(domain)
43 | self.try(self.tp:check("2.."))
44 | self.try(self.tp:command("EHLO", domain or DOMAIN))
45 | return socket.skip(1, self.try(self.tp:check("2..")))
46 | end
47 |
48 | function metat.__index:mail(from)
49 | self.try(self.tp:command("MAIL", "FROM:" .. from))
50 | return self.try(self.tp:check("2.."))
51 | end
52 |
53 | function metat.__index:rcpt(to)
54 | self.try(self.tp:command("RCPT", "TO:" .. to))
55 | return self.try(self.tp:check("2.."))
56 | end
57 |
58 | function metat.__index:data(src, step)
59 | self.try(self.tp:command("DATA"))
60 | self.try(self.tp:check("3.."))
61 | self.try(self.tp:source(src, step))
62 | self.try(self.tp:send("\r\n.\r\n"))
63 | return self.try(self.tp:check("2.."))
64 | end
65 |
66 | function metat.__index:quit()
67 | self.try(self.tp:command("QUIT"))
68 | return self.try(self.tp:check("2.."))
69 | end
70 |
71 | function metat.__index:close()
72 | return self.tp:close()
73 | end
74 |
75 | function metat.__index:login(user, password)
76 | self.try(self.tp:command("AUTH", "LOGIN"))
77 | self.try(self.tp:check("3.."))
78 | self.try(self.tp:command(mime.b64(user)))
79 | self.try(self.tp:check("3.."))
80 | self.try(self.tp:command(mime.b64(password)))
81 | return self.try(self.tp:check("2.."))
82 | end
83 |
84 | function metat.__index:plain(user, password)
85 | local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
86 | self.try(self.tp:command("AUTH", auth))
87 | return self.try(self.tp:check("2.."))
88 | end
89 |
90 | function metat.__index:auth(user, password, ext)
91 | if not user or not password then return 1 end
92 | if string.find(ext, "AUTH[^\n]+LOGIN") then
93 | return self:login(user, password)
94 | elseif string.find(ext, "AUTH[^\n]+PLAIN") then
95 | return self:plain(user, password)
96 | else
97 | self.try(nil, "authentication not supported")
98 | end
99 | end
100 |
101 | -- send message or throw an exception
102 | function metat.__index:send(mailt)
103 | self:mail(mailt.from)
104 | if base.type(mailt.rcpt) == "table" then
105 | for i,v in base.ipairs(mailt.rcpt) do
106 | self:rcpt(v)
107 | end
108 | else
109 | self:rcpt(mailt.rcpt)
110 | end
111 | self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)
112 | end
113 |
114 | function open(server, port, create)
115 | local tp = socket.try(tp.connect(server or SERVER, port or PORT,
116 | TIMEOUT, create))
117 | local s = base.setmetatable({tp = tp}, metat)
118 | -- make sure tp is closed if we get an exception
119 | s.try = socket.newtry(function()
120 | s:close()
121 | end)
122 | return s
123 | end
124 |
125 | -- convert headers to lowercase
126 | local function lower_headers(headers)
127 | local lower = {}
128 | for i,v in base.pairs(headers or lower) do
129 | lower[string.lower(i)] = v
130 | end
131 | return lower
132 | end
133 |
134 | ---------------------------------------------------------------------------
135 | -- Multipart message source
136 | -----------------------------------------------------------------------------
137 | -- returns a hopefully unique mime boundary
138 | local seqno = 0
139 | local function newboundary()
140 | seqno = seqno + 1
141 | return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),
142 | math.random(0, 99999), seqno)
143 | end
144 |
145 | -- send_message forward declaration
146 | local send_message
147 |
148 | -- yield the headers all at once, it's faster
149 | local function send_headers(headers)
150 | local h = "\r\n"
151 | for i,v in base.pairs(headers) do
152 | h = i .. ': ' .. v .. "\r\n" .. h
153 | end
154 | coroutine.yield(h)
155 | end
156 |
157 | -- yield multipart message body from a multipart message table
158 | local function send_multipart(mesgt)
159 | -- make sure we have our boundary and send headers
160 | local bd = newboundary()
161 | local headers = lower_headers(mesgt.headers or {})
162 | headers['content-type'] = headers['content-type'] or 'multipart/mixed'
163 | headers['content-type'] = headers['content-type'] ..
164 | '; boundary="' .. bd .. '"'
165 | send_headers(headers)
166 | -- send preamble
167 | if mesgt.body.preamble then
168 | coroutine.yield(mesgt.body.preamble)
169 | coroutine.yield("\r\n")
170 | end
171 | -- send each part separated by a boundary
172 | for i, m in base.ipairs(mesgt.body) do
173 | coroutine.yield("\r\n--" .. bd .. "\r\n")
174 | send_message(m)
175 | end
176 | -- send last boundary
177 | coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
178 | -- send epilogue
179 | if mesgt.body.epilogue then
180 | coroutine.yield(mesgt.body.epilogue)
181 | coroutine.yield("\r\n")
182 | end
183 | end
184 |
185 | -- yield message body from a source
186 | local function send_source(mesgt)
187 | -- make sure we have a content-type
188 | local headers = lower_headers(mesgt.headers or {})
189 | headers['content-type'] = headers['content-type'] or
190 | 'text/plain; charset="iso-8859-1"'
191 | send_headers(headers)
192 | -- send body from source
193 | while true do
194 | local chunk, err = mesgt.body()
195 | if err then coroutine.yield(nil, err)
196 | elseif chunk then coroutine.yield(chunk)
197 | else break end
198 | end
199 | end
200 |
201 | -- yield message body from a string
202 | local function send_string(mesgt)
203 | -- make sure we have a content-type
204 | local headers = lower_headers(mesgt.headers or {})
205 | headers['content-type'] = headers['content-type'] or
206 | 'text/plain; charset="iso-8859-1"'
207 | send_headers(headers)
208 | -- send body from string
209 | coroutine.yield(mesgt.body)
210 | end
211 |
212 | -- message source
213 | function send_message(mesgt)
214 | if base.type(mesgt.body) == "table" then send_multipart(mesgt)
215 | elseif base.type(mesgt.body) == "function" then send_source(mesgt)
216 | else send_string(mesgt) end
217 | end
218 |
219 | -- set defaul headers
220 | local function adjust_headers(mesgt)
221 | local lower = lower_headers(mesgt.headers)
222 | lower["date"] = lower["date"] or
223 | os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
224 | lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
225 | -- this can't be overriden
226 | lower["mime-version"] = "1.0"
227 | return lower
228 | end
229 |
230 | function message(mesgt)
231 | mesgt.headers = adjust_headers(mesgt)
232 | -- create and return message source
233 | local co = coroutine.create(function() send_message(mesgt) end)
234 | return function()
235 | local ret, a, b = coroutine.resume(co)
236 | if ret then return a, b
237 | else return nil, a end
238 | end
239 | end
240 |
241 | ---------------------------------------------------------------------------
242 | -- High level SMTP API
243 | -----------------------------------------------------------------------------
244 | send = socket.protect(function(mailt)
245 | local s = open(mailt.server, mailt.port, mailt.create)
246 | local ext = s:greet(mailt.domain)
247 | s:auth(mailt.user, mailt.password, ext)
248 | s:send(mailt)
249 | s:quit()
250 | return s:close()
251 | end)
252 |
--------------------------------------------------------------------------------
/lua/socket/tp.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- Unified SMTP/FTP subsystem
3 | -- LuaSocket toolkit.
4 | -- Author: Diego Nehab
5 | -- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module and import dependencies
10 | -----------------------------------------------------------------------------
11 | local base = _G
12 | local string = require("string")
13 | local socket = require("socket")
14 | local ltn12 = require("ltn12")
15 | module("socket.tp")
16 |
17 | -----------------------------------------------------------------------------
18 | -- Program constants
19 | -----------------------------------------------------------------------------
20 | TIMEOUT = 60
21 |
22 | -----------------------------------------------------------------------------
23 | -- Implementation
24 | -----------------------------------------------------------------------------
25 | -- gets server reply (works for SMTP and FTP)
26 | local function get_reply(c)
27 | local code, current, sep
28 | local line, err = c:receive()
29 | local reply = line
30 | if err then return nil, err end
31 | code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
32 | if not code then return nil, "invalid server reply" end
33 | if sep == "-" then -- reply is multiline
34 | repeat
35 | line, err = c:receive()
36 | if err then return nil, err end
37 | current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
38 | reply = reply .. "\n" .. line
39 | -- reply ends with same code
40 | until code == current and sep == " "
41 | end
42 | return code, reply
43 | end
44 |
45 | -- metatable for sock object
46 | local metat = { __index = {} }
47 |
48 | function metat.__index:check(ok)
49 | local code, reply = get_reply(self.c)
50 | if not code then return nil, reply end
51 | if base.type(ok) ~= "function" then
52 | if base.type(ok) == "table" then
53 | for i, v in base.ipairs(ok) do
54 | if string.find(code, v) then
55 | return base.tonumber(code), reply
56 | end
57 | end
58 | return nil, reply
59 | else
60 | if string.find(code, ok) then return base.tonumber(code), reply
61 | else return nil, reply end
62 | end
63 | else return ok(base.tonumber(code), reply) end
64 | end
65 |
66 | function metat.__index:command(cmd, arg)
67 | if arg then
68 | return self.c:send(cmd .. " " .. arg.. "\r\n")
69 | else
70 | return self.c:send(cmd .. "\r\n")
71 | end
72 | end
73 |
74 | function metat.__index:sink(snk, pat)
75 | local chunk, err = c:receive(pat)
76 | return snk(chunk, err)
77 | end
78 |
79 | function metat.__index:send(data)
80 | return self.c:send(data)
81 | end
82 |
83 | function metat.__index:receive(pat)
84 | return self.c:receive(pat)
85 | end
86 |
87 | function metat.__index:getfd()
88 | return self.c:getfd()
89 | end
90 |
91 | function metat.__index:dirty()
92 | return self.c:dirty()
93 | end
94 |
95 | function metat.__index:getcontrol()
96 | return self.c
97 | end
98 |
99 | function metat.__index:source(source, step)
100 | local sink = socket.sink("keep-open", self.c)
101 | local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
102 | return ret, err
103 | end
104 |
105 | -- closes the underlying c
106 | function metat.__index:close()
107 | self.c:close()
108 | return 1
109 | end
110 |
111 | -- connect with server and return c object
112 | function connect(host, port, timeout, create)
113 | local c, e = (create or socket.tcp)()
114 | if not c then return nil, e end
115 | c:settimeout(timeout or TIMEOUT)
116 | local r, e = c:connect(host, port)
117 | if not r then
118 | c:close()
119 | return nil, e
120 | end
121 | return base.setmetatable({c = c}, metat)
122 | end
123 |
124 |
--------------------------------------------------------------------------------
/lua/socket/url.lua:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | -- URI parsing, composition and relative URL resolution
3 | -- LuaSocket toolkit.
4 | -- Author: Diego Nehab
5 | -- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $
6 | -----------------------------------------------------------------------------
7 |
8 | -----------------------------------------------------------------------------
9 | -- Declare module
10 | -----------------------------------------------------------------------------
11 | local string = require("string")
12 | local base = _G
13 | local table = require("table")
14 | module("socket.url")
15 |
16 | -----------------------------------------------------------------------------
17 | -- Module version
18 | -----------------------------------------------------------------------------
19 | _VERSION = "URL 1.0.1"
20 |
21 | -----------------------------------------------------------------------------
22 | -- Encodes a string into its escaped hexadecimal representation
23 | -- Input
24 | -- s: binary string to be encoded
25 | -- Returns
26 | -- escaped representation of string binary
27 | -----------------------------------------------------------------------------
28 | function escape(s)
29 | return string.gsub(s, "([^A-Za-z0-9_])", function(c)
30 | return string.format("%%%02x", string.byte(c))
31 | end)
32 | end
33 |
34 | -----------------------------------------------------------------------------
35 | -- Protects a path segment, to prevent it from interfering with the
36 | -- url parsing.
37 | -- Input
38 | -- s: binary string to be encoded
39 | -- Returns
40 | -- escaped representation of string binary
41 | -----------------------------------------------------------------------------
42 | local function make_set(t)
43 | local s = {}
44 | for i,v in base.ipairs(t) do
45 | s[t[i]] = 1
46 | end
47 | return s
48 | end
49 |
50 | -- these are allowed withing a path segment, along with alphanum
51 | -- other characters must be escaped
52 | local segment_set = make_set {
53 | "-", "_", ".", "!", "~", "*", "'", "(",
54 | ")", ":", "@", "&", "=", "+", "$", ",",
55 | }
56 |
57 | local function protect_segment(s)
58 | return string.gsub(s, "([^A-Za-z0-9_])", function (c)
59 | if segment_set[c] then return c
60 | else return string.format("%%%02x", string.byte(c)) end
61 | end)
62 | end
63 |
64 | -----------------------------------------------------------------------------
65 | -- Encodes a string into its escaped hexadecimal representation
66 | -- Input
67 | -- s: binary string to be encoded
68 | -- Returns
69 | -- escaped representation of string binary
70 | -----------------------------------------------------------------------------
71 | function unescape(s)
72 | return string.gsub(s, "%%(%x%x)", function(hex)
73 | return string.char(base.tonumber(hex, 16))
74 | end)
75 | end
76 |
77 | -----------------------------------------------------------------------------
78 | -- Builds a path from a base path and a relative path
79 | -- Input
80 | -- base_path
81 | -- relative_path
82 | -- Returns
83 | -- corresponding absolute path
84 | -----------------------------------------------------------------------------
85 | local function absolute_path(base_path, relative_path)
86 | if string.sub(relative_path, 1, 1) == "/" then return relative_path end
87 | local path = string.gsub(base_path, "[^/]*$", "")
88 | path = path .. relative_path
89 | path = string.gsub(path, "([^/]*%./)", function (s)
90 | if s ~= "./" then return s else return "" end
91 | end)
92 | path = string.gsub(path, "/%.$", "/")
93 | local reduced
94 | while reduced ~= path do
95 | reduced = path
96 | path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
97 | if s ~= "../../" then return "" else return s end
98 | end)
99 | end
100 | path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
101 | if s ~= "../.." then return "" else return s end
102 | end)
103 | return path
104 | end
105 |
106 | -----------------------------------------------------------------------------
107 | -- Parses a url and returns a table with all its parts according to RFC 2396
108 | -- The following grammar describes the names given to the URL parts
109 | -- ::= :///;?#
110 | -- ::= @:
111 | -- ::= [:]
112 | -- :: = {/}
113 | -- Input
114 | -- url: uniform resource locator of request
115 | -- default: table with default values for each field
116 | -- Returns
117 | -- table with the following fields, where RFC naming conventions have
118 | -- been preserved:
119 | -- scheme, authority, userinfo, user, password, host, port,
120 | -- path, params, query, fragment
121 | -- Obs:
122 | -- the leading '/' in {/} is considered part of
123 | -----------------------------------------------------------------------------
124 | function parse(url, default)
125 | -- initialize default parameters
126 | local parsed = {}
127 | for i,v in base.pairs(default or parsed) do parsed[i] = v end
128 | -- empty url is parsed to nil
129 | if not url or url == "" then return nil, "invalid url" end
130 | -- remove whitespace
131 | -- url = string.gsub(url, "%s", "")
132 | -- get fragment
133 | url = string.gsub(url, "#(.*)$", function(f)
134 | parsed.fragment = f
135 | return ""
136 | end)
137 | -- get scheme
138 | url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
139 | function(s) parsed.scheme = s; return "" end)
140 | -- get authority
141 | url = string.gsub(url, "^//([^/]*)", function(n)
142 | parsed.authority = n
143 | return ""
144 | end)
145 | -- get query stringing
146 | url = string.gsub(url, "%?(.*)", function(q)
147 | parsed.query = q
148 | return ""
149 | end)
150 | -- get params
151 | url = string.gsub(url, "%;(.*)", function(p)
152 | parsed.params = p
153 | return ""
154 | end)
155 | -- path is whatever was left
156 | if url ~= "" then parsed.path = url end
157 | local authority = parsed.authority
158 | if not authority then return parsed end
159 | authority = string.gsub(authority,"^([^@]*)@",
160 | function(u) parsed.userinfo = u; return "" end)
161 | authority = string.gsub(authority, ":([^:]*)$",
162 | function(p) parsed.port = p; return "" end)
163 | if authority ~= "" then parsed.host = authority end
164 | local userinfo = parsed.userinfo
165 | if not userinfo then return parsed end
166 | userinfo = string.gsub(userinfo, ":([^:]*)$",
167 | function(p) parsed.password = p; return "" end)
168 | parsed.user = userinfo
169 | return parsed
170 | end
171 |
172 | -----------------------------------------------------------------------------
173 | -- Rebuilds a parsed URL from its components.
174 | -- Components are protected if any reserved or unallowed characters are found
175 | -- Input
176 | -- parsed: parsed URL, as returned by parse
177 | -- Returns
178 | -- a stringing with the corresponding URL
179 | -----------------------------------------------------------------------------
180 | function build(parsed)
181 | local ppath = parse_path(parsed.path or "")
182 | local url = build_path(ppath)
183 | if parsed.params then url = url .. ";" .. parsed.params end
184 | if parsed.query then url = url .. "?" .. parsed.query end
185 | local authority = parsed.authority
186 | if parsed.host then
187 | authority = parsed.host
188 | if parsed.port then authority = authority .. ":" .. parsed.port end
189 | local userinfo = parsed.userinfo
190 | if parsed.user then
191 | userinfo = parsed.user
192 | if parsed.password then
193 | userinfo = userinfo .. ":" .. parsed.password
194 | end
195 | end
196 | if userinfo then authority = userinfo .. "@" .. authority end
197 | end
198 | if authority then url = "//" .. authority .. url end
199 | if parsed.scheme then url = parsed.scheme .. ":" .. url end
200 | if parsed.fragment then url = url .. "#" .. parsed.fragment end
201 | -- url = string.gsub(url, "%s", "")
202 | return url
203 | end
204 |
205 | -----------------------------------------------------------------------------
206 | -- Builds a absolute URL from a base and a relative URL according to RFC 2396
207 | -- Input
208 | -- base_url
209 | -- relative_url
210 | -- Returns
211 | -- corresponding absolute url
212 | -----------------------------------------------------------------------------
213 | function absolute(base_url, relative_url)
214 | if base.type(base_url) == "table" then
215 | base_parsed = base_url
216 | base_url = build(base_parsed)
217 | else
218 | base_parsed = parse(base_url)
219 | end
220 | local relative_parsed = parse(relative_url)
221 | if not base_parsed then return relative_url
222 | elseif not relative_parsed then return base_url
223 | elseif relative_parsed.scheme then return relative_url
224 | else
225 | relative_parsed.scheme = base_parsed.scheme
226 | if not relative_parsed.authority then
227 | relative_parsed.authority = base_parsed.authority
228 | if not relative_parsed.path then
229 | relative_parsed.path = base_parsed.path
230 | if not relative_parsed.params then
231 | relative_parsed.params = base_parsed.params
232 | if not relative_parsed.query then
233 | relative_parsed.query = base_parsed.query
234 | end
235 | end
236 | else
237 | relative_parsed.path = absolute_path(base_parsed.path or "",
238 | relative_parsed.path)
239 | end
240 | end
241 | return build(relative_parsed)
242 | end
243 | end
244 |
245 | -----------------------------------------------------------------------------
246 | -- Breaks a path into its segments, unescaping the segments
247 | -- Input
248 | -- path
249 | -- Returns
250 | -- segment: a table with one entry per segment
251 | -----------------------------------------------------------------------------
252 | function parse_path(path)
253 | local parsed = {}
254 | path = path or ""
255 | --path = string.gsub(path, "%s", "")
256 | string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
257 | for i = 1, table.getn(parsed) do
258 | parsed[i] = unescape(parsed[i])
259 | end
260 | if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
261 | if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
262 | return parsed
263 | end
264 |
265 | -----------------------------------------------------------------------------
266 | -- Builds a path component from its segments, escaping protected characters.
267 | -- Input
268 | -- parsed: path segments
269 | -- unsafe: if true, segments are not protected before path is built
270 | -- Returns
271 | -- path: corresponding path stringing
272 | -----------------------------------------------------------------------------
273 | function build_path(parsed, unsafe)
274 | local path = ""
275 | local n = table.getn(parsed)
276 | if unsafe then
277 | for i = 1, n-1 do
278 | path = path .. parsed[i]
279 | path = path .. "/"
280 | end
281 | if n > 0 then
282 | path = path .. parsed[n]
283 | if parsed.is_directory then path = path .. "/" end
284 | end
285 | else
286 | for i = 1, n-1 do
287 | path = path .. protect_segment(parsed[i])
288 | path = path .. "/"
289 | end
290 | if n > 0 then
291 | path = path .. protect_segment(parsed[n])
292 | if parsed.is_directory then path = path .. "/" end
293 | end
294 | end
295 | if parsed.is_absolute then path = "/" .. path end
296 | return path
297 | end
298 |
--------------------------------------------------------------------------------
/lua/string2.lua:
--------------------------------------------------------------------------------
1 | function new_string_builder()
2 | local string_table = {}
3 | local object = {}
4 |
5 | function object.insert(...)
6 | for idx, str in ipairs(arg) do
7 | table.insert(string_table, idx, str)
8 | end
9 | end
10 |
11 | function object.add(...)
12 | for idx, str in ipairs(arg) do
13 | table.insert(string_table, str)
14 | end
15 | end
16 |
17 | function object.get(spliter)
18 | spliter = spliter or ""
19 | return table.concat(string_table, spliter)
20 | end
21 |
22 | function object.empty()
23 | return not next(string_table)
24 | end
25 |
26 | function object.len()
27 | local len = 0
28 | for index, str in ipairs(string_table) do
29 | len = len + #str
30 | end
31 | return len
32 | end
33 |
34 | return object
35 | end
36 |
--------------------------------------------------------------------------------
/lua/torred.lua:
--------------------------------------------------------------------------------
1 |
2 | function is_dictionary(data)
3 | return data:sub(1,1) == "d" and data:sub(-1) == "e"
4 | end
5 |
6 | local numbers={["0"]=0,["1"]=1,["2"]=2,["3"]=3,["4"]=4,["5"]=5,["6"]=6,["7"]=7,["8"]=8,["9"]=9}
7 |
8 | function from_bencode(data, no_info)
9 | local table_info
10 | if not no_info then
11 | table_info = {}
12 | end
13 |
14 | local function parse(index)
15 | if index > #data then return end
16 | local t = data:byte(index)
17 |
18 | if (t == 105) or (t >=48 and t <=57) then -- (i)nteger or string
19 | local start, endindex, number = string.find(data, "i?(%-?[0-9]+)[e:]", index)
20 |
21 | if start ~= index then return end
22 |
23 | number = tonumber(number)
24 |
25 | if not number then return end
26 |
27 | if t == 105 then -- is (i)nteger?
28 | return number, endindex
29 | end
30 |
31 | if ( number < 0 ) or (math.floor(number) ~= number) or ( endindex + number > #data ) then return end
32 | local value = data:sub(endindex + 1, endindex + number)
33 | return value, endindex + number
34 |
35 | elseif (t == 108) then -- (l)ist
36 | local value, endindex = parse(index + 1)
37 | local list={}
38 | --
39 | while value do
40 | table.insert(list,value)
41 | value, endindex = parse(endindex + 1)
42 | end
43 |
44 | if not endindex then return end
45 |
46 | return list, endindex
47 | elseif (t == 100) then -- (d)ictionary
48 | local dict={}
49 | local dict_info
50 | if table_info then
51 | dict_info = { o = {} }
52 | table_info[dict] = dict_info
53 | end
54 | local key, endindex = parse(index + 1)
55 | local value
56 | while key do
57 | value, endindex = parse(endindex + 1)
58 | if value then
59 | if dict_info then
60 | table.insert(dict_info.o, key)
61 | end
62 | dict[key]=value
63 | else
64 | break
65 | end
66 | if not endindex then return end
67 | key, endindex = parse(endindex + 1)
68 | if not endindex then return end
69 | end
70 | return dict, endindex
71 | elseif t == 101 then
72 | return nil, index
73 | end
74 | end
75 | return parse(1), table_info
76 | end
77 |
78 | function to_bencode(value, table_info, buff)
79 | --print(type(value))
80 | if not buff then
81 | local buff = {}
82 | to_bencode(value, table_info, buff)
83 | return table.concat(buff)
84 | elseif type(value) == "number" then
85 | table.insert(buff, "i"..value.."e")
86 | elseif type(value) == "string" then
87 | table.insert(buff, #value..":"..value)
88 | elseif type(value) == "table" then
89 |
90 |
91 | if value[1] then
92 | table.insert(buff, "l")
93 | for _, v in ipairs(value) do
94 | to_bencode(v, table_info, buff)
95 | end
96 | else
97 | local dict
98 | if table_info then
99 | dict = table_info[value]
100 | end
101 |
102 | table.insert(buff, "d")
103 | if dict and dict.o then
104 | for _, k in ipairs(dict.o) do
105 | if value[k] then
106 | to_bencode(k , table_info, buff)
107 | to_bencode(value[k] , table_info, buff)
108 | end
109 | end
110 | else
111 | for k, v in pairs(value) do
112 | to_bencode(k , table_info, buff)
113 | to_bencode(v , table_info, buff)
114 | end
115 | end
116 | end
117 |
118 | table.insert(buff, "e")
119 | end
120 | end
121 |
122 | function set_value(tbl, table_info, value, key)
123 | local info = table_info[tbl]
124 | if key and value then
125 | if not info then
126 | info = {t = "d", o = {}}
127 | table_info[tbl] = info
128 | end
129 | tbl[key] = value
130 | table.insert( info.o, key )
131 | elseif value and not (info or key) then
132 | table.insert( tbl, value )
133 | end
134 | end
--------------------------------------------------------------------------------
/lua/util.lua:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/lua/util.lua
--------------------------------------------------------------------------------
/lua5.1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/lua5.1.dll
--------------------------------------------------------------------------------
/lua5.1.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/lua5.1.exe
--------------------------------------------------------------------------------
/magnet_to_uri_res.cmd:
--------------------------------------------------------------------------------
1 | %~d0
2 | cd %~p0
3 | del temp2.txt
4 | lua5.1.exe lua\magnet_to_uri_res.lua > temp2.txt
5 | temp2.txt
6 | pause
--------------------------------------------------------------------------------
/msgs.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/msgs.txt
--------------------------------------------------------------------------------
/rhash.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/rhash.exe
--------------------------------------------------------------------------------
/settings.lua:
--------------------------------------------------------------------------------
1 | script_id = "LD\000\002"
2 | script_name = "Lua DHT Tracker 0.2"
3 | nodes_file = "nodes.tbl"
4 | error_log = "dht_error.log"
5 | global_token = "save"
6 | port_number = 6543
7 | svg_opacity = false
8 | packet_analyze_on = true
9 | mgs_on = true
10 | welcome_msg = [[
11 | Welcome to Lua DHT
12 | http://sourceforge.net/apps/phpbb/shareaza/viewtopic.php?f=2&t=673
13 |
14 | add new tracker for all torrents:
15 | http://127.0.0.1:]]..port_number..[[/announce
16 |
17 | info:
18 | http://127.0.0.1:]]..port_number..[[/
19 | ]]
20 |
21 |
--------------------------------------------------------------------------------
/socket/core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivan386/lua-dht/c4b547ac613940f3a30a7defe97497e13b0130d2/socket/core.dll
--------------------------------------------------------------------------------
/start.cmd:
--------------------------------------------------------------------------------
1 | %~d0
2 | cd %~p0
3 | lua5.1.exe lua\dht.lua > msgs.txt
4 | pause
--------------------------------------------------------------------------------
/uninstall.cmd:
--------------------------------------------------------------------------------
1 | %~d0
2 | cd %~p0
3 | LuaService -u
4 | pause
--------------------------------------------------------------------------------