diff --git a/package/lean/luci-app-mosdns/Makefile b/package/lean/luci-app-mosdns/Makefile
new file mode 100644
index 000000000..dc11b2668
--- /dev/null
+++ b/package/lean/luci-app-mosdns/Makefile
@@ -0,0 +1,19 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-mosdns
+PKG_VERSION:=1.4.3
+PKG_RELEASE:=2
+
+LUCI_TITLE:=LuCI Support for mosdns
+LUCI_PKGARCH:=all
+LUCI_DEPENDS:=+mosdns +jsonfilter +luci-compat +curl +v2ray-geoip +v2ray-geosite
+
+define Package/$(PKG_NAME)/conffiles
+/etc/config/mosdns
+/etc/mosdns/config_custom.yaml
+/etc/mosdns/rule
+endef
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/lean/luci-app-mosdns/luasrc/controller/mosdns.lua b/package/lean/luci-app-mosdns/luasrc/controller/mosdns.lua
new file mode 100644
index 000000000..4a7bb6420
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/controller/mosdns.lua
@@ -0,0 +1,42 @@
+module("luci.controller.mosdns", package.seeall)
+
+function index()
+ if not nixio.fs.access("/etc/config/mosdns") then
+ return
+ end
+
+ local page = entry({"admin", "services", "mosdns"}, alias("admin", "services", "mosdns", "basic"), _("MosDNS"), 30)
+ page.dependent = true
+ page.acl_depends = { "luci-app-mosdns" }
+
+ entry({"admin", "services", "mosdns", "basic"}, cbi("mosdns/basic"), _("Basic Setting"), 1).leaf = true
+ entry({"admin", "services", "mosdns", "rule_list"}, cbi("mosdns/rule_list"), _("Rule List"), 2).leaf = true
+ entry({"admin", "services", "mosdns", "update"}, cbi("mosdns/update"), _("Geodata Update"), 3).leaf = true
+ entry({"admin", "services", "mosdns", "log"}, cbi("mosdns/log"), _("Logs"), 4).leaf = true
+ entry({"admin", "services", "mosdns", "status"}, call("act_status")).leaf = true
+ entry({"admin", "services", "mosdns", "get_log"}, call("get_log")).leaf = true
+ entry({"admin", "services", "mosdns", "clear_log"}, call("clear_log")).leaf = true
+ entry({"admin", "services", "mosdns", "geo_update"}, call("geo_update")).leaf = true
+end
+
+function act_status()
+ local e = {}
+ e.running = luci.sys.call("pgrep -f mosdns >/dev/null") == 0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
+
+function get_log()
+ luci.http.write(luci.sys.exec("cat $(/usr/share/mosdns/mosdns.sh logfile)"))
+end
+
+function clear_log()
+ luci.sys.call("cat /dev/null > $(/usr/share/mosdns/mosdns.sh logfile)")
+end
+
+function geo_update()
+ local e = {}
+ e.updating = luci.sys.call("/usr/share/mosdns/mosdns.sh geodata >/dev/null") == 0
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(e)
+end
diff --git a/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/basic.lua b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/basic.lua
new file mode 100644
index 000000000..e7304147d
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/basic.lua
@@ -0,0 +1,111 @@
+m = Map("mosdns")
+m.title = translate("MosDNS")
+m.description = translate("MosDNS is a 'programmable' DNS forwarder.")
+
+m:section(SimpleSection).template = "mosdns/mosdns_status"
+
+s = m:section(TypedSection, "mosdns")
+s.addremove = false
+s.anonymous = true
+
+enable = s:option(Flag, "enabled", translate("Enable"))
+enable.rmempty = false
+
+configfile = s:option(ListValue, "configfile", translate("Config File"))
+configfile:value("/etc/mosdns/config.yaml", translate("Default Config"))
+configfile:value("/etc/mosdns/config_custom.yaml", translate("Custom Config"))
+configfile.default = "/etc/mosdns/config.yaml"
+
+listenport = s:option(Value, "listen_port", translate("Listen port"))
+listenport.datatype = "and(port,min(1))"
+listenport.default = 5335
+listenport:depends( "configfile", "/etc/mosdns/config.yaml")
+
+loglevel = s:option(ListValue, "log_level", translate("Log Level"))
+loglevel:value("debug", translate("Debug"))
+loglevel:value("info", translate("Info"))
+loglevel:value("warn", translate("Warning"))
+loglevel:value("error", translate("Error"))
+loglevel.default = "info"
+loglevel:depends( "configfile", "/etc/mosdns/config.yaml")
+
+logfile = s:option(Value, "logfile", translate("Log File"))
+logfile.placeholder = "/tmp/mosdns.log"
+logfile.default = "/tmp/mosdns.log"
+logfile:depends( "configfile", "/etc/mosdns/config.yaml")
+
+remote_dns = s:option(DynamicList, "remote_dns", translate("Remote DNS"))
+remote_dns:value("tls://1.1.1.1", "1.1.1.1 (CloudFlare DNS)")
+remote_dns:value("tls://1.0.0.1", "1.0.0.1 (CloudFlare DNS)")
+remote_dns:value("tls://8.8.8.8", "8.8.8.8 (Google DNS)")
+remote_dns:value("tls://8.8.4.4", "8.8.4.4 (Google DNS)")
+remote_dns:value("tls://9.9.9.9", "9.9.9.9 (Quad9 DNS)")
+remote_dns:value("tls://149.112.112.112", "149.112.112.112 (Quad9 DNS)")
+remote_dns:value("tls://45.11.45.11", "45.11.45.11 (DNS.SB)")
+remote_dns:value("tls://208.67.222.222", "208.67.222.222 (Open DNS)")
+remote_dns:value("tls://208.67.220.220", "208.67.220.220 (Open DNS)")
+remote_dns:depends( "configfile", "/etc/mosdns/config.yaml")
+
+remote_dns_pipeline = s:option(Flag, "enable_pipeline", translate("Remote DNS Connection Multiplexing"), translate("Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode"))
+remote_dns_pipeline.rmempty = false
+remote_dns_pipeline.default = false
+remote_dns_pipeline:depends( "configfile", "/etc/mosdns/config.yaml")
+
+cache_size = s:option(Value, "cache_size", translate("DNS Cache Size"))
+cache_size.datatype = "and(uinteger,min(0))"
+cache_size.default = "200000"
+cache_size:depends( "configfile", "/etc/mosdns/config.yaml")
+
+cache_size = s:option(Value, "cache_survival_time", translate("Cache Survival Time"))
+cache_size.datatype = "and(uinteger,min(0))"
+cache_size.default = "259200"
+cache_size:depends( "configfile", "/etc/mosdns/config.yaml")
+
+minimal_ttl = s:option(Value, "minimal_ttl", translate("Minimum TTL"))
+minimal_ttl.datatype = "and(uinteger,min(0))"
+minimal_ttl.datatype = "and(uinteger,max(3600))"
+minimal_ttl.default = "0"
+minimal_ttl:depends( "configfile", "/etc/mosdns/config.yaml")
+
+maximum_ttl = s:option(Value, "maximum_ttl", translate("Maximum TTL"))
+maximum_ttl.datatype = "and(uinteger,min(0))"
+maximum_ttl.default = "0"
+maximum_ttl:depends( "configfile", "/etc/mosdns/config.yaml")
+
+redirect = s:option(Flag, "redirect", translate("Enable DNS Forward"), translate("Forward Dnsmasq Domain Name resolution requests to MosDNS"))
+redirect.default = true
+
+adblock = s:option(Flag, "adblock", translate("Enable DNS ADblock"))
+adblock:depends( "configfile", "/etc/mosdns/config.yaml")
+adblock.default = false
+
+adblock = s:option(Value, "ad_source", translate("ADblock Source"))
+adblock:depends("adblock", "1")
+adblock.default = "https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt"
+adblock:value("geosite.dat", "v2ray-geosite")
+adblock:value("https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt", "anti-AD")
+adblock:value("https://raw.githubusercontent.com/sjhgvr/oisd/main/dbl_basic.txt", "oisd (basic)")
+adblock:value("https://raw.githubusercontent.com/QiuSimons/openwrt-mos/master/dat/serverlist.txt", "QiuSimons/openwrt-mos")
+
+reload_service = s:option( Button, "_reload", translate("Reload Service"), translate("Reload service to take effect of new configuration"))
+reload_service.write = function()
+ luci.sys.exec("/etc/init.d/mosdns reload")
+end
+reload_service:depends( "configfile", "/etc/mosdns/config_custom.yaml")
+
+config = s:option(TextValue, "manual-config")
+config.description = translate("View the Custom YAML Configuration file used by this MosDNS. You can edit it as you own need.")
+config.template = "cbi/tvalue"
+config.rows = 25
+config:depends( "configfile", "/etc/mosdns/config_custom.yaml")
+
+function config.cfgvalue(self, section)
+ return nixio.fs.readfile("/etc/mosdns/config_custom.yaml")
+end
+
+function config.write(self, section, value)
+ value = value:gsub("\r\n?", "\n")
+ nixio.fs.writefile("/etc/mosdns/config_custom.yaml", value)
+end
+
+return m
diff --git a/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/log.lua b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/log.lua
new file mode 100644
index 000000000..79fc1a95e
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/log.lua
@@ -0,0 +1,5 @@
+m = Map("mosdns")
+
+m:append(Template("mosdns/mosdns_log"))
+
+return m
diff --git a/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/rule_list.lua b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/rule_list.lua
new file mode 100644
index 000000000..660b2e77d
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/rule_list.lua
@@ -0,0 +1,87 @@
+local datatypes = require "luci.cbi.datatypes"
+
+local white_list_file = "/etc/mosdns/rule/whitelist.txt"
+local block_list_file = "/etc/mosdns/rule/blocklist.txt"
+local grey_list_file = "/etc/mosdns/rule/greylist.txt"
+local hosts_list_file = "/etc/mosdns/rule/hosts.txt"
+local redirect_list_file = "/etc/mosdns/rule/redirect.txt"
+local local_ptr_file = "/etc/mosdns/rule/local-ptr.txt"
+
+m = Map("mosdns")
+
+s = m:section(TypedSection, "mosdns", translate("Rule Settings"))
+s.anonymous = true
+
+s:tab("white_list", translate("White Lists"))
+s:tab("block_list", translate("Block Lists"))
+s:tab("grey_list", translate("Grey Lists"))
+s:tab("hosts_list", translate("Hosts"))
+s:tab("redirect_list", translate("Redirect"))
+s:tab("local_ptr_list", translate("Block PTR"))
+
+o = s:taboption("white_list", TextValue, "whitelist", "", "" .. translate("These domain names allow DNS resolution with the highest priority. Please input the domain names of websites, every line can input only one website domain. For example: hm.baidu.com.") .. "" .. "" .. translate("
The list of rules only apply to 'Default Config' profiles.") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section) return nixio.fs.readfile(white_list_file) or "" end
+o.write = function(self, section, value) nixio.fs.writefile(white_list_file , value:gsub("\r\n", "\n")) end
+o.remove = function(self, section, value) nixio.fs.writefile(white_list_file , "") end
+o.validate = function(self, value)
+ return value
+end
+
+o = s:taboption("block_list", TextValue, "blocklist", "", "" .. translate("These domains are blocked from DNS resolution. Please input the domain names of websites, every line can input only one website domain. For example: baidu.com.") .. "" .. "" .. translate("
The list of rules only apply to 'Default Config' profiles.") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section) return nixio.fs.readfile(block_list_file) or "" end
+o.write = function(self, section, value) nixio.fs.writefile(block_list_file, value:gsub("\r\n", "\n")) end
+o.remove = function(self, section, value) nixio.fs.writefile(block_list_file, "") end
+o.validate = function(self, value)
+ return value
+end
+
+o = s:taboption("grey_list", TextValue, "greylist", "", "" .. translate("These domains are always resolved using remote DNS. Please input the domain names of websites, every line can input only one website domain. For example: google.com.") .. "" .. "" .. translate("
The list of rules only apply to 'Default Config' profiles.") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section) return nixio.fs.readfile(grey_list_file) or "" end
+o.write = function(self, section, value) nixio.fs.writefile(grey_list_file, value:gsub("\r\n", "\n")) end
+o.remove = function(self, section, value) nixio.fs.writefile(grey_list_file, "") end
+o.validate = function(self, value)
+ return value
+end
+
+o = s:taboption("hosts_list", TextValue, "hosts", "", "" .. translate("Hosts For example: baidu.com 10.0.0.1") .. "" .. "" .. translate("
The list of rules only apply to 'Default Config' profiles.") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section) return nixio.fs.readfile(hosts_list_file) or "" end
+o.write = function(self, section, value) nixio.fs.writefile(hosts_list_file, value:gsub("\r\n", "\n")) end
+o.remove = function(self, section, value) nixio.fs.writefile(hosts_list_file, "") end
+o.validate = function(self, value)
+ return value
+end
+
+o = s:taboption("redirect_list", TextValue, "redirect", "", "" .. translate("The domain name to redirect the request to. Requests domain A, but returns records for domain B. example: a.com b.com") .. "" .. "" .. translate("
The list of rules only apply to 'Default Config' profiles.") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section) return nixio.fs.readfile(redirect_list_file) or "" end
+o.write = function(self, section, value) nixio.fs.writefile(redirect_list_file, value:gsub("\r\n", "\n")) end
+o.remove = function(self, section, value) nixio.fs.writefile(redirect_list_file, "") end
+o.validate = function(self, value)
+ return value
+end
+
+o = s:taboption("local_ptr_list", TextValue, "local_ptr", "", "" .. translate("These domains are blocked from PTR requests") .. "" .. "" .. translate("
The list of rules only apply to 'Default Config' profiles.") .. "")
+o.rows = 15
+o.wrap = "off"
+o.cfgvalue = function(self, section) return nixio.fs.readfile(local_ptr_file) or "" end
+o.write = function(self, section, value) nixio.fs.writefile(local_ptr_file, value:gsub("\r\n", "\n")) end
+o.remove = function(self, section, value) nixio.fs.writefile(local_ptr_file, "") end
+o.validate = function(self, value)
+ return value
+end
+
+local apply = luci.http.formvalue("cbi.apply")
+if apply then
+ luci.sys.exec("/etc/init.d/mosdns reload")
+end
+
+return m
diff --git a/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/update.lua b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/update.lua
new file mode 100644
index 000000000..0beaf6d34
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/model/cbi/mosdns/update.lua
@@ -0,0 +1,31 @@
+m = Map("mosdns")
+
+s = m:section(TypedSection, "mosdns", translate("Update GeoIP & GeoSite dat"))
+s.addremove = false
+s.anonymous = true
+
+enable = s:option(Flag, "geo_auto_update", translate("Enable Auto Database Update"))
+enable.rmempty = false
+
+o = s:option(ListValue, "geo_update_week_time", translate("Update Cycle"))
+o:value("*", translate("Every Day"))
+o:value("1", translate("Every Monday"))
+o:value("2", translate("Every Tuesday"))
+o:value("3", translate("Every Wednesday"))
+o:value("4", translate("Every Thursday"))
+o:value("5", translate("Every Friday"))
+o:value("6", translate("Every Saturday"))
+o:value("7", translate("Every Sunday"))
+o.default = "*"
+
+update_time = s:option(ListValue, "geo_update_day_time", translate("Update Time"))
+for t = 0, 23 do
+ update_time:value(t, t..":00")
+end
+update_time.default = 0
+
+data_update = s:option(Button, "geo_update_database", translate("Database Update"))
+data_update.rawhtml = true
+data_update.template = "mosdns/geo_update"
+
+return m
diff --git a/package/lean/luci-app-mosdns/luasrc/view/mosdns/geo_update.htm b/package/lean/luci-app-mosdns/luasrc/view/mosdns/geo_update.htm
new file mode 100644
index 000000000..68b3027f4
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/view/mosdns/geo_update.htm
@@ -0,0 +1,34 @@
+<%+cbi/valueheader%>
+
+
+<%=self.value%>
+<%+cbi/valuefooter%>
diff --git a/package/lean/luci-app-mosdns/luasrc/view/mosdns/mosdns_log.htm b/package/lean/luci-app-mosdns/luasrc/view/mosdns/mosdns_log.htm
new file mode 100644
index 000000000..c6456983b
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/view/mosdns/mosdns_log.htm
@@ -0,0 +1,29 @@
+
+
diff --git a/package/lean/luci-app-mosdns/luasrc/view/mosdns/mosdns_status.htm b/package/lean/luci-app-mosdns/luasrc/view/mosdns/mosdns_status.htm
new file mode 100644
index 000000000..7f9be5bda
--- /dev/null
+++ b/package/lean/luci-app-mosdns/luasrc/view/mosdns/mosdns_status.htm
@@ -0,0 +1,28 @@
+
+
+
diff --git a/package/lean/luci-app-mosdns/po/zh-cn/mosdns.po b/package/lean/luci-app-mosdns/po/zh-cn/mosdns.po
new file mode 100644
index 000000000..f091dca1c
--- /dev/null
+++ b/package/lean/luci-app-mosdns/po/zh-cn/mosdns.po
@@ -0,0 +1,179 @@
+msgid "Basic Setting"
+msgstr "基本设置"
+
+msgid "MosDNS is a 'programmable' DNS forwarder."
+msgstr "MosDNS 是一个插件化的 DNS 转发/分流器。"
+
+msgid "RUNNING"
+msgstr "运行中"
+
+msgid "NOT RUNNING"
+msgstr "未运行"
+
+msgid "Collecting data..."
+msgstr "获取数据中..."
+
+msgid "Enable"
+msgstr "启用"
+
+msgid "Listen port"
+msgstr "监听端口"
+
+msgid "Log Level"
+msgstr "日志等级"
+
+msgid "Enable DNS Forward"
+msgstr "启用 DNS 转发"
+
+msgid "Forward Dnsmasq Domain Name resolution requests to MosDNS"
+msgstr "将 Dnsmasq 域名解析请求转发到 MosDNS 服务器"
+
+msgid "Enable DNS ADblock"
+msgstr "启用 DNS 广告过滤"
+
+msgid "ADblock Source"
+msgstr "广告过滤来源"
+
+msgid "Reload Service"
+msgstr "重载服务"
+
+msgid "Reload service to take effect of new configuration"
+msgstr "重载 MosDNS 服务生效配置文件"
+
+msgid "View the Custom YAML Configuration file used by this MosDNS. You can edit it as you own need."
+msgstr "注意!此页的更改仅当配置文件为 “自定义” 时才会生效"
+
+msgid "Geodata Update"
+msgstr "更新数据库"
+
+msgid "Update GeoIP & GeoSite dat"
+msgstr "更新 GeoIP & GeoSite 数据库"
+
+msgid "Update Time"
+msgstr "更新时间"
+
+msgid "Update Cycle"
+msgstr "更新周期"
+
+msgid "Every Day"
+msgstr "每天"
+
+msgid "Every Monday"
+msgstr "每周一"
+
+msgid "Every Tuesday"
+msgstr "每周二"
+
+msgid "Every Wednesday"
+msgstr "每周三"
+
+msgid "Every Thursday"
+msgstr "每周四"
+
+msgid "Every Friday"
+msgstr "每周五"
+
+msgid "Every Saturday"
+msgstr "每周六"
+
+msgid "Every Sunday"
+msgstr "每周日"
+
+msgid "Database Update"
+msgstr "数据库更新"
+
+msgid "Check And Update"
+msgstr "检查并更新"
+
+msgid "Enable Auto Database Update"
+msgstr "启用自动更新"
+
+msgid "Updating..."
+msgstr "正在更新..."
+
+msgid "Update success"
+msgstr "更新成功"
+
+msgid "Update failed, Please check the network status"
+msgstr "更新失败,请检查网络状态"
+
+msgid "Config File"
+msgstr "配置文件"
+
+msgid "Default Config"
+msgstr "内置预设"
+
+msgid "Custom Config"
+msgstr "自定义"
+
+msgid "Log File"
+msgstr "日志文件"
+
+msgid "Remote DNS"
+msgstr "远程 DNS"
+
+msgid "Remote DNS Connection Multiplexing"
+msgstr "远程 DNS 连接复用"
+
+msgid "Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode"
+msgstr "启用 TCP/DoT RFC 7766 新型 Query Pipelining 连接复用模式"
+
+msgid "DNS Cache Size"
+msgstr "DNS 缓存大小(条)"
+
+msgid "Cache Survival Time"
+msgstr "缓存生存时间(秒)"
+
+msgid "Minimum TTL"
+msgstr "覆盖最小 TTL 值(默认 0)"
+
+msgid "Maximum TTL"
+msgstr "覆盖最大 TTL 值(默认 0)"
+
+msgid "Logs"
+msgstr "日志"
+
+msgid "Clear logs"
+msgstr "清空日志"
+
+msgid "Rule List"
+msgstr "规则列表"
+
+msgid "Rule Settings"
+msgstr "自定义规则列表"
+
+msgid "
The list of rules only apply to 'Default Config' profiles."
+msgstr "
规则列表仅适用于 “内置预设” 配置文件"
+
+msgid "White Lists"
+msgstr "白名单"
+
+msgid "These domain names allow DNS resolution with the highest priority. Please input the domain names of websites, every line can input only one website domain. For example: hm.baidu.com."
+msgstr "加入的域名始终允许使用 “本地 DNS” 进行解析,且优先级最高(每个域名一行,支持域名匹配规则)"
+
+msgid "Block Lists"
+msgstr "黑名单"
+
+msgid "These domains are blocked from DNS resolution. Please input the domain names of websites, every line can input only one website domain. For example: baidu.com."
+msgstr "加入的域名将屏蔽 DNS 解析(每个域名一行,支持域名匹配规则)"
+
+msgid "Grey Lists"
+msgstr "灰名单"
+
+msgid "These domains are always resolved using remote DNS. Please input the domain names of websites, every line can input only one website domain. For example: google.com."
+msgstr "加入的域名始终使用 “远程 DNS” 进行解析(每个域名一行,支持域名匹配规则)"
+
+msgid "Hosts For example: baidu.com 10.0.0.1"
+msgstr "自定义 Hosts 重写,如:baidu.com 10.0.0.1(每个规则一行)"
+
+msgid "Redirect"
+msgstr "重定向"
+
+msgid "The domain name to redirect the request to. Requests domain A, but returns records for domain B. example: a.com b.com"
+msgstr "重定向请求的域名。请求域名 A,但返回域名 B 的记录,如:baidu.com qq.com(每个规则一行)"
+
+msgid "Block PTR"
+msgstr "PTR 黑名单"
+
+msgid "These domains are blocked from PTR requests"
+msgstr "加入的域名将阻止 PTR 请求(每个域名一行,支持域名匹配规则)"
diff --git a/package/lean/luci-app-mosdns/po/zh_Hans b/package/lean/luci-app-mosdns/po/zh_Hans
new file mode 120000
index 000000000..41451e4a1
--- /dev/null
+++ b/package/lean/luci-app-mosdns/po/zh_Hans
@@ -0,0 +1 @@
+zh-cn
\ No newline at end of file
diff --git a/package/lean/luci-app-mosdns/root/etc/config/mosdns b/package/lean/luci-app-mosdns/root/etc/config/mosdns
new file mode 100644
index 000000000..81e922c9f
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/config/mosdns
@@ -0,0 +1,20 @@
+
+config mosdns 'config'
+ option enabled '0'
+ option listen_port '5335'
+ option geo_auto_update '0'
+ option geo_update_week_time '*'
+ option geo_update_day_time '2'
+ option redirect '1'
+ option adblock '0'
+ option configfile '/etc/mosdns/config.yaml'
+ option log_level 'info'
+ option logfile '/tmp/mosdns.log'
+ option cache_size '200000'
+ option cache_survival_time '259200'
+ option minimal_ttl '0'
+ option maximum_ttl '0'
+ option enable_pipeline '0'
+ list remote_dns 'tls://8.8.8.8'
+ list remote_dns 'tls://1.1.1.1'
+
diff --git a/package/lean/luci-app-mosdns/root/etc/hotplug.d/iface/99-mosdns b/package/lean/luci-app-mosdns/root/etc/hotplug.d/iface/99-mosdns
new file mode 100755
index 000000000..1fb646fb0
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/hotplug.d/iface/99-mosdns
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ "$ACTION" = ifup ] && /etc/init.d/mosdns restart
diff --git a/package/lean/luci-app-mosdns/root/etc/init.d/mosdns b/package/lean/luci-app-mosdns/root/etc/init.d/mosdns
new file mode 100755
index 000000000..08b4cbdec
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/init.d/mosdns
@@ -0,0 +1,151 @@
+#!/bin/sh /etc/rc.common
+#
+# Copyright (C) 2020-2022, IrineSistiana
+#
+# This file is part of mosdns.
+#
+# mosdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# mosdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+START=99
+USE_PROCD=1
+
+PROG=/usr/bin/mosdns
+CONF=$(uci -q get mosdns.config.configfile)
+CRON_FILE=/etc/crontabs/root
+
+get_config() {
+ config_get enabled $1 enabled 0
+ config_get ad_source $1 ad_source ""
+ config_get cache_size $1 cache_size 200000
+ config_get cache_survival_time $1 cache_survival_time 259200
+ config_get enable_pipeline $1 enable_pipeline 0
+ config_get geo_auto_update $1 geo_auto_update 0
+ config_get geo_update_day_time $1 geo_update_day_time 2
+ config_get geo_update_week_time $1 geo_update_week_time "*"
+ config_get listen_port $1 listen_port 5335
+ config_get log_file $1 logfile "/tmp/mosdns.log"
+ config_get log_level $1 log_level "info"
+ config_get maximum_ttl_custom $1 maximum_ttl 0
+ config_get minimal_ttl_custom $1 minimal_ttl 0
+ config_get redirect $1 redirect 0
+ config_get remote_dns $1 remote_dns "tls://8.8.8.8 tls://1.1.1.1"
+}
+
+init_yaml() {
+ tmpdir=$(mktemp -d) || exit 1
+ adblock=$(/usr/share/mosdns/mosdns.sh ad)
+ [ $enable_pipeline = 1 ] && enable_pipeline=true || enable_pipeline=false
+ local_dns=$(/usr/share/mosdns/mosdns.sh dns | xargs -n1 echo " - addr:")
+ remote_dns=$(echo $remote_dns | awk '{for(i=1;i<=NF;i++)printf "%s- addr: %s\n%s enable_pipeline: '${enable_pipeline}'\n",s,$i,s}' s=' ')
+ sed "s,log_level,$log_level,g;s,log_file,$log_file,g; \
+ s,listen_port,$listen_port,g;s,cache_size,$cache_size,g; \
+ s,cache_survival_time,$cache_survival_time,g; \
+ s,minimal_ttl_custom,$minimal_ttl_custom,g; \
+ s,maximum_ttl_custom,$maximum_ttl_custom,g; \
+ s,adblock,$adblock,g;s,remote_dns_pipeline,$enable_pipeline,g" \
+ /usr/share/mosdns/default.yaml > $CONF
+ echo "${local_dns}" > $tmpdir/local_dns.txt
+ echo "${remote_dns}" > $tmpdir/remote_dns.txt
+ sed -i -e '/- addr: local_dns/{r '$tmpdir/local_dns.txt -e';d};/- addr: remote_dns/{r '$tmpdir/remote_dns.txt -e';d}' \
+ $CONF
+ rm -rf $tmpdir
+}
+
+service_triggers() {
+ procd_add_reload_trigger "mosdns"
+}
+
+restore_setting() {
+ rm -f /etc/mosdns/redirect.lock
+ sed -i "/list server/d" /etc/config/dhcp
+ uci set dhcp.@dnsmasq[0].noresolv='0'
+ uci del dhcp.@dnsmasq[0].cachesize
+ uci commit dhcp
+}
+
+redirect_setting() {
+ if [ "${CONF}" = "/etc/mosdns/config.yaml" ]; then
+ sed -i "/list server/d" /etc/config/dhcp
+ uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#$listen_port"
+ uci set dhcp.@dnsmasq[0].rebind_protection='0'
+ uci set dhcp.@dnsmasq[0].noresolv="1"
+ uci set dhcp.@dnsmasq[0].cachesize='0'
+ uci commit dhcp
+ else
+ sed -i "/list server/d" /etc/config/dhcp
+ uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#$(cat /etc/mosdns/config_custom.yaml | awk -F'[:" ]+' '/^\s+addr:/{for(i=1;i<=NF;i++){if($i~/^[0-9]+$/){print $i;exit}}}')"
+ uci set dhcp.@dnsmasq[0].rebind_protection='0'
+ uci set dhcp.@dnsmasq[0].noresolv="1"
+ uci set dhcp.@dnsmasq[0].cachesize='0'
+ uci commit dhcp
+ fi
+ touch /etc/mosdns/redirect.lock
+}
+
+reload_others() {
+ /etc/init.d/network reload
+ /etc/init.d/dnsmasq reload
+}
+
+reload_service() {
+ stop
+ sleep 1
+ start
+}
+
+setcron() {
+ sed -i '/mosdns.sh/d' $CRON_FILE 2>/dev/null
+ [ "$geo_auto_update" -eq 1 ] && echo "0 $geo_update_day_time * * $geo_update_week_time /usr/share/mosdns/mosdns.sh geodata" >>$CRON_FILE
+ crontab $CRON_FILE
+}
+
+delcron() {
+ sed -i '/mosdns.sh/d' $CRON_FILE 2>/dev/null
+ crontab $CRON_FILE
+}
+
+start_service() {
+ config_load "mosdns"
+ config_foreach get_config "mosdns"
+ [ $enabled != 1 ] && return 1
+ delcron
+ setcron
+ [ "${CONF}" = "/etc/mosdns/config.yaml" ] && init_yaml
+ cat /dev/null > $(/usr/share/mosdns/mosdns.sh logfile)
+ [ ! "$ad_source" = "geosite.dat" ] && cat /dev/null > /etc/mosdns/rule/adlist.txt
+ procd_open_instance mosdns
+ procd_set_param command $PROG start -c "$CONF"
+ procd_set_param user root
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
+ procd_close_instance mosdns
+
+ [ "$redirect" -ne 1 ] && [ -f "/etc/mosdns/redirect.lock" ] && restore_setting
+ [ "$redirect" -eq 1 ] && redirect_setting
+
+ reload_others
+
+ [ ! "$ad_source" = "geosite.dat" ] && /usr/share/mosdns/mosdns.sh adlist_update &> /dev/null &
+}
+
+stop_service() {
+ pgrep -f /usr/bin/mosdns | xargs kill -9 >/dev/null 2>&1
+ config_load "mosdns"
+ config_foreach get_config "mosdns"
+ [ "$enabled" = "0" ] && [ -f "/etc/mosdns/redirect.lock" ] && restore_setting
+ reload_others
+ delcron
+}
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/config_custom.yaml b/package/lean/luci-app-mosdns/root/etc/mosdns/config_custom.yaml
new file mode 100644
index 000000000..a291817e3
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/mosdns/config_custom.yaml
@@ -0,0 +1,162 @@
+log:
+ level: info
+ file: "/tmp/mosdns.log"
+
+include: []
+
+data_providers:
+ - tag: geoip
+ file: "/usr/share/v2ray/geoip.dat"
+ auto_reload: true
+
+ - tag: geosite
+ file: "/usr/share/v2ray/geosite.dat"
+ auto_reload: true
+
+ - tag: whitelist
+ file: "/etc/mosdns/rule/whitelist.txt"
+ auto_reload: true
+
+ - tag: blocklist
+ file: "/etc/mosdns/rule/blocklist.txt"
+ auto_reload: true
+
+ - tag: hosts
+ file: "/etc/mosdns/rule/hosts.txt"
+ auto_reload: true
+
+ - tag: redirect
+ file: "/etc/mosdns/rule/redirect.txt"
+ auto_reload: true
+
+plugins:
+ - tag: lazy_cache
+ type: cache
+ args:
+ size: 200000
+ lazy_cache_ttl: 259200
+
+ - tag: modify_ttl
+ type: ttl
+ args:
+ minimal_ttl: 0
+ maximum_ttl: 0
+
+ - tag: "forward_local"
+ type: fast_forward
+ args:
+ upstream:
+ - addr: 119.29.29.29
+ - addr: 114.114.114.114
+
+ - tag: "forward_remote"
+ type: fast_forward
+ args:
+ upstream:
+ - addr: tls://8.8.8.8
+ - addr: tls://1.1.1.1
+
+ - tag: query_is_whitelist_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:whitelist"
+
+ - tag: query_is_blocklist_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:blocklist"
+
+ - tag: query_is_hosts_domain
+ type: hosts
+ args:
+ hosts:
+ - "provider:hosts"
+
+ - tag: query_is_redirect_domain
+ type: redirect
+ args:
+ rule:
+ - "provider:redirect"
+
+ - tag: query_is_local_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:geosite:cn,apple-cn,icloud,google-cn,tld-cn"
+
+ - tag: query_is_non_local_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:geosite:geolocation-!cn"
+
+ - tag: response_has_local_ip
+ type: response_matcher
+ args:
+ ip:
+ - "provider:geoip:cn"
+
+ - tag: query_is_ad_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:geosite:category-ads-all"
+
+ - tag: match_qtype65
+ type: query_matcher
+ args:
+ qtype: [65]
+
+ - tag: "main_sequence"
+ type: "sequence"
+ args:
+ exec:
+ - _misc_optm
+ - query_is_hosts_domain
+ - query_is_redirect_domain
+
+ - if: query_is_whitelist_domain
+ exec:
+ - forward_local
+ - modify_ttl
+ - _return
+
+ - if: "query_is_blocklist_domain || query_is_ad_domain || match_qtype65"
+ exec:
+ - _new_nxdomain_response
+ - _return
+
+ - lazy_cache
+
+ - if: query_is_local_domain
+ exec:
+ - forward_local
+ - modify_ttl
+ - _return
+
+ - if: query_is_non_local_domain
+ exec:
+ - _prefer_ipv4
+ - forward_remote
+ - modify_ttl
+ - _return
+ - primary:
+ - forward_local
+ - if: "(! response_has_local_ip) && [_response_valid_answer]"
+ exec:
+ - _drop_response
+ secondary:
+ - _prefer_ipv4
+ - forward_remote
+ - modify_ttl
+ fast_fallback: 200
+
+servers:
+ - exec: main_sequence
+ listeners:
+ - protocol: udp
+ addr: ":5335"
+ - protocol: tcp
+ addr: ":5335"
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/adlist.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/adlist.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/blocklist.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/blocklist.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/greylist.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/greylist.txt
new file mode 100644
index 000000000..cab65c84c
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/greylist.txt
@@ -0,0 +1,3 @@
+domain:gstatic.com
+domain:kernel.org
+keyword:github
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/hosts.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/hosts.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/local-ptr.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/local-ptr.txt
new file mode 100644
index 000000000..0a66cae4e
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/local-ptr.txt
@@ -0,0 +1,87 @@
+0.in-addr.arpa
+10.in-addr.arpa
+127.in-addr.arpa
+16.172.in-addr.arpa
+17.172.in-addr.arpa
+18.172.in-addr.arpa
+19.172.in-addr.arpa
+20.172.in-addr.arpa
+21.172.in-addr.arpa
+22.172.in-addr.arpa
+23.172.in-addr.arpa
+24.172.in-addr.arpa
+25.172.in-addr.arpa
+26.172.in-addr.arpa
+27.172.in-addr.arpa
+28.172.in-addr.arpa
+29.172.in-addr.arpa
+30.172.in-addr.arpa
+31.172.in-addr.arpa
+64.100.in-addr.arpa
+65.100.in-addr.arpa
+66.100.in-addr.arpa
+67.100.in-addr.arpa
+68.100.in-addr.arpa
+69.100.in-addr.arpa
+70.100.in-addr.arpa
+71.100.in-addr.arpa
+72.100.in-addr.arpa
+73.100.in-addr.arpa
+74.100.in-addr.arpa
+75.100.in-addr.arpa
+76.100.in-addr.arpa
+77.100.in-addr.arpa
+78.100.in-addr.arpa
+79.100.in-addr.arpa
+80.100.in-addr.arpa
+81.100.in-addr.arpa
+82.100.in-addr.arpa
+83.100.in-addr.arpa
+84.100.in-addr.arpa
+85.100.in-addr.arpa
+86.100.in-addr.arpa
+87.100.in-addr.arpa
+88.100.in-addr.arpa
+89.100.in-addr.arpa
+90.100.in-addr.arpa
+91.100.in-addr.arpa
+92.100.in-addr.arpa
+93.100.in-addr.arpa
+94.100.in-addr.arpa
+95.100.in-addr.arpa
+96.100.in-addr.arpa
+97.100.in-addr.arpa
+98.100.in-addr.arpa
+99.100.in-addr.arpa
+100.100.in-addr.arpa
+101.100.in-addr.arpa
+102.100.in-addr.arpa
+103.100.in-addr.arpa
+104.100.in-addr.arpa
+105.100.in-addr.arpa
+106.100.in-addr.arpa
+107.100.in-addr.arpa
+108.100.in-addr.arpa
+109.100.in-addr.arpa
+110.100.in-addr.arpa
+111.100.in-addr.arpa
+112.100.in-addr.arpa
+113.100.in-addr.arpa
+114.100.in-addr.arpa
+115.100.in-addr.arpa
+116.100.in-addr.arpa
+117.100.in-addr.arpa
+118.100.in-addr.arpa
+119.100.in-addr.arpa
+120.100.in-addr.arpa
+121.100.in-addr.arpa
+122.100.in-addr.arpa
+123.100.in-addr.arpa
+124.100.in-addr.arpa
+125.100.in-addr.arpa
+126.100.in-addr.arpa
+127.100.in-addr.arpa
+2.0.192.in-addr.arpa
+168.192.in-addr.arpa
+255.255.255.255.in-addr.arpa
+domain:ip6.arpa
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/redirect.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/redirect.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/package/lean/luci-app-mosdns/root/etc/mosdns/rule/whitelist.txt b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/whitelist.txt
new file mode 100644
index 000000000..bf9456770
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/mosdns/rule/whitelist.txt
@@ -0,0 +1,24 @@
+domain:bing.com
+domain:live.com
+domain:msn.com
+domain:ntp.org
+domain:office.com
+domain:qlogo.cn
+domain:qq.com
+domain:redhat.com
+keyword:aaplimg
+keyword:aicdn
+keyword:akadns
+keyword:akamai
+keyword:apple
+keyword:douyin
+keyword:fclouddns
+keyword:icloud
+keyword:itunes
+keyword:microsoft
+keyword:mzstatic
+keyword:ssrcdn
+keyword:windows
+keyword:xxpkg
+keyword:yximgs
+keyword:yxygslb
diff --git a/package/lean/luci-app-mosdns/root/etc/uci-defaults/luci-mosdns b/package/lean/luci-app-mosdns/root/etc/uci-defaults/luci-mosdns
new file mode 100755
index 000000000..09c39fa02
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/etc/uci-defaults/luci-mosdns
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@mosdns[-1]
+ add ucitrack mosdns
+ set ucitrack.@mosdns[-1].init=mosdns
+ commit ucitrack
+EOF
+
+rm -rf /tmp/luci-*
+exit 0
diff --git a/package/lean/luci-app-mosdns/root/usr/share/mosdns/default.yaml b/package/lean/luci-app-mosdns/root/usr/share/mosdns/default.yaml
new file mode 100644
index 000000000..88012de0c
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/usr/share/mosdns/default.yaml
@@ -0,0 +1,191 @@
+log:
+ level: log_level
+ file: "log_file"
+
+include: []
+
+data_providers:
+ - tag: geoip
+ file: "/usr/share/v2ray/geoip.dat"
+ auto_reload: true
+
+ - tag: geosite
+ file: "/usr/share/v2ray/geosite.dat"
+ auto_reload: true
+
+ - tag: whitelist
+ file: "/etc/mosdns/rule/whitelist.txt"
+ auto_reload: true
+
+ - tag: blocklist
+ file: "/etc/mosdns/rule/blocklist.txt"
+ auto_reload: true
+
+ - tag: greylist
+ file: "/etc/mosdns/rule/greylist.txt"
+ auto_reload: true
+
+ - tag: hosts
+ file: "/etc/mosdns/rule/hosts.txt"
+ auto_reload: true
+
+ - tag: redirect
+ file: "/etc/mosdns/rule/redirect.txt"
+ auto_reload: true
+
+ - tag: local_ptr
+ file: "/etc/mosdns/rule/local-ptr.txt"
+ auto_reload: true
+
+ - tag: adlist
+ file: "/etc/mosdns/rule/adlist.txt"
+ auto_reload: true
+
+plugins:
+ - tag: lazy_cache
+ type: cache
+ args:
+ size: cache_size
+ lazy_cache_ttl: cache_survival_time
+
+ - tag: modify_ttl
+ type: ttl
+ args:
+ minimal_ttl: minimal_ttl_custom
+ maximum_ttl: maximum_ttl_custom
+
+ - tag: "forward_local"
+ type: fast_forward
+ args:
+ upstream:
+ - addr: local_dns
+
+ - tag: "forward_remote"
+ type: fast_forward
+ args:
+ upstream:
+ - addr: remote_dns
+
+ - tag: query_is_whitelist_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:whitelist"
+
+ - tag: query_is_blocklist_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:blocklist"
+
+ - tag: query_is_greylist_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:greylist"
+
+ - tag: query_is_hosts_domain
+ type: hosts
+ args:
+ hosts:
+ - "provider:hosts"
+
+ - tag: query_is_redirect_domain
+ type: redirect
+ args:
+ rule:
+ - "provider:redirect"
+
+ - tag: query_is_local_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:geosite:cn,apple-cn,icloud,google-cn,tld-cn"
+
+ - tag: query_is_non_local_domain
+ type: query_matcher
+ args:
+ domain:
+ - "provider:geosite:geolocation-!cn"
+
+ - tag: response_has_local_ip
+ type: response_matcher
+ args:
+ ip:
+ - "provider:geoip:cn"
+
+ - tag: query_is_ad_domain
+ type: query_matcher
+ args:
+ domain:
+ - "adblock"
+
+ - tag: match_local_ptr
+ type: query_matcher
+ args:
+ qtype: [12]
+ domain:
+ - "provider:local_ptr"
+
+ - tag: match_qtype65
+ type: query_matcher
+ args:
+ qtype: [65]
+
+ - tag: "main_sequence"
+ type: "sequence"
+ args:
+ exec:
+ - _misc_optm
+ - query_is_hosts_domain
+ - query_is_redirect_domain
+
+ - if: query_is_whitelist_domain
+ exec:
+ - forward_local
+ - modify_ttl
+ - _return
+
+ - if: "query_is_blocklist_domain || query_is_ad_domain || match_local_ptr || match_qtype65"
+ exec:
+ - _new_nxdomain_response
+ - _return
+
+ - lazy_cache
+
+ - if: query_is_greylist_domain
+ exec:
+ - forward_remote
+ - modify_ttl
+ - _return
+
+ - if: query_is_local_domain
+ exec:
+ - forward_local
+ - modify_ttl
+ - _return
+
+ - if: query_is_non_local_domain
+ exec:
+ - _prefer_ipv4
+ - forward_remote
+ - modify_ttl
+ - _return
+ - primary:
+ - forward_local
+ - if: "(! response_has_local_ip) && [_response_valid_answer]"
+ exec:
+ - _drop_response
+ secondary:
+ - _prefer_ipv4
+ - forward_remote
+ - modify_ttl
+ fast_fallback: 200
+
+servers:
+ - exec: main_sequence
+ listeners:
+ - protocol: udp
+ addr: ":listen_port"
+ - protocol: tcp
+ addr: ":listen_port"
diff --git a/package/lean/luci-app-mosdns/root/usr/share/mosdns/mosdns.sh b/package/lean/luci-app-mosdns/root/usr/share/mosdns/mosdns.sh
new file mode 100755
index 000000000..22d1af271
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/usr/share/mosdns/mosdns.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+logfile_path() (
+ configfile=$(uci -q get mosdns.config.configfile)
+ if [ "$configfile" = "/etc/mosdns/config.yaml" ]; then
+ uci -q get mosdns.config.logfile
+ else
+ [ ! -f /etc/mosdns/config_custom.yaml ] && exit 1
+ cat /etc/mosdns/config_custom.yaml | grep -A 4 log | grep file | awk -F ":" '{print $2}' | sed 's/\"//g;s/ //g'
+ fi
+)
+
+interface_dns() (
+ peerdns=$(uci -q get network.wan.peerdns)
+ proto=$(uci -q get network.wan.proto)
+ if [ "$peerdns" = 0 ] || [ "$proto" = "static" ]; then
+ uci -q get network.wan.dns
+ else
+ interface_status=$(ubus call network.interface.wan status)
+ echo $interface_status | jsonfilter -e "@['dns-server'][0]"
+ echo $interface_status | jsonfilter -e "@['dns-server'][1]"
+ fi
+ [ $? -ne 0 ] && echo "119.29.29.29"
+)
+
+ad_block() (
+ adblock=$(uci -q get mosdns.config.adblock)
+ if [ "$adblock" -eq 1 ]; then
+ ad_source=$(uci -q get mosdns.config.ad_source)
+ if [ "$ad_source" = "geosite.dat" ]; then
+ echo "provider:geosite:category-ads-all"
+ else
+ echo "provider:adlist"
+ fi
+ else
+ echo "full:disable-category-ads-all.null"
+ fi
+)
+
+adlist_update() (
+ ad_source=$(uci -q get mosdns.config.ad_source)
+ [ "$ad_source" = "geosite.dat" ] && exit 0
+ AD_TMPDIR=$(mktemp -d) || exit 1
+ if [[ "$ad_source" =~ "^https://raw.githubusercontent.com" ]]; then
+ google_status=$(curl -I -4 -m 3 -o /dev/null -s -w %{http_code} http://www.google.com/generate_204)
+ [ "$google_status" -ne "204" ] && mirror="https://ghproxy.com/"
+ fi
+ echo -e "\e[1;32mDownloading $mirror$ad_source\e[0m"
+ curl --connect-timeout 60 -m 90 --ipv4 -fSLo "$AD_TMPDIR/adlist.txt" "$mirror$ad_source"
+ if [ $? -ne 0 ]; then
+ rm -rf "$AD_TMPDIR"
+ exit 1
+ else
+ \cp "$AD_TMPDIR/adlist.txt" /etc/mosdns/rule/adlist.txt
+ rm -rf "$AD_TMPDIR"
+ fi
+)
+
+geodat_update() (
+ geodat_download() (
+ google_status=$(curl -I -4 -m 3 -o /dev/null -s -w %{http_code} http://www.google.com/generate_204)
+ [ "$google_status" -ne "204" ] && mirror="https://ghproxy.com/"
+ echo -e "\e[1;32mDownloading "$mirror"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/$1\e[0m"
+ curl --connect-timeout 60 -m 900 --ipv4 -fSLo "$TMPDIR/$1" ""$mirror"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/$1"
+ )
+ TMPDIR=$(mktemp -d) || exit 1
+ geodat_download geoip.dat && geodat_download geosite.dat
+ if [ $? -ne 0 ]; then
+ rm -rf "$TMPDIR"
+ exit 1
+ fi
+ cp -f "$TMPDIR"/* /usr/share/v2ray
+ rm -rf "$TMPDIR"
+)
+
+if [ "$1" == "dns" ]; then
+ interface_dns
+elif [ "$1" == "ad" ]; then
+ ad_block
+elif [ "$1" == "geodata" ]; then
+ geodat_update && adlist_update
+elif [ "$1" == "logfile" ]; then
+ logfile_path
+elif [ "$1" == "adlist_update" ]; then
+ adlist_update
+fi
diff --git a/package/lean/luci-app-mosdns/root/usr/share/rpcd/acl.d/luci-app-mosdns.json b/package/lean/luci-app-mosdns/root/usr/share/rpcd/acl.d/luci-app-mosdns.json
new file mode 100644
index 000000000..6c79e3307
--- /dev/null
+++ b/package/lean/luci-app-mosdns/root/usr/share/rpcd/acl.d/luci-app-mosdns.json
@@ -0,0 +1,11 @@
+{
+ "luci-app-mosdns": {
+ "description": "Grant UCI access for luci-app-mosdns",
+ "read": {
+ "uci": [ "mosdns" ]
+ },
+ "write": {
+ "uci": [ "mosdns" ]
+ }
+ }
+}
diff --git a/package/lean/mosdns/Makefile b/package/lean/mosdns/Makefile
new file mode 100644
index 000000000..77ad32272
--- /dev/null
+++ b/package/lean/mosdns/Makefile
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-3.0-only
+#
+# Copyright (C) 2021 ImmortalWrt.org
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=mosdns
+PKG_VERSION:=4.5.3
+PKG_RELEASE:=$(AUTORELEASE)
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/IrineSistiana/mosdns/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=2a13b1b1d862731cf946a8ceaa99523a0eb0eaae56045dd31207b61a5a6d47ae
+
+PKG_LICENSE:=GPL-3.0
+PKG_LICENSE_FILE:=LICENSE
+PKG_MAINTAINER:=Tianling Shen
+
+PKG_BUILD_DEPENDS:=golang/host
+PKG_BUILD_PARALLEL:=1
+PKG_USE_MIPS16:=0
+
+GO_PKG:=github.com/IrineSistiana/mosdns
+GO_PKG_LDFLAGS_X:=main.version=v$(PKG_VERSION)
+
+include $(INCLUDE_DIR)/package.mk
+include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
+
+define Package/mosdns
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=IP Addresses and Names
+ TITLE:=A plug-in DNS forwarder/splitter
+ URL:=https://github.com/IrineSistiana/mosdns
+ DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
+endef
+
+GO_PKG_TARGET_VARS:=$(filter-out CGO_ENABLED=%,$(GO_PKG_TARGET_VARS)) CGO_ENABLED=0
+
+define Package/mosdns/install
+ $(call GoPackage/Package/Install/Bin,$(1))
+endef
+
+$(eval $(call GoBinPackage,mosdns))
+$(eval $(call BuildPackage,mosdns))