luci.upnp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #!/usr/bin/env lua
  2. local json = require "luci.jsonc"
  3. local UCI = require "luci.model.uci"
  4. local fs = require "nixio.fs"
  5. local sys = require "luci.sys"
  6. local methods = {
  7. get_status = {
  8. call = function()
  9. local uci = UCI.cursor()
  10. local lease_file = uci:get("upnpd", "config", "upnp_lease_file")
  11. local ipv4_hints = sys.net.ipv4_hints()
  12. local rule = { }
  13. local ipt = io.popen("iptables --line-numbers -t nat -xnvL MINIUPNPD 2>/dev/null")
  14. if ipt then
  15. local upnpf = lease_file and io.open(lease_file, "r")
  16. while true do
  17. local ln = ipt:read("*l")
  18. if not ln then
  19. break
  20. elseif ln:match("^%d+") then
  21. local num, proto, extport, intaddr, intport =
  22. ln:match("^(%d+).-([a-z]+).-dpt:(%d+) to:(%S-):(%d+)")
  23. local descr = ""
  24. if num and proto and extport and intaddr and intport then
  25. extport = tonumber(extport)
  26. intport = tonumber(intport)
  27. if upnpf then
  28. local uln = upnpf:read("*l")
  29. if uln then descr = uln:match(string.format("^%s:%d:%s:%d:%%d*:(.*)$", proto:upper(), extport, intaddr, intport)) end
  30. if not descr then descr = "" end
  31. end
  32. local host_hint, _, e
  33. for _,e in pairs(ipv4_hints) do
  34. if e[1] == intaddr then
  35. host_hint = e[2]
  36. break
  37. end
  38. end
  39. rule[#rule+1] = {
  40. num = num,
  41. proto = proto:upper(),
  42. extport = extport,
  43. intaddr = intaddr,
  44. host_hint = host_hint,
  45. intport = intport,
  46. descr = descr
  47. }
  48. end
  49. end
  50. end
  51. if upnpf then upnpf:close() end
  52. ipt:close()
  53. end
  54. return { rules = rule }
  55. end
  56. },
  57. delete_rule = {
  58. args = { token = "token" },
  59. call = function(args)
  60. local util = require "luci.util"
  61. local idx = args and tonumber(args.token)
  62. local res = {}
  63. if idx and idx > 0 then
  64. local uci = UCI.cursor()
  65. sys.call("iptables -t filter -D MINIUPNPD %d 2>/dev/null" % idx)
  66. sys.call("iptables -t nat -D MINIUPNPD %d 2>/dev/null" % idx)
  67. local lease_file = uci:get("upnpd", "config", "upnp_lease_file")
  68. if lease_file and fs.access(lease_file) then
  69. sys.call("sed -i -e '%dd' %s" %{ idx, util.shellquote(lease_file) })
  70. end
  71. uci.unload()
  72. return { result = "OK" }
  73. end
  74. return { result = "Bad request" }
  75. end
  76. }
  77. }
  78. local function parseInput()
  79. local parse = json.new()
  80. local done, err
  81. while true do
  82. local chunk = io.read(4096)
  83. if not chunk then
  84. break
  85. elseif not done and not err then
  86. done, err = parse:parse(chunk)
  87. end
  88. end
  89. if not done then
  90. print(json.stringify({ error = err or "Incomplete input" }))
  91. os.exit(1)
  92. end
  93. return parse:get()
  94. end
  95. local function validateArgs(func, uargs)
  96. local method = methods[func]
  97. if not method then
  98. print(json.stringify({ error = "Method not found" }))
  99. os.exit(1)
  100. end
  101. if type(uargs) ~= "table" then
  102. print(json.stringify({ error = "Invalid arguments" }))
  103. os.exit(1)
  104. end
  105. uargs.ubus_rpc_session = nil
  106. local k, v
  107. local margs = method.args or {}
  108. for k, v in pairs(uargs) do
  109. if margs[k] == nil or
  110. (v ~= nil and type(v) ~= type(margs[k]))
  111. then
  112. print(json.stringify({ error = "Invalid arguments" }))
  113. os.exit(1)
  114. end
  115. end
  116. return method
  117. end
  118. if arg[1] == "list" then
  119. local _, method, rv = nil, nil, {}
  120. for _, method in pairs(methods) do rv[_] = method.args or {} end
  121. print((json.stringify(rv):gsub(":%[%]", ":{}")))
  122. elseif arg[1] == "call" then
  123. local args = parseInput()
  124. local method = validateArgs(arg[2], args)
  125. local result, code = method.call(args)
  126. print((json.stringify(result):gsub("^%[%]$", "{}")))
  127. os.exit(code or 0)
  128. end