forked from basecamp/xip-pdns
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxip
executable file
·187 lines (149 loc) · 3.48 KB
/
xip
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
#!/usr/bin/env bash
set -e
shopt -s nocasematch
#
# Configuration
#
XIP_DOMAIN=${XIP_DOMAIN:-"xip.test"}
XIP_ROOT_ADDRESSES=${XIP_ROOT_ADDRESS:-( "127.0.0.1" )}
XIP_NS_ADDRESSES=${XIP_NS_ADDRESS:-( "127.0.0.1" )}
XIP_TIMESTAMP=${XIP_TIMESTAMP:-"0"}
XIP_TTL=${XIP_TTL:-300}
if [ -a "$1" ]; then
source "$1"
fi
#
# Protocol helpers
#
read_cmd() {
local IFS=$'\t'
local i=0
local arg
read -ra CMD
for arg; do
eval "$arg=\"\${CMD[$i]}\""
let i=i+1
done
}
send_cmd() {
local IFS=$'\t'
printf "%s\n" "$*"
}
fail() {
send_cmd "FAIL"
log "Exiting"
exit 1
}
read_helo() {
read_cmd HELO VERSION
[ "$HELO" = "HELO" ] && [ "$VERSION" = "1" ]
}
read_query() {
read_cmd TYPE QNAME QCLASS QTYPE ID IP
}
send_answer() {
local type="$1"
shift
send_cmd "DATA" "$QNAME" "$QCLASS" "$type" "$XIP_TTL" "$ID" "$@"
}
log() {
printf "[xip-pdns:$$] %s\n" "$@" >&2
}
#
# xip.io domain helpers
#
XIP_DOMAIN_PATTERN="(^|\.)${XIP_DOMAIN//./\.}\$"
NS_SUBDOMAIN_PATTERN="^ns-([0-9]+)\$"
IP_SUBDOMAIN_PATTERN="(^|\.)(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\$"
BASE36_SUBDOMAIN_PATTERN="(^|\.)([a-z0-9]{1,7})\$"
qtype_is() {
[ "$QTYPE" = "$1" ] || [ "$QTYPE" = "ANY" ]
}
qname_matches_domain() {
[[ "$QNAME" =~ $XIP_DOMAIN_PATTERN ]]
}
qname_is_root_domain() {
[ "$QNAME" = "$XIP_DOMAIN" ]
}
extract_subdomain_from_qname() {
SUBDOMAIN="${QNAME:0:${#QNAME}-${#XIP_DOMAIN}}"
SUBDOMAIN="${SUBDOMAIN%.}"
}
subdomain_is_ns() {
[[ "$SUBDOMAIN" =~ $NS_SUBDOMAIN_PATTERN ]]
}
subdomain_is_ip() {
[[ "$SUBDOMAIN" =~ $IP_SUBDOMAIN_PATTERN ]]
}
subdomain_is_base36() {
[[ "$SUBDOMAIN" =~ $BASE36_SUBDOMAIN_PATTERN ]]
}
resolve_ns_subdomain() {
local index="${SUBDOMAIN:3}"
echo "${XIP_NS_ADDRESSES[$index-1]}"
}
resolve_ip_subdomain() {
[[ "$SUBDOMAIN" =~ $IP_SUBDOMAIN_PATTERN ]] || true
echo "${BASH_REMATCH[2]}"
}
resolve_base36_subdomain() {
[[ "$SUBDOMAIN" =~ $BASE36_SUBDOMAIN_PATTERN ]] || true
local ip=$(( 36#${BASH_REMATCH[2]} ))
printf "%d.%d.%d.%d" $(( ip&0xFF )) $(( (ip>>8)&0xFF )) $(( (ip>>16)&0xFF )) $(( (ip>>24)&0xFF ))
}
answer_soa_query() {
send_answer "SOA" "admin.$XIP_DOMAIN ns-1.$XIP_DOMAIN $XIP_TIMESTAMP $XIP_TTL $XIP_TTL $XIP_TTL $XIP_TTL"
}
answer_ns_query() {
local i=1
local ns_address
for ns_address in "${XIP_NS_ADDRESSES[@]}"; do
send_answer "NS" "ns-$i.$XIP_DOMAIN"
let i+=1
done
}
answer_root_a_query() {
local address
for address in "${XIP_ROOT_ADDRESSES[@]}"; do
send_answer "A" "$address"
done
}
answer_subdomain_a_query_for() {
local type="$1"
local address="$(resolve_${type}_subdomain)"
if [ -n "$address" ]; then
send_answer "A" "$address"
fi
}
#
# PowerDNS pipe backend implementation
#
trap fail err
read_helo
send_cmd "OK" "xip PowerDNS pipe backend (protocol version 1)"
while read_query; do
log "Query: type=$TYPE qname=$QNAME qclass=$QCLASS qtype=$QTYPE id=$ID ip=$IP"
if qname_matches_domain; then
if qname_is_root_domain; then
if qtype_is "SOA"; then
answer_soa_query
fi
if qtype_is "NS"; then
answer_ns_query
fi
if qtype_is "A"; then
answer_root_a_query
fi
elif qtype_is "A"; then
extract_subdomain_from_qname
if subdomain_is_ns; then
answer_subdomain_a_query_for ns
elif subdomain_is_ip; then
answer_subdomain_a_query_for ip
elif subdomain_is_base36; then
answer_subdomain_a_query_for base36
fi
fi
fi
send_cmd "END"
done