🌴 Sync 2026-03-05 23:51:42

This commit is contained in:
github-actions[bot] committed 2026-03-05 23:51:42 +08:00
1 parent 97c049ed0a
commit 0acff03804
14253 files changed
+3893239

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
#!/bin/sh
# Copyright (C) 2023 muink https://github.com/muink
#
# Client listen port refresh script
#
# depends jsonfilter
. /usr/lib/natmap/common.sh
# http_request <cookie> <data>
http_request() {
$CURL $retry --compressed -L -X POST \
-b "$1" -H "Content-Type: application/json" \
-d "$2" \
--url ${scheme}://${host}:${web_port}/json
}
start() {
local retry='--connect-timeout 1 --retry 0'
local cookie="$($CURL $retry --compressed -L -i \
-H "Content-Type: application/json" \
-d '{"method": "auth.login", "params": ["'"${password}"'"], "id": 1}' \
--url ${scheme}://${host}:${web_port}/json \
| grep -i set-cookie | sed -En 's|^.*(_session_id=\S+);.*|\1|p')"
local connected="$(http_request "$cookie" '{"method": "web.connected", "params": [], "id": 1}' | jsonfilter -qe '@.result')"
if [ "$connected" = "true" ]; then
http_request "$cookie" '{"method": "core.set_config", "params": [{"random_port": false}], "id": 1}'
http_request "$cookie" '{"method": "core.set_config", "params": [{"listen_ports": ["'"${port}"'","'"${port}"'"]}], "id": 1}'
fi
http_request "$cookie" '{"method": "auth.delete_session", "params": [], "id": 1}'
}
# All external parameters required
ALL_PARAMS="host port scheme web_port username password"
eval "$(JSON_EXPORT "$1")"; shift
start "$@"
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
# Copyright (C) 2023 muink https://github.com/muink
#
# Client listen port refresh script
#
# depends
. /usr/lib/natmap/common.sh
start() {
local retry='--connect-timeout 1 --retry 0'
scheme='http' # only supports http
local sid="$($CURL $retry -L \
-u ${username}:${password} \
--url ${scheme}://${host}:${web_port}/transmission/rpc \
| sed 's|.*<code>||g;s|</code>.*||g')"
$CURL $retry -L -X POST \
-H "$sid" -u ${username}:${password} \
-d '{"method":"session-set","arguments":{"peer-port":"'"${port}"'"}}' \
--url ${scheme}://${host}:${web_port}/transmission/rpc
}
# All external parameters required
ALL_PARAMS="host port scheme web_port username password"
eval "$(JSON_EXPORT "$1")"; shift
start "$@"
+40
View File
@@ -0,0 +1,40 @@
#!/bin/sh
# Copyright (C) 2023-2025 muink https://github.com/muink
#
# Client listen port refresh script
#
# depends
. /usr/lib/natmap/common.sh
start() {
local retry='--connect-timeout 1 --retry 0'
local self_signed='-k'
local payload
case "$(basename "$0")" in
qBittorrent)
payload='json={"listen_port":'"${port}"'}';;
qBittorrent-announce_port)
payload='json={"announce_port":'"${port}"'}';;
*)
payload='json={"listen_port":'"${port}"'}';;
esac
local cookie="$($CURL $retry $self_signed -L -i \
-H "Referer: ${scheme}://${host}:${web_port}" \
-d "username=${username}&password=${password}" \
--url ${scheme}://${host}:${web_port}/api/v2/auth/login \
| grep -i set-cookie | sed -En 's|^.*(SID=\S+);.*|\1|p')"
$CURL $retry $self_signed -L -X POST \
-b "$cookie" \
-d "$payload" \
--url ${scheme}://${host}:${web_port}/api/v2/app/setPreferences
$CURL $retry $self_signed -L -I \
--cookie "$cookie" \
--url ${scheme}://${host}:${web_port}/api/v2/auth/logout
}
# All external parameters required
ALL_PARAMS="host port scheme web_port username password"
eval "$(JSON_EXPORT "$1")"; shift
start "$@"
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
# Copyright (C) 2023 muink https://github.com/muink
#
# depends curl jsonfilter
CURL="$(command -v natmap-curl)"
# JSON_EXPORT <json>
JSON_EXPORT() {
for k in $ALL_PARAMS; do
jsonfilter -qs "$1" -e "$k=@['$k']"
done
}
# INIT_GLOBAL_VAR <var1> [var2] [var3] ...
INIT_GLOBAL_VAR() {
for _key in "$@"; do
eval "$_key=''"
done
}
+93
View File
@@ -0,0 +1,93 @@
#!/bin/bash
# Copyright (C) 2023 muink https://github.com/muink
#
# DDNS script
#
# depends jsonfilter
. /usr/lib/natmap/common.sh
. /usr/share/libubox/jshn.sh
# http_request <data> [record_id]
http_request() {
local data="$1"
local id="$2"
local method
[ -z "${id}" ] && method=POST || method=PUT # or PATCH
$CURL $retry -L -o /dev/null -X ${method} \
--url "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${id}" \
-H "Authorization: Bearer ${TOKEN}" \
-H 'Content-Type: application/json' \
-d "$data"
}
start() {
local retry='--connect-timeout 3 --retry 5'
#$CURL $retry -L -X GET \
# --url "https://api.cloudflare.com/client/v4/user/tokens/verify" \
# -H "Authorization: Bearer ${TOKEN}" \
# -H "Content-Type:application/json" | jq
#$CURL $retry -L -X GET \
# --url "https://api.cloudflare.com/client/v4/zones" \
# -H "Authorization: Bearer ${TOKEN}" \
# -H "Content-Type:application/json" | jq
local records="$($CURL $retry -L -X GET \
--url "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
-H "Authorization: Bearer ${TOKEN}" \
-H 'Content-Type: application/json')"
[ -z "$records" -o -n "$(grep -E "\"success\":\s*false" <<< "$records")" ] && return 1
local the_record
if [ -n "$host" ]; then
the_record="$(jsonfilter -s "$records" -qe '@.result[*]'|grep "\"type\": \"${hostype}\""|grep "\"name\": \"${host}\"")"
json_init
json_add_string type "${hostype}"
json_add_string name "${host}"
json_add_string content "${ip}"
json_add_int ttl 1
json_add_boolean proxied 0
http_request "$(json_dump)" "$(jsonfilter -s "$the_record" -qe '@.id')"
fi
if [ -n "${srv}" ]; then
the_record="$(jsonfilter -s "$records" -qe '@.result[*]'|grep "\"type\": \"SRV\""|grep "\"name\": \"_${srv_serv}._${srv_proto}.${srv}\""|grep "\"priority\": ${srv_priority}"|grep "\"weight\": ${srv_weight}")"
json_init
json_add_string type "SRV"
json_add_string name "_${srv_serv}._${srv_proto}.${srv}"
json_add_object data
json_add_int priority "${srv_priority}"
json_add_int weight "${srv_weight}"
json_add_int port "${port}"
json_add_string target "${srv_target}"
json_close_object
json_add_int ttl 1
json_add_boolean proxied 0
http_request "$(json_dump)" "$(jsonfilter -s "$the_record" -qe '@.id')"
fi
if [ -n "${https}" ]; then
the_record="$(jsonfilter -s "$records" -qe '@.result[*]'|grep "\"type\": \"HTTPS\""|grep "\"name\": \"${https}\""|grep "\"priority\": ${https_priority}")"
json_init
json_add_string type "HTTPS"
json_add_string name "${https}"
json_add_object data
json_add_int priority "${https_priority}"
json_add_string target "${https_target}"
json_add_string value "${https_svcparams}"
json_close_object
json_add_int ttl 1
json_add_boolean proxied 0
http_request "$(json_dump)" "$(jsonfilter -s "$the_record" -qe '@.id')"
fi
}
# All external parameters required
ALL_PARAMS="hostype host srv srv_serv srv_proto srv_target srv_priority srv_weight ip port https https_target https_svcparams https_priority tokens"
eval "$(JSON_EXPORT "$1")"; shift
# All external tokens required
INIT_GLOBAL_VAR TOKEN ZONE_ID
eval "$tokens"
start "$@"
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
export ip="$1"
export port="$2"
export ip4p="$3"
export inner_port="$4"
export protocol="$5"
export inner_ip="$6"
shift 6
. /usr/share/libubox/jshn.sh
INITD='/etc/init.d/natmap'
STATUS_PATH='/var/run/natmap'
# fallloop <retry interval> <retry limit> <func> [args...]
fallloop() {
local retry="$1"; shift
local limit="$1"; shift
local func="$1"; shift
local error=1 count=0 && until [ $error = 0 -o $count -ge $limit ]; do
$func "$@" && error=0 || error=$?
let count++ && sleep $retry
done
}
# keep dest_port(forward port) consistent with public port
if [ -n "$RWFW" -a "$($INITD info|jsonfilter -qe "@['$(basename $INITD)'].instances['$SECTIONID'].data.firewall[0].dest_port")" != "$port" ]; then
export PUBPORT="$port" #PROCD_DEBUG=1
$INITD start "$SECTIONID"
fi
(
json_init
json_add_string sid "$SECTIONID"
json_add_string comment "$COMMENT"
json_add_string ip "$ip"
json_add_int port "$port"
json_add_string ip4p "$ip4p"
json_add_int inner_port "$inner_port"
json_add_string protocol "$protocol"
json_add_string inner_ip "$inner_ip"
json_dump > "$STATUS_PATH/$PPID.json"
)
if [ -n "$REFRESH" ]; then
json_init
json_load "$REFRESH_PARAM"
json_add_int port "$port"
$REFRESH "$(json_dump)"
fi
if [ -n "$NOTIFY" ]; then
_text="$(jsonfilter -qs "$NOTIFY_PARAM" -e '@["text"]')"
[ -z "$_text" ] && _text="NATMap: ${COMMENT:+$COMMENT: }[${protocol^^}] $inner_ip:$inner_port -> $ip:$port" \
|| _text="$(echo "$_text" | sed " \
s|<comment>|$COMMENT|g; \
s|<protocol>|$protocol|g; \
s|<inner_ip>|$inner_ip|g; \
s|<inner_port>|$inner_port|g; \
s|<ip>|$ip|g; \
s|<port>|$port|g")"
json_init
json_load "$NOTIFY_PARAM"
json_add_string comment "$COMMENT"
json_add_string text "$_text"
fallloop 5m 4 $NOTIFY "$(json_dump)" &
fi
if [ -n "$DDNS" ]; then
_hostype="$(jsonfilter -qs "$DDNS_PARAM" -e '@["hostype"]')"
_svcparams="$(jsonfilter -qs "$DDNS_PARAM" -e '@["https_svcparams"]')"
_svcparams="$(echo "$_svcparams" | sed -E "s,\s*(port=\d*|$), port=${port},")" # port
[ "$_hostype" = A ] && _svcparams="$(echo "$_svcparams" | sed -E "s|\b(ipv4hint=)\S*|\1${ip}|")" # ipv4hint
[ "$_hostype" = AAAA ] && _svcparams="$(echo "$_svcparams" | sed -E "s|\b(ipv6hint=)\S*|\1${ip}|")" # ipv6hint
json_init
json_load "$DDNS_PARAM"
json_add_string https_svcparams "$_svcparams"
json_add_string ip "$ip"
json_add_int port "$port"
fallloop 5m 4 $DDNS "$(json_dump)" &
fi
[ -n "${CUSTOM_SCRIPT}" ] && {
export -n CUSTOM_SCRIPT
exec "${CUSTOM_SCRIPT}" "$ip" "$port" "$ip4p" "$inner_port" "$protocol" "$inner_ip" "$SECTIONID" "$COMMENT" "$@"
}
+100
View File
@@ -0,0 +1,100 @@
config global
option enable '0'
option def_tcp_stun 'stunserver.stunprotocol.org'
option def_udp_stun 'stunserver.stunprotocol.org'
option def_http_server 'example.com'
option def_tcp_interval '30'
option def_udp_interval '15'
option def_udp_stun_cycle '3'
config natmap
option enable '0'
option interval ''
option stun_server ''
option http_server 'www.baidu.com'
option udp_mode '0'
option family ''
option bind_ifname ''
option port '8080'
option forward '0'
option forward_mode 'via'
option natloopback '1'
option forward_target ''
option forward_port ''
option custom_script ''
option log_stdout '1'
option log_stderr '1'
config natmap
option enable '1'
option comment 'MC Server'
option family 'ipv4'
option udp_mode '0'
option port '25565'
option forward '1'
option forward_mode 'dnat'
option natloopback '1'
option forward_target '192.168.1.123'
option forward_port '25565'
option ddns_enable '1'
option ddns_script '/etc/natmap/ddns/Cloudflare'
list ddns_tokens 'TOKEN=<api_token>'
list ddns_tokens 'ZONE_ID=<zone_id>'
option ddns_a 'mc.example.com'
option ddns_srv 'mc.example.com'
option ddns_srv_serv 'minecraft'
option ddns_srv_proto 'tcp'
config natmap
option enable '1'
option comment 'qBittorrent'
option udp_mode '0'
option family 'ipv4'
option port '114514'
option forward '1'
option forward_mode 'dnat'
option natloopback '0'
option forward_target '192.168.1.123'
option forward_port '0'
option refresh '1'
option clt_script '/etc/natmap/client/qBittorrent'
option clt_scheme 'http'
option clt_web_port '8080'
option clt_username 'admin'
option clt_password 'wrt6V5xAB6_t6Cr6Vm7554'
option notify_enable '1'
option notify_script '/etc/natmap/notify/ntfy'
list notify_tokens 'TOPIC=<topic>'
list notify_tokens 'TITLE=<title>'
config natmap
option enable '1'
option comment 'librespeed-go'
option udp_mode '0'
option family 'ipv4'
option bind_ifname 'eth1'
option port '8989'
option forward '1'
option forward_mode 'dnat'
option forward_target '127.0.0.1'
option forward_port '8989'
option ddns_enable '1'
option ddns_script '/etc/natmap/ddns/Cloudflare'
list ddns_tokens 'TOKEN=<api_token>'
list ddns_tokens 'ZONE_ID=<zone_id>'
option ddns_enable '1'
option ddns_a 'web.example.com'
option ddns_https 'web.example.com'
option ddns_https_target '.'
option ddns_https_svcparams 'alpn="h2,http/1.1"'
config natmap
option enable '0'
option comment 'DNSmasq need trun off '"'"'Local_service_only'"'"''
option udp_mode '1'
option family 'ipv4'
option port '5321'
option forward '1'
option forward_mode 'dnat'
option forward_target '0.0.0.0'
option forward_port '53'
+32
View File
@@ -0,0 +1,32 @@
#!/bin/sh
# Copyright (C) 2023 muink https://github.com/muink
CONF='/etc/config/natmap'
NAME='natmap'
NATMAPSECTION='natmap'
. /lib/functions/network.sh
# uci options migration
if [ -z "$(grep '^config global$' "$CONF")" ]; then
sed -i '1i\\config global' "$CONF"
## notify_script --> custom_script
sed -i 's|\boption notify_script|option custom_script|g' "$CONF"
## interface --> bind_ifname
for k in $(uci -q show $NAME|sed -En "s|^$NAME\.@$NATMAPSECTION\[(\d+)\]\.interface='(.*)'$|'\1:\2'|gp"); do
i=${k%%:*}; i=${i#\'}
iface="${k#*:}"; iface="${iface%\'}"
network_get_device ifname "$iface"
[ -z "$ifname" ] && network_get_physdev ifname "$iface"
[ -z "$ifname" ] && continue
uci set $NAME.@$NATMAPSECTION[$i].bind_ifname="$ifname"
uci delete $NAME.@$NATMAPSECTION[$i].interface
uci commit $NAME
done
else
## def_interval --> def_tcp_interval/def_udp_interval
sed -Ei 's|def_interval (.*)|def_tcp_interval \1\n\toption def_udp_interval \1|' "$CONF"
fi
# gen natmap-curl
ln -sf "$(command -v curl)" /usr/bin/natmap-curl
exit 0
+486
View File
@@ -0,0 +1,486 @@
#!/bin/bash /etc/rc.common
. "${IPKG_INSTROOT}/lib/functions/network.sh"
START=97
STOP=90
USE_PROCD=1
# natmap
[ -x "$(command -v nft)" ] && FW='fw4' || FW='fw3'
NAME=natmap
GLOBALSECTION=global
NATMAPSECTION=natmap
PROG=/usr/bin/$NAME
STATUS_PATH=/var/run/natmap
config_load "$NAME"
# define global var: DEF_WAN DEF_WAN6 NIC_* NIC6_*
define_nic() {
local dev sub addr
# get all active NICs
for dev in $(ls /sys/class/net/); do
#ipv4
sub=$(ip -o -4 addr|sed -En "s|.*${dev}\s+inet\s+([0-9\./]+).*|\1|gp")
eval "NIC_${dev//[[:punct:]]/_}=\"\$sub\""
#ipv6
sub=$(ip -o -6 addr|sed -En "s|.*${dev}\s+inet6\s+([A-Za-z0-9\./:]+).*|\1|gp")
# ref: https://github.com/openwrt/openwrt/blob/main/package/base-files/files/lib/functions/network.sh#L53 #network_get_subnet6()
for _ in $sub; do
for addr in $sub; do
case "$addr" in fe[8ab]?:*|f[cd]??:*)
continue
esac
sub=$addr; break
done
# Attempt to return first non-fe80::/10 range
for addr in $sub; do
case "$addr" in fe[8ab]?:*)
continue
esac
sub=$addr; break
done
# Return first item
for addr in $sub; do
sub=$addr; break
done
done
eval "NIC6_${dev//[[:punct:]]/_}=\"\$sub\""
done
# get default gateway 0.0.0.0/::
network_find_wan DEF_WAN true
network_find_wan6 DEF_WAN6 true
return 0
}
define_nic
load_interfaces() {
local bind_ifname enable
config_get bind_ifname "$1" bind_ifname
config_get_bool enable "$1" enable 0
[ "$enable" = 1 ] && interfaces=" $(uci -q show network|grep "device='$bind_ifname'"|cut -f2 -d'.') $interfaces"
}
# define global var: GLOBAL_*
define_global() {
[ "$2" == "0" ] || { >&2 echo "$(basename $0): section $1 validation failed"; return 1; }
local error=0
local v ucivv="enable def_tcp_stun def_udp_stun def_http_server def_fwmark_value def_tcp_interval def_udp_interval def_udp_stun_cycle"
for v in $ucivv; do
[ -z "$(config_get $1 $v)" ] && grep -qEv "^(def_fwmark_value)$" <<< "$v" && {
err_msg__empty $1 $v
let error++
}
eval "GLOBAL_$v=\$$v"
done
[ "$error" -gt 0 ] && return 1 || return 0
}
validate_section_global() {
uci_load_validate "$NAME" "$GLOBALSECTION" "$1" "$2" \
'enable:bool:0' \
'def_tcp_stun:string' \
'def_udp_stun:string' \
'def_http_server:string' \
'def_fwmark_value:string' \
'def_tcp_interval:and(uinteger, min(1)):30' \
'def_udp_interval:and(uinteger, min(1)):15' \
'def_udp_stun_cycle:uinteger:5' \
'test_port:and(port, min(1))'
}
validate_section_natmap() {
uci_load_validate "$NAME" "$NATMAPSECTION" "$1" "$2" \
'enable:bool:0' \
'interval:and(uinteger, min(1))' \
'stun_cycle:uinteger' \
'stun_server:string' \
'http_server:string' \
'fwmark_value:string' \
'comment:string' \
'udp_mode:bool:0' \
'family:or("ipv4", "ipv6"):ipv4' \
'bind_ifname:network' \
'port:or(port, portrange)' \
'port_pointer:bool:0' \
'forward:bool:0' \
'forward_mode:or("dnat", "via"):via' \
'natloopback:bool:1' \
'forward_congestion:string' \
'forward_timeout:and(uinteger, min(1))' \
'forward_target:or(hostname,ipaddr(1))' \
'forward_port:port' \
'refresh:bool:0' \
'clt_script:file' \
'clt_scheme:or("http", "https"):http' \
'clt_web_port:and(port, min(1))' \
'clt_username:string' \
'clt_password:string' \
'notify_enable:bool:0' \
'notify_script:file' \
'notify_tokens:list(string)' \
'notify_custom_domain:hostname' \
'notify_text:string' \
'ddns_enable:bool:0' \
'ddns_script:file' \
'ddns_tokens:list(string)' \
'ddns_a:hostname' \
'ddns_aaaa:hostname' \
'ddns_srv:hostname' \
'ddns_srv_serv:string' \
'ddns_srv_proto:string:tcp' \
'ddns_srv_target:hostname' \
'ddns_srv_priority:range(0, 65535):0' \
'ddns_srv_weight:range(0, 65535):65535' \
'ddns_https:hostname' \
'ddns_https_target:or(".", hostname):.' \
'ddns_https_svcparams:string:alpn="h2,http/1.1"' \
'ddns_https_priority:range(1, 65535):1' \
'custom_script:file' \
'log_stdout:bool:1' \
'log_stderr:bool:1'
}
# ip_match <hostname>
hostname_match() {
[ "$#" -ge 1 ] || return 1
local _hostname="$1"
echo "$_hostname" | grep -E "^[[:alnum:]][-[:alnum:]]{0,62}(\.[[:alnum:]][-[:alnum:]]{0,62})*\.?$"
}
# ip_match <family> <ipaddr>
ip_match() {
[ "$#" -ge 2 ] || return 1
local _family="$1"
local _ip="$2"
case "$_family" in
ipv4)
echo "$_ip" | grep -E "^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$"
;;
ipv6)
echo "$_ip" | grep -E "^(([[:xdigit:]]{1,4}:){7}[[:xdigit:]]{1,4}|([[:xdigit:]]{1,4}:){1,7}:|([[:xdigit:]]{1,4}:){1,6}:[[:xdigit:]]{1,4}|([[:xdigit:]]{1,4}:){1,5}(:[[:xdigit:]]{1,4}){1,2}|([[:xdigit:]]{1,4}:){1,4}(:[[:xdigit:]]{1,4}){1,3}|([[:xdigit:]]{1,4}:){1,3}(:[[:xdigit:]]{1,4}){1,4}|([[:xdigit:]]{1,4}:){1,2}(:[[:xdigit:]]{1,4}){1,5}|[[:xdigit:]]{1,4}:(:[[:xdigit:]]{1,4}){1,6}|:((:[[:xdigit:]]{1,4}){1,7}|:)|\
fe80:(:[[:xdigit:]]{0,4}){0,4}%\w+|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)|([[:xdigit:]]{1,4}:){1,4}:((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))$"
;;
esac
}
# fwmark_match <fwmark>
fwmark_match() {
echo "$1" | grep -E '^(0|0x[[:xdigit:]]+|[1-9][0-9]*|0[0-7]+)$'
}
natmap_instance() {
procd_open_instance "$1"
procd_set_param command "$PROG" \
${interval:+-k "$interval"} \
${stun_cycle:+-c "$stun_cycle"} \
${stun_server:+-s "$stun_server"} \
${http_server:+-h "$http_server"} \
${fwmark_value:+-f "$fwmark_value"} \
${port:+-b "$([ "$port_pointer" = 1 ] && echo ${port/-/\~} || echo $port)"} \
[ "${family}" = ipv4 ] && procd_append_param command -4
[ "${family}" = ipv6 ] && procd_append_param command -6
[ "${udp_mode}" = 1 ] && procd_append_param command -u
procd_set_param netdev "br-lan"
[ -n "${bind_ifname}" ] && {
procd_append_param command -i "$bind_ifname"
procd_append_param netdev "$bind_ifname"
} || {
local ifname
network_get_device ifname "$DEF_WAN" && procd_append_param netdev "$ifname"
network_get_device ifname "$DEF_WAN6" && procd_append_param netdev "$ifname"
}
if [ "$forward" = 1 -a "$forward_mode" = "via" -a -n "${forward_target}" ]; then
procd_append_param command ${forward_congestion:+-C "$forward_congestion"} ${forward_timeout:+-T "$forward_timeout"} -t "$forward_target" -p "$forward_port"
fi
procd_set_param env "SECTIONID=$1"
procd_append_param env "COMMENT=$comment"
if [ "$forward" = 1 ]; then
# Rewrite the dest_port(forward port) using a public port
[ "$forward_port" = 0 -a "$forward_mode" = "dnat" ] && procd_append_param env "RWFW=1"
if [ "$refresh" = 1 ]; then
procd_append_param env "REFRESH=$clt_script"
json_init
json_add_string scheme "$clt_scheme"
json_add_int web_port "$clt_web_port"
json_add_string username "$clt_username"
json_add_string password "$clt_password"
json_add_string host "$forward_target"
procd_append_param env "REFRESH_PARAM=$(json_dump)"
fi
fi
if [ "$notify_enable" = 1 ]; then
procd_append_param env "NOTIFY=$notify_script"
json_init
json_add_string custom_domain "$notify_custom_domain"
json_add_string text "$notify_text"
json_add_string tokens "$notify_tokens"
procd_append_param env "NOTIFY_PARAM=$(json_dump)"
fi
if [ "$ddns_enable" = 1 ]; then
procd_append_param env "DDNS=$ddns_script"
json_init
json_add_string tokens "$ddns_tokens"
[ "$family" = ipv4 ] && json_add_string hostype "A" && json_add_string host "$ddns_a"
[ "$family" = ipv6 ] && json_add_string hostype "AAAA" && json_add_string host "$ddns_aaaa"
[ -n "$ddns_srv" ] && {
json_add_string srv "$ddns_srv"
json_add_string srv_serv "$ddns_srv_serv"
json_add_string srv_proto "$ddns_srv_proto"
json_add_string srv_target "$ddns_srv_target"
json_add_int srv_priority "$ddns_srv_priority"
json_add_int srv_weight "$ddns_srv_weight"
}
[ -n "$ddns_https" ] && {
json_add_string https "$ddns_https"
json_add_string https_target "$ddns_https_target"
json_add_string https_svcparams "$ddns_https_svcparams"
json_add_int https_priority "$ddns_https_priority"
}
procd_append_param env "DDNS_PARAM=$(json_dump)"
fi
[ -n "${custom_script}" ] && procd_append_param env "CUSTOM_SCRIPT=${custom_script}"
procd_append_param command -e /usr/lib/natmap/update.sh
procd_set_param respawn
procd_set_param stdout "$log_stdout"
procd_set_param stderr "$log_stderr"
procd_open_data
# configure firewall
json_add_array firewall
if [ "$forward" = 1 -a "$forward_mode" = "dnat" ]; then
json_add_object ''
json_add_string type redirect
json_add_string target DNAT
json_add_string name "$1"
json_add_string family "$family"
json_add_string proto "$proto"
json_add_string src "$($FW -q device $bind_ifname)"
#json_add_string src_dip "$bind_ip" # fix for #11 (The external address of the firewall will not be updated), fw4 cannot add `iifname` rule in `<action>_<fw zone>` chain
json_add_string src_dport "$port"
#json_add_string dest '' # zlan or zwan
json_add_string dest_ip "$forward_target"
[ -n "$PUBPORT" ] \
&& json_add_string dest_port "$PUBPORT" && unset PUBPORT \
|| json_add_string dest_port "$forward_port"
json_add_boolean reflection $natloopback
json_close_object
fi
if [ "$forward" = 1 -a "$forward_mode" = "via" -o "$forward" = 0 ]; then
json_add_object ''
json_add_string type rule
json_add_string target ACCEPT
json_add_string name "$1"
json_add_string family "$family"
json_add_string proto "$proto"
json_add_string direction in
json_add_string src "$($FW -q device $bind_ifname)"
#json_add_string dest '' # '' = input
#json_add_string dest_ip "$bind_ip" # fix for #11 (The external address of the firewall will not be updated), fw4 cannot add `iifname` rule in `<action>_<fw zone>` chain
json_add_string dest_port "$port"
json_close_object
fi
json_close_array
procd_close_data
procd_close_instance
}
clear_status_files() {
local f sid="$1"
mkdir -p "${STATUS_PATH}" 2>/dev/null
pushd "${STATUS_PATH}" >/dev/null
if [ -n "$sid" ]; then
for f in *.json; do
[ "$(jsonfilter -q -i $f -e '@.sid')" = "$sid" ] || continue
rm -f $f; break
done
else
find . -type f -print0 | xargs -0 rm -f --
fi
popd >/dev/null
}
# err_msg__empty <section> <option>
err_msg__empty() {
>&2 echo "$(basename $0): section $1 option $2 cannot be empty"
}
launcher() {
[ "$2" = 0 ] || { >&2 echo "$(basename $0): section $1 validation failed"; return 1; }
[ "$enable" = 0 ] && return 0
# global options
if [ "$udp_mode" = 1 ]; then
local interval=${interval:-$GLOBAL_def_udp_interval} \
stun_cycle=${stun_cycle:-$GLOBAL_def_udp_stun_cycle} \
stun_server=${stun_server:-$GLOBAL_def_udp_stun} \
http_server='' \
;
else
local interval=${interval:-$GLOBAL_def_tcp_interval} \
stun_cycle='' \
stun_server=${stun_server:-$GLOBAL_def_tcp_stun} \
http_server=${http_server:-$GLOBAL_def_http_server} \
;
fi
local fwmark_value=${fwmark_value:-$GLOBAL_def_fwmark_value}
# natmap options
local error=0
local proto ifname bind_ip lan_addr
[ "$udp_mode" = 1 ] && proto=udp || proto=tcp
case "$family" in
ipv4) network_get_device ifname "$DEF_WAN";;
ipv6) network_get_device ifname "$DEF_WAN6";;
esac
local bind_ifname=${bind_ifname:-$ifname}
[ -z "$bind_ifname" ] && >&2 echo "$(basename $0): section $1 option bind_ifname parsing failed, there may be no $family network connection" && let error++
case "$family" in
ipv4)
eval "bind_ip=\"\${NIC_${bind_ifname//[[:punct:]]/_}%/*}\""
lan_addr="${NIC_br_lan%/*}"
;;
ipv6)
eval "bind_ip=\"\${NIC6_${bind_ifname//[[:punct:]]/_}%/*}\""
lan_addr="${NIC6_br_lan%/*}"
;;
esac
[ -z "$bind_ip" ] && >&2 echo "$(basename $0): section $1 option bind_ip parsing failed, there may be no $family network connection" && let error++
[ -z "$port" ] && err_msg__empty $1 port && let error++
grep -qE '^([1-9]\d*)(-([1-9]\d*))?$' <<< "$port" || { >&2 echo "$(basename $0): section $1 option port '$port' is invalid"; let error++; }
[ -z "$fwmark_value" -o -n "$(fwmark_match "$fwmark_value")" ] || { >&2 echo "$(basename $0): section $1 option fwmark_value '$fwmark_value' is invalid"; let error++; }
## forward
if [ "$forward" = 1 ]; then
[ "$forward_mode" = "dnat" -a "$family" = "ipv6" ] && >&2 echo "$(basename $0): section $1 option forward_mode 'dnat' not support under IPv6" && let error++
[ "$forward_mode" != "dnat" ] && natloopback=0
if [ -n "$forward_target" ]; then
if [ -n "$(hostname_match "$forward_target")" -a -z "$(ip_match ipv4 "$forward_target")" ]; then
# hostname
[ "$forward_mode" = "dnat" ] && >&2 echo "$(basename $0): section $1 option forward_target '$forward_target', hostname not support under DNAT mode" && let error++
else
# ipaddr(1)
[ -n "$(grep -E "^127(\.\d+){3}" <<< $forward_target)" -o "$forward_target" = "::1" ] && forward_target="$lan_addr"
[ -n "$(grep -E "^0(\.\d+){3}" <<< $forward_target)" -o "$forward_target" = "::" ] && forward_target="$bind_ip"
if [ -n "$forward_target" ]; then
[ -z "$(ip_match "$family" "$forward_target")" ] && >&2 echo "$(basename $0): section $1 option forward_target '$forward_target' not a $family address" && let error++
else
>&2 echo "$(basename $0): section $1 option forward_target parsing failed, there may be no $family network connection"; let error++
fi
fi
else
err_msg__empty $1 forward_target; let error++
fi
[ -z "$forward_port" ] && err_msg__empty $1 forward_port && let error++
elif [ "$forward" = 0 ]; then
natloopback=0
unset forward_target forward_port
fi
## refresh
if [ "$refresh" = 1 ]; then
[ -x "$clt_script" ] || { >&2 echo "$(basename $0): section $1 option clt_script '$clt_script' is empty or non-executable"; let error++; }
[ -z "$clt_web_port" ] && err_msg__empty $1 clt_web_port && let error++
fi
## notify
if [ "$notify_enable" = 1 ]; then
[ -x "$notify_script" ] || { >&2 echo "$(basename $0): section $1 option notify_script '$notify_script' is empty or non-executable"; let error++; }
fi
## ddns
if [ "$ddns_enable" = 1 ]; then
[ -x "$ddns_script" ] || { >&2 echo "$(basename $0): section $1 option ddns_script '$ddns_script' is empty or non-executable"; let error++; }
if [ -n "$ddns_srv" ]; then
[ -z "$ddns_srv_serv" ] && err_msg__empty $1 ddns_srv_serv && let error++
local ddns_srv_target=${ddns_srv_target:-$ddns_srv}
fi
fi
# review
[ -n "$NATMAP_DEBUG" ] && {
local v ucivv="enable interval stun_cycle stun_server http_server fwmark_value comment udp_mode proto family bind_ifname ifname bind_ip lan_addr port port_pointer forward forward_mode natloopback forward_congestion forward_timeout forward_target forward_port refresh clt_script clt_scheme clt_web_port clt_username clt_password notify_enable notify_script notify_tokens notify_custom_domain notify_text ddns_enable ddns_script ddns_tokens ddns_a ddns_aaaa ddns_srv ddns_srv_serv ddns_srv_proto ddns_srv_target ddns_srv_priority ddns_srv_weight ddns_https ddns_https_target ddns_https_svcparams ddns_https_priority custom_script"
for v in $ucivv; do eval "echo $1 $v=\'\$$v\'"; done # ash not support ${!v}
}
[ "$error" -gt 0 ] && return 1
natmap_instance "$1"
}
service_triggers() {
procd_add_reload_trigger "$NAME" 'network'
local interfaces
config_foreach load_interfaces $NATMAPSECTION
[ -n "$interfaces" ] && {
for n in $interfaces; do
procd_add_reload_interface_trigger $n
done
} || {
for n in $DEF_WAN $DEF_WAN6; do
procd_add_reload_interface_trigger $n
done
}
interfaces=$(uci show network|grep "device='br-lan'"|cut -f2 -d'.')
[ -n "$interfaces" ] && {
for n in $interfaces; do
procd_add_reload_interface_trigger $n
done
}
procd_add_validation validate_section_natmap
}
start_service() {
local sid="$1"
config_foreach validate_section_global "$GLOBALSECTION" define_global || return $?
[ "${GLOBAL_enable:=0}" == "0" ] && return 1
if [ -n "$sid" ]; then
clear_status_files "$sid"
validate_section_natmap "$sid" launcher
else
clear_status_files
config_foreach validate_section_natmap "$NATMAPSECTION" launcher
fi
}
stop_service() {
local sid="$1"
clear_status_files "$sid"
}
service_stopped() {
sleep 1s # Wait for procd_kill complete
ps | grep natmap | awk '{print $1}' | xargs kill -9
}
reload_service() {
stop "$@"
start "$@"
}
service_started() { procd_set_config_changed firewall; }
service_stopped() { procd_set_config_changed firewall; }
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
# Copyright (C) 2023 muink https://github.com/muink
#
# Port update notify script
#
# depends
. /usr/lib/natmap/common.sh
start() {
local retry='--connect-timeout 3 --retry 5'
$CURL $retry -L -o /dev/null -X POST \
-H "Access-Token: ${TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"type":"note","body":"'"${text}"'","title":"'"${TITLE}"'"}' \
--url "https://${custom_domain:-api.pushbullet.com}/v2/pushes"
}
# All external parameters required
ALL_PARAMS="custom_domain text tokens"
eval "$(JSON_EXPORT "$1")"; shift
# All external tokens required
INIT_GLOBAL_VAR TOKEN TITLE
eval "$tokens"
start "$@"
+25
View File
@@ -0,0 +1,25 @@
#!/bin/sh
# Copyright (C) 2023 muink https://github.com/muink
#
# Port update notify script
#
# depends
. /usr/lib/natmap/common.sh
start() {
local retry='--connect-timeout 3 --retry 5'
$CURL $retry -L -o /dev/null -X POST \
-H 'Content-Type: application/json' \
-d '{"token":"'"${TOKEN}"'","user":"'"${TO_ID}"'","device":"'"${DEVICE}"'","message":"'"${text}"'","title":"'"${TITLE}"'","html":"'"${HTML}"'"}' \
--url https://${custom_domain:-api.pushover.net}/1/messages.json
}
# All external parameters required
ALL_PARAMS="custom_domain text tokens"
eval "$(JSON_EXPORT "$1")"; shift
# All external tokens required
INIT_GLOBAL_VAR TOKEN TO_ID DEVICE TITLE HTML
eval "$tokens"
start "$@"
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
# Copyright (C) 2023-2025 muink https://github.com/muink
#
# Port update notify script
#
# depends
. /usr/lib/natmap/common.sh
start() {
local retry='--connect-timeout 3 --retry 5'
# 如果 CUESTOM_PROXY 不为空,则添加 -x 参数
local proxy_param=${CUESTOM_PROXY:+-x $CUESTOM_PROXY}
$CURL $retry -L -o /dev/null -X POST $proxy_param \
-H 'Content-Type: application/json' \
-d '{"chat_id":"'"${CHAT_ID}"'","text":"'"${text}"'","parse_mode":"HTML","disable_notification":"false"}' \
--url "https://${custom_domain:-api.telegram.org}/bot${TOKEN}/sendMessage"
}
# All external parameters required
ALL_PARAMS="custom_domain text tokens"
eval "$(JSON_EXPORT "$1")"; shift
# All external tokens required
INIT_GLOBAL_VAR TOKEN CHAT_ID CUESTOM_PROXY
eval "$tokens"
start "$@"
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# Copyright (C) 2023 muink https://github.com/muink
#
# Port update notify script
#
# depends
. /usr/lib/natmap/common.sh
. /usr/share/libubox/jshn.sh
# http_request <data>
http_request() {
local data="$1"
$CURL $retry -L -o /dev/null -X POST \
-H 'Content-Type: application/json' \
-d "$data" \
--url https://${custom_domain:-ntfy.sh}/
}
start() {
local retry='--connect-timeout 3 --retry 5'
json_init
json_add_string topic "${TOPIC}"
json_add_string message "${text}"
json_add_string title "${TITLE}"
# Add priority and tag option
json_add_int priority "${PRIORITY:-3}"
json_add_array tags
for _t in ${TAGS//,/ }; do
json_add_string "" "$_t"
done
json_close_array
[ -n "${WACTION}" ] && {
json_add_array actions
json_add_object
json_add_string action "view"
json_add_string label "Open page"
json_add_string url "http${HTTPS:+s}://${ip}:${port}"
json_add_boolean clear 0
json_close_object
json_close_array
}
http_request "$(json_dump)"
}
# All external parameters required
ALL_PARAMS="custom_domain text tokens"
eval "$(JSON_EXPORT "$1")"; shift
# All external tokens required
INIT_GLOBAL_VAR TOPIC TITLE WACTION HTTPS PRIORITY TAGS
eval "$tokens"
start "$@"