update.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. #!/usr/bin/lua
  2. ------------------------------------------------
  3. -- This file is part of the luci-app-ssr-plus update.lua
  4. -- By Mattraks
  5. ------------------------------------------------
  6. require "luci.sys"
  7. require "luci.model.uci"
  8. local icount = 0
  9. local args = arg[1]
  10. local uci = luci.model.uci.cursor()
  11. -- 以下设置更新数据库至 DNSMASQ 配置路径
  12. -- 获取 DNSMasq 配置 ID
  13. local DEFAULT_DNSMASQ_CFGID = uci:get_first("dhcp", "dnsmasq", ".name")
  14. if not DEFAULT_DNSMASQ_CFGID then
  15. error("未找到默认的 DNSMasq 配置 ID")
  16. end
  17. -- 查找包含 conf-dir 选项的 dnsmasq.conf 文件路径
  18. local DNSMASQ_CONF_PATH_CMD = string.format("grep -l '^conf-dir=' /tmp/etc/dnsmasq.conf.%s*", DEFAULT_DNSMASQ_CFGID)
  19. local DNSMASQ_CONF_PATH = io.popen(DNSMASQ_CONF_PATH_CMD):read("*l")
  20. if not DNSMASQ_CONF_PATH or DNSMASQ_CONF_PATH:match("^%s*$") then
  21. error("无法找到包含 conf-dir 选项的 dnsmasq.conf 文件路径")
  22. end
  23. DNSMASQ_CONF_PATH = DNSMASQ_CONF_PATH:gsub("%s+", "") -- 去除空白字符
  24. -- 获取 DNSMASQ 配置路径
  25. local DNSMASQ_CONF_DIR_CMD = string.format("grep '^conf-dir=' %s | cut -d'=' -f2 | head -n 1", DNSMASQ_CONF_PATH)
  26. local DNSMASQ_CONF_DIR = io.popen(DNSMASQ_CONF_DIR_CMD):read("*l")
  27. if not DNSMASQ_CONF_DIR or DNSMASQ_CONF_DIR:match("^%s*$") then
  28. error("无法提取 conf-dir 配置,请检查 dnsmasq.conf 文件内容")
  29. end
  30. DNSMASQ_CONF_DIR = DNSMASQ_CONF_DIR:gsub("%s+", "") -- 去除空白字符
  31. -- 设置 dnsmasq-ssrplus.d 目录路径,并去除路径末尾的斜杠
  32. local TMP_DNSMASQ_PATH = DNSMASQ_CONF_DIR:match("^(.-)/?$") .. "/dnsmasq-ssrplus.d"
  33. if not TMP_DNSMASQ_PATH or TMP_DNSMASQ_PATH:match("^%s*$") then
  34. error("无法找到包含 dnsmasq 选项的 dnsmasq-ssrplus.d 目录路径")
  35. end
  36. local TMP_PATH = "/var/etc/ssrplus"
  37. -- match comments/title/whitelist/ip address/excluded_domain
  38. local comment_pattern = "^[!\\[@]+"
  39. local ip_pattern = "^%d+%.%d+%.%d+%.%d+"
  40. local domain_pattern = "([%w%-%_]+%.[%w%.%-%_]+)[%/%*]*"
  41. local excluded_domain = {
  42. "apple.com", "sina.cn", "sina.com.cn", "baidu.com", "byr.cn", "jlike.com",
  43. "weibo.com", "zhongsou.com", "youdao.com", "sogou.com", "so.com", "soso.com",
  44. "aliyun.com", "taobao.com", "jd.com", "qq.com"
  45. }
  46. -- gfwlist parameter
  47. local mydnsip = '127.0.0.1'
  48. local mydnsport = '5335'
  49. local ipsetname = 'gfwlist'
  50. local new_appledns = uci:get_first("shadowsocksr", "global", "apple_dns")
  51. local bc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  52. -- base64decoding
  53. local function base64_dec(data)
  54. data = string.gsub(data, '[^' .. bc .. '=]', '')
  55. return (data:gsub('.', function(x)
  56. if (x == '=') then
  57. return ''
  58. end
  59. local r, f = '', (bc:find(x) - 1)
  60. for i = 6, 1, -1 do
  61. r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0')
  62. end
  63. return r;
  64. end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
  65. if (#x ~= 8) then
  66. return ''
  67. end
  68. local c = 0
  69. for i = 1, 8 do
  70. c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0)
  71. end
  72. return string.char(c)
  73. end))
  74. end
  75. -- check if domain is excluded
  76. local function check_excluded_domain(value)
  77. for _, domain in ipairs(excluded_domain) do
  78. if value:find(domain) then
  79. return true
  80. end
  81. end
  82. end
  83. -- gfwlist转码至dnsmasq格式
  84. local function generate_gfwlist(type)
  85. local domains, domains_map = {}, {}
  86. local out = io.open("/tmp/ssr-update." .. type, "w")
  87. for line in io.lines("/tmp/ssr-update.tmp") do
  88. if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or check_excluded_domain(line)) then
  89. local start, finish, match = string.find(line, domain_pattern)
  90. if start and not domains_map[match] then
  91. domains_map[match] = true
  92. table.insert(domains, match)
  93. end
  94. end
  95. end
  96. for _, domain in ipairs(domains) do
  97. out:write(string.format("server=/%s/%s#%s\n", domain, mydnsip, mydnsport))
  98. out:write(string.format("ipset=/%s/%s\n", domain, ipsetname))
  99. end
  100. out:close()
  101. os.remove("/tmp/ssr-update.tmp")
  102. end
  103. -- 更换 Apple dns
  104. local function generate_apple(type)
  105. local domains, domains_map = {}, {}
  106. local out = io.open("/tmp/ssr-update." .. type, "w")
  107. for line in io.lines("/tmp/ssr-update.tmp") do
  108. if not (string.find(line, comment_pattern)) then
  109. local start, finish, match = string.find(line, domain_pattern)
  110. if start and not domains_map[match] then
  111. domains_map[match] = true
  112. match = string.gsub(match, "%s", "") --从域名中去除所有空白字符
  113. table.insert(domains, match)
  114. end
  115. end
  116. end
  117. for _, domain in ipairs(domains) do
  118. if new_appledns and new_appledns ~= "" then
  119. out:write(string.format("server=/%s/%s\n", domain, new_appledns))
  120. end
  121. end
  122. out:close()
  123. os.remove("/tmp/ssr-update.tmp")
  124. end
  125. -- adblock转码至dnsmasq格式
  126. local function generate_adblock(type)
  127. local domains, domains_map = {}, {}
  128. local out = io.open("/tmp/ssr-update." .. type, "w")
  129. for line in io.lines("/tmp/ssr-update.tmp") do
  130. if not (string.find(line, comment_pattern)) then
  131. local start, finish, match = string.find(line, domain_pattern)
  132. if start and not domains_map[match] then
  133. domains_map[match] = true
  134. table.insert(domains, match)
  135. end
  136. end
  137. end
  138. for _, domain in ipairs(domains) do
  139. out:write(string.format("address=/%s/\n", domain))
  140. end
  141. out:close()
  142. os.remove("/tmp/ssr-update.tmp")
  143. end
  144. local log = function(...)
  145. if args then
  146. print("{ret=" .. table.concat({...}, ",retcount=") .. "}")
  147. else
  148. print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({...}, " "))
  149. end
  150. end
  151. local function update(url, file, type, file2)
  152. local Num = 1
  153. local refresh_cmd = "wget --no-check-certificate -q -O /tmp/ssr-update." .. type .. " " .. url
  154. local sret = luci.sys.call(refresh_cmd)
  155. if sret == 0 then
  156. if type == "gfw_data" then
  157. local gfwlist = io.open("/tmp/ssr-update." .. type, "r")
  158. local decode = gfwlist:read("*a")
  159. if not decode:find("google") then
  160. decode = base64_dec(decode)
  161. end
  162. gfwlist:close()
  163. -- 写回gfwlist
  164. gfwlist = io.open("/tmp/ssr-update.tmp", "w")
  165. gfwlist:write(decode)
  166. gfwlist:close()
  167. generate_gfwlist(type)
  168. Num = 2
  169. end
  170. if type == "apple_data" then
  171. local apple = io.open("/tmp/ssr-update." .. type, "r")
  172. local decode = apple:read("*a")
  173. if not decode:find("apple") then
  174. decode = base64_dec(decode)
  175. end
  176. apple:close()
  177. -- 写回applechina
  178. apple = io.open("/tmp/ssr-update.tmp", "w")
  179. apple:write(decode)
  180. apple:close()
  181. if new_appledns and new_appledns ~= "" then
  182. generate_apple(type)
  183. end
  184. end
  185. if type == "ad_data" then
  186. local adblock = io.open("/tmp/ssr-update." .. type, "r")
  187. local decode = adblock:read("*a")
  188. if decode:find("address=") then
  189. adblock:close()
  190. else
  191. adblock:close()
  192. -- 写回adblock
  193. adblock = io.open("/tmp/ssr-update.tmp", "w")
  194. adblock:write(decode)
  195. adblock:close()
  196. generate_adblock(type)
  197. end
  198. end
  199. local new_md5 = luci.sys.exec("echo -n $([ -f '/tmp/ssr-update." .. type .. "' ] && md5sum /tmp/ssr-update." .. type .. " | awk '{print $1}')")
  200. local old_md5 = luci.sys.exec("echo -n $([ -f '" .. file .. "' ] && md5sum " .. file .. " | awk '{print $1}')")
  201. if new_md5 == old_md5 then
  202. if args then
  203. log(1)
  204. else
  205. log("你已经是最新数据,无需更新!")
  206. end
  207. else
  208. icount = luci.sys.exec("cat /tmp/ssr-update." .. type .. " | wc -l")
  209. luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file)
  210. if file2 then
  211. luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file2)
  212. end
  213. if type == "gfw_data" or type == "ad_data" then
  214. luci.sys.call("/usr/share/shadowsocksr/gfw2ipset.sh")
  215. else
  216. luci.sys.call("/usr/share/shadowsocksr/chinaipset.sh " .. TMP_PATH .. "/china_ssr.txt")
  217. end
  218. if args then
  219. log(0, tonumber(icount) / Num)
  220. else
  221. log("更新成功! 新的总记录数:" .. tostring(tonumber(icount) / Num))
  222. end
  223. end
  224. else
  225. if args then
  226. log(-1)
  227. else
  228. log("更新失败!")
  229. end
  230. end
  231. os.remove("/tmp/ssr-update." .. type)
  232. end
  233. if args then
  234. if args == "gfw_data" then
  235. update(uci:get_first("shadowsocksr", "global", "gfwlist_url"), "/etc/ssrplus/gfw_list.conf", args, TMP_DNSMASQ_PATH .. "/gfw_list.conf")
  236. os.exit(0)
  237. end
  238. if args == "ip_data" then
  239. update(uci:get_first("shadowsocksr", "global", "chnroute_url"), "/etc/ssrplus/china_ssr.txt", args, TMP_PATH .. "/china_ssr.txt")
  240. os.exit(0)
  241. end
  242. if args == "apple_data" then
  243. update(uci:get_first("shadowsocksr", "global", "apple_url"), "/etc/ssrplus/applechina.conf", args, TMP_DNSMASQ_PATH .. "/applechina.conf")
  244. os.exit(0)
  245. end
  246. if args == "ad_data" then
  247. update(uci:get_first("shadowsocksr", "global", "adblock_url"), "/etc/ssrplus/ad.conf", args, TMP_DNSMASQ_PATH .. "/ad.conf")
  248. os.exit(0)
  249. end
  250. if args == "nfip_data" then
  251. update(uci:get_first("shadowsocksr", "global", "nfip_url"), "/etc/ssrplus/netflixip.list", args, TMP_DNSMASQ_PATH .. "/netflixip.list")
  252. os.exit(0)
  253. end
  254. else
  255. log("正在更新【GFW列表】数据库")
  256. update(uci:get_first("shadowsocksr", "global", "gfwlist_url"), "/etc/ssrplus/gfw_list.conf", "gfw_data", TMP_DNSMASQ_PATH .. "/gfw_list.conf")
  257. log("正在更新【国内IP段】数据库")
  258. update(uci:get_first("shadowsocksr", "global", "chnroute_url"), "/etc/ssrplus/china_ssr.txt", "ip_data", TMP_PATH .. "/china_ssr.txt")
  259. if uci:get_first("shadowsocksr", "global", "apple_optimization", "0") == "1" then
  260. log("正在更新【Apple域名】数据库")
  261. update(uci:get_first("shadowsocksr", "global", "apple_url"), "/etc/ssrplus/applechina.conf", "apple_data", TMP_DNSMASQ_PATH .. "/applechina.conf")
  262. end
  263. if uci:get_first("shadowsocksr", "global", "adblock", "0") == "1" then
  264. log("正在更新【广告屏蔽】数据库")
  265. update(uci:get_first("shadowsocksr", "global", "adblock_url"), "/etc/ssrplus/ad.conf", "ad_data", TMP_DNSMASQ_PATH .. "/ad.conf")
  266. end
  267. if uci:get_first("shadowsocksr", "global", "netflix_enable", "0") == "1" then
  268. log("正在更新【Netflix IP段】数据库")
  269. update(uci:get_first("shadowsocksr", "global", "nfip_url"), "/etc/ssrplus/netflixip.list", "nfip_data", TMP_DNSMASQ_PATH .. "/netflixip.list")
  270. end
  271. -- log("正在更新【Netflix IP段】数据库")
  272. -- update(uci:get_first("shadowsocksr", "global", "nfip_url"), "/etc/ssrplus/netflixip.list", "nfip_data")
  273. end