Advertisement
Tatantyler

CRFv3.1 - LAN Client Side

Aug 11th, 2013
722
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.87 KB | None | 0 0
  1. -- Local Area Networking w/ CRFv3.1, Client Side
  2. -- Most of the explanations are in the CRFv3.1 source code.
  3.  
  4. local identStr = "LAN"
  5. local freq = 0xC01D
  6.  
  7. local router_id = -1
  8. local modemSide = ""
  9. local modem = {}
  10. local lan_key = {}
  11. local networks_available = {}
  12.  
  13. local svcRunning = false
  14.  
  15. if not fs.exists("AES") then
  16.     print("AES library does not exist, attempting to download...")
  17.     if http then
  18.         local wHandle = http.get("http://pastebin.com/raw.php?i=rCYDnCxn")
  19.         if wHandle then
  20.             local fHandle = fs.open("AES", "w")
  21.             if fHandle then
  22.                 fHandle.write(wHandle.readAll())
  23.                 fHandle.close()
  24.             else
  25.                 print("Could not open AES for writing.")
  26.             end
  27.             wHandle.close()
  28.         else
  29.             print("Could not connect to pastebin.")
  30.         end
  31.     else
  32.         print("HTTP is disabled.")
  33.     end
  34. end
  35.  
  36. if not fs.exists("SHA2") then
  37.     print("SHA2 library does not exist, attempting to download...")
  38.     if http then
  39.         local wHandle = http.get("http://pastebin.com/raw.php?i=9c1h7812")
  40.         if wHandle then
  41.             local fHandle = fs.open("SHA2", "w")
  42.             if fHandle then
  43.                 fHandle.write(wHandle.readAll())
  44.                 fHandle.close()
  45.             else
  46.                 print("Could not open SHA2 for writing.")
  47.             end
  48.             wHandle.close()
  49.         else
  50.             print("Could not connect to pastebin.")
  51.         end
  52.     else
  53.         print("HTTP is disabled.")
  54.     end
  55. end
  56.  
  57. local secureLANEnabled = true
  58.  
  59. if (not ((AES ~= nil) or os.loadAPI("AES"))) or (not ((SHA2 ~= nil) or os.loadAPI("SHA2"))) then
  60.     print("Could not load encryption libraries; encrypted LAN functionality disabled!")
  61.     secureLANEnabled = false
  62. end
  63.  
  64.  
  65. local function raw_recv(timeout, id)
  66.     local t = {}
  67.     if timeout then
  68.         t = os.startTimer(timeout)
  69.     end
  70.     while true do
  71.         local event, side, sCH, rCh, data = os.pullEvent()
  72.         if event == "modem_message" then
  73.             data = textutils.unserialize(data)
  74.             if type(data) == "table" then
  75.                 if data[1] == identStr then
  76.                     if data[4] == os.computerID() then
  77.                         if ((id ~= nil) and (id == data[2])) or (id == nil) then
  78.                             return side, data
  79.                         end
  80.                     end
  81.                 end
  82.             end
  83.         elseif event == "timer" then
  84.             if side == t then
  85.                 return
  86.             end
  87.         end
  88.     end
  89. end
  90.  
  91. function authenticate(side, id, key)
  92.     assert((type(key) == "string"), "authenticate: key type must be a string")
  93.     local _, k = SHA2.digestStr(key)
  94.     lan_key = SHA2.hashToBytes(k)
  95.     if peripheral.isPresent(side) then
  96.         modem = peripheral.wrap(side)
  97.         modem.open(freq)
  98.         modemSide = side
  99.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "auth_req", id} ))
  100.         while true do
  101.             local sender, data = raw_recv(10, id)
  102.             if data then
  103.                 if data[3] == "auth_challenge" then
  104.                     local challenge = data[5]
  105.                     local response = AES.encrypt_block(challenge, lan_key)
  106.                     modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "auth_response", id, response} ))
  107.                 elseif data[3] == "auth_good" then
  108.                     return true
  109.                 elseif data[3] == "auth_fail" then
  110.                     --modem.closeAll()
  111.                     lan_key = {}
  112.                     --modem = {}
  113.                     --modemSide = ""
  114.                     return false
  115.                 end
  116.             else
  117.                 return false
  118.             end
  119.         end
  120.     end
  121. end
  122.  
  123. function associate(side, id)
  124.     if peripheral.isPresent(side) then
  125.         modem = peripheral.wrap(side)
  126.         modem.open(freq)
  127.         modemSide = side
  128.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "assoc", id} ))
  129.         local s, data = raw_recv(10, id)
  130.         if data then
  131.             if data[3] == "assoc_ack" then
  132.                 router_id = id
  133.                 return true
  134.             end
  135.         end
  136.     end
  137.     modem = {}
  138.     modemSide = ""
  139.     return false
  140. end
  141.  
  142. function disassociate()
  143.     if modem.transmit and (router_id > -1) then
  144.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "disassoc", router_id} ))
  145.         local s, data = raw_recv(10, router_id)
  146.         if data then
  147.             if data[3] == "disassoc_ack" then
  148.                 modem.closeAll()
  149.                 router_id = -1
  150.                 modem = {}
  151.                 modemSide = ""
  152.                 return true
  153.             end
  154.         end
  155.     end
  156.     return false
  157. end
  158.  
  159. function requiresAuth(side, id)
  160.     if networks_available[id] then
  161.         return networks_available[id][2]
  162.     end
  163.     if peripheral.isPresent(side) then
  164.         local m = peripheral.wrap(side)
  165.         m.open(freq)
  166.         m.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "auth_required", id} ))
  167.         while true do
  168.             local sender, data = raw_recv(10, id)
  169.             if data then
  170.                 if data[3] == "auth_not_required" then
  171.                     m.closeAll()
  172.                     networks_available[id] = {"", false, os.clock()}
  173.                     return false
  174.                 elseif data[3] == "auth_required" then
  175.                     m.closeAll()
  176.                     networks_available[id] = {"", true, os.clock()}
  177.                     return true
  178.                 end
  179.             else
  180.                 return false
  181.             end
  182.         end
  183.     end
  184. end
  185.  
  186. function connect(side, id, key)
  187.     if requiresAuth(side, id) then
  188.         if not authenticate(side, id, key) then
  189.             return false
  190.         end
  191.     end
  192.     return associate(side, id)
  193. end
  194.  
  195. function force_disconnect()
  196.     if modem.transmit and (router_id > -1) then
  197.         if not disconnect() then
  198.             modem.closeAll()
  199.             router_id = -1
  200.             modem = {}
  201.             modemSide = ""
  202.         end
  203.         return true
  204.     end
  205.     return false
  206. end
  207.  
  208. function get_router()
  209.     return router_id
  210. end
  211.  
  212. function get_id_list()
  213.     if modem.transmit and (router_id > -1) then
  214.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "id_list", router_id} ))
  215.         local s, data = raw_recv(10, router_id)
  216.         if data then
  217.             if data[3] == "id_list" then
  218.                 return data[5]
  219.             end
  220.         end
  221.     end
  222.     return
  223. end
  224.  
  225. function get_lan_list()
  226.     if modem.transmit and (router_id > -1) then
  227.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "lan_list", router_id} ))
  228.         local s, data = raw_recv(10, router_id)
  229.         if data then
  230.             if data[3] == "id_list" then
  231.                 return data[5]
  232.             end
  233.         end
  234.     end
  235.     return
  236. end
  237.  
  238. function send(id, data)
  239.     if modem.transmit and (router_id > -1) then
  240.         if (#lan_key == 0) then
  241.             modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "data", router_id, id, data} ))
  242.             local s, data = raw_recv(10, router_id)
  243.             if data then
  244.                 if data[3] == "data_ack" then
  245.                     return true
  246.                 elseif data[3] == "data_fail" then
  247.                     return false
  248.                 end
  249.             end
  250.         else
  251.             local packet = {id, data}
  252.             local plain_data = {}
  253.             local plain_data_str = textutils.serialize(packet)
  254.             local iv = {}
  255.             for i=1, 16 do
  256.                 iv[i] = math.random(0, 255)
  257.             end
  258.             for i=1, #plain_data_str do
  259.                 plain_data[i] = string.byte(plain_data_str, i, i)
  260.             end
  261.             local enc_packet = AES.encrypt_bytestream(plain_data, lan_key, iv)
  262.             modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "encrypted_data", router_id, iv, enc_packet} ))
  263.             local s, data = raw_recv(10, router_id)
  264.             if data then
  265.                 if data[3] == "data_ack" then
  266.                     return true
  267.                 elseif data[3] == "data_fail" then
  268.                     return false
  269.                 end
  270.             end
  271.         end
  272.     end
  273.     return false
  274. end
  275.  
  276. function broadcast(data)
  277.     return send(-1, data)
  278. end
  279.  
  280. function backgroundLoop()
  281.     svcRunning = true
  282.     local timer = os.startTimer(30)
  283.     while true do
  284.         local event, side, sCh, rCh, data = os.pullEvent()
  285.         if event == "modem_message" then
  286.             --print(data)
  287.             data = textutils.unserialize(data)
  288.             if type(data) == "table" then
  289.                 if (data[1] == identStr) and ((data[4] == os.computerID()) or (data[4] == -1)) then
  290.                     if (data[3] == "data") then
  291.                         os.queueEvent("routed_message", data[5], data[6])
  292.                     elseif (data[3] == "encrypted_data") then
  293.                         local iv = data[5]
  294.                         local enc_data = data[6]
  295.                         if (#lan_key ~= 0) then
  296.                             local dec_data = AES.decrypt_bytestream(enc_data, lan_key, iv)
  297.                             local dec_str = ""
  298.                             for i=1, #dec_data do
  299.                                 dec_str = dec_str..string.char(dec_data[i])
  300.                             end
  301.                             dec_packet = textutils.unserialize(dec_str)
  302.                             if type(dec_packet) == "table" then
  303.                                 os.queueEvent("routed_message", dec_packet[1], dec_packet[2])
  304.                                 --handle_datagram(side, {data[1], data[2], "data", data[4]}, dec_packet[1], dec_packet[2])
  305.                             end
  306.                         end
  307.                     elseif (data[3] == "rekey") then
  308.                         os.queueEvent("LAN_network_rekey")
  309.                     elseif (data[3] == "beacon") then
  310.                         if not networks_available[data[2]] then
  311.                             os.queueEvent("LAN_new_network", data[2], data[5], data[6])
  312.                         end
  313.                         networks_available[data[2]] = {data[5], data[6], os.clock()}
  314.                     end
  315.                 end
  316.             end
  317.         elseif event == "timer" then
  318.             if side == timer then
  319.                 local removeList = {}
  320.                 for i,v in pairs(networks_available) do
  321.                     if v[3] then
  322.                         if (os.clock() - v[3]) >= 45 then
  323.                             table.insert(removeList, i)
  324.                         end
  325.                     end
  326.                 end
  327.                 for i=1, #removeList do
  328.                     networks_available[removeList[i]] = nil
  329.                 end
  330.             end
  331.         end
  332.     end
  333. end
  334.  
  335. function receive(timeout, id)
  336.     while true do
  337.         local side, data = raw_recv(timeout, router_id)
  338.         if type(data) == "table" then
  339.             if (data[1] == "LAN") and ((data[3] == "data") or (data[3] == "encrypted_data")) and (data[4] == os.computerID()) and ((id == nil) or (data[5] == id)) then -- In order: Ident string checking, packet type checking, recipient checking, and non-local sender checking if applicable.
  340.                 if (data[3] == "encrypted_data") then
  341.                     local iv = data[5]
  342.                     local enc_data = data[6]
  343.                     if (#lan_key ~= 0) then
  344.                         local dec_data = AES.decrypt_bytestream(enc_data, lan_key, iv)
  345.                         local dec_str = ""
  346.                         for i=1, #dec_data do
  347.                             dec_str = dec_str..string.char(dec_data[i])
  348.                         end
  349.                         dec_packet = textutils.unserialize(dec_str)
  350.                         if type(dec_packet) == "table" then
  351.                             return dec_packet[1], dec_packet[2]
  352.                             --handle_datagram(side, {data[1], data[2], "data", data[4]}, dec_packet[1], dec_packet[2])
  353.                         end
  354.                     end
  355.                 else
  356.                     return data[5], data[6]
  357.                 end
  358.             end
  359.         elseif side == nil then
  360.             return
  361.         end
  362.     end
  363. end
  364.  
  365. function join_group(group)
  366.     if modem.transmit and (router_id > -1) then
  367.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "join_group", router_id, group} ))
  368.         local s, data = raw_recv(10, router_id)
  369.         if data then
  370.             if data[3] == "group_ack" then
  371.                 return true
  372.             elseif data[3] == "group_fail" then
  373.                 return false
  374.             end
  375.         end
  376.     else
  377.         return false
  378.     end
  379. end
  380.  
  381. function leave_group(group)
  382.     if modem.transmit and (router_id > -1) then
  383.         modem.transmit(freq, freq, textutils.serialize( {identStr, os.computerID(), "leave_group", router_id, group} ))
  384.         local s, data = raw_recv(10, router_id)
  385.         if data then
  386.             if data[3] == "group_ack" then
  387.                 return true
  388.             elseif data[3] == "group_fail" then
  389.                 return false
  390.             end
  391.         end
  392.     else
  393.         return false
  394.     end
  395. end
  396.  
  397. function get_networks()
  398.     return networks_available
  399. end
  400.  
  401. if shell then
  402.     local file = fs.open(shell.getRunningProgram(), "r")
  403.     if file then
  404.         local l = file.readLine()
  405.         file.close()
  406.         if string.match(l, "Local Area Network") and string.match(l, "Client Side") then
  407.             local apiName = fs.getName(shell.getRunningProgram())
  408.             if not _G[apiName] then
  409.                 os.loadAPI(shell.getRunningProgram())
  410.             end
  411.             local args = {...}
  412.             if args[1] == "connect" then
  413.                 connect(args[2], args[3], args[4])
  414.             elseif (args[1] == "disconnect") or (args[1] == "disassociate") then
  415.                 disassociate()
  416.             elseif (args[1] == "receive") or (args[1] == "recv") then
  417.                 receive(tonumber(args[2]))
  418.             elseif (args[1] == "send") then
  419.                 if (tonumber(args[2]) ~= nil) and (type(args[3]) == "string") then
  420.                     send(tonumber(args[2]), args[3])
  421.                 end
  422.             elseif (args[1] == "help") or (#args == 0) then
  423.                 print("LANClient -- CRFv3.1 LAN API and basic interface")
  424.                 print(string.rep("-", term.getSize()))
  425.                 print("Usage: LANClient [action] [args]")
  426.                 print("Actions:")
  427.                 print("connect (\"connect [side] [id] [key]\"): Connect to a LAN.")
  428.                 print("disconnect (\"disconnect\"): Disconnect from a LAN.  ")
  429.                 print("receive / recv (\"recv [timeout]\"): Receive from a LAN.")
  430.                 print("send (\"send [id] [data]\"): Send simple messages via a LAN.")
  431.                 print("Information on the API can be found on the ComputerCraft forum topic.")
  432.             end
  433.         end
  434.     end
  435. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement