blob: cff9ce1c5a390434435594f2d6d383b78faaeb06 [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley849a8352006-06-09 21:02:31 +010017/* define this to get facilitynames */
18#define SYSLOG_NAMES
Simon Kelley9e4abcb2004-01-22 19:47:41 +000019#include "dnsmasq.h"
Simon Kelley824af852008-02-12 20:43:05 +000020#include <setjmp.h>
21
Simon Kelley7622fc02009-06-04 20:32:05 +010022static volatile int mem_recover = 0;
23static jmp_buf mem_jmp;
Simon Kelley395eb712012-07-06 22:07:05 +010024static int one_file(char *file, int hard_opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010025
Simon Kelley824af852008-02-12 20:43:05 +000026/* Solaris headers don't have facility names. */
27#ifdef HAVE_SOLARIS_NETWORK
28static const struct {
29 char *c_name;
30 unsigned int c_val;
31} facilitynames[] = {
32 { "kern", LOG_KERN },
33 { "user", LOG_USER },
34 { "mail", LOG_MAIL },
35 { "daemon", LOG_DAEMON },
36 { "auth", LOG_AUTH },
37 { "syslog", LOG_SYSLOG },
38 { "lpr", LOG_LPR },
39 { "news", LOG_NEWS },
40 { "uucp", LOG_UUCP },
Simon Kelley824af852008-02-12 20:43:05 +000041 { "audit", LOG_AUDIT },
Simon Kelley824af852008-02-12 20:43:05 +000042 { "cron", LOG_CRON },
43 { "local0", LOG_LOCAL0 },
44 { "local1", LOG_LOCAL1 },
45 { "local2", LOG_LOCAL2 },
46 { "local3", LOG_LOCAL3 },
47 { "local4", LOG_LOCAL4 },
48 { "local5", LOG_LOCAL5 },
49 { "local6", LOG_LOCAL6 },
50 { "local7", LOG_LOCAL7 },
51 { NULL, 0 }
52};
53#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000054
Simon Kelley849a8352006-06-09 21:02:31 +010055#ifndef HAVE_GETOPT_LONG
Simon Kelley9e4abcb2004-01-22 19:47:41 +000056struct myoption {
57 const char *name;
58 int has_arg;
59 int *flag;
60 int val;
61};
Simon Kelley849a8352006-06-09 21:02:31 +010062#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000063
Simon Kelley9009d742008-11-14 20:04:27 +000064#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
Simon Kelley9e4abcb2004-01-22 19:47:41 +000065
Simon Kelley16972692006-10-16 20:04:18 +010066/* options which don't have a one-char version */
Simon Kelley832af0b2007-01-21 20:01:28 +000067#define LOPT_RELOAD 256
68#define LOPT_NO_NAMES 257
69#define LOPT_TFTP 258
70#define LOPT_SECURE 259
71#define LOPT_PREFIX 260
72#define LOPT_PTR 261
73#define LOPT_BRIDGE 262
74#define LOPT_TFTP_MAX 263
Simon Kelley6b010842007-02-12 20:32:07 +000075#define LOPT_FORCE 264
76#define LOPT_NOBLOCK 265
Simon Kelleyf2621c72007-04-29 19:47:21 +010077#define LOPT_LOG_OPTS 266
78#define LOPT_MAX_LOGS 267
79#define LOPT_CIRCUIT 268
80#define LOPT_REMOTE 269
81#define LOPT_SUBSCR 270
82#define LOPT_INTNAME 271
Simon Kelley5aabfc72007-08-29 11:24:47 +010083#define LOPT_BANK 272
84#define LOPT_DHCP_HOST 273
85#define LOPT_APREF 274
Simon Kelley824af852008-02-12 20:43:05 +000086#define LOPT_OVERRIDE 275
87#define LOPT_TFTPPORTS 276
88#define LOPT_REBIND 277
89#define LOPT_NOLAST 278
90#define LOPT_OPTS 279
91#define LOPT_DHCP_OPTS 280
92#define LOPT_MATCH 281
93#define LOPT_BROADCAST 282
94#define LOPT_NEGTTL 283
Simon Kelley9e038942008-05-30 20:06:34 +010095#define LOPT_ALTPORT 284
96#define LOPT_SCRIPTUSR 285
Simon Kelley1a6bca82008-07-11 11:11:42 +010097#define LOPT_LOCAL 286
98#define LOPT_NAPTR 287
99#define LOPT_MINPORT 288
Simon Kelley9009d742008-11-14 20:04:27 +0000100#define LOPT_DHCP_FQDN 289
101#define LOPT_CNAME 290
Simon Kelley7622fc02009-06-04 20:32:05 +0100102#define LOPT_PXE_PROMT 291
103#define LOPT_PXE_SERV 292
104#define LOPT_TEST 293
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100105#define LOPT_TAG_IF 294
106#define LOPT_PROXY 295
107#define LOPT_GEN_NAMES 296
108#define LOPT_MAXTTL 297
109#define LOPT_NO_REBIND 298
110#define LOPT_LOC_REBND 299
Simon Kelley28866e92011-02-14 20:19:14 +0000111#define LOPT_ADD_MAC 300
Giovanni Bajo237724c2012-04-05 02:46:52 +0200112#define LOPT_SEC_PROXY 301
Simon Kelley7de060b2011-08-26 17:24:52 +0100113#define LOPT_INCR_ADDR 302
114#define LOPT_CONNTRACK 303
Simon Kelleyc72daea2012-01-05 21:33:27 +0000115#define LOPT_FQDN 304
116#define LOPT_LUASCRIPT 305
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000117#define LOPT_RA 306
Simon Kelley8b372702012-03-09 17:45:10 +0000118#define LOPT_DUID 307
Simon Kelleye759d422012-03-16 13:18:57 +0000119#define LOPT_HOST_REC 308
Simon Kelley61ce6002012-04-20 21:28:49 +0100120#define LOPT_TFTP_LC 309
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100121#define LOPT_RR 310
Simon Kelley54dd3932012-06-20 11:23:38 +0100122#define LOPT_CLVERBIND 311
Simon Kelley1d860412012-09-20 20:48:04 +0100123#define LOPT_MAXCTTL 312
Simon Kelley4f7b3042012-11-28 21:27:02 +0000124#define LOPT_AUTHZONE 313
125#define LOPT_AUTHSERV 314
Simon Kelleye1ff4192012-12-09 17:08:47 +0000126#define LOPT_AUTHTTL 315
Simon Kelley4f7b3042012-11-28 21:27:02 +0000127#define LOPT_AUTHSOA 316
Simon Kelleye1ff4192012-12-09 17:08:47 +0000128#define LOPT_AUTHSFS 317
Simon Kelley49678762012-12-09 18:24:58 +0000129#define LOPT_AUTHPEER 318
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000130#define LOPT_IPSET 319
Simon Kelley2bb73af2013-04-24 17:38:19 +0100131#define LOPT_SYNTH 320
Simon Kelleyc6309242013-03-07 20:59:28 +0000132#ifdef OPTION6_PREFIX_CLASS
Simon Kelley2bb73af2013-04-24 17:38:19 +0100133#define LOPT_PREF_CLSS 321
Simon Kelleyc6309242013-03-07 20:59:28 +0000134#endif
Simon Kelley8d030462013-07-29 15:41:26 +0100135#define LOPT_FAST_RA 322
Simon Kelley16972692006-10-16 20:04:18 +0100136
Simon Kelley849a8352006-06-09 21:02:31 +0100137#ifdef HAVE_GETOPT_LONG
138static const struct option opts[] =
139#else
140static const struct myoption opts[] =
141#endif
142 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100143 { "version", 0, 0, 'v' },
144 { "no-hosts", 0, 0, 'h' },
145 { "no-poll", 0, 0, 'n' },
146 { "help", 0, 0, 'w' },
147 { "no-daemon", 0, 0, 'd' },
148 { "log-queries", 0, 0, 'q' },
149 { "user", 2, 0, 'u' },
150 { "group", 2, 0, 'g' },
151 { "resolv-file", 2, 0, 'r' },
152 { "mx-host", 1, 0, 'm' },
153 { "mx-target", 1, 0, 't' },
154 { "cache-size", 2, 0, 'c' },
155 { "port", 1, 0, 'p' },
156 { "dhcp-leasefile", 2, 0, 'l' },
157 { "dhcp-lease", 1, 0, 'l' },
158 { "dhcp-host", 1, 0, 'G' },
159 { "dhcp-range", 1, 0, 'F' },
160 { "dhcp-option", 1, 0, 'O' },
161 { "dhcp-boot", 1, 0, 'M' },
162 { "domain", 1, 0, 's' },
163 { "domain-suffix", 1, 0, 's' },
164 { "interface", 1, 0, 'i' },
165 { "listen-address", 1, 0, 'a' },
166 { "bogus-priv", 0, 0, 'b' },
167 { "bogus-nxdomain", 1, 0, 'B' },
168 { "selfmx", 0, 0, 'e' },
169 { "filterwin2k", 0, 0, 'f' },
170 { "pid-file", 2, 0, 'x' },
171 { "strict-order", 0, 0, 'o' },
172 { "server", 1, 0, 'S' },
173 { "local", 1, 0, LOPT_LOCAL },
174 { "address", 1, 0, 'A' },
175 { "conf-file", 2, 0, 'C' },
176 { "no-resolv", 0, 0, 'R' },
177 { "expand-hosts", 0, 0, 'E' },
178 { "localmx", 0, 0, 'L' },
179 { "local-ttl", 1, 0, 'T' },
180 { "no-negcache", 0, 0, 'N' },
181 { "addn-hosts", 1, 0, 'H' },
182 { "query-port", 1, 0, 'Q' },
183 { "except-interface", 1, 0, 'I' },
184 { "no-dhcp-interface", 1, 0, '2' },
185 { "domain-needed", 0, 0, 'D' },
186 { "dhcp-lease-max", 1, 0, 'X' },
187 { "bind-interfaces", 0, 0, 'z' },
188 { "read-ethers", 0, 0, 'Z' },
189 { "alias", 1, 0, 'V' },
190 { "dhcp-vendorclass", 1, 0, 'U' },
191 { "dhcp-userclass", 1, 0, 'j' },
192 { "dhcp-ignore", 1, 0, 'J' },
193 { "edns-packet-max", 1, 0, 'P' },
194 { "keep-in-foreground", 0, 0, 'k' },
195 { "dhcp-authoritative", 0, 0, 'K' },
196 { "srv-host", 1, 0, 'W' },
197 { "localise-queries", 0, 0, 'y' },
198 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100199 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100200 { "enable-dbus", 2, 0, '1' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100201 { "bootp-dynamic", 2, 0, '3' },
202 { "dhcp-mac", 1, 0, '4' },
203 { "no-ping", 0, 0, '5' },
204 { "dhcp-script", 1, 0, '6' },
205 { "conf-dir", 1, 0, '7' },
206 { "log-facility", 1, 0 ,'8' },
207 { "leasefile-ro", 0, 0, '9' },
208 { "dns-forward-max", 1, 0, '0' },
209 { "clear-on-reload", 0, 0, LOPT_RELOAD },
210 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100211 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100212 { "tftp-secure", 0, 0, LOPT_SECURE },
213 { "tftp-unique-root", 0, 0, LOPT_APREF },
214 { "tftp-root", 1, 0, LOPT_PREFIX },
215 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelley61ce6002012-04-20 21:28:49 +0100216 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100217 { "ptr-record", 1, 0, LOPT_PTR },
218 { "naptr-record", 1, 0, LOPT_NAPTR },
219 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
220 { "dhcp-option-force", 1, 0, LOPT_FORCE },
221 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
222 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
223 { "log-async", 2, 0, LOPT_MAX_LOGS },
224 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
225 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
226 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
227 { "interface-name", 1, 0, LOPT_INTNAME },
228 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
229 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
230 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
231 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
232 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100233 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100234 { "all-servers", 0, 0, LOPT_NOLAST },
235 { "dhcp-match", 1, 0, LOPT_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100236 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100237 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100238 { "max-ttl", 1, 0, LOPT_MAXTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100239 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100240 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
241 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
242 { "min-port", 1, 0, LOPT_MINPORT },
243 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
244 { "cname", 1, 0, LOPT_CNAME },
245 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
246 { "pxe-service", 1, 0, LOPT_PXE_SERV },
247 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100248 { "tag-if", 1, 0, LOPT_TAG_IF },
249 { "dhcp-proxy", 2, 0, LOPT_PROXY },
250 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
251 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley28866e92011-02-14 20:19:14 +0000252 { "add-mac", 0, 0, LOPT_ADD_MAC },
Giovanni Bajo237724c2012-04-05 02:46:52 +0200253 { "proxy-dnssec", 0, 0, LOPT_SEC_PROXY },
Simon Kelley7de060b2011-08-26 17:24:52 +0100254 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
255 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000256 { "dhcp-client-update", 0, 0, LOPT_FQDN },
257 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000258 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000259 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000260 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100261 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000262 { "auth-zone", 1, 0, LOPT_AUTHZONE },
263 { "auth-server", 1, 0, LOPT_AUTHSERV },
264 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
265 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000266 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000267 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000268 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100269 { "synth-domain", 1, 0, LOPT_SYNTH },
Simon Kelleyc6309242013-03-07 20:59:28 +0000270#ifdef OPTION6_PREFIX_CLASS
271 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
272#endif
Simon Kelley8d030462013-07-29 15:41:26 +0100273 { "force-fast-ra", 0, 0, LOPT_FAST_RA },
Simon Kelley849a8352006-06-09 21:02:31 +0100274 { NULL, 0, 0, 0 }
275 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000276
Simon Kelley28866e92011-02-14 20:19:14 +0000277
278#define ARG_DUP OPT_LAST
279#define ARG_ONE OPT_LAST + 1
280#define ARG_USED_CL OPT_LAST + 2
281#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000282
Simon Kelley1a6bca82008-07-11 11:11:42 +0100283static struct {
284 int opt;
285 unsigned int rept;
286 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000287 char * const desc;
288 char * const arg;
289} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000290 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
291 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100292 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000293 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
294 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
295 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100296 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
297 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
298 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
299 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
300 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000301 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
302 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100303 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000304 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
305 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100306 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100307 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000308 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
309 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
310 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100311 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
312 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
313 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
314 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
315 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
316 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100317 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
318 { 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000319 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100320 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000321 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100322 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
323 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
324 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
325 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
326 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
327 { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000328 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
329 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100330 { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000331 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100332 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000333 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
334 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
335 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000336 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000337 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
338 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
339 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
340 { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
341 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100342 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100343 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000344 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
345 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100346 { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000347 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
348 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100349 { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000350 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
351 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
352 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100353 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
354 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100355 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000356 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100357 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
358 { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000359 { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100360 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000361 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
362 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
363 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
364 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100365 { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
366 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000367 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100368 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100369 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100370 { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100371 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100372 { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100373 { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
374 { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000375 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100376 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100377 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100378 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
379 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000380 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100381 { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100382 { LOPT_LOC_REBND, OPT_LOCAL_REBIND, NULL, gettext_noop("Allow rebinding of 127.0.0.0/8, for RBL servers."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000383 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100384 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100385 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100386 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100387 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
388 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000389 { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000390 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
391 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000392 { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100393 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
394 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
395 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100396 { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
Giovanni Bajo237724c2012-04-05 02:46:52 +0200397 { LOPT_SEC_PROXY, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100398 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
399 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000400 { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000401 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8d030462013-07-29 15:41:26 +0100402 { LOPT_FAST_RA, OPT_FAST_RA, NULL, gettext_noop("Always send frequent router-advertisements"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000403 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleye759d422012-03-16 13:18:57 +0000404 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100405 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000406 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000407 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000408 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000409 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
410 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000411 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
412 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000413 { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
Simon Kelley48fd1c42013-04-25 09:49:38 +0100414 { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for sythesised names"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000415#ifdef OPTION6_PREFIX_CLASS
416 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
417#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100418 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000419};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000420
Simon Kelley3d8df262005-08-29 12:19:27 +0100421/* We hide metacharaters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100422 character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
Simon Kelley3d8df262005-08-29 12:19:27 +0100423 following sequence so that they map to themselves: it is therefore possible to call
424 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000425 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100426 couple of other places.
427 Note that space is included here so that
428 --dhcp-option=3, string
429 has five characters, whilst
430 --dhcp-option=3," string"
431 has six.
432*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100433
Simon Kelleyf2621c72007-04-29 19:47:21 +0100434static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100435
436static char hide_meta(char c)
437{
438 unsigned int i;
439
440 for (i = 0; i < (sizeof(meta) - 1); i++)
441 if (c == meta[i])
442 return (char)i;
443
444 return c;
445}
446
447static char unhide_meta(char cr)
448{
449 unsigned int c = cr;
450
451 if (c < (sizeof(meta) - 1))
452 cr = meta[c];
453
454 return cr;
455}
456
457static void unhide_metas(char *cp)
458{
459 if (cp)
460 for(; *cp; cp++)
461 *cp = unhide_meta(*cp);
462}
463
Simon Kelley824af852008-02-12 20:43:05 +0000464static void *opt_malloc(size_t size)
465{
466 void *ret;
467
468 if (mem_recover)
469 {
470 ret = whine_malloc(size);
471 if (!ret)
472 longjmp(mem_jmp, 1);
473 }
474 else
475 ret = safe_malloc(size);
476
477 return ret;
478}
479
480static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100481{
482 char *ret = NULL;
483
484 if (cp && strlen(cp) != 0)
485 {
Simon Kelley824af852008-02-12 20:43:05 +0000486 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100487 strcpy(ret, cp);
488
489 /* restore hidden metachars */
490 unhide_metas(ret);
491 }
492
493 return ret;
494}
495
Simon Kelley3d8df262005-08-29 12:19:27 +0100496
Simon Kelleyf2621c72007-04-29 19:47:21 +0100497/* find next comma, split string with zero and eliminate spaces.
498 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000499
500static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100501{
502 char *comma, *p;
503
Simon Kelley73a08a22009-02-05 20:28:08 +0000504 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100505 return NULL;
506
507 p = comma;
508 *comma = ' ';
509
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100510 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100511
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100512 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100513 *p = 0;
514
515 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100516}
517
Simon Kelley73a08a22009-02-05 20:28:08 +0000518static char *split(char *s)
519{
520 return split_chr(s, ',');
521}
522
Simon Kelley1f15b812009-10-13 17:49:32 +0100523static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100524{
Simon Kelley1f15b812009-10-13 17:49:32 +0100525 char *ret;
526 int nomem;
527
Simon Kelley3d8df262005-08-29 12:19:27 +0100528 if (!s)
529 return 0;
530
531 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100532 if (!(ret = canonicalise(s, &nomem)) && nomem)
533 {
534 if (mem_recover)
535 longjmp(mem_jmp, 1);
536 else
537 die(_("could not get memory"), NULL, EC_NOMEM);
538 }
539
540 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100541}
542
543static int atoi_check(char *a, int *res)
544{
545 char *p;
546
547 if (!a)
548 return 0;
549
550 unhide_metas(a);
551
552 for (p = a; *p; p++)
553 if (*p < '0' || *p > '9')
554 return 0;
555
556 *res = atoi(a);
557 return 1;
558}
559
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100560static int atoi_check16(char *a, int *res)
561{
562 if (!(atoi_check(a, res)) ||
563 *res < 0 ||
564 *res > 0xffff)
565 return 0;
566
567 return 1;
568}
569
Simon Kelley5aabfc72007-08-29 11:24:47 +0100570static void add_txt(char *name, char *txt)
Simon Kelley0a852542005-03-23 20:28:59 +0000571{
572 size_t len = strlen(txt);
Simon Kelley824af852008-02-12 20:43:05 +0000573 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelley0a852542005-03-23 20:28:59 +0000574
Simon Kelley824af852008-02-12 20:43:05 +0000575 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000576 r->next = daemon->txt;
577 daemon->txt = r;
578 r->class = C_CHAOS;
Simon Kelley824af852008-02-12 20:43:05 +0000579 r->txt = opt_malloc(len+1);
Simon Kelley0a852542005-03-23 20:28:59 +0000580 r->len = len+1;
581 *(r->txt) = len;
582 memcpy((r->txt)+1, txt, len);
583}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000584
Simon Kelley849a8352006-06-09 21:02:31 +0100585static void do_usage(void)
586{
587 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000588 int i, j;
589
590 struct {
591 char handle;
592 int val;
593 } tab[] = {
594 { '$', CACHESIZ },
595 { '*', EDNS_PKTSZ },
596 { '&', MAXLEASES },
597 { '!', FTABSIZ },
598 { '#', TFTP_MAX_CONNECTIONS },
599 { '\0', 0 }
600 };
Simon Kelley849a8352006-06-09 21:02:31 +0100601
602 printf(_("Usage: dnsmasq [options]\n\n"));
603#ifndef HAVE_GETOPT_LONG
604 printf(_("Use short options only on the command line.\n"));
605#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100606 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100607
Simon Kelley1a6bca82008-07-11 11:11:42 +0100608 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100609 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100610 char *desc = usage[i].flagdesc;
611 char *eq = "=";
612
613 if (!desc || *desc == '[')
614 eq = "";
615
616 if (!desc)
617 desc = "";
618
619 for ( j = 0; opts[j].name; j++)
620 if (opts[j].val == usage[i].opt)
621 break;
622 if (usage[i].opt < 256)
623 sprintf(buff, "-%c, ", usage[i].opt);
624 else
625 sprintf(buff, " ");
626
627 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100628 printf("%-40.40s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100629
Simon Kelley849a8352006-06-09 21:02:31 +0100630 if (usage[i].arg)
631 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000632 strcpy(buff, usage[i].arg);
633 for (j = 0; tab[j].handle; j++)
634 if (tab[j].handle == *(usage[i].arg))
635 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100636 }
Simon Kelley849a8352006-06-09 21:02:31 +0100637 printf(_(usage[i].desc), buff);
638 printf("\n");
639 }
640}
641
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100642#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
643
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100644char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
645{
646 int source_port = 0, serv_port = NAMESERVER_PORT;
647 char *portno, *source;
648#ifdef HAVE_IPV6
649 int scope_index = 0;
650 char *scope_id;
651#endif
652
653 if ((source = split_chr(arg, '@')) && /* is there a source. */
654 (portno = split_chr(source, '#')) &&
655 !atoi_check16(portno, &source_port))
656 return _("bad port");
657
658 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
659 !atoi_check16(portno, &serv_port))
660 return _("bad port");
661
662#ifdef HAVE_IPV6
663 scope_id = split_chr(arg, '%');
664#endif
665
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100666 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100667 {
668 addr->in.sin_port = htons(serv_port);
669 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
670#ifdef HAVE_SOCKADDR_SA_LEN
671 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
672#endif
673 source_addr->in.sin_addr.s_addr = INADDR_ANY;
674 source_addr->in.sin_port = htons(daemon->query_port);
675
676 if (source)
677 {
678 if (flags)
679 *flags |= SERV_HAS_SOURCE;
680 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100681 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100682 {
683#if defined(SO_BINDTODEVICE)
684 source_addr->in.sin_addr.s_addr = INADDR_ANY;
685 strncpy(interface, source, IF_NAMESIZE - 1);
686#else
687 return _("interface binding not supported");
688#endif
689 }
690 }
691 }
692#ifdef HAVE_IPV6
693 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
694 {
695 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
696 return _("bad interface name");
697
698 addr->in6.sin6_port = htons(serv_port);
699 addr->in6.sin6_scope_id = scope_index;
700 source_addr->in6.sin6_addr = in6addr_any;
701 source_addr->in6.sin6_port = htons(daemon->query_port);
702 source_addr->in6.sin6_scope_id = 0;
703 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
704 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
705#ifdef HAVE_SOCKADDR_SA_LEN
706 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
707#endif
708 if (source)
709 {
710 if (flags)
711 *flags |= SERV_HAS_SOURCE;
712 source_addr->in6.sin6_port = htons(source_port);
713 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
714 {
715#if defined(SO_BINDTODEVICE)
716 source_addr->in6.sin6_addr = in6addr_any;
717 strncpy(interface, source, IF_NAMESIZE - 1);
718#else
719 return _("interface binding not supported");
720#endif
721 }
722 }
723 }
724#endif
725 else
726 return _("bad address");
727
728 return NULL;
729}
730
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000731#ifdef HAVE_DHCP
732
733static int is_tag_prefix(char *arg)
734{
735 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
736 return 1;
737
738 return 0;
739}
740
741static char *set_prefix(char *arg)
742{
743 if (strstr(arg, "set:") == arg)
744 return arg+4;
745
746 return arg;
747}
748
Simon Kelley832af0b2007-01-21 20:01:28 +0000749/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100750static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000751{
Simon Kelley824af852008-02-12 20:43:05 +0000752 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000753 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000754 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100755 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100756 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000757 u16 opt_len = 0;
758 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100759 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000760
761 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000762 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000763 new->netid = NULL;
764 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100765 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000766
Simon Kelleyf2621c72007-04-29 19:47:21 +0100767 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000768 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100769 comma = split(arg);
770
771 for (cp = arg; *cp; cp++)
772 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000773 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100774
775 if (!*cp)
776 {
777 new->opt = atoi(arg);
778 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100779 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100780 break;
781 }
782
783 if (strstr(arg, "option:") == arg)
784 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100785 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
786 {
787 opt_len = lookup_dhcp_len(AF_INET, new->opt);
788 /* option:<optname> must follow tag and vendor string. */
789 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
790 option_ok = 1;
791 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100792 break;
793 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000794#ifdef HAVE_DHCP6
795 else if (strstr(arg, "option6:") == arg)
796 {
797 for (cp = arg+8; *cp; cp++)
798 if (*cp < '0' || *cp > '9')
799 break;
800
801 if (!*cp)
802 {
803 new->opt = atoi(arg+8);
804 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100805 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000806 }
807 else
Simon Kelley40ef23b2012-03-13 21:59:28 +0000808 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100809 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
810 {
811 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
812 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
813 option_ok = 1;
814 }
Simon Kelley40ef23b2012-03-13 21:59:28 +0000815 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000816 /* option6:<opt>|<optname> must follow tag and vendor string. */
817 is6 = 1;
818 break;
819 }
820#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +0100821 else if (strstr(arg, "vendor:") == arg)
822 {
Simon Kelley73a08a22009-02-05 20:28:08 +0000823 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
824 new->flags |= DHOPT_VENDOR;
825 }
826 else if (strstr(arg, "encap:") == arg)
827 {
828 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100829 new->flags |= DHOPT_ENCAPSULATE;
830 }
Simon Kelley316e2732010-01-22 20:16:09 +0000831 else if (strstr(arg, "vi-encap:") == arg)
832 {
833 new->u.encap = atoi(arg+9);
834 new->flags |= DHOPT_RFC3925;
835 if (flags == DHOPT_MATCH)
836 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100837 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +0000838 break;
839 }
840 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100841 else
842 {
Simon Kelley824af852008-02-12 20:43:05 +0000843 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100844 /* allow optional "net:" or "tag:" for consistency */
845 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +0000846 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100847 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100848 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100849 new->netid->next = np;
850 np = new->netid;
851 }
852
853 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +0000854 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000855
856#ifdef HAVE_DHCP6
857 if (is6)
858 {
859 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100860 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000861
862 if (opt_len == 0 &&
863 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +0100864 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000865 }
866 else
867#endif
868 if (opt_len == 0 &&
869 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +0100870 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +0000871
Simon Kelley316e2732010-01-22 20:16:09 +0000872 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +0100873 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100874 ret_err(_("bad dhcp-option"));
875
876 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +0000877 {
878 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100879 char c;
Simon Kelley28866e92011-02-14 20:19:14 +0000880 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000881 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000882 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100883 dots = 0;
884 for (cp = comma; (c = *cp); cp++)
885 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +0000886 {
887 addrs++;
888 is_dec = is_hex = 0;
889 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100890 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +0000891 {
892 digs++;
893 is_dec = is_addr = 0;
894 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100895 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000896 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000897 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000898 if (cp == comma) /* leading / means a pathname */
899 is_addr = 0;
900 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100901 else if (c == '.')
902 {
Simon Kelley23245c02012-07-18 16:21:11 +0100903 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100904 dots++;
905 }
906 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +0000907 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100908 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +0000909 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100910 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +0000911 {
912 is_addr = 0;
913 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100914 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +0000915 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100916 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +0000917 *cp = 0;
918 }
919 else
920 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100921 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000922 (c >='a' && c <= 'f') ||
923 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000924 {
925 is_hex = 0;
926 if (c != '[' && c != ']')
927 is_addr6 = 0;
928 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000929 }
Simon Kelley28866e92011-02-14 20:19:14 +0000930 else
931 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100932
Simon Kelley28866e92011-02-14 20:19:14 +0000933 if (!found_dig)
934 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000935
Simon Kelleyf2621c72007-04-29 19:47:21 +0100936 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +0100937 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100938 {
939 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000940
941 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100942 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000943
944 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100945 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100946 }
Simon Kelley28866e92011-02-14 20:19:14 +0000947 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000948 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
949 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +0100950
951 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
952 {
953 int val, fac = 1;
954
955 switch (comma[strlen(comma) - 1])
956 {
Simon Kelley42243212012-07-20 15:19:18 +0100957 case 'w':
958 case 'W':
959 fac *= 7;
960 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +0100961 case 'd':
962 case 'D':
963 fac *= 24;
964 /* fall though */
965 case 'h':
966 case 'H':
967 fac *= 60;
968 /* fall through */
969 case 'm':
970 case 'M':
971 fac *= 60;
972 /* fall through */
973 case 's':
974 case 'S':
975 comma[strlen(comma) - 1] = 0;
976 }
977
978 new->len = 4;
979 new->val = opt_malloc(4);
980 val = atoi(comma);
981 *((int *)new->val) = htonl(val * fac);
982 }
983 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000984 {
985 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +0000986 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +0000987 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
988 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +0000989 }
990 else if (is_dec)
991 {
992 int i, val = atoi(comma);
993 /* assume numeric arg is 1 byte except for
994 options where it is known otherwise.
995 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100996 if (opt_len != 0)
997 new->len = opt_len;
998 else if (val & 0xffff0000)
999 new->len = 4;
1000 else if (val & 0xff00)
1001 new->len = 2;
1002 else
1003 new->len = 1;
1004
Simon Kelley832af0b2007-01-21 20:01:28 +00001005 if (lenchar == 'b')
1006 new->len = 1;
1007 else if (lenchar == 's')
1008 new->len = 2;
1009 else if (lenchar == 'i')
1010 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001011
Simon Kelley824af852008-02-12 20:43:05 +00001012 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001013 for (i=0; i<new->len; i++)
1014 new->val[i] = val>>((new->len - i - 1)*8);
1015 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001016 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001017 {
1018 struct in_addr in;
1019 unsigned char *op;
1020 char *slash;
1021 /* max length of address/subnet descriptor is five bytes,
1022 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001023 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001024 new->flags |= DHOPT_ADDR;
1025
Simon Kelley572b41e2011-02-18 18:11:18 +00001026 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1027 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001028 {
Simon Kelley6b010842007-02-12 20:32:07 +00001029 *(op++) = 1; /* RFC 3361 "enc byte" */
1030 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001031 }
1032 while (addrs--)
1033 {
1034 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001035 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001036 slash = split_chr(cp, '/');
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001037 inet_pton(AF_INET, cp, &in);
Simon Kelley832af0b2007-01-21 20:01:28 +00001038 if (!slash)
1039 {
1040 memcpy(op, &in, INADDRSZ);
1041 op += INADDRSZ;
1042 }
1043 else
1044 {
1045 unsigned char *p = (unsigned char *)&in;
1046 int netsize = atoi(slash);
1047 *op++ = netsize;
1048 if (netsize > 0)
1049 *op++ = *p++;
1050 if (netsize > 8)
1051 *op++ = *p++;
1052 if (netsize > 16)
1053 *op++ = *p++;
1054 if (netsize > 24)
1055 *op++ = *p++;
1056 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1057 }
1058 }
1059 new->len = op - new->val;
1060 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001061 else if (is_addr6 && is6)
1062 {
1063 unsigned char *op;
1064 new->val = op = opt_malloc(16 * addrs);
1065 new->flags |= DHOPT_ADDR6;
1066 while (addrs--)
1067 {
1068 cp = comma;
1069 comma = split(cp);
1070
1071 /* check for [1234::7] */
1072 if (*cp == '[')
1073 cp++;
1074 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1075 cp[strlen(cp)-1] = 0;
1076
1077 if (inet_pton(AF_INET6, cp, op))
1078 {
1079 op += IN6ADDRSZ;
1080 continue;
1081 }
1082
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001083 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001084 }
1085 new->len = op - new->val;
1086 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001087 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001088 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001089 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001090 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001091 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001092 {
1093 /* dns search, RFC 3397, or SIP, RFC 3361 */
1094 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001095 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001096 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001097 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001098
1099 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001100 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001101
1102 while (arg && *arg)
1103 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001104 char *in, *dom = NULL;
1105 size_t domlen = 1;
1106 /* Allow "." as an empty domain */
1107 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001108 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001109 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001110 ret_err(_("bad domain in dhcp-option"));
1111
Simon Kelleyc52e1892010-06-07 22:01:39 +01001112 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001113 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001114
1115 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001116 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001117 {
1118 memcpy(newp, m, header_size + len);
1119 free(m);
1120 }
Simon Kelley824af852008-02-12 20:43:05 +00001121 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001122 p = m + header_size;
1123 q = p + len;
1124
1125 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001126 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001127 {
1128 unsigned char *cp = q++;
1129 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001130 for (j = 0; *in && (*in != '.'); in++, j++)
1131 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001132 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001133 if (*in)
1134 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001135 }
1136 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001137 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001138
Simon Kelley832af0b2007-01-21 20:01:28 +00001139 /* Now tail-compress using earlier names. */
1140 newlen = q - p;
1141 for (tail = p + len; *tail; tail += (*tail) + 1)
1142 for (r = p; r - p < (int)len; r += (*r) + 1)
1143 if (strcmp((char *)r, (char *)tail) == 0)
1144 {
1145 PUTSHORT((r - p) | 0xc000, tail);
1146 newlen = tail - p;
1147 goto end;
1148 }
1149 end:
1150 len = newlen;
1151
1152 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001153 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001154 }
1155
1156 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001157 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001158 m[0] = 0;
1159 new->len = (int) len + header_size;
1160 new->val = m;
1161 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001162#ifdef HAVE_DHCP6
1163 else if (comma && (opt_len & OT_CSTRING))
1164 {
1165 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001166 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001167 unsigned char *p, *newp;
1168
Simon Kelley40ef23b2012-03-13 21:59:28 +00001169 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001170 if (comma[i] == ',')
1171 commas++;
1172
1173 newp = opt_malloc(strlen(comma)+(2*commas));
1174 p = newp;
1175 arg = comma;
1176 comma = split(arg);
1177
1178 while (arg && *arg)
1179 {
1180 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001181 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001182 PUTSHORT(len, p);
1183 memcpy(p, arg, len);
1184 p += len;
1185
1186 arg = comma;
1187 comma = split(arg);
1188 }
1189
1190 new->val = newp;
1191 new->len = p - newp;
1192 }
1193 else if (comma && (opt_len & OT_RFC1035_NAME))
1194 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001195 unsigned char *p = NULL, *newp, *end;
1196 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001197 arg = comma;
1198 comma = split(arg);
1199
1200 while (arg && *arg)
1201 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001202 char *dom = canonicalise_opt(arg);
1203 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001204 ret_err(_("bad domain in dhcp-option"));
1205
Simon Kelley18f0fb02012-03-31 21:18:55 +01001206 newp = opt_malloc(len + strlen(dom) + 2);
1207
1208 if (p)
1209 {
1210 memcpy(newp, p, len);
1211 free(p);
1212 }
1213
1214 p = newp;
1215 end = do_rfc1035_name(p + len, dom);
1216 *end++ = 0;
1217 len = end - p;
1218 free(dom);
1219
Simon Kelley4cb1b322012-02-06 14:30:41 +00001220 arg = comma;
1221 comma = split(arg);
1222 }
1223
Simon Kelley18f0fb02012-03-31 21:18:55 +01001224 new->val = p;
1225 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001226 }
1227#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001228 else
1229 {
1230 new->len = strlen(comma);
1231 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001232 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001233 new->flags |= DHOPT_STRING;
1234 }
1235 }
1236 }
1237
Simon Kelley4cb1b322012-02-06 14:30:41 +00001238 if (!is6 &&
1239 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001240 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001241 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001242 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001243
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001244 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001245 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001246 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1247 !new->netid ||
1248 new->netid->next)
1249 ret_err(_("illegal dhcp-match"));
1250
1251 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001252 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001253 new->next = daemon->dhcp_match6;
1254 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001255 }
1256 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001257 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001258 new->next = daemon->dhcp_match;
1259 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001260 }
Simon Kelley824af852008-02-12 20:43:05 +00001261 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001262 else if (is6)
1263 {
1264 new->next = daemon->dhcp_opts6;
1265 daemon->dhcp_opts6 = new;
1266 }
1267 else
1268 {
1269 new->next = daemon->dhcp_opts;
1270 daemon->dhcp_opts = new;
1271 }
1272
1273 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001274}
1275
Simon Kelley7622fc02009-06-04 20:32:05 +01001276#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001277
Simon Kelley28866e92011-02-14 20:19:14 +00001278void set_option_bool(unsigned int opt)
1279{
1280 if (opt < 32)
1281 daemon->options |= 1u << opt;
1282 else
1283 daemon->options2 |= 1u << (opt - 32);
1284}
1285
Simon Kelley2b5bae92012-06-26 16:55:23 +01001286void reset_option_bool(unsigned int opt)
1287{
1288 if (opt < 32)
1289 daemon->options &= ~(1u << opt);
1290 else
1291 daemon->options2 &= ~(1u << (opt - 32));
1292}
1293
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001294static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
Simon Kelley849a8352006-06-09 21:02:31 +01001295{
1296 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001297 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001298
Simon Kelley832af0b2007-01-21 20:01:28 +00001299 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001300 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001301
Simon Kelley1a6bca82008-07-11 11:11:42 +01001302 for (i=0; usage[i].opt != 0; i++)
1303 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001304 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001305 int rept = usage[i].rept;
1306
Simon Kelley28866e92011-02-14 20:19:14 +00001307 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001308 {
1309 /* command line */
1310 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001311 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001312 if (rept == ARG_ONE)
1313 usage[i].rept = ARG_USED_CL;
1314 }
1315 else
1316 {
1317 /* allow file to override command line */
1318 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001319 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001320 if (rept == ARG_USED_CL || rept == ARG_ONE)
1321 usage[i].rept = ARG_USED_FILE;
1322 }
1323
1324 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1325 {
Simon Kelley28866e92011-02-14 20:19:14 +00001326 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001327 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001328 }
1329
1330 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001331 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001332
Simon Kelley849a8352006-06-09 21:02:31 +01001333 switch (option)
1334 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001335 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001336 {
Simon Kelley824af852008-02-12 20:43:05 +00001337 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001338 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001339 {
Simon Kelley28866e92011-02-14 20:19:14 +00001340 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001341 free(file);
1342 }
Simon Kelley849a8352006-06-09 21:02:31 +01001343 break;
1344 }
1345
Simon Kelleyf2621c72007-04-29 19:47:21 +01001346 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001347 {
1348 DIR *dir_stream;
1349 struct dirent *ent;
1350 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001351 struct list {
1352 char *suffix;
1353 struct list *next;
1354 } *ignore_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001355
Simon Kelley1f15b812009-10-13 17:49:32 +01001356 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001357 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001358 break;
1359
Simon Kelley1f15b812009-10-13 17:49:32 +01001360 for (arg = comma; arg; arg = comma)
1361 {
1362 comma = split(arg);
1363 li = opt_malloc(sizeof(struct list));
1364 li->next = ignore_suffix;
1365 ignore_suffix = li;
1366 /* Have to copy: buffer is overwritten */
1367 li->suffix = opt_string_alloc(arg);
1368 };
1369
Simon Kelley849a8352006-06-09 21:02:31 +01001370 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001371 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001372
Simon Kelley849a8352006-06-09 21:02:31 +01001373 while ((ent = readdir(dir_stream)))
1374 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001375 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001376 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001377
1378 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001379 if (len == 0 ||
1380 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001381 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1382 ent->d_name[0] == '.')
1383 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001384
Simon Kelley1f15b812009-10-13 17:49:32 +01001385 for (li = ignore_suffix; li; li = li->next)
1386 {
1387 /* check for proscribed suffices */
1388 size_t ls = strlen(li->suffix);
1389 if (len > ls &&
1390 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1391 break;
1392 }
1393 if (li)
1394 continue;
1395
Simon Kelley824af852008-02-12 20:43:05 +00001396 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001397 strcpy(path, directory);
1398 strcat(path, "/");
1399 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001400
Simon Kelley39595cf2013-02-04 21:40:07 +00001401 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001402 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001403 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001404
Simon Kelley39595cf2013-02-04 21:40:07 +00001405 /* only reg files allowed. */
1406 if (S_ISREG(buf.st_mode))
1407 one_file(path, 0);
1408
Simon Kelley849a8352006-06-09 21:02:31 +01001409 free(path);
1410 }
1411
1412 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001413 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001414 for(; ignore_suffix; ignore_suffix = li)
1415 {
1416 li = ignore_suffix->next;
1417 free(ignore_suffix->suffix);
1418 free(ignore_suffix);
1419 }
1420
Simon Kelley849a8352006-06-09 21:02:31 +01001421 break;
1422 }
1423
Simon Kelleyad094272012-08-10 17:10:54 +01001424 case '1': /* --enable-dbus */
1425 set_option_bool(OPT_DBUS);
1426 if (arg)
1427 daemon->dbus_name = opt_string_alloc(arg);
1428 else
1429 daemon->dbus_name = DNSMASQ_SERVICE;
1430 break;
1431
Simon Kelleyf2621c72007-04-29 19:47:21 +01001432 case '8': /* --log-facility */
1433 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001434 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001435 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001436 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001437 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001438#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001439 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001440#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001441 for (i = 0; facilitynames[i].c_name; i++)
1442 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1443 break;
1444
1445 if (facilitynames[i].c_name)
1446 daemon->log_fac = facilitynames[i].c_val;
1447 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001448 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001449#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001450 }
1451 break;
1452
Simon Kelleyf2621c72007-04-29 19:47:21 +01001453 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001454 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001455 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001456
Simon Kelleyf2621c72007-04-29 19:47:21 +01001457 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001458 {
Simon Kelley824af852008-02-12 20:43:05 +00001459 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001460 struct resolvc *new, *list = daemon->resolv_files;
1461
1462 if (list && list->is_default)
1463 {
1464 /* replace default resolv file - possibly with nothing */
1465 if (name)
1466 {
1467 list->is_default = 0;
1468 list->name = name;
1469 }
1470 else
1471 list = NULL;
1472 }
1473 else if (name)
1474 {
Simon Kelley824af852008-02-12 20:43:05 +00001475 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001476 new->next = list;
1477 new->name = name;
1478 new->is_default = 0;
1479 new->mtime = 0;
1480 new->logged = 0;
1481 list = new;
1482 }
1483 daemon->resolv_files = list;
1484 break;
1485 }
1486
Simon Kelleyf2621c72007-04-29 19:47:21 +01001487 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001488 {
1489 int pref = 1;
1490 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001491 char *name, *target = NULL;
1492
Simon Kelleyf2621c72007-04-29 19:47:21 +01001493 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001494 {
1495 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001496 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001497 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001498 }
1499
Simon Kelley1f15b812009-10-13 17:49:32 +01001500 if (!(name = canonicalise_opt(arg)) ||
1501 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001502 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001503
Simon Kelley824af852008-02-12 20:43:05 +00001504 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001505 new->next = daemon->mxnames;
1506 daemon->mxnames = new;
1507 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001508 new->name = name;
1509 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001510 new->weight = pref;
1511 break;
1512 }
1513
Simon Kelleyf2621c72007-04-29 19:47:21 +01001514 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001515 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001516 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001517 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001518
1519#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001520 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001521 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001522 break;
1523
Simon Kelleyc72daea2012-01-05 21:33:27 +00001524 /* Sorry about the gross pre-processor abuse */
1525 case '6': /* --dhcp-script */
1526 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001527# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001528 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001529# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001530 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001531# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001532 if (option == LOPT_LUASCRIPT)
1533# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001534 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001535# else
1536 daemon->luascript = opt_string_alloc(arg);
1537# endif
1538 else
1539 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001540# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001541 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001542#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001543
Simon Kelley28866e92011-02-14 20:19:14 +00001544 case LOPT_DHCP_HOST: /* --dhcp-hostfile */
1545 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001546 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001547 {
Simon Kelley824af852008-02-12 20:43:05 +00001548 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley849a8352006-06-09 21:02:31 +01001549 static int hosts_index = 1;
Simon Kelley824af852008-02-12 20:43:05 +00001550 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001551 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001552 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001553 if (option == 'H')
1554 {
1555 new->next = daemon->addn_hosts;
1556 daemon->addn_hosts = new;
1557 }
1558 else if (option == LOPT_DHCP_HOST)
1559 {
1560 new->next = daemon->dhcp_hosts_file;
1561 daemon->dhcp_hosts_file = new;
1562 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001563 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001564 {
1565 new->next = daemon->dhcp_opts_file;
1566 daemon->dhcp_opts_file = new;
1567 }
Simon Kelley849a8352006-06-09 21:02:31 +01001568 break;
1569 }
1570
Simon Kelley4f7b3042012-11-28 21:27:02 +00001571 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001572 if (!(comma = split(arg)))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001573 ret_err(gen_err);
1574
Simon Kelley4f7b3042012-11-28 21:27:02 +00001575 daemon->authserver = opt_string_alloc(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001576 arg = comma;
1577 do {
1578 struct iname *new = opt_malloc(sizeof(struct iname));
1579 comma = split(arg);
1580 new->name = NULL;
1581 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001582 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
Simon Kelley429798f2012-12-10 20:45:53 +00001583 new->addr.sa.sa_family = AF_INET;
1584#ifdef HAVE_IPV6
1585 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1586 new->addr.sa.sa_family = AF_INET6;
1587#endif
1588 else
1589 new->name = opt_string_alloc(arg);
1590
1591 new->next = daemon->authinterface;
1592 daemon->authinterface = new;
1593
1594 arg = comma;
1595 } while (arg);
1596
Simon Kelley4f7b3042012-11-28 21:27:02 +00001597 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001598
1599 case LOPT_AUTHSFS: /* --auth-sec-servers */
1600 {
1601 struct name_list *new;
1602
1603 do {
1604 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001605 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001606 new->name = opt_string_alloc(arg);
1607 new->next = daemon->secondary_forward_server;
1608 daemon->secondary_forward_server = new;
1609 arg = comma;
1610 } while (arg);
1611 break;
1612 }
1613
Simon Kelley4f7b3042012-11-28 21:27:02 +00001614 case LOPT_AUTHZONE: /* --auth-zone */
1615 {
1616 struct auth_zone *new;
1617
1618 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001619
Simon Kelley429798f2012-12-10 20:45:53 +00001620 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001621 new->domain = opt_string_alloc(arg);
1622 new->subnet = NULL;
1623 new->next = daemon->auth_zones;
1624 daemon->auth_zones = new;
1625
1626 while ((arg = comma))
1627 {
1628 int prefixlen = 0;
1629 char *prefix;
Simon Kelley429798f2012-12-10 20:45:53 +00001630 struct subnet *subnet = opt_malloc(sizeof(struct subnet));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001631
1632 subnet->next = new->subnet;
1633 new->subnet = subnet;
1634
1635 comma = split(arg);
1636 prefix = split_chr(arg, '/');
1637
1638 if (prefix && !atoi_check(prefix, &prefixlen))
1639 ret_err(gen_err);
1640
1641 if (inet_pton(AF_INET, arg, &subnet->addr4))
1642 {
1643 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
1644 subnet->is6 = 0;
1645 }
1646#ifdef HAVE_IPV6
1647 else if (inet_pton(AF_INET6, arg, &subnet->addr6))
1648 {
1649 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
1650 subnet->is6 = 1;
1651 }
1652#endif
1653 else
1654 ret_err(gen_err);
1655 }
1656 break;
1657 }
1658
1659 case LOPT_AUTHSOA: /* --auth-soa */
1660 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001661 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001662 if (comma)
1663 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001664 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001665 arg = comma;
1666 comma = split(arg);
1667 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001668 for (cp = daemon->hostmaster; *cp; cp++)
1669 if (*cp == '@')
1670 *cp = '.';
1671
Simon Kelley4f7b3042012-11-28 21:27:02 +00001672 if (comma)
1673 {
1674 arg = comma;
1675 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001676 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001677 if (comma)
1678 {
1679 arg = comma;
1680 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001681 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001682 if (comma)
1683 {
1684 arg = comma;
1685 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001686 daemon->soa_expiry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001687 }
1688 }
1689 }
1690 }
1691
1692 break;
1693
Simon Kelley2bb73af2013-04-24 17:38:19 +01001694 case 's': /* --domain */
1695 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01001696 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00001697 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01001698 else
Simon Kelley9009d742008-11-14 20:04:27 +00001699 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001700 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00001701 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01001702 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001703 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001704 else
1705 {
Simon Kelley9009d742008-11-14 20:04:27 +00001706 if (comma)
1707 {
Simon Kelley429798f2012-12-10 20:45:53 +00001708 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00001709 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01001710
Simon Kelley48fd1c42013-04-25 09:49:38 +01001711 new->prefix = NULL;
1712
Simon Kelley9009d742008-11-14 20:04:27 +00001713 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00001714 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00001715 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00001716 int msize;
1717
Simon Kelley28866e92011-02-14 20:19:14 +00001718 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001719 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001720 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001721 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00001722 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00001723 int mask = (1 << (32 - msize)) - 1;
1724 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001725 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
1726 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00001727 if (arg)
1728 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001729 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01001730 {
1731 if (!(new->prefix = canonicalise_opt(arg)) ||
1732 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
1733 ret_err(_("bad prefix"));
1734 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01001735 else if (strcmp(arg, "local") != 0 ||
1736 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001737 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00001738 else
1739 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001740 /* generate the equivalent of
1741 local=/<domain>/
1742 local=/xxx.yyy.zzz.in-addr.arpa/ */
Simon Kelley28866e92011-02-14 20:19:14 +00001743 struct server *serv = opt_malloc(sizeof(struct server));
1744 in_addr_t a = ntohl(new->start.s_addr) >> 8;
1745 char *p;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001746
Simon Kelley28866e92011-02-14 20:19:14 +00001747 memset(serv, 0, sizeof(struct server));
1748 serv->domain = d;
1749 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1750 serv->next = daemon->servers;
1751 daemon->servers = serv;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001752
Simon Kelley28866e92011-02-14 20:19:14 +00001753 serv = opt_malloc(sizeof(struct server));
1754 memset(serv, 0, sizeof(struct server));
1755 p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
1756
1757 if (msize == 24)
1758 p += sprintf(p, "%d.", a & 0xff);
1759 a = a >> 8;
1760 if (msize != 8)
1761 p += sprintf(p, "%d.", a & 0xff);
1762 a = a >> 8;
1763 p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
Simon Kelley48fd1c42013-04-25 09:49:38 +01001764
Simon Kelley28866e92011-02-14 20:19:14 +00001765 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1766 serv->next = daemon->servers;
1767 daemon->servers = serv;
1768 }
1769 }
Simon Kelley9009d742008-11-14 20:04:27 +00001770 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00001771#ifdef HAVE_IPV6
1772 else if (inet_pton(AF_INET6, comma, &new->start6))
1773 {
1774 u64 mask = (1LLU << (128 - msize)) - 1LLU;
1775 u64 addrpart = addr6part(&new->start6);
1776 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001777
Simon Kelleyd74942a2012-02-07 20:51:56 +00001778 /* prefix==64 overflows the mask calculation above */
1779 if (msize == 64)
1780 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001781
Simon Kelleyd74942a2012-02-07 20:51:56 +00001782 new->end6 = new->start6;
1783 setaddr6part(&new->start6, addrpart & ~mask);
1784 setaddr6part(&new->end6, addrpart | mask);
1785
1786 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001787 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001788 else if (arg)
1789 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001790 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01001791 {
1792 if (!(new->prefix = canonicalise_opt(arg)) ||
1793 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
1794 ret_err(_("bad prefix"));
1795 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01001796 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001797 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001798 else
1799 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001800 /* generate the equivalent of
1801 local=/<domain>/
1802 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyd74942a2012-02-07 20:51:56 +00001803 struct server *serv = opt_malloc(sizeof(struct server));
Simon Kelleyd74942a2012-02-07 20:51:56 +00001804 char *p;
Simon Kelleyceae00d2012-02-09 21:28:14 +00001805
Simon Kelleyd74942a2012-02-07 20:51:56 +00001806 memset(serv, 0, sizeof(struct server));
1807 serv->domain = d;
1808 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1809 serv->next = daemon->servers;
1810 daemon->servers = serv;
1811
1812 serv = opt_malloc(sizeof(struct server));
1813 memset(serv, 0, sizeof(struct server));
1814 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
1815
1816 for (i = msize-1; i >= 0; i -= 4)
1817 {
1818 int dig = ((unsigned char *)&new->start6)[i>>3];
1819 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
1820 }
1821 p += sprintf(p, "ip6.arpa");
1822
1823 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1824 serv->next = daemon->servers;
1825 daemon->servers = serv;
1826 }
1827 }
1828 }
1829#endif
1830 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001831 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001832 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01001833 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00001834 {
1835 arg = split(comma);
1836 if (inet_pton(AF_INET, comma, &new->start))
1837 {
1838 new->is6 = 0;
1839 if (!arg)
1840 new->end.s_addr = new->start.s_addr;
1841 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001842 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001843 }
1844#ifdef HAVE_IPV6
1845 else if (inet_pton(AF_INET6, comma, &new->start6))
1846 {
1847 new->is6 = 1;
1848 if (!arg)
1849 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
1850 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001851 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001852 }
1853#endif
1854 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001855 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001856 }
Simon Kelley2307eac2012-02-13 10:13:13 +00001857
1858 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01001859 if (option == 's')
1860 {
1861 new->next = daemon->cond_domain;
1862 daemon->cond_domain = new;
1863 }
1864 else
1865 {
1866 new->next = daemon->synth_domains;
1867 daemon->synth_domains = new;
1868 }
Simon Kelley9009d742008-11-14 20:04:27 +00001869 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01001870 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00001871 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01001872 else
1873 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001874 }
1875 }
Simon Kelley849a8352006-06-09 21:02:31 +01001876 break;
1877
Simon Kelleyf2621c72007-04-29 19:47:21 +01001878 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00001879 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001880 break;
1881
Simon Kelleyf2621c72007-04-29 19:47:21 +01001882 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00001883 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001884 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01001885 break;
Simon Kelley9e038942008-05-30 20:06:34 +01001886
Simon Kelley7622fc02009-06-04 20:32:05 +01001887#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01001888 case LOPT_SCRIPTUSR: /* --scriptuser */
1889 daemon->scriptuser = opt_string_alloc(arg);
1890 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001891#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001892
Simon Kelleyf2621c72007-04-29 19:47:21 +01001893 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01001894 do {
Simon Kelley824af852008-02-12 20:43:05 +00001895 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001896 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001897 new->next = daemon->if_names;
1898 daemon->if_names = new;
1899 /* new->name may be NULL if someone does
1900 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00001901 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01001902 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001903 arg = comma;
1904 } while (arg);
1905 break;
1906
Simon Kelley2937f8a2013-07-29 19:49:07 +01001907 case LOPT_TFTP: /* --enable-tftp */
1908 set_option_bool(OPT_TFTP);
1909 if (!arg)
1910 break;
1911 /* fall through */
1912
Simon Kelleyf2621c72007-04-29 19:47:21 +01001913 case 'I': /* --except-interface */
1914 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01001915 do {
Simon Kelley824af852008-02-12 20:43:05 +00001916 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001917 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001918 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001919 if (option == 'I')
1920 {
1921 new->next = daemon->if_except;
1922 daemon->if_except = new;
1923 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01001924 else if (option == LOPT_TFTP)
1925 {
1926 new->next = daemon->tftp_interfaces;
1927 daemon->tftp_interfaces = new;
1928 }
Simon Kelley849a8352006-06-09 21:02:31 +01001929 else
1930 {
1931 new->next = daemon->dhcp_except;
1932 daemon->dhcp_except = new;
1933 }
1934 arg = comma;
1935 } while (arg);
1936 break;
1937
Simon Kelleyf2621c72007-04-29 19:47:21 +01001938 case 'B': /* --bogus-nxdomain */
Simon Kelley849a8352006-06-09 21:02:31 +01001939 {
1940 struct in_addr addr;
1941 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001942 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01001943 {
Simon Kelley824af852008-02-12 20:43:05 +00001944 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Simon Kelley849a8352006-06-09 21:02:31 +01001945 baddr->next = daemon->bogus_addr;
1946 daemon->bogus_addr = baddr;
1947 baddr->addr = addr;
1948 }
1949 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001950 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01001951 break;
1952 }
1953
Simon Kelleyf2621c72007-04-29 19:47:21 +01001954 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00001955 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01001956 do {
Simon Kelley824af852008-02-12 20:43:05 +00001957 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001958 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001959 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001960 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01001961 {
1962 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00001963 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001964#ifdef HAVE_SOCKADDR_SA_LEN
1965 new->addr.in.sin_len = sizeof(new->addr.in);
1966#endif
1967 }
1968#ifdef HAVE_IPV6
1969 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1970 {
1971 new->addr.sa.sa_family = AF_INET6;
1972 new->addr.in6.sin6_flowinfo = 0;
1973 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00001974 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001975#ifdef HAVE_SOCKADDR_SA_LEN
1976 new->addr.in6.sin6_len = sizeof(new->addr.in6);
1977#endif
1978 }
1979#endif
1980 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001981 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01001982
1983 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00001984 if (option == 'a')
1985 {
1986 new->next = daemon->if_addrs;
1987 daemon->if_addrs = new;
1988 }
1989 else
1990 {
1991 new->next = daemon->auth_peers;
1992 daemon->auth_peers = new;
1993 }
Simon Kelley849a8352006-06-09 21:02:31 +01001994 arg = comma;
1995 } while (arg);
1996 break;
1997
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001998 case 'S': /* --server */
1999 case LOPT_LOCAL: /* --local */
2000 case 'A': /* --address */
2001 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002002 {
2003 struct server *serv, *newlist = NULL;
2004
2005 unhide_metas(arg);
2006
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002007 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002008 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002009 int rebind = !(*arg == '/');
2010 char *end = NULL;
2011 if (!rebind)
2012 arg++;
2013 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002014 {
2015 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002016 /* elide leading dots - they are implied in the search algorithm */
2017 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002018 /* # matches everything and becomes a zero length domain string */
2019 if (strcmp(arg, "#") == 0)
2020 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002021 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002022 option = '?';
Simon Kelley824af852008-02-12 20:43:05 +00002023 serv = opt_malloc(sizeof(struct server));
2024 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002025 serv->next = newlist;
2026 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002027 serv->domain = domain;
2028 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002029 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002030 if (rebind)
2031 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002032 }
2033 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002034 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002035 }
2036 else
2037 {
Simon Kelley824af852008-02-12 20:43:05 +00002038 newlist = opt_malloc(sizeof(struct server));
2039 memset(newlist, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002040 }
2041
2042 if (option == 'A')
2043 {
2044 newlist->flags |= SERV_LITERAL_ADDRESS;
2045 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002046 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002047 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002048 else if (option == LOPT_NO_REBIND)
2049 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002050
2051 if (!arg || !*arg)
2052 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002053 if (!(newlist->flags & SERV_NO_REBIND))
2054 newlist->flags |= SERV_NO_ADDR; /* no server */
2055 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002056 ret_err(gen_err);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002057 }
2058
2059 else if (strcmp(arg, "#") == 0)
2060 {
2061 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002062 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002063 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002064 }
2065 else
2066 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002067 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2068 if (err)
2069 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002070 }
2071
Simon Kelleyf2621c72007-04-29 19:47:21 +01002072 serv = newlist;
2073 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002074 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002075 serv->next->flags = serv->flags;
2076 serv->next->addr = serv->addr;
2077 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002078 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002079 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002080 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002081 serv->next = daemon->servers;
2082 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002083 break;
2084 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002085
2086 case LOPT_IPSET: /* --ipset */
2087#ifndef HAVE_IPSET
2088 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2089 break;
2090#else
2091 {
2092 struct ipsets ipsets_head;
2093 struct ipsets *ipsets = &ipsets_head;
2094 int size;
2095 char *end;
2096 char **sets, **sets_pos;
2097 memset(ipsets, 0, sizeof(struct ipsets));
2098 unhide_metas(arg);
2099 if (arg && *arg == '/')
2100 {
2101 arg++;
2102 while ((end = split_chr(arg, '/')))
2103 {
2104 char *domain = NULL;
2105 /* elide leading dots - they are implied in the search algorithm */
2106 while (*arg == '.')
2107 arg++;
2108 /* # matches everything and becomes a zero length domain string */
2109 if (strcmp(arg, "#") == 0 || !*arg)
2110 domain = "";
2111 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
2112 option = '?';
2113 ipsets->next = opt_malloc(sizeof(struct ipsets));
2114 ipsets = ipsets->next;
2115 memset(ipsets, 0, sizeof(struct ipsets));
2116 ipsets->domain = domain;
2117 arg = end;
2118 }
2119 }
2120 else
2121 {
2122 ipsets->next = opt_malloc(sizeof(struct ipsets));
2123 ipsets = ipsets->next;
2124 memset(ipsets, 0, sizeof(struct ipsets));
2125 ipsets->domain = "";
2126 }
2127 if (!arg || !*arg)
2128 {
2129 option = '?';
2130 break;
2131 }
2132 size = 2;
2133 for (end = arg; *end; ++end)
2134 if (*end == ',')
2135 ++size;
2136
2137 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2138
2139 do {
2140 end = split(arg);
2141 *sets_pos++ = opt_string_alloc(arg);
2142 arg = end;
2143 } while (end);
2144 *sets_pos = 0;
2145 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2146 ipsets->next->sets = sets;
2147 ipsets->next = daemon->ipsets;
2148 daemon->ipsets = ipsets_head.next;
2149
2150 break;
2151 }
2152#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002153
Simon Kelleyf2621c72007-04-29 19:47:21 +01002154 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002155 {
2156 int size;
2157
2158 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002159 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002160 else
2161 {
2162 /* zero is OK, and means no caching. */
2163
2164 if (size < 0)
2165 size = 0;
2166 else if (size > 10000)
2167 size = 10000;
2168
2169 daemon->cachesize = size;
2170 }
2171 break;
2172 }
2173
Simon Kelleyf2621c72007-04-29 19:47:21 +01002174 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002175 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002176 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002177 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002178
Simon Kelley1a6bca82008-07-11 11:11:42 +01002179 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002180 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002181 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002182 break;
2183
Simon Kelleyf2621c72007-04-29 19:47:21 +01002184 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002185 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002186 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002187 break;
2188
Simon Kelleyf2621c72007-04-29 19:47:21 +01002189 case LOPT_MAX_LOGS: /* --log-async */
2190 daemon->max_logs = LOG_MAX; /* default */
2191 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002192 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002193 else if (daemon->max_logs > 100)
2194 daemon->max_logs = 100;
2195 break;
2196
2197 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002198 {
2199 int i;
2200 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002201 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002202 daemon->edns_pktsz = (unsigned short)i;
2203 break;
2204 }
2205
Simon Kelleyf2621c72007-04-29 19:47:21 +01002206 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002207 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002208 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002209 /* if explicitly set to zero, use single OS ephemeral port
2210 and disable random ports */
2211 if (daemon->query_port == 0)
2212 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002213 break;
2214
Simon Kelley824af852008-02-12 20:43:05 +00002215 case 'T': /* --local-ttl */
2216 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002217 case LOPT_MAXTTL: /* --max-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002218 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002219 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002220 {
2221 int ttl;
2222 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002223 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002224 else if (option == LOPT_NEGTTL)
2225 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002226 else if (option == LOPT_MAXTTL)
2227 daemon->max_ttl = (unsigned long)ttl;
Simon Kelley1d860412012-09-20 20:48:04 +01002228 else if (option == LOPT_MAXCTTL)
2229 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002230 else if (option == LOPT_AUTHTTL)
2231 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley849a8352006-06-09 21:02:31 +01002232 else
2233 daemon->local_ttl = (unsigned long)ttl;
2234 break;
2235 }
2236
Simon Kelley7622fc02009-06-04 20:32:05 +01002237#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002238 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002239 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002240 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002241 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002242#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002243
Simon Kelley7622fc02009-06-04 20:32:05 +01002244#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002245 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002246 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002247 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002248 break;
2249
Simon Kelley824af852008-02-12 20:43:05 +00002250 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002251 comma = split(arg);
2252 if (comma)
2253 {
2254 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2255 new->interface = opt_string_alloc(comma);
2256 new->prefix = opt_string_alloc(arg);
2257 new->next = daemon->if_prefix;
2258 daemon->if_prefix = new;
2259 }
2260 else
2261 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002262 break;
2263
Simon Kelley824af852008-02-12 20:43:05 +00002264 case LOPT_TFTPPORTS: /* --tftp-port-range */
2265 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002266 !atoi_check16(arg, &daemon->start_tftp_port) ||
2267 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002268 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002269
2270 if (daemon->start_tftp_port > daemon->end_tftp_port)
2271 {
2272 int tmp = daemon->start_tftp_port;
2273 daemon->start_tftp_port = daemon->end_tftp_port;
2274 daemon->end_tftp_port = tmp;
2275 }
2276
2277 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002278#endif
Simon Kelley824af852008-02-12 20:43:05 +00002279
Simon Kelleyf2621c72007-04-29 19:47:21 +01002280 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002281 {
Simon Kelley824af852008-02-12 20:43:05 +00002282 struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley316e2732010-01-22 20:16:09 +00002283 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002284 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002285
Simon Kelley316e2732010-01-22 20:16:09 +00002286 strcpy(new->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002287 new->alias = NULL;
2288 new->next = daemon->bridges;
2289 daemon->bridges = new;
2290
2291 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002292 arg = comma;
2293 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002294 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002295 {
Simon Kelley824af852008-02-12 20:43:05 +00002296 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002297 b->next = new->alias;
2298 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002299 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002300 }
2301 } while (comma);
2302
2303 break;
2304 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002305
Simon Kelley7622fc02009-06-04 20:32:05 +01002306#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002307 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002308 {
2309 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002310 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002311 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002312
Simon Kelley52b92f42012-01-22 16:05:15 +00002313 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002314 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002315
Simon Kelley849a8352006-06-09 21:02:31 +01002316 if (!arg)
2317 {
2318 option = '?';
2319 break;
2320 }
2321
2322 while(1)
2323 {
2324 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002325 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2326 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2327 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002328 break;
2329
Simon Kelleyf2621c72007-04-29 19:47:21 +01002330 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002331 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002332 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002333 {
Simon Kelley824af852008-02-12 20:43:05 +00002334 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2335 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002336 tt->next = new->filter;
2337 new->filter = tt;
2338 }
2339 else
2340 {
2341 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002342 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002343 else if (strstr(arg, "set:") == arg)
2344 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002345 else
Simon Kelley824af852008-02-12 20:43:05 +00002346 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002347 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002348 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002349 }
2350 else
2351 {
2352 a[0] = arg;
2353 break;
2354 }
2355 }
2356
Simon Kelley1f776932012-12-16 19:46:08 +00002357 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002358 if (!(a[k] = split(a[k-1])))
2359 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002360
Simon Kelley52b92f42012-01-22 16:05:15 +00002361 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002362 ret_err(_("bad dhcp-range"));
2363
2364 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002365 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002366 new->next = daemon->dhcp;
2367 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002368 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002369 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002370 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002371 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002372 new->flags |= CONTEXT_PROXY;
2373 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002374 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002375
2376 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2377 {
2378 struct in_addr tmp = new->start;
2379 new->start = new->end;
2380 new->end = tmp;
2381 }
2382
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002383 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002384 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002385 {
2386 new->flags |= CONTEXT_NETMASK;
2387 leasepos = 3;
2388 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002389 ret_err(_("inconsistent DHCP range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002390 }
2391
2392 if (k >= 4 && strchr(a[3], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002393 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002394 {
2395 new->flags |= CONTEXT_BRDCAST;
2396 leasepos = 4;
2397 }
Simon Kelley849a8352006-06-09 21:02:31 +01002398 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002399#ifdef HAVE_DHCP6
2400 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002401 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002402 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002403 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002404 new->next = daemon->dhcp6;
2405 daemon->dhcp6 = new;
2406
Simon Kelley30cd9662012-03-25 20:44:38 +01002407 for (leasepos = 1; leasepos < k; leasepos++)
2408 {
2409 if (strcmp(a[leasepos], "static") == 0)
2410 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2411 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley1f776932012-12-16 19:46:08 +00002412 new->flags |= CONTEXT_RA_ONLY | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002413 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002414 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002415 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002416 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002417 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2418 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002419 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2420 {
2421 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2422 new->flags |= CONTEXT_TEMPLATE;
2423 }
Simon Kelley921360c2013-05-31 14:07:22 +01002424 else if (strstr(a[leasepos], "constructor-noauth:") == a[leasepos])
2425 {
2426 new->template_interface = opt_string_alloc(a[leasepos] + 19);
2427 new->flags |= CONTEXT_TEMPLATE | CONTEXT_NOAUTH;
2428 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002429 else
2430 break;
2431 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002432
Simon Kelley52b92f42012-01-22 16:05:15 +00002433 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002434 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002435 {
2436 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002437 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002438 if (!(*cp >= '0' && *cp <= '9'))
2439 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002440 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002441 {
2442 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002443 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002444 }
2445 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002446
Simon Kelley6692a1a2013-08-20 14:41:31 +01002447 if (new->prefix != 64)
2448 {
2449 if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
2450 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2451 else if (new->flags & CONTEXT_TEMPLATE)
2452 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2453 }
2454
2455 if (new->prefix < 64)
2456 ret_err(_("prefix length must be at least 64"));
2457
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002458 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2459 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002460
2461 /* dhcp-range=:: enables DHCP stateless on any interface */
2462 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2463 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002464
2465 if (new->flags & CONTEXT_TEMPLATE)
2466 {
2467 struct in6_addr zero;
2468 memset(&zero, 0, sizeof(zero));
2469 if (!is_same_net6(&zero, &new->start6, new->prefix))
2470 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2471 }
2472
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002473 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002474 {
2475 struct in6_addr tmp = new->start6;
2476 new->start6 = new->end6;
2477 new->end6 = tmp;
2478 }
Simon Kelley849a8352006-06-09 21:02:31 +01002479 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002480#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002481 else
2482 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002483
Simon Kelley30cd9662012-03-25 20:44:38 +01002484 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002485 {
2486 if (strcmp(a[leasepos], "infinite") == 0)
2487 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002488 else if (strcmp(a[leasepos], "deprecated") == 0)
2489 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002490 else
2491 {
2492 int fac = 1;
2493 if (strlen(a[leasepos]) > 0)
2494 {
2495 switch (a[leasepos][strlen(a[leasepos]) - 1])
2496 {
Simon Kelley42243212012-07-20 15:19:18 +01002497 case 'w':
2498 case 'W':
2499 fac *= 7;
2500 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002501 case 'd':
2502 case 'D':
2503 fac *= 24;
2504 /* fall though */
2505 case 'h':
2506 case 'H':
2507 fac *= 60;
2508 /* fall through */
2509 case 'm':
2510 case 'M':
2511 fac *= 60;
2512 /* fall through */
2513 case 's':
2514 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002515 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002516 }
2517
Simon Kelleybe379862012-12-23 12:01:39 +00002518 for (cp = a[leasepos]; *cp; cp++)
2519 if (!(*cp >= '0' && *cp <= '9'))
2520 break;
2521
Simon Kelley54dae552013-02-05 17:55:10 +00002522 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00002523 ret_err(_("bad dhcp-range"));
2524
Simon Kelley849a8352006-06-09 21:02:31 +01002525 new->lease_time = atoi(a[leasepos]) * fac;
2526 /* Leases of a minute or less confuse
2527 some clients, notably Apple's */
2528 if (new->lease_time < 120)
2529 new->lease_time = 120;
2530 }
2531 }
2532 }
2533 break;
2534 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01002535
Simon Kelley5aabfc72007-08-29 11:24:47 +01002536 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01002537 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01002538 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002539 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01002540 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01002541 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01002542 struct in_addr in;
2543
Simon Kelley824af852008-02-12 20:43:05 +00002544 new = opt_malloc(sizeof(struct dhcp_config));
2545
Simon Kelley849a8352006-06-09 21:02:31 +01002546 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00002547 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
2548 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002549 new->netid = NULL;
2550
Simon Kelley849a8352006-06-09 21:02:31 +01002551 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01002552 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002553 if (!(a[k] = split(a[k-1])))
2554 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002555
2556 for (j = 0; j < k; j++)
2557 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
2558 {
2559 char *arg = a[j];
2560
2561 if ((arg[0] == 'i' || arg[0] == 'I') &&
2562 (arg[1] == 'd' || arg[1] == 'D') &&
2563 arg[2] == ':')
2564 {
2565 if (arg[3] == '*')
2566 new->flags |= CONFIG_NOCLID;
2567 else
2568 {
2569 int len;
2570 arg += 3; /* dump id: */
2571 if (strchr(arg, ':'))
2572 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
2573 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01002574 {
2575 unhide_metas(arg);
2576 len = (int) strlen(arg);
2577 }
2578
Simon Kelley28866e92011-02-14 20:19:14 +00002579 if (len == -1)
Simon Kelley9f7f3b12012-05-28 21:39:57 +01002580
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002581 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00002582 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01002583 {
2584 new->flags |= CONFIG_CLID;
2585 new->clid_len = len;
2586 memcpy(new->clid, arg, len);
2587 }
Simon Kelley849a8352006-06-09 21:02:31 +01002588 }
2589 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002590 /* dhcp-host has strange backwards-compat needs. */
2591 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01002592 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002593 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2594 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2595 newtag->net = opt_malloc(strlen(arg + 4) + 1);
2596 newlist->next = new->netid;
2597 new->netid = newlist;
2598 newlist->list = newtag;
2599 strcpy(newtag->net, arg+4);
2600 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01002601 }
Simon Kelley7de060b2011-08-26 17:24:52 +01002602 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002603 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00002604#ifdef HAVE_DHCP6
2605 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
2606 {
2607 arg[strlen(arg)-1] = 0;
2608 arg++;
2609
2610 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002611 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00002612
2613 for (i= 0; i < 8; i++)
2614 if (new->addr6.s6_addr[i] != 0)
2615 break;
2616
2617 /* set WILDCARD if network part all zeros */
2618 if (i == 8)
2619 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00002620
2621 new->flags |= CONFIG_ADDR6;
2622 }
2623#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01002624 else
Simon Kelley849a8352006-06-09 21:02:31 +01002625 {
Simon Kelley9009d742008-11-14 20:04:27 +00002626 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00002627 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
2628 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002629 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00002630 else
2631 {
2632
2633 newhw->next = new->hwaddr;
2634 new->hwaddr = newhw;
2635 }
Simon Kelley849a8352006-06-09 21:02:31 +01002636 }
2637 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002638 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002639 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002640 struct dhcp_config *configs;
2641
Simon Kelley849a8352006-06-09 21:02:31 +01002642 new->addr = in;
2643 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002644
2645 /* If the same IP appears in more than one host config, then DISCOVER
2646 for one of the hosts will get the address, but REQUEST will be NAKed,
2647 since the address is reserved by the other one -> protocol loop. */
2648 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
2649 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
2650 {
2651 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
2652 return 0;
2653 }
Simon Kelley849a8352006-06-09 21:02:31 +01002654 }
2655 else
2656 {
2657 char *cp, *lastp = NULL, last = 0;
2658 int fac = 1;
2659
2660 if (strlen(a[j]) > 1)
2661 {
2662 lastp = a[j] + strlen(a[j]) - 1;
2663 last = *lastp;
2664 switch (last)
2665 {
Simon Kelley42243212012-07-20 15:19:18 +01002666 case 'w':
2667 case 'W':
2668 fac *= 7;
2669 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002670 case 'd':
2671 case 'D':
2672 fac *= 24;
2673 /* fall through */
2674 case 'h':
2675 case 'H':
2676 fac *= 60;
2677 /* fall through */
2678 case 'm':
2679 case 'M':
2680 fac *= 60;
2681 /* fall through */
2682 case 's':
2683 case 'S':
2684 *lastp = 0;
2685 }
2686 }
2687
2688 for (cp = a[j]; *cp; cp++)
Simon Kelley572b41e2011-02-18 18:11:18 +00002689 if (!isdigit((unsigned char)*cp) && *cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01002690 break;
2691
2692 if (*cp)
2693 {
2694 if (lastp)
2695 *lastp = last;
2696 if (strcmp(a[j], "infinite") == 0)
2697 {
2698 new->lease_time = 0xffffffff;
2699 new->flags |= CONFIG_TIME;
2700 }
2701 else if (strcmp(a[j], "ignore") == 0)
2702 new->flags |= CONFIG_DISABLE;
2703 else
2704 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002705 if (!(new->hostname = canonicalise_opt(a[j])) ||
2706 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002707 ret_err(_("bad DHCP host name"));
2708
2709 new->flags |= CONFIG_NAME;
2710 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01002711 }
2712 }
2713 else
2714 {
2715 new->lease_time = atoi(a[j]) * fac;
2716 /* Leases of a minute or less confuse
2717 some clients, notably Apple's */
2718 if (new->lease_time < 120)
2719 new->lease_time = 120;
2720 new->flags |= CONFIG_TIME;
2721 }
2722 }
2723
Simon Kelley5aabfc72007-08-29 11:24:47 +01002724 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01002725 break;
2726 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002727
2728 case LOPT_TAG_IF: /* --tag-if */
2729 {
2730 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
2731
2732 new->tag = NULL;
2733 new->set = NULL;
2734 new->next = NULL;
2735
2736 /* preserve order */
2737 if (!daemon->tag_if)
2738 daemon->tag_if = new;
2739 else
2740 {
2741 struct tag_if *tmp;
2742 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
2743 tmp->next = new;
2744 }
2745
2746 while (arg)
2747 {
2748 size_t len;
2749
2750 comma = split(arg);
2751 len = strlen(arg);
2752
2753 if (len < 5)
2754 {
2755 new->set = NULL;
2756 break;
2757 }
2758 else
2759 {
2760 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2761 newtag->net = opt_malloc(len - 3);
2762 strcpy(newtag->net, arg+4);
2763 unhide_metas(newtag->net);
2764
2765 if (strstr(arg, "set:") == arg)
2766 {
2767 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2768 newlist->next = new->set;
2769 new->set = newlist;
2770 newlist->list = newtag;
2771 }
2772 else if (strstr(arg, "tag:") == arg)
2773 {
2774 newtag->next = new->tag;
2775 new->tag = newtag;
2776 }
2777 else
2778 {
2779 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00002780 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002781 break;
2782 }
2783 }
2784
2785 arg = comma;
2786 }
2787
2788 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002789 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002790
2791 break;
2792 }
2793
Simon Kelley849a8352006-06-09 21:02:31 +01002794
Simon Kelley73a08a22009-02-05 20:28:08 +00002795 case 'O': /* --dhcp-option */
2796 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00002797 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00002798 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002799 return parse_dhcp_opt(errstr, arg,
2800 option == LOPT_FORCE ? DHOPT_FORCE :
2801 (option == LOPT_MATCH ? DHOPT_MATCH :
2802 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
2803
Simon Kelleyf2621c72007-04-29 19:47:21 +01002804 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01002805 {
2806 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002807 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002808 {
Simon Kelley824af852008-02-12 20:43:05 +00002809 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01002810 newid->next = id;
2811 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002812 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002813 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002814 arg = comma;
2815 };
2816
2817 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002818 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002819 else
2820 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002821 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01002822 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002823 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002824 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002825 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002826 dhcp_next_server.s_addr = 0;
2827 if (comma)
2828 {
2829 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002830 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002831 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002832 if (comma)
2833 {
2834 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002835 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
2836 {
2837 /*
2838 * The user may have specified the tftp hostname here.
2839 * save it so that it can be resolved/looked up during
2840 * actual dhcp_reply().
2841 */
2842
2843 tftp_sname = opt_string_alloc(comma);
2844 dhcp_next_server.s_addr = 0;
2845 }
Simon Kelley849a8352006-06-09 21:02:31 +01002846 }
2847 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002848
2849 new = opt_malloc(sizeof(struct dhcp_boot));
2850 new->file = dhcp_file;
2851 new->sname = dhcp_sname;
2852 new->tftp_sname = tftp_sname;
2853 new->next_server = dhcp_next_server;
2854 new->netid = id;
2855 new->next = daemon->boot_config;
2856 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01002857 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002858
Simon Kelley849a8352006-06-09 21:02:31 +01002859 break;
2860 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002861
2862 case LOPT_PXE_PROMT: /* --pxe-prompt */
2863 {
2864 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
2865 int timeout;
2866
2867 new->netid = NULL;
2868 new->opt = 10; /* PXE_MENU_PROMPT */
2869
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002870 while (is_tag_prefix(arg))
2871 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002872 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2873 comma = split(arg);
2874 nn->next = new->netid;
2875 new->netid = nn;
2876 nn->net = opt_string_alloc(arg+4);
2877 arg = comma;
2878 }
2879
2880 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002881 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01002882 else
2883 {
2884 comma = split(arg);
2885 unhide_metas(arg);
2886 new->len = strlen(arg) + 1;
2887 new->val = opt_malloc(new->len);
2888 memcpy(new->val + 1, arg, new->len - 1);
2889
2890 new->u.vendor_class = (unsigned char *)"PXEClient";
2891 new->flags = DHOPT_VENDOR;
2892
2893 if (comma && atoi_check(comma, &timeout))
2894 *(new->val) = timeout;
2895 else
2896 *(new->val) = 255;
2897
2898 new->next = daemon->dhcp_opts;
2899 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01002900 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01002901 }
2902
2903 break;
2904 }
2905
2906 case LOPT_PXE_SERV: /* --pxe-service */
2907 {
2908 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
2909 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
2910 "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };
2911 static int boottype = 32768;
2912
2913 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00002914 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01002915 new->server.s_addr = 0;
2916
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002917 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01002918 {
2919 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2920 comma = split(arg);
2921 nn->next = new->netid;
2922 new->netid = nn;
2923 nn->net = opt_string_alloc(arg+4);
2924 arg = comma;
2925 }
2926
2927 if (arg && (comma = split(arg)))
2928 {
2929 for (i = 0; CSA[i]; i++)
2930 if (strcasecmp(CSA[i], arg) == 0)
2931 break;
2932
2933 if (CSA[i] || atoi_check(arg, &i))
2934 {
2935 arg = comma;
2936 comma = split(arg);
2937
2938 new->CSA = i;
2939 new->menu = opt_string_alloc(arg);
2940
Simon Kelley316e2732010-01-22 20:16:09 +00002941 if (!comma)
2942 {
2943 new->type = 0; /* local boot */
2944 new->basename = NULL;
2945 }
2946 else
Simon Kelley7622fc02009-06-04 20:32:05 +01002947 {
2948 arg = comma;
2949 comma = split(arg);
2950 if (atoi_check(arg, &i))
2951 {
2952 new->type = i;
2953 new->basename = NULL;
2954 }
2955 else
2956 {
2957 new->type = boottype++;
2958 new->basename = opt_string_alloc(arg);
2959 }
2960
Simon Kelley751d6f42012-02-10 15:24:51 +00002961 if (comma)
2962 {
2963 if (!inet_pton(AF_INET, comma, &new->server))
2964 {
2965 new->server.s_addr = 0;
2966 new->sname = opt_string_alloc(comma);
2967 }
2968
2969 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002970 }
Simon Kelley751d6f42012-02-10 15:24:51 +00002971
Simon Kelley316e2732010-01-22 20:16:09 +00002972 /* Order matters */
2973 new->next = NULL;
2974 if (!daemon->pxe_services)
2975 daemon->pxe_services = new;
2976 else
2977 {
2978 struct pxe_service *s;
2979 for (s = daemon->pxe_services; s->next; s = s->next);
2980 s->next = new;
2981 }
2982
2983 daemon->enable_pxe = 1;
2984 break;
2985
Simon Kelley7622fc02009-06-04 20:32:05 +01002986 }
2987 }
2988
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002989 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01002990 }
2991
Simon Kelleyf2621c72007-04-29 19:47:21 +01002992 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01002993 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002994 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002995 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002996 else
2997 {
Simon Kelley824af852008-02-12 20:43:05 +00002998 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002999 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003000 unhide_metas(comma);
3001 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003002 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003003 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003004 else
3005 {
3006 new->next = daemon->dhcp_macs;
3007 daemon->dhcp_macs = new;
3008 }
Simon Kelley849a8352006-06-09 21:02:31 +01003009 }
3010 }
3011 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003012
3013#ifdef OPTION6_PREFIX_CLASS
3014 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3015 {
3016 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3017
3018 if (!(comma = split(arg)) ||
3019 !atoi_check16(comma, &new->class))
3020 ret_err(gen_err);
3021
3022 new->tag.net = opt_string_alloc(set_prefix(arg));
3023 new->next = daemon->prefix_classes;
3024 daemon->prefix_classes = new;
3025
3026 break;
3027 }
3028#endif
3029
3030
Simon Kelleyf2621c72007-04-29 19:47:21 +01003031 case 'U': /* --dhcp-vendorclass */
3032 case 'j': /* --dhcp-userclass */
3033 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3034 case LOPT_REMOTE: /* --dhcp-remoteid */
3035 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003036 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003037 unsigned char *p;
3038 int dig = 0;
3039 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3040
3041 if (!(comma = split(arg)))
3042 ret_err(gen_err);
3043
3044 new->netid.net = opt_string_alloc(set_prefix(arg));
3045 /* check for hex string - must digits may include : must not have nothing else,
3046 only allowed for agent-options. */
3047
3048 arg = comma;
3049 if ((comma = split(arg)))
3050 {
3051 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3052 ret_err(gen_err);
3053 else
3054 new->enterprise = atoi(arg+11);
3055 }
3056 else
3057 comma = arg;
3058
3059 for (p = (unsigned char *)comma; *p; p++)
3060 if (isxdigit(*p))
3061 dig = 1;
3062 else if (*p != ':')
3063 break;
3064 unhide_metas(comma);
3065 if (option == 'U' || option == 'j' || *p || !dig)
3066 {
3067 new->len = strlen(comma);
3068 new->data = opt_malloc(new->len);
3069 memcpy(new->data, comma, new->len);
3070 }
3071 else
3072 {
3073 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3074 new->data = opt_malloc(new->len);
3075 memcpy(new->data, comma, new->len);
3076 }
3077
3078 switch (option)
3079 {
3080 case 'j':
3081 new->match_type = MATCH_USER;
3082 break;
3083 case 'U':
3084 new->match_type = MATCH_VENDOR;
3085 break;
3086 case LOPT_CIRCUIT:
3087 new->match_type = MATCH_CIRCUIT;
3088 break;
3089 case LOPT_REMOTE:
3090 new->match_type = MATCH_REMOTE;
3091 break;
3092 case LOPT_SUBSCR:
3093 new->match_type = MATCH_SUBSCRIBER;
3094 break;
3095 }
3096 new->next = daemon->dhcp_vendors;
3097 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003098
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003099 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003100 }
3101
Simon Kelley9e038942008-05-30 20:06:34 +01003102 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3103 if (!arg)
3104 {
3105 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3106 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3107 }
3108 else
3109 {
3110 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003111 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3112 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003113 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003114 if (!comma)
3115 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3116 }
3117 break;
3118
Simon Kelley824af852008-02-12 20:43:05 +00003119 case 'J': /* --dhcp-ignore */
3120 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3121 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003122 case '3': /* --bootp-dynamic */
3123 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003124 {
Simon Kelley824af852008-02-12 20:43:05 +00003125 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003126 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003127 if (option == 'J')
3128 {
3129 new->next = daemon->dhcp_ignore;
3130 daemon->dhcp_ignore = new;
3131 }
Simon Kelley824af852008-02-12 20:43:05 +00003132 else if (option == LOPT_BROADCAST)
3133 {
3134 new->next = daemon->force_broadcast;
3135 daemon->force_broadcast = new;
3136 }
Simon Kelley9009d742008-11-14 20:04:27 +00003137 else if (option == '3')
3138 {
3139 new->next = daemon->bootp_dynamic;
3140 daemon->bootp_dynamic = new;
3141 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003142 else if (option == LOPT_GEN_NAMES)
3143 {
3144 new->next = daemon->dhcp_gen_names;
3145 daemon->dhcp_gen_names = new;
3146 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003147 else
3148 {
3149 new->next = daemon->dhcp_ignore_names;
3150 daemon->dhcp_ignore_names = new;
3151 }
3152
3153 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003154 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003155 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003156 member->next = list;
3157 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003158 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003159 member->net = opt_string_alloc(arg+4);
3160 else
3161 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003162 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003163 }
Simon Kelley849a8352006-06-09 21:02:31 +01003164
3165 new->list = list;
3166 break;
3167 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003168
3169 case LOPT_PROXY: /* --dhcp-proxy */
3170 daemon->override = 1;
3171 while (arg) {
3172 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3173 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003174 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003175 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003176 new->next = daemon->override_relays;
3177 daemon->override_relays = new;
3178 arg = comma;
3179 }
3180 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01003181#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003182
Simon Kelley8b372702012-03-09 17:45:10 +00003183#ifdef HAVE_DHCP6
3184 case LOPT_DUID: /* --dhcp-duid */
3185 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003186 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003187 else
3188 {
3189 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3190 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3191 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3192 }
3193 break;
3194#endif
3195
Simon Kelleyf2621c72007-04-29 19:47:21 +01003196 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003197 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003198 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003199 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003200 struct doctor *new = opt_malloc(sizeof(struct doctor));
3201 new->next = daemon->doctors;
3202 daemon->doctors = new;
3203 new->mask.s_addr = 0xffffffff;
3204 new->end.s_addr = 0;
3205
Simon Kelley849a8352006-06-09 21:02:31 +01003206 if ((a[0] = arg))
3207 for (k = 1; k < 3; k++)
3208 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003209 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003210 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003211 unhide_metas(a[k]);
3212 }
Simon Kelley849a8352006-06-09 21:02:31 +01003213
Simon Kelley73a08a22009-02-05 20:28:08 +00003214 dash = split_chr(a[0], '-');
3215
Simon Kelley849a8352006-06-09 21:02:31 +01003216 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003217 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
3218 (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
Simon Kelley73a08a22009-02-05 20:28:08 +00003219 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003220
3221 if (k == 3)
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003222 inet_pton(AF_INET, a[2], &new->mask);
Simon Kelley849a8352006-06-09 21:02:31 +01003223
Simon Kelley73a08a22009-02-05 20:28:08 +00003224 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003225 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003226 !is_same_net(new->in, new->end, new->mask) ||
3227 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003228 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003229
3230 break;
3231 }
3232
Simon Kelleyf2621c72007-04-29 19:47:21 +01003233 case LOPT_INTNAME: /* --interface-name */
3234 {
3235 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003236 char *domain = NULL;
3237
Simon Kelleyf2621c72007-04-29 19:47:21 +01003238 comma = split(arg);
3239
Simon Kelley1f15b812009-10-13 17:49:32 +01003240 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003241 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003242
Simon Kelley824af852008-02-12 20:43:05 +00003243 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003244 new->next = NULL;
Simon Kelley115ac3e2013-05-20 11:28:32 +01003245 new->addr4 = NULL;
3246#ifdef HAVE_IPV6
3247 new->addr6 = NULL;
3248#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01003249 /* Add to the end of the list, so that first name
3250 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003251 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003252 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003253 new->name = domain;
Simon Kelley824af852008-02-12 20:43:05 +00003254 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003255 break;
3256 }
Simon Kelley9009d742008-11-14 20:04:27 +00003257
3258 case LOPT_CNAME: /* --cname */
3259 {
3260 struct cname *new;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003261 char *alias;
3262 char *target;
3263
Simon Kelley9009d742008-11-14 20:04:27 +00003264 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003265 ret_err(gen_err);
3266
3267 alias = canonicalise_opt(arg);
3268 target = canonicalise_opt(comma);
3269
3270 if (!alias || !target)
3271 ret_err(_("bad CNAME"));
Simon Kelley9009d742008-11-14 20:04:27 +00003272 else
3273 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003274 for (new = daemon->cnames; new; new = new->next)
3275 if (hostname_isequal(new->alias, arg))
3276 ret_err(_("duplicate CNAME"));
3277 new = opt_malloc(sizeof(struct cname));
3278 new->next = daemon->cnames;
3279 daemon->cnames = new;
3280 new->alias = alias;
3281 new->target = target;
Simon Kelley9009d742008-11-14 20:04:27 +00003282 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003283
Simon Kelley9009d742008-11-14 20:04:27 +00003284 break;
3285 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003286
3287 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003288 {
3289 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003290 char *dom, *target = NULL;
3291
Simon Kelleyf2621c72007-04-29 19:47:21 +01003292 comma = split(arg);
3293
Simon Kelley1f15b812009-10-13 17:49:32 +01003294 if (!(dom = canonicalise_opt(arg)) ||
3295 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003296 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003297 else
3298 {
3299 new = opt_malloc(sizeof(struct ptr_record));
3300 new->next = daemon->ptr;
3301 daemon->ptr = new;
3302 new->name = dom;
3303 new->ptr = target;
3304 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003305 break;
3306 }
3307
Simon Kelley1a6bca82008-07-11 11:11:42 +01003308 case LOPT_NAPTR: /* --naptr-record */
3309 {
3310 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3311 int k = 0;
3312 struct naptr *new;
3313 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003314 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003315
3316 if ((a[0] = arg))
3317 for (k = 1; k < 7; k++)
3318 if (!(a[k] = split(a[k-1])))
3319 break;
3320
3321
3322 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003323 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003324 !atoi_check16(a[1], &order) ||
3325 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003326 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003327 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003328 else
3329 {
3330 new = opt_malloc(sizeof(struct naptr));
3331 new->next = daemon->naptr;
3332 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003333 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003334 new->flags = opt_string_alloc(a[3]);
3335 new->services = opt_string_alloc(a[4]);
3336 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003337 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003338 new->order = order;
3339 new->pref = pref;
3340 }
3341 break;
3342 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003343
3344 case LOPT_RR: /* dns-rr */
3345 {
3346 struct txt_record *new;
3347 size_t len;
3348 char *data;
3349 int val;
3350
3351 comma = split(arg);
3352 data = split(comma);
3353
3354 new = opt_malloc(sizeof(struct txt_record));
3355 new->next = daemon->rr;
3356 daemon->rr = new;
3357
3358 if (!atoi_check(comma, &val) ||
3359 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003360 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003361 ret_err(_("bad RR record"));
3362
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003363 new->class = val;
3364 new->len = 0;
3365
3366 if (data)
3367 {
3368 new->txt=opt_malloc(len);
3369 new->len = len;
3370 memcpy(new->txt, data, len);
3371 }
3372
3373 break;
3374 }
3375
Simon Kelleyf2621c72007-04-29 19:47:21 +01003376 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003377 {
3378 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003379 unsigned char *p, *cnt;
3380 size_t len;
3381
3382 comma = split(arg);
3383
Simon Kelley824af852008-02-12 20:43:05 +00003384 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003385 new->next = daemon->txt;
3386 daemon->txt = new;
3387 new->class = C_IN;
Simon Kelley849a8352006-06-09 21:02:31 +01003388
Simon Kelley1f15b812009-10-13 17:49:32 +01003389 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003390 ret_err(_("bad TXT record"));
3391
Simon Kelley28866e92011-02-14 20:19:14 +00003392 len = comma ? strlen(comma) : 0;
3393 len += (len/255) + 1; /* room for extra counts */
3394 new->txt = p = opt_malloc(len);
3395
3396 cnt = p++;
3397 *cnt = 0;
3398
3399 while (comma && *comma)
3400 {
3401 unsigned char c = (unsigned char)*comma++;
3402
3403 if (c == ',' || *cnt == 255)
3404 {
3405 if (c != ',')
3406 comma--;
3407 cnt = p++;
3408 *cnt = 0;
3409 }
3410 else
3411 {
3412 *p++ = unhide_meta(c);
3413 (*cnt)++;
3414 }
3415 }
3416
3417 new->len = p - new->txt;
3418
Simon Kelley849a8352006-06-09 21:02:31 +01003419 break;
3420 }
3421
Simon Kelleyf2621c72007-04-29 19:47:21 +01003422 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003423 {
3424 int port = 1, priority = 0, weight = 0;
3425 char *name, *target = NULL;
3426 struct mx_srv_record *new;
3427
Simon Kelleyf2621c72007-04-29 19:47:21 +01003428 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003429
Simon Kelley1f15b812009-10-13 17:49:32 +01003430 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003431 ret_err(_("bad SRV record"));
3432
Simon Kelley849a8352006-06-09 21:02:31 +01003433 if (comma)
3434 {
3435 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003436 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003437 if (!(target = canonicalise_opt(arg)))
3438 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00003439
Simon Kelley849a8352006-06-09 21:02:31 +01003440 if (comma)
3441 {
3442 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003443 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003444 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003445 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00003446
Simon Kelley849a8352006-06-09 21:02:31 +01003447 if (comma)
3448 {
3449 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003450 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003451 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003452 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00003453
Simon Kelley849a8352006-06-09 21:02:31 +01003454 if (comma)
3455 {
3456 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003457 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003458 if (!atoi_check16(arg, &weight))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003459 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01003460 }
3461 }
3462 }
3463 }
3464
Simon Kelley824af852008-02-12 20:43:05 +00003465 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003466 new->next = daemon->mxnames;
3467 daemon->mxnames = new;
3468 new->issrv = 1;
3469 new->name = name;
3470 new->target = target;
3471 new->srvport = port;
3472 new->priority = priority;
3473 new->weight = weight;
3474 break;
3475 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003476
Simon Kelleye759d422012-03-16 13:18:57 +00003477 case LOPT_HOST_REC: /* --host-record */
3478 {
3479 struct host_record *new = opt_malloc(sizeof(struct host_record));
3480 memset(new, 0, sizeof(struct host_record));
3481
3482 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003483 ret_err(_("Bad host-record"));
3484
3485 while (arg)
3486 {
3487 struct all_addr addr;
3488 if (inet_pton(AF_INET, arg, &addr))
3489 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00003490#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003491 else if (inet_pton(AF_INET6, arg, &addr))
3492 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00003493#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003494 else
3495 {
3496 int nomem;
3497 char *canon = canonicalise(arg, &nomem);
3498 struct name_list *nl = opt_malloc(sizeof(struct name_list));
3499 if (!canon)
3500 ret_err(_("Bad name in host-record"));
3501
3502 nl->name = canon;
3503 /* keep order, so that PTR record goes to first name */
3504 nl->next = NULL;
3505 if (!new->names)
3506 new->names = nl;
3507 else
3508 {
3509 struct name_list *tmp;
3510 for (tmp = new->names; tmp->next; tmp = tmp->next);
3511 tmp->next = nl;
3512 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003513 }
Simon Kelleye4807d82012-09-27 21:52:26 +01003514
3515 arg = comma;
3516 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003517 }
Simon Kelleye759d422012-03-16 13:18:57 +00003518
3519 /* Keep list order */
3520 if (!daemon->host_records_tail)
3521 daemon->host_records = new;
3522 else
3523 daemon->host_records_tail->next = new;
3524 new->next = NULL;
3525 daemon->host_records_tail = new;
3526 break;
3527 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003528
Simon Kelley7622fc02009-06-04 20:32:05 +01003529 default:
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003530 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
3531
Simon Kelley849a8352006-06-09 21:02:31 +01003532 }
Simon Kelley824af852008-02-12 20:43:05 +00003533
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003534 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01003535}
3536
Simon Kelley28866e92011-02-14 20:19:14 +00003537static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01003538{
Simon Kelley824af852008-02-12 20:43:05 +00003539 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003540 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01003541
3542 while (fgets(buff, MAXDNAME, f))
3543 {
Simon Kelley611ebc52012-07-16 16:23:46 +01003544 int white, i, option = hard_opt;
3545 char *errmess, *p, *arg = NULL, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003546 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00003547
Simon Kelley824af852008-02-12 20:43:05 +00003548 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley611ebc52012-07-16 16:23:46 +01003549 if (option != 0)
Simon Kelley824af852008-02-12 20:43:05 +00003550 {
3551 if (setjmp(mem_jmp))
3552 continue;
3553 mem_recover = 1;
3554 }
3555
Simon Kelley849a8352006-06-09 21:02:31 +01003556 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00003557 errmess = NULL;
3558
Simon Kelley849a8352006-06-09 21:02:31 +01003559 /* Implement quotes, inside quotes we allow \\ \" \n and \t
3560 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003561 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01003562 {
3563 if (*p == '"')
3564 {
3565 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003566
Simon Kelley849a8352006-06-09 21:02:31 +01003567 for(; *p && *p != '"'; p++)
3568 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003569 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01003570 {
3571 if (p[1] == 't')
3572 p[1] = '\t';
3573 else if (p[1] == 'n')
3574 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01003575 else if (p[1] == 'b')
3576 p[1] = '\b';
3577 else if (p[1] == 'r')
3578 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00003579 else if (p[1] == 'e') /* escape */
3580 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01003581 memmove(p, p+1, strlen(p+1)+1);
3582 }
3583 *p = hide_meta(*p);
3584 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003585
3586 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003587 {
3588 errmess = _("missing \"");
3589 goto oops;
3590 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003591
3592 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01003593 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003594
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003595 if (isspace(*p))
3596 {
3597 *p = ' ';
3598 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01003599 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003600 else
3601 {
3602 if (white && *p == '#')
3603 {
3604 *p = 0;
3605 break;
3606 }
3607 white = 0;
3608 }
Simon Kelley849a8352006-06-09 21:02:31 +01003609 }
3610
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003611
3612 /* strip leading spaces */
3613 for (start = buff; *start && *start == ' '; start++);
3614
3615 /* strip trailing spaces */
3616 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
3617
3618 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01003619 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003620 else
3621 start[len] = 0;
3622
Simon Kelley611ebc52012-07-16 16:23:46 +01003623 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003624 arg = start;
3625 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01003626 {
3627 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003628 for (arg = p+1; *arg == ' '; arg++);
3629 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01003630 *p = 0;
3631 }
3632 else
3633 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003634
Simon Kelley611ebc52012-07-16 16:23:46 +01003635 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01003636 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003637 for (option = 0, i = 0; opts[i].name; i++)
3638 if (strcmp(opts[i].name, start) == 0)
3639 {
3640 option = opts[i].val;
3641 break;
3642 }
3643
3644 if (!option)
3645 errmess = _("bad option");
3646 else if (opts[i].has_arg == 0 && arg)
3647 errmess = _("extraneous parameter");
3648 else if (opts[i].has_arg == 1 && !arg)
3649 errmess = _("missing parameter");
3650 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003651
3652 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00003653 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003654 strcpy(daemon->namebuff, errmess);
3655
3656 if (errmess || !one_opt(option, arg, buff, _("error"), 0))
Simon Kelleyf2621c72007-04-29 19:47:21 +01003657 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003658 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00003659 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003660 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01003661 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003662 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003663 }
Simon Kelley849a8352006-06-09 21:02:31 +01003664 }
3665
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003666 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003667 fclose(f);
3668}
3669
Simon Kelley395eb712012-07-06 22:07:05 +01003670static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00003671{
3672 FILE *f;
3673 int nofile_ok = 0;
3674 static int read_stdin = 0;
3675 static struct fileread {
3676 dev_t dev;
3677 ino_t ino;
3678 struct fileread *next;
3679 } *filesread = NULL;
3680
3681 if (hard_opt == '7')
3682 {
3683 /* default conf-file reading */
3684 hard_opt = 0;
3685 nofile_ok = 1;
3686 }
3687
3688 if (hard_opt == 0 && strcmp(file, "-") == 0)
3689 {
3690 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01003691 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003692 read_stdin = 1;
3693 file = "stdin";
3694 f = stdin;
3695 }
3696 else
3697 {
3698 /* ignore repeated files. */
3699 struct stat statbuf;
3700
3701 if (hard_opt == 0 && stat(file, &statbuf) == 0)
3702 {
3703 struct fileread *r;
3704
3705 for (r = filesread; r; r = r->next)
3706 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01003707 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003708
3709 r = safe_malloc(sizeof(struct fileread));
3710 r->next = filesread;
3711 filesread = r;
3712 r->dev = statbuf.st_dev;
3713 r->ino = statbuf.st_ino;
3714 }
3715
3716 if (!(f = fopen(file, "r")))
3717 {
3718 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01003719 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00003720 else
3721 {
3722 char *str = _("cannot read %s: %s");
3723 if (hard_opt != 0)
3724 {
3725 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01003726 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00003727 }
3728 else
3729 die(str, file, EC_FILE);
3730 }
3731 }
3732 }
3733
3734 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01003735 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003736}
3737
3738/* expand any name which is a directory */
3739struct hostsfile *expand_filelist(struct hostsfile *list)
3740{
3741 int i;
3742 struct hostsfile *ah;
3743
3744 for (i = 0, ah = list; ah; ah = ah->next)
3745 {
3746 if (i <= ah->index)
3747 i = ah->index + 1;
3748
3749 if (ah->flags & AH_DIR)
3750 ah->flags |= AH_INACTIVE;
3751 else
3752 ah->flags &= ~AH_INACTIVE;
3753 }
3754
3755 for (ah = list; ah; ah = ah->next)
3756 if (!(ah->flags & AH_INACTIVE))
3757 {
3758 struct stat buf;
3759 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
3760 {
3761 DIR *dir_stream;
3762 struct dirent *ent;
3763
3764 /* don't read this as a file */
3765 ah->flags |= AH_INACTIVE;
3766
3767 if (!(dir_stream = opendir(ah->fname)))
3768 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
3769 ah->fname, strerror(errno));
3770 else
3771 {
3772 while ((ent = readdir(dir_stream)))
3773 {
3774 size_t lendir = strlen(ah->fname);
3775 size_t lenfile = strlen(ent->d_name);
3776 struct hostsfile *ah1;
3777 char *path;
3778
3779 /* ignore emacs backups and dotfiles */
3780 if (lenfile == 0 ||
3781 ent->d_name[lenfile - 1] == '~' ||
3782 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
3783 ent->d_name[0] == '.')
3784 continue;
3785
3786 /* see if we have an existing record.
3787 dir is ah->fname
3788 file is ent->d_name
3789 path to match is ah1->fname */
3790
3791 for (ah1 = list; ah1; ah1 = ah1->next)
3792 {
3793 if (lendir < strlen(ah1->fname) &&
3794 strstr(ah1->fname, ah->fname) == ah1->fname &&
3795 ah1->fname[lendir] == '/' &&
3796 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
3797 {
3798 ah1->flags &= ~AH_INACTIVE;
3799 break;
3800 }
3801 }
3802
3803 /* make new record */
3804 if (!ah1)
3805 {
3806 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
3807 continue;
3808
3809 if (!(path = whine_malloc(lendir + lenfile + 2)))
3810 {
3811 free(ah1);
3812 continue;
3813 }
3814
3815 strcpy(path, ah->fname);
3816 strcat(path, "/");
3817 strcat(path, ent->d_name);
3818 ah1->fname = path;
3819 ah1->index = i++;
3820 ah1->flags = AH_DIR;
3821 ah1->next = list;
3822 list = ah1;
3823 }
3824
3825 /* inactivate record if not regular file */
3826 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
3827 ah1->flags |= AH_INACTIVE;
3828
3829 }
3830 closedir(dir_stream);
3831 }
3832 }
3833 }
3834
3835 return list;
3836}
3837
3838
Simon Kelley7622fc02009-06-04 20:32:05 +01003839#ifdef HAVE_DHCP
Simon Kelley824af852008-02-12 20:43:05 +00003840void reread_dhcp(void)
3841{
Simon Kelley28866e92011-02-14 20:19:14 +00003842 struct hostsfile *hf;
3843
Simon Kelley824af852008-02-12 20:43:05 +00003844 if (daemon->dhcp_hosts_file)
3845 {
3846 struct dhcp_config *configs, *cp, **up;
Simon Kelley28866e92011-02-14 20:19:14 +00003847
Simon Kelley824af852008-02-12 20:43:05 +00003848 /* remove existing... */
3849 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
3850 {
3851 cp = configs->next;
3852
3853 if (configs->flags & CONFIG_BANK)
3854 {
Simon Kelley9009d742008-11-14 20:04:27 +00003855 struct hwaddr_config *mac, *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003856 struct dhcp_netid_list *list, *tmplist;
Simon Kelley9009d742008-11-14 20:04:27 +00003857
3858 for (mac = configs->hwaddr; mac; mac = tmp)
3859 {
3860 tmp = mac->next;
3861 free(mac);
3862 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003863
Simon Kelley824af852008-02-12 20:43:05 +00003864 if (configs->flags & CONFIG_CLID)
3865 free(configs->clid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003866
3867 for (list = configs->netid; list; list = tmplist)
3868 {
3869 free(list->list);
3870 tmplist = list->next;
3871 free(list);
3872 }
3873
Simon Kelley824af852008-02-12 20:43:05 +00003874 if (configs->flags & CONFIG_NAME)
3875 free(configs->hostname);
3876
3877 *up = configs->next;
3878 free(configs);
3879 }
3880 else
3881 up = &configs->next;
3882 }
3883
Simon Kelley28866e92011-02-14 20:19:14 +00003884 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
3885 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
3886 if (!(hf->flags & AH_INACTIVE))
3887 {
Simon Kelley395eb712012-07-06 22:07:05 +01003888 if (one_file(hf->fname, LOPT_BANK))
3889 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00003890 }
Simon Kelley824af852008-02-12 20:43:05 +00003891 }
3892
3893 if (daemon->dhcp_opts_file)
3894 {
3895 struct dhcp_opt *opts, *cp, **up;
3896 struct dhcp_netid *id, *next;
3897
3898 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
3899 {
3900 cp = opts->next;
3901
3902 if (opts->flags & DHOPT_BANK)
3903 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003904 if ((opts->flags & DHOPT_VENDOR))
3905 free(opts->u.vendor_class);
Simon Kelley824af852008-02-12 20:43:05 +00003906 free(opts->val);
3907 for (id = opts->netid; id; id = next)
3908 {
3909 next = id->next;
3910 free(id->net);
3911 free(id);
3912 }
3913 *up = opts->next;
3914 free(opts);
3915 }
3916 else
3917 up = &opts->next;
3918 }
3919
Simon Kelley28866e92011-02-14 20:19:14 +00003920 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
3921 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
3922 if (!(hf->flags & AH_INACTIVE))
3923 {
Simon Kelley395eb712012-07-06 22:07:05 +01003924 if (one_file(hf->fname, LOPT_OPTS))
3925 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00003926 }
Simon Kelley824af852008-02-12 20:43:05 +00003927 }
3928}
Simon Kelley7622fc02009-06-04 20:32:05 +01003929#endif
Simon Kelley824af852008-02-12 20:43:05 +00003930
Simon Kelley5aabfc72007-08-29 11:24:47 +01003931void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003932{
Simon Kelley824af852008-02-12 20:43:05 +00003933 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00003934 int option, conffile_opt = '7', testmode = 0;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003935 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01003936
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003937 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01003938
Simon Kelley824af852008-02-12 20:43:05 +00003939 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01003940 memset(daemon, 0, sizeof(struct daemon));
3941 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003942
Simon Kelley3be34542004-09-11 19:12:13 +01003943 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01003944 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01003945 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01003946 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01003947 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
3948 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01003949 daemon->default_resolv.is_default = 1;
3950 daemon->default_resolv.name = RESOLVFILE;
3951 daemon->resolv_files = &daemon->default_resolv;
3952 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01003953 daemon->runfile = RUNFILE;
3954 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00003955 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01003956 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01003957 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00003958 daemon->auth_ttl = AUTH_TTL;
3959 daemon->soa_refresh = SOA_REFRESH;
3960 daemon->soa_retry = SOA_RETRY;
3961 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley5aabfc72007-08-29 11:24:47 +01003962 add_txt("version.bind", "dnsmasq-" VERSION );
3963 add_txt("authors.bind", "Simon Kelley");
3964 add_txt("copyright.bind", COPYRIGHT);
Simon Kelley0a852542005-03-23 20:28:59 +00003965
Simon Kelley849a8352006-06-09 21:02:31 +01003966 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003967 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003968#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01003969 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003970#else
Simon Kelley849a8352006-06-09 21:02:31 +01003971 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003972#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003973
3974 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00003975 {
Simon Kelley572b41e2011-02-18 18:11:18 +00003976 for (; optind < argc; optind++)
3977 {
3978 unsigned char *c = (unsigned char *)argv[optind];
3979 for (; *c != 0; c++)
3980 if (!isspace(*c))
3981 die(_("junk found in command line"), NULL, EC_BADCONF);
3982 }
Simon Kelley28866e92011-02-14 20:19:14 +00003983 break;
3984 }
3985
Simon Kelley849a8352006-06-09 21:02:31 +01003986 /* Copy optarg so that argv doesn't get changed */
3987 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003988 {
Simon Kelley849a8352006-06-09 21:02:31 +01003989 strncpy(buff, optarg, MAXDNAME);
3990 buff[MAXDNAME-1] = 0;
3991 arg = buff;
3992 }
3993 else
3994 arg = NULL;
3995
3996 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01003997 if (option == LOPT_TEST)
3998 testmode = 1;
3999 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004000 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004001#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004002 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004003 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004004#ifdef HAVE_DHCP6
4005 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4006 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004007#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004008 else
4009#endif
4010 do_usage();
4011
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004012 exit(0);
4013 }
Simon Kelley849a8352006-06-09 21:02:31 +01004014 else if (option == 'v')
4015 {
4016 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004017 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004018 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4019 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004020 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004021 exit(0);
4022 }
Simon Kelley849a8352006-06-09 21:02:31 +01004023 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004024 {
Simon Kelley28866e92011-02-14 20:19:14 +00004025 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004026 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004027 }
Simon Kelley849a8352006-06-09 21:02:31 +01004028 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004029 {
Simon Kelley26128d22004-11-14 16:43:54 +00004030#ifdef HAVE_GETOPT_LONG
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004031 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
Simon Kelley849a8352006-06-09 21:02:31 +01004032#else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004033 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
Simon Kelley849a8352006-06-09 21:02:31 +01004034#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004035 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004036 }
4037 }
Simon Kelley849a8352006-06-09 21:02:31 +01004038
4039 if (conffile)
Simon Kelley28866e92011-02-14 20:19:14 +00004040 one_file(conffile, conffile_opt);
Simon Kelley849a8352006-06-09 21:02:31 +01004041
Simon Kelley1a6bca82008-07-11 11:11:42 +01004042 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004043 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004044 {
4045 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004046 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004047 if (!(tmp->flags & SERV_HAS_SOURCE))
4048 {
4049 if (tmp->source_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004050 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004051#ifdef HAVE_IPV6
4052 else if (tmp->source_addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004053 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004054#endif
4055 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004056 }
4057
Simon Kelley3be34542004-09-11 19:12:13 +01004058 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004059 {
4060 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004061 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004062 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004063 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004064#ifdef HAVE_IPV6
4065 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004066 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004067#endif /* IPv6 */
4068 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004069
4070 /* create default, if not specified */
4071 if (daemon->authserver && !daemon->hostmaster)
4072 {
4073 strcpy(buff, "hostmaster.");
4074 strcat(buff, daemon->authserver);
4075 daemon->hostmaster = opt_string_alloc(buff);
4076 }
4077
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004078 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004079 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004080 {
Simon Kelley0a852542005-03-23 20:28:59 +00004081 struct mx_srv_record *mx;
4082
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004083 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004084 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004085
Simon Kelley0a852542005-03-23 20:28:59 +00004086 for (mx = daemon->mxnames; mx; mx = mx->next)
4087 if (!mx->issrv && hostname_isequal(mx->name, buff))
4088 break;
4089
Simon Kelley28866e92011-02-14 20:19:14 +00004090 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004091 {
Simon Kelley824af852008-02-12 20:43:05 +00004092 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004093 mx->next = daemon->mxnames;
4094 mx->issrv = 0;
4095 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004096 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004097 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004098 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004099
Simon Kelley3be34542004-09-11 19:12:13 +01004100 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004101 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004102
4103 for (mx = daemon->mxnames; mx; mx = mx->next)
4104 if (!mx->issrv && !mx->target)
4105 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004106 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004107
Simon Kelley28866e92011-02-14 20:19:14 +00004108 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004109 daemon->resolv_files &&
4110 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004111 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004112 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004113
Simon Kelley28866e92011-02-14 20:19:14 +00004114 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004115 {
4116 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004117 FILE *f;
4118
Simon Kelley28866e92011-02-14 20:19:14 +00004119 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004120 !daemon->resolv_files ||
4121 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004122 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004123
Simon Kelley3be34542004-09-11 19:12:13 +01004124 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004125 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004126
4127 while ((line = fgets(buff, MAXDNAME, f)))
4128 {
4129 char *token = strtok(line, " \t\n\r");
4130
4131 if (!token || strcmp(token, "search") != 0)
4132 continue;
4133
4134 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004135 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004136 break;
4137 }
Simon Kelley3be34542004-09-11 19:12:13 +01004138
Simon Kelleyde379512004-06-22 20:23:33 +01004139 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004140
Simon Kelley3be34542004-09-11 19:12:13 +01004141 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004142 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004143 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004144
4145 if (daemon->domain_suffix)
4146 {
4147 /* add domain for any srv record without one. */
4148 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004149
Simon Kelley3d8df262005-08-29 12:19:27 +01004150 for (srv = daemon->mxnames; srv; srv = srv->next)
4151 if (srv->issrv &&
4152 strchr(srv->name, '.') &&
4153 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4154 {
4155 strcpy(buff, srv->name);
4156 strcat(buff, ".");
4157 strcat(buff, daemon->domain_suffix);
4158 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004159 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004160 }
4161 }
Simon Kelley28866e92011-02-14 20:19:14 +00004162 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004163 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004164
4165 if (testmode)
4166 {
4167 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
4168 exit(0);
4169 }
Simon Kelley849a8352006-06-09 21:02:31 +01004170}