Post your best Lua script examples here

User avatar
By grytole
#67605 This is a simple client for STUN service.
It is used by VoIP and some crypto currencies to make able connections between peers under NAT firewalls (your WiFi routers).

Usage:
Code: Select allstun = dofile("stun.lua")

stun(function(ip, port)
  print("My ext. IP/port:", ip, port)
end)

stun(function(ip, port)
  print("My ext. IP/port:", ip, port)
end, "stun.sipgate.net")

stun(function(ip, port)
  print("My ext. IP/port:", ip, port)
end, "stun.sipgate.net", 3478)

Callback gets ip/port on success or nil/nil on failure.

Code of module - IT LEAKS! Feel free to fix)
Code: Select allreturn function(callback, serverAddr, serverPort)
  if type(callback) ~= "function" then
    return false
  else
    if not serverAddr or serverAddr == "" then
      serverAddr = "stun.wtfismyip.com"
    end
    serverPort = tonumber(serverPort or 3478)
  end

  local stun = {}

  stun.BIND_REQUEST = string.char(0x00, 0x01)
  stun.BIND_SUCCESS = string.char(0x01, 0x01)
  stun.MAGIC_COOKIE = string.char(0x21, 0x12, 0xA4, 0x42)
  stun.ATTR_MAPPEDADDR = 0x0001

  local function r16u(data, pos)
    return string.byte(data, pos) * 256 + string.byte(data, pos + 1)
  end

  local function w16u(val)
    local lo = val % 256
    local hi = (val - lo) / 256
    return string.char(hi, lo)
  end

  local function parseIp(data, pos)
    local ip = string.format("%d.%d.%d.%d",
                             string.byte(data, pos),
                             string.byte(data, pos + 1),
                             string.byte(data, pos + 2),
                             string.byte(data, pos + 3))
    return ip
  end

  local function createTxId()
    local tid = ""
    for i = 1, 12 do
      tid = tid .. string.char(node.random(0, 255))
    end
    return tid
  end

  local function createStunRequest()
    local request = ""
    request = request .. stun.BIND_REQUEST
    request = request .. w16u(0)
    request = request .. stun.MAGIC_COOKIE
    request = request .. stun.txid
    return request
  end

  local function parseStunPayload(payload)
    local pos = 1
    while pos < #payload do
      local attrType = r16u(payload, pos)
      local attrLength = r16u(payload, pos + 2)
      if attrType == stun.ATTR_MAPPEDADDR then
        local port = r16u(payload, pos + 6)
        local ip = parseIp(payload, pos + 8)
        return ip, port
      end
      pos = pos + attrLength + 4
    end
    return nil, nil
  end

  local function parseStunResponse(data)
    local response = string.sub(data, 1, 2)
    local payloadLength = r16u(data, 3)
    local cookie = string.sub(data, 5, 8)
    local txid = string.sub(data, 9, 20)
    local payload = string.sub(data, 21, 21 + payloadLength)

    if response ~= stun.BIND_SUCCESS
    or cookie ~= stun.MAGIC_COOKIE
    or txid ~= stun.txid
    then
      return nil, nil
    else
      return parseStunPayload(payload)
    end
  end

  net.dns.resolve(serverAddr, function(_, ip)
    if not ip then
      pcall(callback, nil, nil)
      stun = nil
    else
      stun.socket = net.createUDPSocket()
      stun.socket:on("receive", function(socket, data, port, ip)
        stun.timer:unregister()
        stun.socket:close()
        local extIp, extPort = parseStunResponse(data)
        pcall(callback, extIp, extPort)
        stun = nil
      end)
      stun.txid = createTxId()
      stun.socket:send(serverPort, ip, createStunRequest())
      stun.timer = tmr.create()
      stun.timer:alarm(3000, tmr.ALARM_SINGLE, function()
        stun.socket:close()
        pcall(callback, nil, nil)
        stun = nil
      end)
    end
  end)

  return ok
end