blob: 1ed03ecf478a94b5ccf48669a413ec3d18788651 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
#!/bin/sh
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Script for updating NameSilo DNS records
# 2026 Lin Fan <im dot linfan at gmail dot com>
#
# using following options from /etc/config/ddns
# domain - domain (e.g. example.com)
# username - sub domain (e.g. www)
# password - api key
#
# optional parameters (param_opt) from /etc/config/ddns
# ttl - ttl in seconds (e.g. ttl=7200, default '3600')
# pp - proxy protocol (e.g. pp=socks5h, default 'http')
# fmt - response format (e.g. fmt=xml, default 'json')
#
# reference: https://www.namesilo.com/api-reference
#
# SECURITY NOTE:
# The NameSilo API requires the API key to be passed as a URL query parameter.
# This means the key may be exposed in shell history, process listings (ps),
# and system logs that record full command lines. Use a dedicated API key and
# protect access to this system and its logs accordingly.
# check curl
[ -z "$CURL" ] || [ -z "$CURL_SSL" ] && {
write_log 14 "NameSilo script requires cURL with SSL support"
return 1
}
[ -n "$proxy" ] && [ -z "$CURL_PROXY" ] && {
write_log 14 "cURL: libcurl compiled without proxy support"
return 1
}
# check options
[ -z "$username" ] && write_log 14 "NameSilo: 'username' (sub domain) not set" && return 1
[ -z "$password" ] && write_log 14 "NameSilo: 'password' (api key) not set" && return 1
[ -z "$domain" ] && write_log 14 "NameSilo: 'domain' not set" && return 1
[ "$use_ipv6" -eq 1 ] && type="AAAA" || type="A"
# parse optional parameters
[ -n "$param_opt" ] && {
for pair in $param_opt ; do
case $pair in
ttl=*)
param_opt_ttl=${pair#*=}
;;
pp=*)
param_opt_pp=${pair#*=}
;;
fmt=*)
param_opt_fmt=${pair#*=}
;;
*)
# ignore others
;;
esac
done
}
# check optional parameters
ttl="${param_opt_ttl:-3600}"
fmt=$(echo ${param_opt_fmt:-json} | tr 'A-Z' 'a-z')
[ "$fmt" != "json" ] && [ "$fmt" != "xml" ] && {
write_log 14 "NameSilo: optional parameter 'fmt' must be json (default) or xml"
return 1
}
# load jshn.sh
[ "$fmt" == "json" ] && {
. /usr/share/libubox/jshn.sh
}
# check xmllint (optional)
[ "$fmt" == "xml" ] && {
XMLLINT=$(command -v xmllint)
[ -z "$XMLLINT" ] && {
write_log 7 "Suggestion: install 'libxml2-utils' to parse XML response accurately"
}
}
# curl command
CURL_CMD="$CURL -sSf -o $DATFILE --stderr $ERRFILE"
[ -n "$proxy" ] && {
[ -z "$param_opt_pp" ] && {
proxy_arg="--proxy ${proxy}"
} || {
proxy_arg="--proxy ${param_opt_pp}://${proxy}"
}
CURL_CMD="$CURL_CMD $proxy_arg"
}
# extract response code
get_code() {
# json
[ "$fmt" == "json" ] && {
local json=$(cat)
local code
json_load "$json"
json_select "reply"
json_get_var code "code"
printf "%s\n" "$code"
return
}
# xml
[ -n "$XMLLINT" ] && {
# try xmllint first
$XMLLINT --xpath "string(/namesilo/reply/code)" - 2>/dev/null
} || {
# fallback to grep/sed
grep -o '<code>.*</code>' | sed 's/<code>//;s/<\/code>//' 2>/dev/null
}
}
# extract detail message
get_detail() {
# json
[ "$fmt" == "json" ] && {
local json=$(cat)
local detail
json_load "$json"
json_select "reply"
json_get_var detail "detail"
printf "%s\n" "$detail"
return
}
# xml
[ -n "$XMLLINT" ] && {
# try xmllint first
$XMLLINT --xpath "string(/namesilo/reply/detail)" - 2>/dev/null
} || {
# fallback to grep/sed
grep -o '<detail>.*</detail>' | sed 's/<detail>//;s/<\/detail>//' 2>/dev/null
}
}
# extract rrid
get_rrid() {
# json
[ "$fmt" == "json" ] && {
local json=$(cat)
json_load "$json"
json_select "reply"
json_select "resource_record"
local idx=1
while json_is_a $idx object ; do
local host
local rtyp
local rrid
json_select $idx
json_get_var host "host"
json_get_var rtyp "type"
json_get_var rrid "record_id"
[ "$host" == "$username" ] && [ "$rtyp" == "$type" ] && {
printf "%s\n" "$rrid"
return
}
idx=$(( idx + 1 ))
json_select ..
done
return
}
# xml
[ -n "$XMLLINT" ] && {
# try xmllint first
$XMLLINT --xpath "string(/namesilo/reply/resource_record[host='$username'][type='$type']/record_id)" - 2>/dev/null
} || {
# fallback to grep/sed
local record
for record in $(sed "s/<resource_record>/\n<resource_record>/g" |
grep -o "<resource_record>.*<host>$username</host>.*</resource_record>") ; do
local rtyp=$(printf "%s\n" "$record" | sed "s/.*<type>//;s/<\/type>.*//")
[ "$rtyp" == "$type" ] && {
printf "%s\n" "$record" | sed "s/.*<record_id>//;s/<\/record_id>.*//"
return
}
done
}
}
# call domain api
call_api() {
local endpoint="$1"
local params="$2"
local url="https://www.namesilo.com/api/${endpoint}?version=1&type=${fmt}&key=${password}&${params}"
# call api
$CURL_CMD "$url"
local rc=$?
[ "$rc" -ne 0 ] && {
write_log 3 "NameSilo: API request to '$endpoint' failed with exit code $rc"
return 1
}
# check response
local response=$(cat "$DATFILE")
[ -z "$response" ] && {
write_log 3 "NameSilo: Empty response from API '$endpoint'"
return 1
}
# check reply code
local code=$(printf "%s\n" "$response" | get_code)
[ "$code" != "300" ] && {
local detail=$(printf "%s\n" "$response" | get_detail)
write_log 3 "NameSilo: API request to '$endpoint' returned with error '$code - $detail'"
return 1
}
printf '%s\n' "$response"
return 0
}
# get rrid
response=$(call_api dnsListRecords "domain=$domain")
[ -z "$response" ] && return 1
rrid=$(printf "%s\n" "$response" | get_rrid)
[ -z "$rrid" ] && {
write_log 14 "NameSilo: No matching '$type' DNS record found for host '$username' in domain '$domain'"
return 1
}
# update subdomain record
call_api dnsUpdateRecord "domain=$domain&rrid=$rrid&rrhost=$username&rrvalue=$__IP&rrttl=$ttl" && {
write_log 7 "NameSilo: '$type' DNS record for '$username.$domain' updated successfully to IP '$__IP'"
return 0
} || {
write_log 3 "NameSilo: Failed to update '$type' DNS record for '$username.$domain' to IP '$__IP'"
return 1
}
|