blob: d358d998db1847a5fe0609f49b46bcd4f9051eb3 [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 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 Kelleye98bd522014-03-28 20:41:23 +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
75#define LOPT_FORCE 264
76#define LOPT_NOBLOCK 265
77#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
83#define LOPT_BANK 272
84#define LOPT_DHCP_HOST 273
85#define LOPT_APREF 274
86#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
95#define LOPT_ALTPORT 284
96#define LOPT_SCRIPTUSR 285
97#define LOPT_LOCAL 286
98#define LOPT_NAPTR 287
99#define LOPT_MINPORT 288
100#define LOPT_DHCP_FQDN 289
101#define LOPT_CNAME 290
102#define LOPT_PXE_PROMT 291
103#define LOPT_PXE_SERV 292
104#define LOPT_TEST 293
105#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
111#define LOPT_ADD_MAC 300
112#define LOPT_DNSSEC 301
113#define LOPT_INCR_ADDR 302
114#define LOPT_CONNTRACK 303
115#define LOPT_FQDN 304
116#define LOPT_LUASCRIPT 305
117#define LOPT_RA 306
118#define LOPT_DUID 307
119#define LOPT_HOST_REC 308
120#define LOPT_TFTP_LC 309
121#define LOPT_RR 310
122#define LOPT_CLVERBIND 311
123#define LOPT_MAXCTTL 312
124#define LOPT_AUTHZONE 313
125#define LOPT_AUTHSERV 314
126#define LOPT_AUTHTTL 315
127#define LOPT_AUTHSOA 316
128#define LOPT_AUTHSFS 317
129#define LOPT_AUTHPEER 318
130#define LOPT_IPSET 319
131#define LOPT_SYNTH 320
Simon Kelleyc6309242013-03-07 20:59:28 +0000132#ifdef OPTION6_PREFIX_CLASS
Simon Kelleye98bd522014-03-28 20:41:23 +0000133#define LOPT_PREF_CLSS 321
Simon Kelleyc6309242013-03-07 20:59:28 +0000134#endif
Simon Kelleye98bd522014-03-28 20:41:23 +0000135#define LOPT_RELAY 323
136#define LOPT_RA_PARAM 324
137#define LOPT_ADD_SBNET 325
138#define LOPT_QUIET_DHCP 326
139#define LOPT_QUIET_DHCP6 327
140#define LOPT_QUIET_RA 328
141#define LOPT_SEC_VALID 329
142#define LOPT_TRUST_ANCHOR 330
143#define LOPT_DNSSEC_DEBUG 331
144#define LOPT_REV_SERV 332
145#define LOPT_SERVERS_FILE 333
146#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000147#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000148#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100149#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000150#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000151#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000152#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000153#define LOPT_DHOPT_INOTIFY 341
154#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000155#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100156#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000157#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000158#define LOPT_CPE_ID 346
159#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000160#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000161#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100162#define LOPT_REPLY_DELAY 350
Simon Kelleybec366b2016-02-24 22:03:26 +0000163
Simon Kelley849a8352006-06-09 21:02:31 +0100164#ifdef HAVE_GETOPT_LONG
165static const struct option opts[] =
166#else
167static const struct myoption opts[] =
168#endif
169 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100170 { "version", 0, 0, 'v' },
171 { "no-hosts", 0, 0, 'h' },
172 { "no-poll", 0, 0, 'n' },
173 { "help", 0, 0, 'w' },
174 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000175 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100176 { "user", 2, 0, 'u' },
177 { "group", 2, 0, 'g' },
178 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000179 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100180 { "mx-host", 1, 0, 'm' },
181 { "mx-target", 1, 0, 't' },
182 { "cache-size", 2, 0, 'c' },
183 { "port", 1, 0, 'p' },
184 { "dhcp-leasefile", 2, 0, 'l' },
185 { "dhcp-lease", 1, 0, 'l' },
186 { "dhcp-host", 1, 0, 'G' },
187 { "dhcp-range", 1, 0, 'F' },
188 { "dhcp-option", 1, 0, 'O' },
189 { "dhcp-boot", 1, 0, 'M' },
190 { "domain", 1, 0, 's' },
191 { "domain-suffix", 1, 0, 's' },
192 { "interface", 1, 0, 'i' },
193 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000194 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100195 { "bogus-priv", 0, 0, 'b' },
196 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000197 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100198 { "selfmx", 0, 0, 'e' },
199 { "filterwin2k", 0, 0, 'f' },
200 { "pid-file", 2, 0, 'x' },
201 { "strict-order", 0, 0, 'o' },
202 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000203 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100204 { "local", 1, 0, LOPT_LOCAL },
205 { "address", 1, 0, 'A' },
206 { "conf-file", 2, 0, 'C' },
207 { "no-resolv", 0, 0, 'R' },
208 { "expand-hosts", 0, 0, 'E' },
209 { "localmx", 0, 0, 'L' },
210 { "local-ttl", 1, 0, 'T' },
211 { "no-negcache", 0, 0, 'N' },
212 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000213 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 { "query-port", 1, 0, 'Q' },
215 { "except-interface", 1, 0, 'I' },
216 { "no-dhcp-interface", 1, 0, '2' },
217 { "domain-needed", 0, 0, 'D' },
218 { "dhcp-lease-max", 1, 0, 'X' },
219 { "bind-interfaces", 0, 0, 'z' },
220 { "read-ethers", 0, 0, 'Z' },
221 { "alias", 1, 0, 'V' },
222 { "dhcp-vendorclass", 1, 0, 'U' },
223 { "dhcp-userclass", 1, 0, 'j' },
224 { "dhcp-ignore", 1, 0, 'J' },
225 { "edns-packet-max", 1, 0, 'P' },
226 { "keep-in-foreground", 0, 0, 'k' },
227 { "dhcp-authoritative", 0, 0, 'K' },
228 { "srv-host", 1, 0, 'W' },
229 { "localise-queries", 0, 0, 'y' },
230 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100231 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100232 { "enable-dbus", 2, 0, '1' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100233 { "bootp-dynamic", 2, 0, '3' },
234 { "dhcp-mac", 1, 0, '4' },
235 { "no-ping", 0, 0, '5' },
236 { "dhcp-script", 1, 0, '6' },
237 { "conf-dir", 1, 0, '7' },
238 { "log-facility", 1, 0 ,'8' },
239 { "leasefile-ro", 0, 0, '9' },
240 { "dns-forward-max", 1, 0, '0' },
241 { "clear-on-reload", 0, 0, LOPT_RELOAD },
242 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100243 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100244 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100245 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100246 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100247 { "tftp-root", 1, 0, LOPT_PREFIX },
248 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000249 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100250 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100251 { "ptr-record", 1, 0, LOPT_PTR },
252 { "naptr-record", 1, 0, LOPT_NAPTR },
253 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
254 { "dhcp-option-force", 1, 0, LOPT_FORCE },
255 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
256 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
257 { "log-async", 2, 0, LOPT_MAX_LOGS },
258 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
259 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
260 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
261 { "interface-name", 1, 0, LOPT_INTNAME },
262 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
263 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000264 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000265 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100266 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
267 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
268 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100269 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100270 { "all-servers", 0, 0, LOPT_NOLAST },
271 { "dhcp-match", 1, 0, LOPT_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100272 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100273 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100274 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000275 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100276 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100277 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
278 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
279 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000280 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100281 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
282 { "cname", 1, 0, LOPT_CNAME },
283 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
284 { "pxe-service", 1, 0, LOPT_PXE_SERV },
285 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100286 { "tag-if", 1, 0, LOPT_TAG_IF },
287 { "dhcp-proxy", 2, 0, LOPT_PROXY },
288 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
289 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000290 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100291 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000292 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000293 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100294 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
295 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000296 { "dhcp-client-update", 0, 0, LOPT_FQDN },
297 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000298 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000299 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000300 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100301 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000302 { "auth-zone", 1, 0, LOPT_AUTHZONE },
303 { "auth-server", 1, 0, LOPT_AUTHSERV },
304 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
305 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000306 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000307 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000308 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100309 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200310 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000311 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000312 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelley00a5b5d2014-02-28 18:10:55 +0000313 { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000314 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000315 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyc6309242013-03-07 20:59:28 +0000316#ifdef OPTION6_PREFIX_CLASS
317 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
318#endif
Simon Kelleyff7eea22013-09-04 18:01:38 +0100319 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100320 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100321 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
322 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
323 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100324 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000325 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000326 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100327 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley849a8352006-06-09 21:02:31 +0100328 { NULL, 0, 0, 0 }
329 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000330
Simon Kelley28866e92011-02-14 20:19:14 +0000331
332#define ARG_DUP OPT_LAST
333#define ARG_ONE OPT_LAST + 1
334#define ARG_USED_CL OPT_LAST + 2
335#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000336
Simon Kelley1a6bca82008-07-11 11:11:42 +0100337static struct {
338 int opt;
339 unsigned int rept;
340 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000341 char * const desc;
342 char * const arg;
343} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000344 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
345 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100346 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000347 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
348 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
349 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100350 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
351 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
352 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
353 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
354 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000355 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
356 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100357 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000358 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
359 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000360 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000361 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100362 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100363 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000364 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
Simon Kelley70d18732015-01-31 19:59:29 +0000365 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000366 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
367 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100368 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
369 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
370 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
371 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
372 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
373 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100374 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
375 { '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 +0000376 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100379 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
380 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
381 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
382 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
383 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
384 { 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 +0000385 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
386 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000387 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000388 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100389 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000390 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000391 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000392 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000393 { LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000394 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000395 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000396 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
397 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
398 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
399 { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
RinSatsuki28de3872015-01-10 15:22:21 +0000400 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
401 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000402 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100403 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100404 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000405 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
406 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100407 { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp or --help dhcp6 for known DHCP options."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000408 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
409 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { '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 +0000411 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
412 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
413 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100414 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
415 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100416 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000417 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100418 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
419 { '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 +0000420 { 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 +0100421 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000422 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
423 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
424 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000425 { LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000426 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000427 { '8', ARG_ONE, "<facility>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100428 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000429 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100430 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100431 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100432 { 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 +0100433 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100434 { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
Floris Bos60704f52017-04-09 22:22:49 +0100435 { LOPT_APREF, ARG_DUP, "[=ip|mac]", gettext_noop("Add client IP or hardware address to tftp-root."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100436 { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100437 { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000438 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000439 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100440 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100441 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100442 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
443 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000444 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100445 { 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 +0100446 { 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 +0000447 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100448 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100449 { 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 +0100450 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
452 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000453 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000454 { 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 +0000455 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
456 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100457 { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000458 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100459 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
460 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
461 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000462 { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
Ed Bardsleya7369be2015-08-05 21:17:18 +0100463 { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000464 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100465 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100466 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
467 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000468 { 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 +0000469 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000470 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000471 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100472 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000473 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000474 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000475 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000476 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000477 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000478 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
479 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100480 { LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
Gildasa9bf81a2013-10-24 13:31:40 +0100481 { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
Simon Kelley3a237152013-12-12 12:15:50 +0000482 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000483 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000484 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelley00a5b5d2014-02-28 18:10:55 +0000485 { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000486 { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000487 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000488#ifdef OPTION6_PREFIX_CLASS
489 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
490#endif
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100491 { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100492 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
493 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
494 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000495 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
496 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000497 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000498 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100499 { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100500 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000501};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000502
Josh Soref730c6742017-02-06 16:14:04 +0000503/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100504 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 +0100505 following sequence so that they map to themselves: it is therefore possible to call
506 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000507 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100508 couple of other places.
509 Note that space is included here so that
510 --dhcp-option=3, string
511 has five characters, whilst
512 --dhcp-option=3," string"
513 has six.
514*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100515
Simon Kelleyf2621c72007-04-29 19:47:21 +0100516static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100517
518static char hide_meta(char c)
519{
520 unsigned int i;
521
522 for (i = 0; i < (sizeof(meta) - 1); i++)
523 if (c == meta[i])
524 return (char)i;
525
526 return c;
527}
528
529static char unhide_meta(char cr)
530{
531 unsigned int c = cr;
532
533 if (c < (sizeof(meta) - 1))
534 cr = meta[c];
535
536 return cr;
537}
538
539static void unhide_metas(char *cp)
540{
541 if (cp)
542 for(; *cp; cp++)
543 *cp = unhide_meta(*cp);
544}
545
Simon Kelley824af852008-02-12 20:43:05 +0000546static void *opt_malloc(size_t size)
547{
548 void *ret;
549
550 if (mem_recover)
551 {
552 ret = whine_malloc(size);
553 if (!ret)
554 longjmp(mem_jmp, 1);
555 }
556 else
557 ret = safe_malloc(size);
558
559 return ret;
560}
561
562static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100563{
564 char *ret = NULL;
565
566 if (cp && strlen(cp) != 0)
567 {
Simon Kelley824af852008-02-12 20:43:05 +0000568 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100569 strcpy(ret, cp);
570
571 /* restore hidden metachars */
572 unhide_metas(ret);
573 }
574
575 return ret;
576}
577
Simon Kelley3d8df262005-08-29 12:19:27 +0100578
Simon Kelleyf2621c72007-04-29 19:47:21 +0100579/* find next comma, split string with zero and eliminate spaces.
580 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000581
582static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100583{
584 char *comma, *p;
585
Simon Kelley73a08a22009-02-05 20:28:08 +0000586 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100587 return NULL;
588
589 p = comma;
590 *comma = ' ';
591
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100592 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100593
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100594 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100595 *p = 0;
596
597 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100598}
599
Simon Kelley73a08a22009-02-05 20:28:08 +0000600static char *split(char *s)
601{
602 return split_chr(s, ',');
603}
604
Simon Kelley1f15b812009-10-13 17:49:32 +0100605static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100606{
Simon Kelley1f15b812009-10-13 17:49:32 +0100607 char *ret;
608 int nomem;
609
Simon Kelley3d8df262005-08-29 12:19:27 +0100610 if (!s)
611 return 0;
612
613 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100614 if (!(ret = canonicalise(s, &nomem)) && nomem)
615 {
616 if (mem_recover)
617 longjmp(mem_jmp, 1);
618 else
619 die(_("could not get memory"), NULL, EC_NOMEM);
620 }
621
622 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100623}
624
625static int atoi_check(char *a, int *res)
626{
627 char *p;
628
629 if (!a)
630 return 0;
631
632 unhide_metas(a);
633
634 for (p = a; *p; p++)
635 if (*p < '0' || *p > '9')
636 return 0;
637
638 *res = atoi(a);
639 return 1;
640}
641
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100642static int atoi_check16(char *a, int *res)
643{
644 if (!(atoi_check(a, res)) ||
645 *res < 0 ||
646 *res > 0xffff)
647 return 0;
648
649 return 1;
650}
Simon Kelleyee415862014-02-11 11:07:22 +0000651
Simon Kelleyde73a492014-02-17 21:43:27 +0000652#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000653static int atoi_check8(char *a, int *res)
654{
655 if (!(atoi_check(a, res)) ||
656 *res < 0 ||
657 *res > 0xff)
658 return 0;
659
660 return 1;
661}
Simon Kelleyde73a492014-02-17 21:43:27 +0000662#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100663
664#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000665static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000666{
Simon Kelley824af852008-02-12 20:43:05 +0000667 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000668
669 if (txt)
670 {
671 size_t len = strlen(txt);
672 r->txt = opt_malloc(len+1);
673 r->len = len+1;
674 *(r->txt) = len;
675 memcpy((r->txt)+1, txt, len);
676 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100677
Simon Kelleyfec216d2014-03-27 20:54:34 +0000678 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000679 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000680 r->next = daemon->txt;
681 daemon->txt = r;
682 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000683}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100684#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000685
Simon Kelley849a8352006-06-09 21:02:31 +0100686static void do_usage(void)
687{
688 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000689 int i, j;
690
691 struct {
692 char handle;
693 int val;
694 } tab[] = {
695 { '$', CACHESIZ },
696 { '*', EDNS_PKTSZ },
697 { '&', MAXLEASES },
698 { '!', FTABSIZ },
699 { '#', TFTP_MAX_CONNECTIONS },
700 { '\0', 0 }
701 };
Simon Kelley849a8352006-06-09 21:02:31 +0100702
703 printf(_("Usage: dnsmasq [options]\n\n"));
704#ifndef HAVE_GETOPT_LONG
705 printf(_("Use short options only on the command line.\n"));
706#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100707 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100708
Simon Kelley1a6bca82008-07-11 11:11:42 +0100709 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100710 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100711 char *desc = usage[i].flagdesc;
712 char *eq = "=";
713
714 if (!desc || *desc == '[')
715 eq = "";
716
717 if (!desc)
718 desc = "";
719
720 for ( j = 0; opts[j].name; j++)
721 if (opts[j].val == usage[i].opt)
722 break;
723 if (usage[i].opt < 256)
724 sprintf(buff, "-%c, ", usage[i].opt);
725 else
726 sprintf(buff, " ");
727
728 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100729 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100730
Simon Kelley849a8352006-06-09 21:02:31 +0100731 if (usage[i].arg)
732 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000733 strcpy(buff, usage[i].arg);
734 for (j = 0; tab[j].handle; j++)
735 if (tab[j].handle == *(usage[i].arg))
736 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100737 }
Simon Kelley849a8352006-06-09 21:02:31 +0100738 printf(_(usage[i].desc), buff);
739 printf("\n");
740 }
741}
742
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100743#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
744
Ed Bardsleya7369be2015-08-05 21:17:18 +0100745static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
746{
747 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
748 addr->sa.sa_family = AF_INET;
749#ifdef HAVE_IPV6
750 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
751 addr->sa.sa_family = AF_INET6;
752#endif
753 else
754 return _("bad address");
755
756 return NULL;
757}
758
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100759char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
760{
761 int source_port = 0, serv_port = NAMESERVER_PORT;
762 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000763 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100764#ifdef HAVE_IPV6
765 int scope_index = 0;
766 char *scope_id;
767#endif
768
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000769 if (!arg || strlen(arg) == 0)
770 {
771 *flags |= SERV_NO_ADDR;
772 *interface = 0;
773 return NULL;
774 }
775
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100776 if ((source = split_chr(arg, '@')) && /* is there a source. */
777 (portno = split_chr(source, '#')) &&
778 !atoi_check16(portno, &source_port))
779 return _("bad port");
780
781 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
782 !atoi_check16(portno, &serv_port))
783 return _("bad port");
784
785#ifdef HAVE_IPV6
786 scope_id = split_chr(arg, '%');
787#endif
788
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000789 if (source) {
790 interface_opt = split_chr(source, '@');
791
792 if (interface_opt)
793 {
794#if defined(SO_BINDTODEVICE)
795 strncpy(interface, interface_opt, IF_NAMESIZE - 1);
796#else
797 return _("interface binding not supported");
798#endif
799 }
800 }
801
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100802 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100803 {
804 addr->in.sin_port = htons(serv_port);
805 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
806#ifdef HAVE_SOCKADDR_SA_LEN
807 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
808#endif
809 source_addr->in.sin_addr.s_addr = INADDR_ANY;
810 source_addr->in.sin_port = htons(daemon->query_port);
811
812 if (source)
813 {
814 if (flags)
815 *flags |= SERV_HAS_SOURCE;
816 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100817 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100818 {
819#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000820 if (interface_opt)
821 return _("interface can only be specified once");
822
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100823 source_addr->in.sin_addr.s_addr = INADDR_ANY;
824 strncpy(interface, source, IF_NAMESIZE - 1);
825#else
826 return _("interface binding not supported");
827#endif
828 }
829 }
830 }
831#ifdef HAVE_IPV6
832 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
833 {
834 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
835 return _("bad interface name");
836
837 addr->in6.sin6_port = htons(serv_port);
838 addr->in6.sin6_scope_id = scope_index;
839 source_addr->in6.sin6_addr = in6addr_any;
840 source_addr->in6.sin6_port = htons(daemon->query_port);
841 source_addr->in6.sin6_scope_id = 0;
842 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
843 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
844#ifdef HAVE_SOCKADDR_SA_LEN
845 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
846#endif
847 if (source)
848 {
849 if (flags)
850 *flags |= SERV_HAS_SOURCE;
851 source_addr->in6.sin6_port = htons(source_port);
852 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
853 {
854#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000855 if (interface_opt)
856 return _("interface can only be specified once");
857
858 source_addr->in6.sin6_addr = in6addr_any;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100859 strncpy(interface, source, IF_NAMESIZE - 1);
860#else
861 return _("interface binding not supported");
862#endif
863 }
864 }
865 }
866#endif
867 else
868 return _("bad address");
869
870 return NULL;
871}
872
Simon Kelleyde73a492014-02-17 21:43:27 +0000873static struct server *add_rev4(struct in_addr addr, int msize)
874{
875 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000876 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000877 char *p;
878
879 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000880 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
881
882 switch (msize)
883 {
884 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100885 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000886 /* fall through */
887 case 24:
888 p += sprintf(p, "%d.", (a >> 8) & 0xff);
889 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000890 case 16:
891 p += sprintf(p, "%d.", (a >> 16) & 0xff);
892 /* fall through */
893 case 8:
894 p += sprintf(p, "%d.", (a >> 24) & 0xff);
895 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000896 default:
897 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000898 }
899
900 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000901
902 serv->flags = SERV_HAS_DOMAIN;
903 serv->next = daemon->servers;
904 daemon->servers = serv;
905
906 return serv;
907
908}
909
910static struct server *add_rev6(struct in6_addr *addr, int msize)
911{
912 struct server *serv = opt_malloc(sizeof(struct server));
913 char *p;
914 int i;
915
916 memset(serv, 0, sizeof(struct server));
917 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
918
919 for (i = msize-1; i >= 0; i -= 4)
920 {
921 int dig = ((unsigned char *)addr)[i>>3];
922 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
923 }
924 p += sprintf(p, "ip6.arpa");
925
926 serv->flags = SERV_HAS_DOMAIN;
927 serv->next = daemon->servers;
928 daemon->servers = serv;
929
930 return serv;
931}
932
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000933#ifdef HAVE_DHCP
934
935static int is_tag_prefix(char *arg)
936{
937 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
938 return 1;
939
940 return 0;
941}
942
943static char *set_prefix(char *arg)
944{
945 if (strstr(arg, "set:") == arg)
946 return arg+4;
947
948 return arg;
949}
950
Simon Kelley832af0b2007-01-21 20:01:28 +0000951/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100952static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000953{
Simon Kelley824af852008-02-12 20:43:05 +0000954 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000955 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000956 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100957 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100958 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000959 u16 opt_len = 0;
960 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100961 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000962
963 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000964 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000965 new->netid = NULL;
966 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100967 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000968
Simon Kelleyf2621c72007-04-29 19:47:21 +0100969 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000970 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100971 comma = split(arg);
972
973 for (cp = arg; *cp; cp++)
974 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000975 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100976
977 if (!*cp)
978 {
979 new->opt = atoi(arg);
980 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100981 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100982 break;
983 }
984
985 if (strstr(arg, "option:") == arg)
986 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100987 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
988 {
989 opt_len = lookup_dhcp_len(AF_INET, new->opt);
990 /* option:<optname> must follow tag and vendor string. */
991 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
992 option_ok = 1;
993 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100994 break;
995 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000996#ifdef HAVE_DHCP6
997 else if (strstr(arg, "option6:") == arg)
998 {
999 for (cp = arg+8; *cp; cp++)
1000 if (*cp < '0' || *cp > '9')
1001 break;
1002
1003 if (!*cp)
1004 {
1005 new->opt = atoi(arg+8);
1006 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001007 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001008 }
1009 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001010 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001011 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1012 {
1013 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1014 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1015 option_ok = 1;
1016 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001017 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001018 /* option6:<opt>|<optname> must follow tag and vendor string. */
1019 is6 = 1;
1020 break;
1021 }
1022#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001023 else if (strstr(arg, "vendor:") == arg)
1024 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001025 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1026 new->flags |= DHOPT_VENDOR;
1027 }
1028 else if (strstr(arg, "encap:") == arg)
1029 {
1030 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001031 new->flags |= DHOPT_ENCAPSULATE;
1032 }
Simon Kelley316e2732010-01-22 20:16:09 +00001033 else if (strstr(arg, "vi-encap:") == arg)
1034 {
1035 new->u.encap = atoi(arg+9);
1036 new->flags |= DHOPT_RFC3925;
1037 if (flags == DHOPT_MATCH)
1038 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001039 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001040 break;
1041 }
1042 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001043 else
1044 {
Simon Kelley824af852008-02-12 20:43:05 +00001045 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001046 /* allow optional "net:" or "tag:" for consistency */
1047 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +00001048 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001049 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001050 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001051 new->netid->next = np;
1052 np = new->netid;
1053 }
1054
1055 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001056 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001057
1058#ifdef HAVE_DHCP6
1059 if (is6)
1060 {
1061 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001062 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001063
1064 if (opt_len == 0 &&
1065 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001066 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001067 }
1068 else
1069#endif
1070 if (opt_len == 0 &&
1071 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001072 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001073
Simon Kelley316e2732010-01-22 20:16:09 +00001074 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001075 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001076 ret_err(_("bad dhcp-option"));
1077
1078 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001079 {
1080 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001081 char c;
Simon Kelley28866e92011-02-14 20:19:14 +00001082 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001083 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001084 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001085 dots = 0;
1086 for (cp = comma; (c = *cp); cp++)
1087 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001088 {
1089 addrs++;
1090 is_dec = is_hex = 0;
1091 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001092 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001093 {
1094 digs++;
1095 is_dec = is_addr = 0;
1096 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001097 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001098 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001099 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001100 if (cp == comma) /* leading / means a pathname */
1101 is_addr = 0;
1102 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001103 else if (c == '.')
1104 {
Simon Kelley23245c02012-07-18 16:21:11 +01001105 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001106 dots++;
1107 }
1108 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001109 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001110 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001111 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001113 {
1114 is_addr = 0;
1115 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001116 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001117 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001118 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001119 *cp = 0;
1120 }
1121 else
1122 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001123 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001124 (c >='a' && c <= 'f') ||
1125 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001126 {
1127 is_hex = 0;
1128 if (c != '[' && c != ']')
1129 is_addr6 = 0;
1130 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001131 }
Simon Kelley28866e92011-02-14 20:19:14 +00001132 else
1133 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001134
Simon Kelley28866e92011-02-14 20:19:14 +00001135 if (!found_dig)
1136 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001137
Simon Kelleyf2621c72007-04-29 19:47:21 +01001138 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001139 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001140 {
1141 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001142
1143 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001144 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001145
1146 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001147 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001148 }
Simon Kelley28866e92011-02-14 20:19:14 +00001149 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001150 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1151 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001152
1153 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1154 {
1155 int val, fac = 1;
1156
1157 switch (comma[strlen(comma) - 1])
1158 {
Simon Kelley42243212012-07-20 15:19:18 +01001159 case 'w':
1160 case 'W':
1161 fac *= 7;
1162 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001163 case 'd':
1164 case 'D':
1165 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001166 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001167 case 'h':
1168 case 'H':
1169 fac *= 60;
1170 /* fall through */
1171 case 'm':
1172 case 'M':
1173 fac *= 60;
1174 /* fall through */
1175 case 's':
1176 case 'S':
1177 comma[strlen(comma) - 1] = 0;
1178 }
1179
1180 new->len = 4;
1181 new->val = opt_malloc(4);
1182 val = atoi(comma);
1183 *((int *)new->val) = htonl(val * fac);
1184 }
1185 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001186 {
1187 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001188 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001189 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1190 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001191 }
1192 else if (is_dec)
1193 {
1194 int i, val = atoi(comma);
1195 /* assume numeric arg is 1 byte except for
1196 options where it is known otherwise.
1197 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001198 if (opt_len != 0)
1199 new->len = opt_len;
1200 else if (val & 0xffff0000)
1201 new->len = 4;
1202 else if (val & 0xff00)
1203 new->len = 2;
1204 else
1205 new->len = 1;
1206
Simon Kelley832af0b2007-01-21 20:01:28 +00001207 if (lenchar == 'b')
1208 new->len = 1;
1209 else if (lenchar == 's')
1210 new->len = 2;
1211 else if (lenchar == 'i')
1212 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001213
Simon Kelley824af852008-02-12 20:43:05 +00001214 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001215 for (i=0; i<new->len; i++)
1216 new->val[i] = val>>((new->len - i - 1)*8);
1217 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001218 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001219 {
1220 struct in_addr in;
1221 unsigned char *op;
1222 char *slash;
1223 /* max length of address/subnet descriptor is five bytes,
1224 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001225 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001226 new->flags |= DHOPT_ADDR;
1227
Simon Kelley572b41e2011-02-18 18:11:18 +00001228 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1229 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001230 {
Simon Kelley6b010842007-02-12 20:32:07 +00001231 *(op++) = 1; /* RFC 3361 "enc byte" */
1232 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001233 }
1234 while (addrs--)
1235 {
1236 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001237 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001238 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001239 if (!inet_pton(AF_INET, cp, &in))
1240 ret_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001241 if (!slash)
1242 {
1243 memcpy(op, &in, INADDRSZ);
1244 op += INADDRSZ;
1245 }
1246 else
1247 {
1248 unsigned char *p = (unsigned char *)&in;
1249 int netsize = atoi(slash);
1250 *op++ = netsize;
1251 if (netsize > 0)
1252 *op++ = *p++;
1253 if (netsize > 8)
1254 *op++ = *p++;
1255 if (netsize > 16)
1256 *op++ = *p++;
1257 if (netsize > 24)
1258 *op++ = *p++;
1259 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1260 }
1261 }
1262 new->len = op - new->val;
1263 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001264 else if (is_addr6 && is6)
1265 {
1266 unsigned char *op;
1267 new->val = op = opt_malloc(16 * addrs);
1268 new->flags |= DHOPT_ADDR6;
1269 while (addrs--)
1270 {
1271 cp = comma;
1272 comma = split(cp);
1273
1274 /* check for [1234::7] */
1275 if (*cp == '[')
1276 cp++;
1277 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1278 cp[strlen(cp)-1] = 0;
1279
1280 if (inet_pton(AF_INET6, cp, op))
1281 {
1282 op += IN6ADDRSZ;
1283 continue;
1284 }
1285
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001286 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001287 }
1288 new->len = op - new->val;
1289 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001290 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001291 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001292 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001293 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001294 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001295 {
1296 /* dns search, RFC 3397, or SIP, RFC 3361 */
1297 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001298 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001299 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001300 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001301
1302 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001303 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001304
1305 while (arg && *arg)
1306 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001307 char *in, *dom = NULL;
1308 size_t domlen = 1;
1309 /* Allow "." as an empty domain */
1310 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001311 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001312 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001313 ret_err(_("bad domain in dhcp-option"));
1314
Simon Kelleyc52e1892010-06-07 22:01:39 +01001315 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001316 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001317
1318 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001319 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001320 {
1321 memcpy(newp, m, header_size + len);
1322 free(m);
1323 }
Simon Kelley824af852008-02-12 20:43:05 +00001324 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001325 p = m + header_size;
1326 q = p + len;
1327
1328 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001329 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001330 {
1331 unsigned char *cp = q++;
1332 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001333 for (j = 0; *in && (*in != '.'); in++, j++)
1334 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001335 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001336 if (*in)
1337 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001338 }
1339 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001340 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001341
Simon Kelley832af0b2007-01-21 20:01:28 +00001342 /* Now tail-compress using earlier names. */
1343 newlen = q - p;
1344 for (tail = p + len; *tail; tail += (*tail) + 1)
1345 for (r = p; r - p < (int)len; r += (*r) + 1)
1346 if (strcmp((char *)r, (char *)tail) == 0)
1347 {
1348 PUTSHORT((r - p) | 0xc000, tail);
1349 newlen = tail - p;
1350 goto end;
1351 }
1352 end:
1353 len = newlen;
1354
1355 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001356 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001357 }
1358
1359 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001360 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001361 m[0] = 0;
1362 new->len = (int) len + header_size;
1363 new->val = m;
1364 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001365#ifdef HAVE_DHCP6
1366 else if (comma && (opt_len & OT_CSTRING))
1367 {
1368 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001369 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001370 unsigned char *p, *newp;
1371
Simon Kelley40ef23b2012-03-13 21:59:28 +00001372 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001373 if (comma[i] == ',')
1374 commas++;
1375
1376 newp = opt_malloc(strlen(comma)+(2*commas));
1377 p = newp;
1378 arg = comma;
1379 comma = split(arg);
1380
1381 while (arg && *arg)
1382 {
1383 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001384 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001385 PUTSHORT(len, p);
1386 memcpy(p, arg, len);
1387 p += len;
1388
1389 arg = comma;
1390 comma = split(arg);
1391 }
1392
1393 new->val = newp;
1394 new->len = p - newp;
1395 }
1396 else if (comma && (opt_len & OT_RFC1035_NAME))
1397 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001398 unsigned char *p = NULL, *newp, *end;
1399 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001400 arg = comma;
1401 comma = split(arg);
1402
1403 while (arg && *arg)
1404 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001405 char *dom = canonicalise_opt(arg);
1406 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001407 ret_err(_("bad domain in dhcp-option"));
1408
Simon Kelley18f0fb02012-03-31 21:18:55 +01001409 newp = opt_malloc(len + strlen(dom) + 2);
1410
1411 if (p)
1412 {
1413 memcpy(newp, p, len);
1414 free(p);
1415 }
1416
1417 p = newp;
Simon Kelley0549c732017-09-25 18:17:11 +01001418 end = do_rfc1035_name(p + len, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001419 *end++ = 0;
1420 len = end - p;
1421 free(dom);
1422
Simon Kelley4cb1b322012-02-06 14:30:41 +00001423 arg = comma;
1424 comma = split(arg);
1425 }
1426
Simon Kelley18f0fb02012-03-31 21:18:55 +01001427 new->val = p;
1428 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001429 }
1430#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001431 else
1432 {
1433 new->len = strlen(comma);
1434 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001435 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001436 new->flags |= DHOPT_STRING;
1437 }
1438 }
1439 }
1440
Simon Kelley4cb1b322012-02-06 14:30:41 +00001441 if (!is6 &&
1442 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001443 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001444 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001445 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001446
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001447 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001448 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001449 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1450 !new->netid ||
1451 new->netid->next)
1452 ret_err(_("illegal dhcp-match"));
1453
1454 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001455 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001456 new->next = daemon->dhcp_match6;
1457 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001458 }
1459 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001460 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001461 new->next = daemon->dhcp_match;
1462 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001463 }
Simon Kelley824af852008-02-12 20:43:05 +00001464 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001465 else if (is6)
1466 {
1467 new->next = daemon->dhcp_opts6;
1468 daemon->dhcp_opts6 = new;
1469 }
1470 else
1471 {
1472 new->next = daemon->dhcp_opts;
1473 daemon->dhcp_opts = new;
1474 }
1475
1476 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001477}
1478
Simon Kelley7622fc02009-06-04 20:32:05 +01001479#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001480
Simon Kelley28866e92011-02-14 20:19:14 +00001481void set_option_bool(unsigned int opt)
1482{
1483 if (opt < 32)
1484 daemon->options |= 1u << opt;
1485 else
1486 daemon->options2 |= 1u << (opt - 32);
1487}
1488
Simon Kelley2b5bae92012-06-26 16:55:23 +01001489void reset_option_bool(unsigned int opt)
1490{
1491 if (opt < 32)
1492 daemon->options &= ~(1u << opt);
1493 else
1494 daemon->options2 &= ~(1u << (opt - 32));
1495}
1496
Simon Kelley7b1eae42014-02-20 13:43:28 +00001497static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
Simon Kelley849a8352006-06-09 21:02:31 +01001498{
1499 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001500 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001501
Simon Kelley832af0b2007-01-21 20:01:28 +00001502 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001503 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001504
Simon Kelley1a6bca82008-07-11 11:11:42 +01001505 for (i=0; usage[i].opt != 0; i++)
1506 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001507 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001508 int rept = usage[i].rept;
1509
Simon Kelley28866e92011-02-14 20:19:14 +00001510 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001511 {
1512 /* command line */
1513 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001514 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001515 if (rept == ARG_ONE)
1516 usage[i].rept = ARG_USED_CL;
1517 }
1518 else
1519 {
1520 /* allow file to override command line */
1521 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001522 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001523 if (rept == ARG_USED_CL || rept == ARG_ONE)
1524 usage[i].rept = ARG_USED_FILE;
1525 }
1526
1527 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1528 {
Simon Kelley28866e92011-02-14 20:19:14 +00001529 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001530 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001531 }
1532
1533 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001534 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001535
Simon Kelley849a8352006-06-09 21:02:31 +01001536 switch (option)
1537 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001538 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001539 {
Simon Kelley824af852008-02-12 20:43:05 +00001540 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001541 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001542 {
Simon Kelley28866e92011-02-14 20:19:14 +00001543 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001544 free(file);
1545 }
Simon Kelley849a8352006-06-09 21:02:31 +01001546 break;
1547 }
1548
Simon Kelleyf2621c72007-04-29 19:47:21 +01001549 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001550 {
1551 DIR *dir_stream;
1552 struct dirent *ent;
1553 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001554 struct list {
1555 char *suffix;
1556 struct list *next;
Simon Kelley3e1551a2014-09-09 21:46:07 +01001557 } *ignore_suffix = NULL, *match_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001558
Simon Kelley1f15b812009-10-13 17:49:32 +01001559 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001560 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001561 break;
1562
Simon Kelley1f15b812009-10-13 17:49:32 +01001563 for (arg = comma; arg; arg = comma)
1564 {
1565 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001566 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001567 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001568 li = opt_malloc(sizeof(struct list));
1569 if (*arg == '*')
1570 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001571 /* "*" with no suffix is a no-op */
1572 if (arg[1] == 0)
1573 free(li);
1574 else
1575 {
1576 li->next = match_suffix;
1577 match_suffix = li;
1578 /* Have to copy: buffer is overwritten */
1579 li->suffix = opt_string_alloc(arg+1);
1580 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001581 }
1582 else
1583 {
1584 li->next = ignore_suffix;
1585 ignore_suffix = li;
1586 /* Have to copy: buffer is overwritten */
1587 li->suffix = opt_string_alloc(arg);
1588 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001589 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001590 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001591
Simon Kelley849a8352006-06-09 21:02:31 +01001592 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001593 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001594
Simon Kelley849a8352006-06-09 21:02:31 +01001595 while ((ent = readdir(dir_stream)))
1596 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001597 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001598 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001599
1600 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001601 if (len == 0 ||
1602 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001603 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1604 ent->d_name[0] == '.')
1605 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001606
Simon Kelley3e1551a2014-09-09 21:46:07 +01001607 if (match_suffix)
1608 {
1609 for (li = match_suffix; li; li = li->next)
1610 {
1611 /* check for required suffices */
1612 size_t ls = strlen(li->suffix);
1613 if (len > ls &&
1614 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1615 break;
1616 }
1617 if (!li)
1618 continue;
1619 }
1620
Simon Kelley1f15b812009-10-13 17:49:32 +01001621 for (li = ignore_suffix; li; li = li->next)
1622 {
1623 /* check for proscribed suffices */
1624 size_t ls = strlen(li->suffix);
1625 if (len > ls &&
1626 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1627 break;
1628 }
1629 if (li)
1630 continue;
1631
Simon Kelley824af852008-02-12 20:43:05 +00001632 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001633 strcpy(path, directory);
1634 strcat(path, "/");
1635 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001636
Simon Kelley39595cf2013-02-04 21:40:07 +00001637 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001638 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001639 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001640
Simon Kelley39595cf2013-02-04 21:40:07 +00001641 /* only reg files allowed. */
1642 if (S_ISREG(buf.st_mode))
1643 one_file(path, 0);
1644
Simon Kelley849a8352006-06-09 21:02:31 +01001645 free(path);
1646 }
1647
1648 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001649 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001650 for(; ignore_suffix; ignore_suffix = li)
1651 {
1652 li = ignore_suffix->next;
1653 free(ignore_suffix->suffix);
1654 free(ignore_suffix);
1655 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001656 for(; match_suffix; match_suffix = li)
1657 {
1658 li = match_suffix->next;
1659 free(match_suffix->suffix);
1660 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001661 }
Simon Kelley849a8352006-06-09 21:02:31 +01001662 break;
1663 }
1664
Simon Kelleyed4c0762013-10-08 20:46:34 +01001665 case LOPT_ADD_SBNET: /* --add-subnet */
1666 set_option_bool(OPT_CLIENT_SUBNET);
1667 if (arg)
1668 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001669 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001670 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001671
1672 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1673 if ((end = split_chr(arg, '/')))
1674 {
1675 /* has subnet+len */
1676 err = parse_mysockaddr(arg, &new->addr);
1677 if (err)
1678 ret_err(err);
1679 if (!atoi_check(end, &new->mask))
1680 ret_err(gen_err);
1681 new->addr_used = 1;
1682 }
1683 else if (!atoi_check(arg, &new->mask))
1684 ret_err(gen_err);
1685
1686 daemon->add_subnet4 = new;
1687
Ed Bardsleya7369be2015-08-05 21:17:18 +01001688 if (comma)
1689 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001690 new = opt_malloc(sizeof(struct mysubnet));
1691 if ((end = split_chr(comma, '/')))
1692 {
1693 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001694 err = parse_mysockaddr(comma, &new->addr);
1695 if (err)
1696 ret_err(err);
1697 if (!atoi_check(end, &new->mask))
1698 ret_err(gen_err);
1699 new->addr_used = 1;
1700 }
1701 else
1702 {
1703 if (!atoi_check(comma, &new->mask))
1704 ret_err(gen_err);
1705 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001706
1707 daemon->add_subnet6 = new;
1708 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001709 }
1710 break;
1711
Simon Kelleyad094272012-08-10 17:10:54 +01001712 case '1': /* --enable-dbus */
1713 set_option_bool(OPT_DBUS);
1714 if (arg)
1715 daemon->dbus_name = opt_string_alloc(arg);
1716 else
1717 daemon->dbus_name = DNSMASQ_SERVICE;
1718 break;
1719
Simon Kelleyf2621c72007-04-29 19:47:21 +01001720 case '8': /* --log-facility */
1721 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001722 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001723 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001724 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001725 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001726#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001727 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001728#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001729 for (i = 0; facilitynames[i].c_name; i++)
1730 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1731 break;
1732
1733 if (facilitynames[i].c_name)
1734 daemon->log_fac = facilitynames[i].c_val;
1735 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001736 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001737#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001738 }
1739 break;
1740
Simon Kelleyf2621c72007-04-29 19:47:21 +01001741 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001742 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001743 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001744
Simon Kelleyf2621c72007-04-29 19:47:21 +01001745 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001746 {
Simon Kelley824af852008-02-12 20:43:05 +00001747 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001748 struct resolvc *new, *list = daemon->resolv_files;
1749
1750 if (list && list->is_default)
1751 {
1752 /* replace default resolv file - possibly with nothing */
1753 if (name)
1754 {
1755 list->is_default = 0;
1756 list->name = name;
1757 }
1758 else
1759 list = NULL;
1760 }
1761 else if (name)
1762 {
Simon Kelley824af852008-02-12 20:43:05 +00001763 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001764 new->next = list;
1765 new->name = name;
1766 new->is_default = 0;
1767 new->mtime = 0;
1768 new->logged = 0;
1769 list = new;
1770 }
1771 daemon->resolv_files = list;
1772 break;
1773 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001774
1775 case LOPT_SERVERS_FILE:
1776 daemon->servers_file = opt_string_alloc(arg);
1777 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001778
Simon Kelleyf2621c72007-04-29 19:47:21 +01001779 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001780 {
1781 int pref = 1;
1782 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001783 char *name, *target = NULL;
1784
Simon Kelleyf2621c72007-04-29 19:47:21 +01001785 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001786 {
1787 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001788 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001789 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001790 }
1791
Simon Kelley1f15b812009-10-13 17:49:32 +01001792 if (!(name = canonicalise_opt(arg)) ||
1793 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001794 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001795
Simon Kelley824af852008-02-12 20:43:05 +00001796 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001797 new->next = daemon->mxnames;
1798 daemon->mxnames = new;
1799 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001800 new->name = name;
1801 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001802 new->weight = pref;
1803 break;
1804 }
1805
Simon Kelleyf2621c72007-04-29 19:47:21 +01001806 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001807 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001808 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001809 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001810
1811#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001812 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001813 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001814 break;
1815
Simon Kelleyc72daea2012-01-05 21:33:27 +00001816 /* Sorry about the gross pre-processor abuse */
1817 case '6': /* --dhcp-script */
1818 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001819# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001820 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001821# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001822 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001823# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001824 if (option == LOPT_LUASCRIPT)
1825# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001826 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001827# else
1828 daemon->luascript = opt_string_alloc(arg);
1829# endif
1830 else
1831 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001832# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001833 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001834#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001835
Simon Kelley70d18732015-01-31 19:59:29 +00001836 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
1837 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
1838 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
1839 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
1840 case LOPT_HOST_INOTIFY: /* --hostsdir */
1841 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001842 {
Simon Kelley824af852008-02-12 20:43:05 +00001843 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00001844 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00001845 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001846 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001847 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001848 if (option == 'H')
1849 {
1850 new->next = daemon->addn_hosts;
1851 daemon->addn_hosts = new;
1852 }
1853 else if (option == LOPT_DHCP_HOST)
1854 {
1855 new->next = daemon->dhcp_hosts_file;
1856 daemon->dhcp_hosts_file = new;
1857 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001858 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001859 {
1860 new->next = daemon->dhcp_opts_file;
1861 daemon->dhcp_opts_file = new;
1862 }
Simon Kelley70d18732015-01-31 19:59:29 +00001863 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001864 {
Simon Kelley70d18732015-01-31 19:59:29 +00001865 new->next = daemon->dynamic_dirs;
1866 daemon->dynamic_dirs = new;
1867 if (option == LOPT_DHCP_INOTIFY)
1868 new->flags |= AH_DHCP_HST;
1869 else if (option == LOPT_DHOPT_INOTIFY)
1870 new->flags |= AH_DHCP_OPT;
1871 else if (option == LOPT_HOST_INOTIFY)
1872 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001873 }
1874
Simon Kelley849a8352006-06-09 21:02:31 +01001875 break;
1876 }
1877
Simon Kelleyf373a152013-09-23 12:47:47 +01001878
1879#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +00001880 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001881 if (!(comma = split(arg)))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001882 ret_err(gen_err);
1883
Simon Kelley4f7b3042012-11-28 21:27:02 +00001884 daemon->authserver = opt_string_alloc(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001885 arg = comma;
1886 do {
1887 struct iname *new = opt_malloc(sizeof(struct iname));
1888 comma = split(arg);
1889 new->name = NULL;
1890 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001891 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
Simon Kelley429798f2012-12-10 20:45:53 +00001892 new->addr.sa.sa_family = AF_INET;
1893#ifdef HAVE_IPV6
1894 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1895 new->addr.sa.sa_family = AF_INET6;
1896#endif
1897 else
Simon Kelleyf25e6c62013-11-17 12:23:42 +00001898 {
1899 char *fam = split_chr(arg, '/');
1900 new->name = opt_string_alloc(arg);
1901 new->addr.sa.sa_family = 0;
1902 if (fam)
1903 {
1904 if (strcmp(fam, "4") == 0)
1905 new->addr.sa.sa_family = AF_INET;
1906#ifdef HAVE_IPV6
1907 else if (strcmp(fam, "6") == 0)
1908 new->addr.sa.sa_family = AF_INET6;
1909#endif
1910 else
1911 ret_err(gen_err);
1912 }
1913 }
Simon Kelley429798f2012-12-10 20:45:53 +00001914 new->next = daemon->authinterface;
1915 daemon->authinterface = new;
1916
1917 arg = comma;
1918 } while (arg);
1919
Simon Kelley4f7b3042012-11-28 21:27:02 +00001920 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001921
1922 case LOPT_AUTHSFS: /* --auth-sec-servers */
1923 {
1924 struct name_list *new;
1925
1926 do {
1927 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001928 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001929 new->name = opt_string_alloc(arg);
1930 new->next = daemon->secondary_forward_server;
1931 daemon->secondary_forward_server = new;
1932 arg = comma;
1933 } while (arg);
1934 break;
1935 }
1936
Simon Kelley4f7b3042012-11-28 21:27:02 +00001937 case LOPT_AUTHZONE: /* --auth-zone */
1938 {
1939 struct auth_zone *new;
1940
1941 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001942
Simon Kelley429798f2012-12-10 20:45:53 +00001943 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001944 new->domain = opt_string_alloc(arg);
1945 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001946 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00001947 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001948 new->next = daemon->auth_zones;
1949 daemon->auth_zones = new;
1950
1951 while ((arg = comma))
1952 {
1953 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001954 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001955 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00001956 struct addrlist *subnet = NULL;
1957 struct all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001958
1959 comma = split(arg);
1960 prefix = split_chr(arg, '/');
1961
1962 if (prefix && !atoi_check(prefix, &prefixlen))
1963 ret_err(gen_err);
1964
Mathias Kresin094bfae2016-07-24 14:15:22 +01001965 if (strstr(arg, "exclude:") == arg)
1966 {
1967 is_exclude = 1;
1968 arg = arg+8;
1969 }
1970
Simon Kelley376d48c2013-11-13 13:04:30 +00001971 if (inet_pton(AF_INET, arg, &addr.addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001972 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001973 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001974 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001975 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001976 }
1977#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +00001978 else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001979 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001980 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001981 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001982 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001983 }
1984#endif
Simon Kelley376d48c2013-11-13 13:04:30 +00001985 else
1986 {
1987 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
1988 name->name = opt_string_alloc(arg);
1989 name->flags = AUTH4 | AUTH6;
1990 name->next = new->interface_names;
1991 new->interface_names = name;
1992 if (prefix)
1993 {
1994 if (prefixlen == 4)
1995 name->flags &= ~AUTH6;
1996#ifdef HAVE_IPV6
1997 else if (prefixlen == 6)
1998 name->flags &= ~AUTH4;
1999#endif
2000 else
2001 ret_err(gen_err);
2002 }
2003 }
2004
2005 if (subnet)
2006 {
2007 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002008
2009 if (is_exclude)
2010 {
2011 subnet->next = new->exclude;
2012 new->exclude = subnet;
2013 }
2014 else
2015 {
2016 subnet->next = new->subnet;
2017 new->subnet = subnet;
2018 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002019 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002020 }
2021 break;
2022 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002023
Simon Kelley4f7b3042012-11-28 21:27:02 +00002024 case LOPT_AUTHSOA: /* --auth-soa */
2025 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002026 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002027 if (comma)
2028 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002029 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002030 arg = comma;
2031 comma = split(arg);
2032 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002033 for (cp = daemon->hostmaster; *cp; cp++)
2034 if (*cp == '@')
2035 *cp = '.';
2036
Simon Kelley4f7b3042012-11-28 21:27:02 +00002037 if (comma)
2038 {
2039 arg = comma;
2040 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002041 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002042 if (comma)
2043 {
2044 arg = comma;
2045 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002046 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002047 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002048 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002049 }
2050 }
2051 }
2052
2053 break;
Simon Kelleyf373a152013-09-23 12:47:47 +01002054#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +00002055
Simon Kelley2bb73af2013-04-24 17:38:19 +01002056 case 's': /* --domain */
2057 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002058 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002059 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002060 else
Simon Kelley9009d742008-11-14 20:04:27 +00002061 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002062 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002063 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002064 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002065 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002066 else
2067 {
Simon Kelley9009d742008-11-14 20:04:27 +00002068 if (comma)
2069 {
Simon Kelley429798f2012-12-10 20:45:53 +00002070 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002071 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002072
Simon Kelley48fd1c42013-04-25 09:49:38 +01002073 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002074 new->indexed = 0;
2075
Simon Kelley9009d742008-11-14 20:04:27 +00002076 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002077 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002078 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002079 int msize;
2080
Simon Kelley28866e92011-02-14 20:19:14 +00002081 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002082 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002083 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002084 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002085 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002086 int mask = (1 << (32 - msize)) - 1;
2087 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002088 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2089 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002090 if (arg)
2091 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002092 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002093 {
2094 if (!(new->prefix = canonicalise_opt(arg)) ||
2095 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2096 ret_err(_("bad prefix"));
2097 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002098 else if (strcmp(arg, "local") != 0 ||
2099 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002100 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00002101 else
2102 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002103 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002104 local=/xxx.yyy.zzz.in-addr.arpa/ */
2105 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002106 if (!serv)
2107 ret_err(_("bad prefix"));
2108
Simon Kelleyde73a492014-02-17 21:43:27 +00002109 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002110
2111 /* local=/<domain>/ */
2112 serv = opt_malloc(sizeof(struct server));
2113 memset(serv, 0, sizeof(struct server));
2114 serv->domain = d;
2115 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2116 serv->next = daemon->servers;
2117 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002118 }
2119 }
Simon Kelley9009d742008-11-14 20:04:27 +00002120 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002121#ifdef HAVE_IPV6
2122 else if (inet_pton(AF_INET6, comma, &new->start6))
2123 {
2124 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2125 u64 addrpart = addr6part(&new->start6);
2126 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002127
Simon Kelleyd74942a2012-02-07 20:51:56 +00002128 /* prefix==64 overflows the mask calculation above */
2129 if (msize == 64)
2130 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002131
Simon Kelleyd74942a2012-02-07 20:51:56 +00002132 new->end6 = new->start6;
2133 setaddr6part(&new->start6, addrpart & ~mask);
2134 setaddr6part(&new->end6, addrpart | mask);
2135
2136 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002137 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002138 else if (arg)
2139 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002140 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002141 {
2142 if (!(new->prefix = canonicalise_opt(arg)) ||
2143 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
2144 ret_err(_("bad prefix"));
2145 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002146 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002147 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002148 else
2149 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002150 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002151 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002152 struct server *serv = add_rev6(&new->start6, msize);
2153 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002154
2155 /* local=/<domain>/ */
2156 serv = opt_malloc(sizeof(struct server));
2157 memset(serv, 0, sizeof(struct server));
2158 serv->domain = d;
2159 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2160 serv->next = daemon->servers;
2161 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002162 }
2163 }
2164 }
2165#endif
2166 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002167 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002168 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002169 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002170 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002171 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002172 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002173 prefstr = split(arg);
2174
Simon Kelleyd74942a2012-02-07 20:51:56 +00002175 if (inet_pton(AF_INET, comma, &new->start))
2176 {
2177 new->is6 = 0;
2178 if (!arg)
2179 new->end.s_addr = new->start.s_addr;
2180 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002181 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002182 }
2183#ifdef HAVE_IPV6
2184 else if (inet_pton(AF_INET6, comma, &new->start6))
2185 {
2186 new->is6 = 1;
2187 if (!arg)
2188 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2189 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002190 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002191 }
2192#endif
2193 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002194 ret_err(gen_err);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002195
2196 if (option != 's' && prefstr)
2197 {
2198 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2199 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2200 ret_err(_("bad prefix"));
2201 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002202 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002203
2204 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002205 if (option == 's')
2206 {
2207 new->next = daemon->cond_domain;
2208 daemon->cond_domain = new;
2209 }
2210 else
2211 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002212 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002213 new->next = daemon->synth_domains;
2214 daemon->synth_domains = new;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002215 if ((star = strrchr(new->prefix, '*')) && *(star+1) == 0)
2216 {
2217 *star = 0;
2218 new->indexed = 1;
2219 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002220 }
Simon Kelley9009d742008-11-14 20:04:27 +00002221 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002222 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002223 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002224 else
2225 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002226 }
2227 }
Simon Kelley849a8352006-06-09 21:02:31 +01002228 break;
2229
Simon Kelley1e505122016-01-25 21:29:23 +00002230 case LOPT_CPE_ID: /* --add-dns-client */
2231 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002232 daemon->dns_client_id = opt_string_alloc(arg);
2233 break;
2234
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002235 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002236 if (!arg)
2237 set_option_bool(OPT_ADD_MAC);
2238 else
2239 {
2240 unhide_metas(arg);
2241 if (strcmp(arg, "base64") == 0)
2242 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002243 else if (strcmp(arg, "text") == 0)
2244 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002245 else
2246 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002247 }
2248 break;
2249
Simon Kelleyf2621c72007-04-29 19:47:21 +01002250 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002251 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002252 break;
2253
Simon Kelleyf2621c72007-04-29 19:47:21 +01002254 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002255 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002256 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002257 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002258
Simon Kelley7622fc02009-06-04 20:32:05 +01002259#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002260 case LOPT_SCRIPTUSR: /* --scriptuser */
2261 daemon->scriptuser = opt_string_alloc(arg);
2262 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002263#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002264
Simon Kelleyf2621c72007-04-29 19:47:21 +01002265 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002266 do {
Simon Kelley824af852008-02-12 20:43:05 +00002267 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002268 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002269 new->next = daemon->if_names;
2270 daemon->if_names = new;
2271 /* new->name may be NULL if someone does
2272 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002273 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002274 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002275 arg = comma;
2276 } while (arg);
2277 break;
2278
Simon Kelley2937f8a2013-07-29 19:49:07 +01002279 case LOPT_TFTP: /* --enable-tftp */
2280 set_option_bool(OPT_TFTP);
2281 if (!arg)
2282 break;
2283 /* fall through */
2284
Simon Kelleyf2621c72007-04-29 19:47:21 +01002285 case 'I': /* --except-interface */
2286 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002287 do {
Simon Kelley824af852008-02-12 20:43:05 +00002288 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002289 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002290 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002291 if (option == 'I')
2292 {
2293 new->next = daemon->if_except;
2294 daemon->if_except = new;
2295 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002296 else if (option == LOPT_TFTP)
2297 {
2298 new->next = daemon->tftp_interfaces;
2299 daemon->tftp_interfaces = new;
2300 }
Simon Kelley849a8352006-06-09 21:02:31 +01002301 else
2302 {
2303 new->next = daemon->dhcp_except;
2304 daemon->dhcp_except = new;
2305 }
2306 arg = comma;
2307 } while (arg);
2308 break;
2309
Simon Kelleyf2621c72007-04-29 19:47:21 +01002310 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002311 case LOPT_IGNORE_ADDR: /* --ignore-address */
2312 {
Simon Kelley849a8352006-06-09 21:02:31 +01002313 struct in_addr addr;
2314 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002315 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002316 {
Simon Kelley824af852008-02-12 20:43:05 +00002317 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002318 if (option == 'B')
2319 {
2320 baddr->next = daemon->bogus_addr;
2321 daemon->bogus_addr = baddr;
2322 }
2323 else
2324 {
2325 baddr->next = daemon->ignore_addr;
2326 daemon->ignore_addr = baddr;
2327 }
Simon Kelley849a8352006-06-09 21:02:31 +01002328 baddr->addr = addr;
2329 }
2330 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002331 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002332 break;
2333 }
2334
Simon Kelleyf2621c72007-04-29 19:47:21 +01002335 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002336 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002337 do {
Simon Kelley824af852008-02-12 20:43:05 +00002338 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002339 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002340 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002341 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002342 {
2343 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002344 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002345#ifdef HAVE_SOCKADDR_SA_LEN
2346 new->addr.in.sin_len = sizeof(new->addr.in);
2347#endif
2348 }
2349#ifdef HAVE_IPV6
2350 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2351 {
2352 new->addr.sa.sa_family = AF_INET6;
2353 new->addr.in6.sin6_flowinfo = 0;
2354 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002355 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002356#ifdef HAVE_SOCKADDR_SA_LEN
2357 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2358#endif
2359 }
2360#endif
2361 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002362 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002363
2364 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002365 if (option == 'a')
2366 {
2367 new->next = daemon->if_addrs;
2368 daemon->if_addrs = new;
2369 }
2370 else
2371 {
2372 new->next = daemon->auth_peers;
2373 daemon->auth_peers = new;
2374 }
Simon Kelley849a8352006-06-09 21:02:31 +01002375 arg = comma;
2376 } while (arg);
2377 break;
2378
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002379 case 'S': /* --server */
2380 case LOPT_LOCAL: /* --local */
2381 case 'A': /* --address */
2382 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002383 {
2384 struct server *serv, *newlist = NULL;
2385
2386 unhide_metas(arg);
2387
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002388 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002389 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002390 int rebind = !(*arg == '/');
2391 char *end = NULL;
2392 if (!rebind)
2393 arg++;
2394 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002395 {
2396 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002397 /* elide leading dots - they are implied in the search algorithm */
2398 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002399 /* # matches everything and becomes a zero length domain string */
2400 if (strcmp(arg, "#") == 0)
2401 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002402 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002403 option = '?';
Simon Kelley824af852008-02-12 20:43:05 +00002404 serv = opt_malloc(sizeof(struct server));
2405 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002406 serv->next = newlist;
2407 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002408 serv->domain = domain;
2409 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002410 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002411 if (rebind)
2412 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002413 }
2414 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002415 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002416 }
2417 else
2418 {
Simon Kelley824af852008-02-12 20:43:05 +00002419 newlist = opt_malloc(sizeof(struct server));
2420 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002421#ifdef HAVE_LOOP
2422 newlist->uid = rand32();
2423#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002424 }
2425
Simon Kelley7b1eae42014-02-20 13:43:28 +00002426 if (servers_only && option == 'S')
2427 newlist->flags |= SERV_FROM_FILE;
2428
Simon Kelley849a8352006-06-09 21:02:31 +01002429 if (option == 'A')
2430 {
2431 newlist->flags |= SERV_LITERAL_ADDRESS;
2432 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002433 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002434 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002435 else if (option == LOPT_NO_REBIND)
2436 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002437
2438 if (!arg || !*arg)
2439 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002440 if (!(newlist->flags & SERV_NO_REBIND))
2441 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002442 }
2443
2444 else if (strcmp(arg, "#") == 0)
2445 {
2446 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002447 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002448 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002449 }
2450 else
2451 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002452 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2453 if (err)
2454 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002455 }
2456
Simon Kelleyf2621c72007-04-29 19:47:21 +01002457 serv = newlist;
2458 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002459 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002460 serv->next->flags = serv->flags;
2461 serv->next->addr = serv->addr;
2462 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002463 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002464 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002465 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002466 serv->next = daemon->servers;
2467 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002468 break;
2469 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002470
Simon Kelleyde73a492014-02-17 21:43:27 +00002471 case LOPT_REV_SERV: /* --rev-server */
2472 {
2473 char *string;
2474 int size;
2475 struct server *serv;
2476 struct in_addr addr4;
2477#ifdef HAVE_IPV6
2478 struct in6_addr addr6;
2479#endif
2480
2481 unhide_metas(arg);
2482 if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2483 ret_err(gen_err);
2484
2485 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002486 {
2487 serv = add_rev4(addr4, size);
2488 if (!serv)
2489 ret_err(_("bad prefix"));
2490 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002491#ifdef HAVE_IPV6
2492 else if (inet_pton(AF_INET6, arg, &addr6))
2493 serv = add_rev6(&addr6, size);
2494#endif
2495 else
2496 ret_err(gen_err);
2497
2498 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2499
2500 if (string)
2501 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002502
2503 if (servers_only)
2504 serv->flags |= SERV_FROM_FILE;
2505
Simon Kelleyde73a492014-02-17 21:43:27 +00002506 break;
2507 }
2508
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002509 case LOPT_IPSET: /* --ipset */
2510#ifndef HAVE_IPSET
2511 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2512 break;
2513#else
2514 {
2515 struct ipsets ipsets_head;
2516 struct ipsets *ipsets = &ipsets_head;
2517 int size;
2518 char *end;
2519 char **sets, **sets_pos;
2520 memset(ipsets, 0, sizeof(struct ipsets));
2521 unhide_metas(arg);
2522 if (arg && *arg == '/')
2523 {
2524 arg++;
2525 while ((end = split_chr(arg, '/')))
2526 {
2527 char *domain = NULL;
2528 /* elide leading dots - they are implied in the search algorithm */
2529 while (*arg == '.')
2530 arg++;
2531 /* # matches everything and becomes a zero length domain string */
2532 if (strcmp(arg, "#") == 0 || !*arg)
2533 domain = "";
2534 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
2535 option = '?';
2536 ipsets->next = opt_malloc(sizeof(struct ipsets));
2537 ipsets = ipsets->next;
2538 memset(ipsets, 0, sizeof(struct ipsets));
2539 ipsets->domain = domain;
2540 arg = end;
2541 }
2542 }
2543 else
2544 {
2545 ipsets->next = opt_malloc(sizeof(struct ipsets));
2546 ipsets = ipsets->next;
2547 memset(ipsets, 0, sizeof(struct ipsets));
2548 ipsets->domain = "";
2549 }
2550 if (!arg || !*arg)
2551 {
2552 option = '?';
2553 break;
2554 }
2555 size = 2;
2556 for (end = arg; *end; ++end)
2557 if (*end == ',')
2558 ++size;
2559
2560 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2561
2562 do {
2563 end = split(arg);
2564 *sets_pos++ = opt_string_alloc(arg);
2565 arg = end;
2566 } while (end);
2567 *sets_pos = 0;
2568 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2569 ipsets->next->sets = sets;
2570 ipsets->next = daemon->ipsets;
2571 daemon->ipsets = ipsets_head.next;
2572
2573 break;
2574 }
2575#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002576
Simon Kelleyf2621c72007-04-29 19:47:21 +01002577 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002578 {
2579 int size;
2580
2581 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002582 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002583 else
2584 {
2585 /* zero is OK, and means no caching. */
2586
2587 if (size < 0)
2588 size = 0;
2589 else if (size > 10000)
2590 size = 10000;
2591
2592 daemon->cachesize = size;
2593 }
2594 break;
2595 }
2596
Simon Kelleyf2621c72007-04-29 19:47:21 +01002597 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002598 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002599 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002600 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002601
Simon Kelley1a6bca82008-07-11 11:11:42 +01002602 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002603 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002604 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002605 break;
2606
Hans Dedecker926332a2016-01-23 10:48:12 +00002607 case LOPT_MAXPORT: /* --max-port */
2608 if (!atoi_check16(arg, &daemon->max_port))
2609 ret_err(gen_err);
2610 break;
2611
Simon Kelleyf2621c72007-04-29 19:47:21 +01002612 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002613 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002614 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002615 break;
2616
Simon Kelley25cf5e32015-01-09 15:53:03 +00002617 case 'q': /* --log-queries */
2618 set_option_bool(OPT_LOG);
2619 if (arg && strcmp(arg, "extra") == 0)
2620 set_option_bool(OPT_EXTRALOG);
2621 break;
2622
Simon Kelleyf2621c72007-04-29 19:47:21 +01002623 case LOPT_MAX_LOGS: /* --log-async */
2624 daemon->max_logs = LOG_MAX; /* default */
2625 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002626 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002627 else if (daemon->max_logs > 100)
2628 daemon->max_logs = 100;
2629 break;
2630
2631 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002632 {
2633 int i;
2634 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002635 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002636 daemon->edns_pktsz = (unsigned short)i;
2637 break;
2638 }
2639
Simon Kelleyf2621c72007-04-29 19:47:21 +01002640 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002641 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002642 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002643 /* if explicitly set to zero, use single OS ephemeral port
2644 and disable random ports */
2645 if (daemon->query_port == 0)
2646 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002647 break;
2648
Simon Kelley824af852008-02-12 20:43:05 +00002649 case 'T': /* --local-ttl */
2650 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002651 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002652 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002653 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002654 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002655 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002656 {
2657 int ttl;
2658 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002659 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002660 else if (option == LOPT_NEGTTL)
2661 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002662 else if (option == LOPT_MAXTTL)
2663 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002664 else if (option == LOPT_MINCTTL)
2665 {
2666 if (ttl > TTL_FLOOR_LIMIT)
2667 ttl = TTL_FLOOR_LIMIT;
2668 daemon->min_cache_ttl = (unsigned long)ttl;
2669 }
Simon Kelley1d860412012-09-20 20:48:04 +01002670 else if (option == LOPT_MAXCTTL)
2671 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002672 else if (option == LOPT_AUTHTTL)
2673 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002674 else if (option == LOPT_DHCPTTL)
2675 {
2676 daemon->dhcp_ttl = (unsigned long)ttl;
2677 daemon->use_dhcp_ttl = 1;
2678 }
Simon Kelley849a8352006-06-09 21:02:31 +01002679 else
2680 daemon->local_ttl = (unsigned long)ttl;
2681 break;
2682 }
2683
Simon Kelley7622fc02009-06-04 20:32:05 +01002684#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002685 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002686 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002687 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002688 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002689#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002690
Simon Kelley7622fc02009-06-04 20:32:05 +01002691#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002692 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002693 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002694 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002695 break;
2696
Simon Kelleybec366b2016-02-24 22:03:26 +00002697 case LOPT_TFTP_MTU: /* --tftp-mtu */
2698 if (!atoi_check(arg, &daemon->tftp_mtu))
2699 ret_err(gen_err);
2700 break;
2701
Simon Kelley824af852008-02-12 20:43:05 +00002702 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002703 comma = split(arg);
2704 if (comma)
2705 {
2706 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2707 new->interface = opt_string_alloc(comma);
2708 new->prefix = opt_string_alloc(arg);
2709 new->next = daemon->if_prefix;
2710 daemon->if_prefix = new;
2711 }
2712 else
2713 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002714 break;
2715
Simon Kelley824af852008-02-12 20:43:05 +00002716 case LOPT_TFTPPORTS: /* --tftp-port-range */
2717 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002718 !atoi_check16(arg, &daemon->start_tftp_port) ||
2719 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002720 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002721
2722 if (daemon->start_tftp_port > daemon->end_tftp_port)
2723 {
2724 int tmp = daemon->start_tftp_port;
2725 daemon->start_tftp_port = daemon->end_tftp_port;
2726 daemon->end_tftp_port = tmp;
2727 }
2728
2729 break;
Floris Bos60704f52017-04-09 22:22:49 +01002730
2731 case LOPT_APREF: /* --tftp-unique-root */
2732 if (!arg || strcasecmp(arg, "ip") == 0)
2733 set_option_bool(OPT_TFTP_APREF_IP);
2734 else if (strcasecmp(arg, "mac") == 0)
2735 set_option_bool(OPT_TFTP_APREF_MAC);
2736 else
2737 ret_err(gen_err);
2738 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002739#endif
Simon Kelley824af852008-02-12 20:43:05 +00002740
Simon Kelleyf2621c72007-04-29 19:47:21 +01002741 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002742 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002743 struct dhcp_bridge *new;
2744
Simon Kelley316e2732010-01-22 20:16:09 +00002745 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002746 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002747
Simon Kelley22cd8602018-01-14 22:57:14 +00002748 for (new = daemon->bridges; new; new = new->next)
2749 if (strcmp(new->iface, arg) == 0)
2750 break;
2751
2752 if (!new)
2753 {
2754 new = opt_malloc(sizeof(struct dhcp_bridge));
2755 strcpy(new->iface, arg);
2756 new->alias = NULL;
2757 new->next = daemon->bridges;
2758 daemon->bridges = new;
2759 }
2760
Simon Kelley832af0b2007-01-21 20:01:28 +00002761 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002762 arg = comma;
2763 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002764 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002765 {
Simon Kelley824af852008-02-12 20:43:05 +00002766 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002767 b->next = new->alias;
2768 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002769 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002770 }
2771 } while (comma);
2772
2773 break;
2774 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002775
Simon Kelley7622fc02009-06-04 20:32:05 +01002776#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002777 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002778 {
2779 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002780 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002781 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002782
Simon Kelley52b92f42012-01-22 16:05:15 +00002783 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002784 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002785
Simon Kelley849a8352006-06-09 21:02:31 +01002786 if (!arg)
2787 {
2788 option = '?';
2789 break;
2790 }
2791
2792 while(1)
2793 {
2794 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002795 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2796 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2797 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002798 break;
2799
Simon Kelleyf2621c72007-04-29 19:47:21 +01002800 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002801 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002802 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002803 {
Simon Kelley824af852008-02-12 20:43:05 +00002804 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2805 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002806 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002807 /* ignore empty tag */
2808 if (tt->net)
2809 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002810 }
2811 else
2812 {
2813 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002814 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002815 else if (strstr(arg, "set:") == arg)
2816 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002817 else
Simon Kelley824af852008-02-12 20:43:05 +00002818 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002819 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002820 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002821 }
2822 else
2823 {
2824 a[0] = arg;
2825 break;
2826 }
2827 }
2828
Simon Kelley1f776932012-12-16 19:46:08 +00002829 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002830 if (!(a[k] = split(a[k-1])))
2831 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002832
Simon Kelley52b92f42012-01-22 16:05:15 +00002833 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002834 ret_err(_("bad dhcp-range"));
2835
2836 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002837 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002838 new->next = daemon->dhcp;
2839 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002840 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002841 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002842 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002843 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002844 new->flags |= CONTEXT_PROXY;
2845 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002846 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002847
2848 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2849 {
2850 struct in_addr tmp = new->start;
2851 new->start = new->end;
2852 new->end = tmp;
2853 }
2854
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002855 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002856 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002857 {
2858 new->flags |= CONTEXT_NETMASK;
2859 leasepos = 3;
2860 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002861 ret_err(_("inconsistent DHCP range"));
Simon Kelleyfa794662016-03-03 20:33:54 +00002862
Simon Kelley52b92f42012-01-22 16:05:15 +00002863
Simon Kelleyfa794662016-03-03 20:33:54 +00002864 if (k >= 4 && strchr(a[3], '.') &&
2865 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
2866 {
2867 new->flags |= CONTEXT_BRDCAST;
2868 leasepos = 4;
2869 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002870 }
Simon Kelley849a8352006-06-09 21:02:31 +01002871 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002872#ifdef HAVE_DHCP6
2873 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002874 {
Simon Kelley89500e32013-09-20 16:29:20 +01002875 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002876 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002877 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002878 new->next = daemon->dhcp6;
2879 daemon->dhcp6 = new;
2880
Simon Kelley30cd9662012-03-25 20:44:38 +01002881 for (leasepos = 1; leasepos < k; leasepos++)
2882 {
2883 if (strcmp(a[leasepos], "static") == 0)
2884 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2885 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002886 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002887 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002888 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002889 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
2890 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002891 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002892 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01002893 else if (strcmp(a[leasepos], "off-link") == 0)
2894 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01002895 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2896 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002897 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2898 {
2899 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2900 new->flags |= CONTEXT_TEMPLATE;
2901 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002902 else
2903 break;
2904 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002905
Simon Kelley52b92f42012-01-22 16:05:15 +00002906 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002907 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002908 {
2909 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002910 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002911 if (!(*cp >= '0' && *cp <= '9'))
2912 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002913 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002914 {
2915 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002916 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002917 }
2918 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002919
Simon Kelley6692a1a2013-08-20 14:41:31 +01002920 if (new->prefix != 64)
2921 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002922 if (new->flags & CONTEXT_RA)
Simon Kelley6692a1a2013-08-20 14:41:31 +01002923 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2924 else if (new->flags & CONTEXT_TEMPLATE)
2925 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2926 }
2927
2928 if (new->prefix < 64)
2929 ret_err(_("prefix length must be at least 64"));
2930
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002931 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2932 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002933
2934 /* dhcp-range=:: enables DHCP stateless on any interface */
2935 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2936 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002937
2938 if (new->flags & CONTEXT_TEMPLATE)
2939 {
2940 struct in6_addr zero;
2941 memset(&zero, 0, sizeof(zero));
2942 if (!is_same_net6(&zero, &new->start6, new->prefix))
2943 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2944 }
2945
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002946 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002947 {
2948 struct in6_addr tmp = new->start6;
2949 new->start6 = new->end6;
2950 new->end6 = tmp;
2951 }
Simon Kelley849a8352006-06-09 21:02:31 +01002952 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002953#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002954 else
2955 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002956
Simon Kelley30cd9662012-03-25 20:44:38 +01002957 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002958 {
Simon Kelleyfa794662016-03-03 20:33:54 +00002959 if (leasepos != k-1)
2960 ret_err(_("bad dhcp-range"));
2961
Simon Kelley849a8352006-06-09 21:02:31 +01002962 if (strcmp(a[leasepos], "infinite") == 0)
2963 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002964 else if (strcmp(a[leasepos], "deprecated") == 0)
2965 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002966 else
2967 {
2968 int fac = 1;
2969 if (strlen(a[leasepos]) > 0)
2970 {
2971 switch (a[leasepos][strlen(a[leasepos]) - 1])
2972 {
Simon Kelley42243212012-07-20 15:19:18 +01002973 case 'w':
2974 case 'W':
2975 fac *= 7;
2976 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002977 case 'd':
2978 case 'D':
2979 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00002980 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002981 case 'h':
2982 case 'H':
2983 fac *= 60;
2984 /* fall through */
2985 case 'm':
2986 case 'M':
2987 fac *= 60;
2988 /* fall through */
2989 case 's':
2990 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002991 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002992 }
2993
Simon Kelleybe379862012-12-23 12:01:39 +00002994 for (cp = a[leasepos]; *cp; cp++)
2995 if (!(*cp >= '0' && *cp <= '9'))
2996 break;
2997
Simon Kelley54dae552013-02-05 17:55:10 +00002998 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00002999 ret_err(_("bad dhcp-range"));
3000
Simon Kelley849a8352006-06-09 21:02:31 +01003001 new->lease_time = atoi(a[leasepos]) * fac;
3002 /* Leases of a minute or less confuse
3003 some clients, notably Apple's */
3004 if (new->lease_time < 120)
3005 new->lease_time = 120;
3006 }
3007 }
3008 }
3009 break;
3010 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003011
Simon Kelley5aabfc72007-08-29 11:24:47 +01003012 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003013 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003014 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003015 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01003016 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01003017 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003018 struct in_addr in;
3019
Simon Kelley824af852008-02-12 20:43:05 +00003020 new = opt_malloc(sizeof(struct dhcp_config));
3021
Simon Kelley849a8352006-06-09 21:02:31 +01003022 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003023 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3024 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003025 new->netid = NULL;
3026
Simon Kelley849a8352006-06-09 21:02:31 +01003027 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01003028 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003029 if (!(a[k] = split(a[k-1])))
3030 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003031
3032 for (j = 0; j < k; j++)
3033 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
3034 {
3035 char *arg = a[j];
3036
3037 if ((arg[0] == 'i' || arg[0] == 'I') &&
3038 (arg[1] == 'd' || arg[1] == 'D') &&
3039 arg[2] == ':')
3040 {
3041 if (arg[3] == '*')
3042 new->flags |= CONFIG_NOCLID;
3043 else
3044 {
3045 int len;
3046 arg += 3; /* dump id: */
3047 if (strchr(arg, ':'))
3048 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3049 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01003050 {
3051 unhide_metas(arg);
3052 len = (int) strlen(arg);
3053 }
3054
Simon Kelley28866e92011-02-14 20:19:14 +00003055 if (len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003056 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003057 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003058 {
3059 new->flags |= CONFIG_CLID;
3060 new->clid_len = len;
3061 memcpy(new->clid, arg, len);
3062 }
Simon Kelley849a8352006-06-09 21:02:31 +01003063 }
3064 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003065 /* dhcp-host has strange backwards-compat needs. */
3066 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01003067 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003068 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3069 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3070 newtag->net = opt_malloc(strlen(arg + 4) + 1);
3071 newlist->next = new->netid;
3072 new->netid = newlist;
3073 newlist->list = newtag;
3074 strcpy(newtag->net, arg+4);
3075 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01003076 }
Simon Kelley7de060b2011-08-26 17:24:52 +01003077 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003078 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00003079#ifdef HAVE_DHCP6
3080 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3081 {
3082 arg[strlen(arg)-1] = 0;
3083 arg++;
3084
3085 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003086 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00003087
3088 for (i= 0; i < 8; i++)
3089 if (new->addr6.s6_addr[i] != 0)
3090 break;
3091
3092 /* set WILDCARD if network part all zeros */
3093 if (i == 8)
3094 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00003095
3096 new->flags |= CONFIG_ADDR6;
3097 }
3098#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01003099 else
Simon Kelley849a8352006-06-09 21:02:31 +01003100 {
Simon Kelley9009d742008-11-14 20:04:27 +00003101 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00003102 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
3103 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003104 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003105 else
3106 {
3107
3108 newhw->next = new->hwaddr;
3109 new->hwaddr = newhw;
3110 }
Simon Kelley849a8352006-06-09 21:02:31 +01003111 }
3112 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003113 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01003114 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003115 struct dhcp_config *configs;
3116
Simon Kelley849a8352006-06-09 21:02:31 +01003117 new->addr = in;
3118 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003119
3120 /* If the same IP appears in more than one host config, then DISCOVER
3121 for one of the hosts will get the address, but REQUEST will be NAKed,
3122 since the address is reserved by the other one -> protocol loop. */
3123 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3124 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
3125 {
3126 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3127 return 0;
3128 }
Simon Kelley849a8352006-06-09 21:02:31 +01003129 }
3130 else
3131 {
3132 char *cp, *lastp = NULL, last = 0;
Simon Kelley76ff4402013-12-17 16:29:14 +00003133 int fac = 1, isdig = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003134
3135 if (strlen(a[j]) > 1)
3136 {
3137 lastp = a[j] + strlen(a[j]) - 1;
3138 last = *lastp;
3139 switch (last)
3140 {
Simon Kelley42243212012-07-20 15:19:18 +01003141 case 'w':
3142 case 'W':
3143 fac *= 7;
3144 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003145 case 'd':
3146 case 'D':
3147 fac *= 24;
3148 /* fall through */
3149 case 'h':
3150 case 'H':
3151 fac *= 60;
3152 /* fall through */
3153 case 'm':
3154 case 'M':
3155 fac *= 60;
3156 /* fall through */
3157 case 's':
3158 case 'S':
3159 *lastp = 0;
3160 }
3161 }
3162
3163 for (cp = a[j]; *cp; cp++)
Simon Kelley76ff4402013-12-17 16:29:14 +00003164 if (isdigit((unsigned char)*cp))
3165 isdig = 1;
3166 else if (*cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01003167 break;
Simon Kelley76ff4402013-12-17 16:29:14 +00003168
Simon Kelley849a8352006-06-09 21:02:31 +01003169 if (*cp)
3170 {
3171 if (lastp)
3172 *lastp = last;
3173 if (strcmp(a[j], "infinite") == 0)
3174 {
3175 new->lease_time = 0xffffffff;
3176 new->flags |= CONFIG_TIME;
3177 }
3178 else if (strcmp(a[j], "ignore") == 0)
3179 new->flags |= CONFIG_DISABLE;
3180 else
3181 {
Simon Kelley1f15b812009-10-13 17:49:32 +01003182 if (!(new->hostname = canonicalise_opt(a[j])) ||
3183 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003184 ret_err(_("bad DHCP host name"));
3185
3186 new->flags |= CONFIG_NAME;
3187 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01003188 }
3189 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003190 else if (isdig)
Simon Kelley849a8352006-06-09 21:02:31 +01003191 {
3192 new->lease_time = atoi(a[j]) * fac;
3193 /* Leases of a minute or less confuse
3194 some clients, notably Apple's */
3195 if (new->lease_time < 120)
3196 new->lease_time = 120;
3197 new->flags |= CONFIG_TIME;
3198 }
3199 }
3200
Simon Kelley5aabfc72007-08-29 11:24:47 +01003201 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003202 break;
3203 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003204
3205 case LOPT_TAG_IF: /* --tag-if */
3206 {
3207 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3208
3209 new->tag = NULL;
3210 new->set = NULL;
3211 new->next = NULL;
3212
3213 /* preserve order */
3214 if (!daemon->tag_if)
3215 daemon->tag_if = new;
3216 else
3217 {
3218 struct tag_if *tmp;
3219 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3220 tmp->next = new;
3221 }
3222
3223 while (arg)
3224 {
3225 size_t len;
3226
3227 comma = split(arg);
3228 len = strlen(arg);
3229
3230 if (len < 5)
3231 {
3232 new->set = NULL;
3233 break;
3234 }
3235 else
3236 {
3237 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3238 newtag->net = opt_malloc(len - 3);
3239 strcpy(newtag->net, arg+4);
3240 unhide_metas(newtag->net);
3241
3242 if (strstr(arg, "set:") == arg)
3243 {
3244 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3245 newlist->next = new->set;
3246 new->set = newlist;
3247 newlist->list = newtag;
3248 }
3249 else if (strstr(arg, "tag:") == arg)
3250 {
3251 newtag->next = new->tag;
3252 new->tag = newtag;
3253 }
3254 else
3255 {
3256 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00003257 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003258 break;
3259 }
3260 }
3261
3262 arg = comma;
3263 }
3264
3265 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003266 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003267
3268 break;
3269 }
3270
Simon Kelley849a8352006-06-09 21:02:31 +01003271
Simon Kelley73a08a22009-02-05 20:28:08 +00003272 case 'O': /* --dhcp-option */
3273 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003274 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003275 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003276 return parse_dhcp_opt(errstr, arg,
3277 option == LOPT_FORCE ? DHOPT_FORCE :
3278 (option == LOPT_MATCH ? DHOPT_MATCH :
3279 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
3280
Simon Kelleyf2621c72007-04-29 19:47:21 +01003281 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003282 {
3283 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003284 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003285 {
Simon Kelley824af852008-02-12 20:43:05 +00003286 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01003287 newid->next = id;
3288 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003289 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003290 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01003291 arg = comma;
3292 };
3293
3294 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003295 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003296 else
3297 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003298 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003299 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003300 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003301 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003302 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003303 dhcp_next_server.s_addr = 0;
3304 if (comma)
3305 {
3306 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003307 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003308 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003309 if (comma)
3310 {
3311 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003312 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3313 {
3314 /*
3315 * The user may have specified the tftp hostname here.
3316 * save it so that it can be resolved/looked up during
3317 * actual dhcp_reply().
3318 */
3319
3320 tftp_sname = opt_string_alloc(comma);
3321 dhcp_next_server.s_addr = 0;
3322 }
Simon Kelley849a8352006-06-09 21:02:31 +01003323 }
3324 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003325
3326 new = opt_malloc(sizeof(struct dhcp_boot));
3327 new->file = dhcp_file;
3328 new->sname = dhcp_sname;
3329 new->tftp_sname = tftp_sname;
3330 new->next_server = dhcp_next_server;
3331 new->netid = id;
3332 new->next = daemon->boot_config;
3333 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003334 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003335
Simon Kelley849a8352006-06-09 21:02:31 +01003336 break;
3337 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003338
Floris Bos503c6092017-04-09 23:07:13 +01003339 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3340 {
3341 struct dhcp_netid *id = NULL;
3342 while (is_tag_prefix(arg))
3343 {
3344 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
3345 newid->next = id;
3346 id = newid;
3347 comma = split(arg);
3348 newid->net = opt_string_alloc(arg+4);
3349 arg = comma;
3350 };
3351
3352 if (!arg)
3353 ret_err(gen_err);
3354 else
3355 {
3356 struct delay_config *new;
3357 int delay;
3358 if (!atoi_check(arg, &delay))
3359 ret_err(gen_err);
3360
3361 new = opt_malloc(sizeof(struct delay_config));
3362 new->delay = delay;
3363 new->netid = id;
3364 new->next = daemon->delay_conf;
3365 daemon->delay_conf = new;
3366 }
3367
3368 break;
3369 }
3370
Simon Kelley7622fc02009-06-04 20:32:05 +01003371 case LOPT_PXE_PROMT: /* --pxe-prompt */
3372 {
3373 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3374 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003375
Simon Kelley7622fc02009-06-04 20:32:05 +01003376 new->netid = NULL;
3377 new->opt = 10; /* PXE_MENU_PROMPT */
3378
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003379 while (is_tag_prefix(arg))
3380 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003381 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3382 comma = split(arg);
3383 nn->next = new->netid;
3384 new->netid = nn;
3385 nn->net = opt_string_alloc(arg+4);
3386 arg = comma;
3387 }
3388
3389 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003390 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003391 else
3392 {
3393 comma = split(arg);
3394 unhide_metas(arg);
3395 new->len = strlen(arg) + 1;
3396 new->val = opt_malloc(new->len);
3397 memcpy(new->val + 1, arg, new->len - 1);
3398
3399 new->u.vendor_class = (unsigned char *)"PXEClient";
3400 new->flags = DHOPT_VENDOR;
3401
3402 if (comma && atoi_check(comma, &timeout))
3403 *(new->val) = timeout;
3404 else
3405 *(new->val) = 255;
3406
3407 new->next = daemon->dhcp_opts;
3408 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003409 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003410 }
3411
3412 break;
3413 }
3414
3415 case LOPT_PXE_SERV: /* --pxe-service */
3416 {
3417 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3418 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003419 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3420 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003421 static int boottype = 32768;
3422
3423 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003424 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003425 new->server.s_addr = 0;
3426
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003427 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01003428 {
3429 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3430 comma = split(arg);
3431 nn->next = new->netid;
3432 new->netid = nn;
3433 nn->net = opt_string_alloc(arg+4);
3434 arg = comma;
3435 }
3436
3437 if (arg && (comma = split(arg)))
3438 {
3439 for (i = 0; CSA[i]; i++)
3440 if (strcasecmp(CSA[i], arg) == 0)
3441 break;
3442
3443 if (CSA[i] || atoi_check(arg, &i))
3444 {
3445 arg = comma;
3446 comma = split(arg);
3447
3448 new->CSA = i;
3449 new->menu = opt_string_alloc(arg);
3450
Simon Kelley316e2732010-01-22 20:16:09 +00003451 if (!comma)
3452 {
3453 new->type = 0; /* local boot */
3454 new->basename = NULL;
3455 }
3456 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003457 {
3458 arg = comma;
3459 comma = split(arg);
3460 if (atoi_check(arg, &i))
3461 {
3462 new->type = i;
3463 new->basename = NULL;
3464 }
3465 else
3466 {
3467 new->type = boottype++;
3468 new->basename = opt_string_alloc(arg);
3469 }
3470
Simon Kelley751d6f42012-02-10 15:24:51 +00003471 if (comma)
3472 {
3473 if (!inet_pton(AF_INET, comma, &new->server))
3474 {
3475 new->server.s_addr = 0;
3476 new->sname = opt_string_alloc(comma);
3477 }
3478
3479 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003480 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003481
Simon Kelley316e2732010-01-22 20:16:09 +00003482 /* Order matters */
3483 new->next = NULL;
3484 if (!daemon->pxe_services)
3485 daemon->pxe_services = new;
3486 else
3487 {
3488 struct pxe_service *s;
3489 for (s = daemon->pxe_services; s->next; s = s->next);
3490 s->next = new;
3491 }
3492
3493 daemon->enable_pxe = 1;
3494 break;
3495
Simon Kelley7622fc02009-06-04 20:32:05 +01003496 }
3497 }
3498
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003499 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003500 }
3501
Simon Kelleyf2621c72007-04-29 19:47:21 +01003502 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003503 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003504 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003505 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003506 else
3507 {
Simon Kelley824af852008-02-12 20:43:05 +00003508 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003509 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003510 unhide_metas(comma);
3511 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003512 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003513 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003514 else
3515 {
3516 new->next = daemon->dhcp_macs;
3517 daemon->dhcp_macs = new;
3518 }
Simon Kelley849a8352006-06-09 21:02:31 +01003519 }
3520 }
3521 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003522
3523#ifdef OPTION6_PREFIX_CLASS
3524 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3525 {
3526 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3527
3528 if (!(comma = split(arg)) ||
3529 !atoi_check16(comma, &new->class))
3530 ret_err(gen_err);
3531
3532 new->tag.net = opt_string_alloc(set_prefix(arg));
3533 new->next = daemon->prefix_classes;
3534 daemon->prefix_classes = new;
3535
3536 break;
3537 }
3538#endif
3539
3540
Simon Kelleyf2621c72007-04-29 19:47:21 +01003541 case 'U': /* --dhcp-vendorclass */
3542 case 'j': /* --dhcp-userclass */
3543 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3544 case LOPT_REMOTE: /* --dhcp-remoteid */
3545 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003546 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003547 unsigned char *p;
3548 int dig = 0;
3549 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3550
3551 if (!(comma = split(arg)))
3552 ret_err(gen_err);
3553
3554 new->netid.net = opt_string_alloc(set_prefix(arg));
3555 /* check for hex string - must digits may include : must not have nothing else,
3556 only allowed for agent-options. */
3557
3558 arg = comma;
3559 if ((comma = split(arg)))
3560 {
3561 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3562 ret_err(gen_err);
3563 else
3564 new->enterprise = atoi(arg+11);
3565 }
3566 else
3567 comma = arg;
3568
3569 for (p = (unsigned char *)comma; *p; p++)
3570 if (isxdigit(*p))
3571 dig = 1;
3572 else if (*p != ':')
3573 break;
3574 unhide_metas(comma);
3575 if (option == 'U' || option == 'j' || *p || !dig)
3576 {
3577 new->len = strlen(comma);
3578 new->data = opt_malloc(new->len);
3579 memcpy(new->data, comma, new->len);
3580 }
3581 else
3582 {
3583 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3584 new->data = opt_malloc(new->len);
3585 memcpy(new->data, comma, new->len);
3586 }
3587
3588 switch (option)
3589 {
3590 case 'j':
3591 new->match_type = MATCH_USER;
3592 break;
3593 case 'U':
3594 new->match_type = MATCH_VENDOR;
3595 break;
3596 case LOPT_CIRCUIT:
3597 new->match_type = MATCH_CIRCUIT;
3598 break;
3599 case LOPT_REMOTE:
3600 new->match_type = MATCH_REMOTE;
3601 break;
3602 case LOPT_SUBSCR:
3603 new->match_type = MATCH_SUBSCRIBER;
3604 break;
3605 }
3606 new->next = daemon->dhcp_vendors;
3607 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003608
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003609 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003610 }
3611
Simon Kelley9e038942008-05-30 20:06:34 +01003612 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3613 if (!arg)
3614 {
3615 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3616 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3617 }
3618 else
3619 {
3620 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003621 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3622 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003623 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003624 if (!comma)
3625 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3626 }
3627 break;
3628
Simon Kelley824af852008-02-12 20:43:05 +00003629 case 'J': /* --dhcp-ignore */
3630 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3631 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003632 case '3': /* --bootp-dynamic */
3633 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003634 {
Simon Kelley824af852008-02-12 20:43:05 +00003635 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003636 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003637 if (option == 'J')
3638 {
3639 new->next = daemon->dhcp_ignore;
3640 daemon->dhcp_ignore = new;
3641 }
Simon Kelley824af852008-02-12 20:43:05 +00003642 else if (option == LOPT_BROADCAST)
3643 {
3644 new->next = daemon->force_broadcast;
3645 daemon->force_broadcast = new;
3646 }
Simon Kelley9009d742008-11-14 20:04:27 +00003647 else if (option == '3')
3648 {
3649 new->next = daemon->bootp_dynamic;
3650 daemon->bootp_dynamic = new;
3651 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003652 else if (option == LOPT_GEN_NAMES)
3653 {
3654 new->next = daemon->dhcp_gen_names;
3655 daemon->dhcp_gen_names = new;
3656 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003657 else
3658 {
3659 new->next = daemon->dhcp_ignore_names;
3660 daemon->dhcp_ignore_names = new;
3661 }
3662
3663 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003664 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003665 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003666 member->next = list;
3667 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003668 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003669 member->net = opt_string_alloc(arg+4);
3670 else
3671 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003672 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003673 }
Simon Kelley849a8352006-06-09 21:02:31 +01003674
3675 new->list = list;
3676 break;
3677 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003678
3679 case LOPT_PROXY: /* --dhcp-proxy */
3680 daemon->override = 1;
3681 while (arg) {
3682 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3683 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003684 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003685 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003686 new->next = daemon->override_relays;
3687 daemon->override_relays = new;
3688 arg = comma;
3689 }
3690 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003691
3692 case LOPT_RELAY: /* --dhcp-relay */
3693 {
3694 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3695 comma = split(arg);
3696 new->interface = opt_string_alloc(split(comma));
3697 new->iface_index = 0;
3698 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3699 {
3700 new->next = daemon->relay4;
3701 daemon->relay4 = new;
3702 }
3703#ifdef HAVE_DHCP6
3704 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3705 {
3706 new->next = daemon->relay6;
3707 daemon->relay6 = new;
3708 }
3709#endif
3710 else
3711 ret_err(_("Bad dhcp-relay"));
3712
3713 break;
3714 }
3715
Simon Kelley7622fc02009-06-04 20:32:05 +01003716#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003717
Simon Kelley8b372702012-03-09 17:45:10 +00003718#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003719 case LOPT_RA_PARAM: /* --ra-param */
3720 if ((comma = split(arg)))
3721 {
3722 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3723 new->lifetime = -1;
3724 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003725 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003726 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003727 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003728 if (strcasestr(comma, "mtu:") == comma)
3729 {
3730 arg = comma + 4;
3731 if (!(comma = split(comma)))
3732 goto err;
3733 if (!strcasecmp(arg, "off"))
3734 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003735 else if (!atoi_check(arg, &new->mtu))
3736 new->mtu_name = opt_string_alloc(arg);
3737 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003738 goto err;
3739 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003740 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3741 {
3742 if (*comma == 'l' || *comma == 'L')
3743 new->prio = 0x18;
3744 else
3745 new->prio = 0x08;
3746 comma = split(comma);
3747 }
3748 arg = split(comma);
3749 if (!atoi_check(comma, &new->interval) ||
3750 (arg && !atoi_check(arg, &new->lifetime)))
David Flamand005c46d2017-04-11 11:49:54 +01003751err:
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003752 ret_err(_("bad RA-params"));
3753
3754 new->next = daemon->ra_interfaces;
3755 daemon->ra_interfaces = new;
3756 }
3757 break;
3758
Simon Kelley8b372702012-03-09 17:45:10 +00003759 case LOPT_DUID: /* --dhcp-duid */
3760 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003761 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003762 else
3763 {
3764 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3765 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3766 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3767 }
3768 break;
3769#endif
3770
Simon Kelleyf2621c72007-04-29 19:47:21 +01003771 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003772 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003773 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003774 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003775 struct doctor *new = opt_malloc(sizeof(struct doctor));
3776 new->next = daemon->doctors;
3777 daemon->doctors = new;
3778 new->mask.s_addr = 0xffffffff;
3779 new->end.s_addr = 0;
3780
Simon Kelley849a8352006-06-09 21:02:31 +01003781 if ((a[0] = arg))
3782 for (k = 1; k < 3; k++)
3783 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003784 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003785 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003786 unhide_metas(a[k]);
3787 }
Simon Kelley849a8352006-06-09 21:02:31 +01003788
Simon Kelley73a08a22009-02-05 20:28:08 +00003789 dash = split_chr(a[0], '-');
3790
Simon Kelley849a8352006-06-09 21:02:31 +01003791 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003792 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
3793 (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
Simon Kelley73a08a22009-02-05 20:28:08 +00003794 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003795
Simon Kelleya2bc2542016-04-21 22:34:22 +01003796 if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))
3797 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003798
Simon Kelley73a08a22009-02-05 20:28:08 +00003799 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003800 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003801 !is_same_net(new->in, new->end, new->mask) ||
3802 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003803 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003804
3805 break;
3806 }
3807
Simon Kelleyf2621c72007-04-29 19:47:21 +01003808 case LOPT_INTNAME: /* --interface-name */
3809 {
3810 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003811 char *domain = NULL;
3812
Simon Kelleyf2621c72007-04-29 19:47:21 +01003813 comma = split(arg);
3814
Simon Kelley1f15b812009-10-13 17:49:32 +01003815 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003816 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003817
Simon Kelley824af852008-02-12 20:43:05 +00003818 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003819 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00003820 new->addr = NULL;
3821
Simon Kelleyf2621c72007-04-29 19:47:21 +01003822 /* Add to the end of the list, so that first name
3823 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003824 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003825 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003826 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003827 new->family = 0;
3828 arg = split_chr(comma, '/');
3829 if (arg)
3830 {
3831 if (strcmp(arg, "4") == 0)
3832 new->family = AF_INET;
3833#ifdef HAVE_IPV6
3834 else if (strcmp(arg, "6") == 0)
3835 new->family = AF_INET6;
3836#endif
3837 else
3838 ret_err(gen_err);
3839 }
Simon Kelley824af852008-02-12 20:43:05 +00003840 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003841 break;
3842 }
Simon Kelley9009d742008-11-14 20:04:27 +00003843
3844 case LOPT_CNAME: /* --cname */
3845 {
3846 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003847 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003848 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003849
Simon Kelleya1d973f2016-12-22 22:09:50 +00003850 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00003851 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00003852 pen = last;
3853 last = comma;
3854 }
3855
3856 if (!pen)
3857 ret_err(_("bad CNAME"));
3858
3859 if (pen != arg && atoi_check(last, &ttl))
3860 last = pen;
3861
3862 target = canonicalise_opt(last);
3863
3864 while (arg != last)
3865 {
Petr Menšík56f06232018-03-06 23:13:32 +00003866 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00003867 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01003868
3869 if (!alias || !target)
3870 ret_err(_("bad CNAME"));
Simon Kelleya1d973f2016-12-22 22:09:50 +00003871
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003872 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01003873 if (hostname_isequal(new->alias, alias))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003874 ret_err(_("duplicate CNAME"));
3875 new = opt_malloc(sizeof(struct cname));
3876 new->next = daemon->cnames;
3877 daemon->cnames = new;
3878 new->alias = alias;
3879 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003880 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003881
Petr Menšík56f06232018-03-06 23:13:32 +00003882 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00003883 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003884
Simon Kelley9009d742008-11-14 20:04:27 +00003885 break;
3886 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003887
3888 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003889 {
3890 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003891 char *dom, *target = NULL;
3892
Simon Kelleyf2621c72007-04-29 19:47:21 +01003893 comma = split(arg);
3894
Simon Kelley1f15b812009-10-13 17:49:32 +01003895 if (!(dom = canonicalise_opt(arg)) ||
3896 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003897 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003898 else
3899 {
3900 new = opt_malloc(sizeof(struct ptr_record));
3901 new->next = daemon->ptr;
3902 daemon->ptr = new;
3903 new->name = dom;
3904 new->ptr = target;
3905 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003906 break;
3907 }
3908
Simon Kelley1a6bca82008-07-11 11:11:42 +01003909 case LOPT_NAPTR: /* --naptr-record */
3910 {
3911 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3912 int k = 0;
3913 struct naptr *new;
3914 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003915 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003916
3917 if ((a[0] = arg))
3918 for (k = 1; k < 7; k++)
3919 if (!(a[k] = split(a[k-1])))
3920 break;
3921
3922
3923 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003924 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003925 !atoi_check16(a[1], &order) ||
3926 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003927 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003928 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003929 else
3930 {
3931 new = opt_malloc(sizeof(struct naptr));
3932 new->next = daemon->naptr;
3933 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003934 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003935 new->flags = opt_string_alloc(a[3]);
3936 new->services = opt_string_alloc(a[4]);
3937 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003938 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003939 new->order = order;
3940 new->pref = pref;
3941 }
3942 break;
3943 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003944
3945 case LOPT_RR: /* dns-rr */
3946 {
3947 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00003948 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003949 char *data;
3950 int val;
3951
3952 comma = split(arg);
3953 data = split(comma);
3954
3955 new = opt_malloc(sizeof(struct txt_record));
3956 new->next = daemon->rr;
3957 daemon->rr = new;
3958
3959 if (!atoi_check(comma, &val) ||
3960 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003961 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003962 ret_err(_("bad RR record"));
3963
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003964 new->class = val;
3965 new->len = 0;
3966
3967 if (data)
3968 {
3969 new->txt=opt_malloc(len);
3970 new->len = len;
3971 memcpy(new->txt, data, len);
3972 }
3973
3974 break;
3975 }
3976
Simon Kelleyf2621c72007-04-29 19:47:21 +01003977 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003978 {
3979 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003980 unsigned char *p, *cnt;
3981 size_t len;
3982
3983 comma = split(arg);
3984
Simon Kelley824af852008-02-12 20:43:05 +00003985 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003986 new->next = daemon->txt;
3987 daemon->txt = new;
3988 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00003989 new->stat = 0;
3990
Simon Kelley1f15b812009-10-13 17:49:32 +01003991 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003992 ret_err(_("bad TXT record"));
3993
Simon Kelley28866e92011-02-14 20:19:14 +00003994 len = comma ? strlen(comma) : 0;
3995 len += (len/255) + 1; /* room for extra counts */
3996 new->txt = p = opt_malloc(len);
3997
3998 cnt = p++;
3999 *cnt = 0;
4000
4001 while (comma && *comma)
4002 {
4003 unsigned char c = (unsigned char)*comma++;
4004
4005 if (c == ',' || *cnt == 255)
4006 {
4007 if (c != ',')
4008 comma--;
4009 cnt = p++;
4010 *cnt = 0;
4011 }
4012 else
4013 {
4014 *p++ = unhide_meta(c);
4015 (*cnt)++;
4016 }
4017 }
4018
4019 new->len = p - new->txt;
4020
Simon Kelley849a8352006-06-09 21:02:31 +01004021 break;
4022 }
4023
Simon Kelleyf2621c72007-04-29 19:47:21 +01004024 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004025 {
4026 int port = 1, priority = 0, weight = 0;
4027 char *name, *target = NULL;
4028 struct mx_srv_record *new;
4029
Simon Kelleyf2621c72007-04-29 19:47:21 +01004030 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004031
Simon Kelley1f15b812009-10-13 17:49:32 +01004032 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004033 ret_err(_("bad SRV record"));
4034
Simon Kelley849a8352006-06-09 21:02:31 +01004035 if (comma)
4036 {
4037 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004038 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004039 if (!(target = canonicalise_opt(arg)))
4040 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00004041
Simon Kelley849a8352006-06-09 21:02:31 +01004042 if (comma)
4043 {
4044 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004045 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004046 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004047 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00004048
Simon Kelley849a8352006-06-09 21:02:31 +01004049 if (comma)
4050 {
4051 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004052 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004053 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004054 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00004055
Simon Kelley407a1f32016-03-01 17:06:07 +00004056 if (comma && !atoi_check16(comma, &weight))
4057 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01004058 }
4059 }
4060 }
4061
Simon Kelley824af852008-02-12 20:43:05 +00004062 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004063 new->next = daemon->mxnames;
4064 daemon->mxnames = new;
4065 new->issrv = 1;
4066 new->name = name;
4067 new->target = target;
4068 new->srvport = port;
4069 new->priority = priority;
4070 new->weight = weight;
4071 break;
4072 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004073
Simon Kelleye759d422012-03-16 13:18:57 +00004074 case LOPT_HOST_REC: /* --host-record */
4075 {
4076 struct host_record *new = opt_malloc(sizeof(struct host_record));
4077 memset(new, 0, sizeof(struct host_record));
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004078 new->ttl = -1;
4079
Simon Kelleye759d422012-03-16 13:18:57 +00004080 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004081 ret_err(_("Bad host-record"));
4082
4083 while (arg)
4084 {
4085 struct all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004086 char *dig;
4087
4088 for (dig = arg; *dig != 0; dig++)
4089 if (*dig < '0' || *dig > '9')
4090 break;
4091 if (*dig == 0)
4092 new->ttl = atoi(arg);
4093 else if (inet_pton(AF_INET, arg, &addr))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004094 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00004095#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004096 else if (inet_pton(AF_INET6, arg, &addr))
4097 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00004098#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004099 else
4100 {
4101 int nomem;
4102 char *canon = canonicalise(arg, &nomem);
4103 struct name_list *nl = opt_malloc(sizeof(struct name_list));
4104 if (!canon)
4105 ret_err(_("Bad name in host-record"));
4106
4107 nl->name = canon;
4108 /* keep order, so that PTR record goes to first name */
4109 nl->next = NULL;
4110 if (!new->names)
4111 new->names = nl;
4112 else
4113 {
4114 struct name_list *tmp;
4115 for (tmp = new->names; tmp->next; tmp = tmp->next);
4116 tmp->next = nl;
4117 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004118 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004119
4120 arg = comma;
4121 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004122 }
Simon Kelleye759d422012-03-16 13:18:57 +00004123
4124 /* Keep list order */
4125 if (!daemon->host_records_tail)
4126 daemon->host_records = new;
4127 else
4128 daemon->host_records_tail->next = new;
4129 new->next = NULL;
4130 daemon->host_records_tail = new;
4131 break;
4132 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004133
4134#ifdef HAVE_DNSSEC
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004135 case LOPT_DNSSEC_STAMP:
4136 daemon->timestamp_file = opt_string_alloc(arg);
4137 break;
4138
Simon Kelleyee415862014-02-11 11:07:22 +00004139 case LOPT_TRUST_ANCHOR:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004140 {
Simon Kelleyee415862014-02-11 11:07:22 +00004141 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4142 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4143 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004144
4145 new->class = C_IN;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004146
Simon Kelleycbf13a22014-01-25 17:59:14 +00004147 if ((comma = split(arg)) && (algo = split(comma)))
4148 {
4149 int class = 0;
4150 if (strcmp(comma, "IN") == 0)
4151 class = C_IN;
4152 else if (strcmp(comma, "CH") == 0)
4153 class = C_CHAOS;
4154 else if (strcmp(comma, "HS") == 0)
4155 class = C_HESIOD;
4156
4157 if (class != 0)
4158 {
4159 new->class = class;
4160 comma = algo;
4161 algo = split(comma);
4162 }
4163 }
4164
Simon Kelleyee415862014-02-11 11:07:22 +00004165 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4166 !atoi_check16(comma, &new->keytag) ||
4167 !atoi_check8(algo, &new->algo) ||
4168 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004169 !(new->name = canonicalise_opt(arg)))
Simon Kelleyee415862014-02-11 11:07:22 +00004170 ret_err(_("bad trust anchor"));
Simon Kelleycbf13a22014-01-25 17:59:14 +00004171
Simon Kelley0fc2f312014-01-08 10:26:58 +00004172 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004173 len = (2*strlen(keyhex))+1;
4174 new->digest = opt_malloc(len);
4175 unhide_metas(keyhex);
4176 /* 4034: "Whitespace is allowed within digits" */
4177 for (cp = keyhex; *cp; )
4178 if (isspace(*cp))
4179 for (cp1 = cp; *cp1; cp1++)
4180 *cp1 = *(cp1+1);
4181 else
4182 cp++;
4183 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
4184 ret_err(_("bad HEX in trust anchor"));
Simon Kelley0fc2f312014-01-08 10:26:58 +00004185
Simon Kelleyee415862014-02-11 11:07:22 +00004186 new->next = daemon->ds;
4187 daemon->ds = new;
4188
Simon Kelley0fc2f312014-01-08 10:26:58 +00004189 break;
4190 }
4191#endif
4192
Simon Kelley7622fc02009-06-04 20:32:05 +01004193 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004194 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004195
Simon Kelley849a8352006-06-09 21:02:31 +01004196 }
Simon Kelley824af852008-02-12 20:43:05 +00004197
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004198 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004199}
4200
Simon Kelley28866e92011-02-14 20:19:14 +00004201static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004202{
Simon Kelley824af852008-02-12 20:43:05 +00004203 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004204 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004205
4206 while (fgets(buff, MAXDNAME, f))
4207 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004208 int white, i;
4209 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004210 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004211 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004212
Simon Kelley824af852008-02-12 20:43:05 +00004213 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004214 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004215 {
4216 if (setjmp(mem_jmp))
4217 continue;
4218 mem_recover = 1;
4219 }
4220
Simon Kelley13dee6f2017-02-28 16:51:58 +00004221 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004222 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004223 errmess = NULL;
4224
Simon Kelley849a8352006-06-09 21:02:31 +01004225 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4226 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004227 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004228 {
4229 if (*p == '"')
4230 {
4231 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004232
Simon Kelley849a8352006-06-09 21:02:31 +01004233 for(; *p && *p != '"'; p++)
4234 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004235 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004236 {
4237 if (p[1] == 't')
4238 p[1] = '\t';
4239 else if (p[1] == 'n')
4240 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004241 else if (p[1] == 'b')
4242 p[1] = '\b';
4243 else if (p[1] == 'r')
4244 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004245 else if (p[1] == 'e') /* escape */
4246 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004247 memmove(p, p+1, strlen(p+1)+1);
4248 }
4249 *p = hide_meta(*p);
4250 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004251
4252 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004253 {
4254 errmess = _("missing \"");
4255 goto oops;
4256 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004257
4258 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004259 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004260
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004261 if (isspace(*p))
4262 {
4263 *p = ' ';
4264 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004265 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004266 else
4267 {
4268 if (white && *p == '#')
4269 {
4270 *p = 0;
4271 break;
4272 }
4273 white = 0;
4274 }
Simon Kelley849a8352006-06-09 21:02:31 +01004275 }
4276
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004277
4278 /* strip leading spaces */
4279 for (start = buff; *start && *start == ' '; start++);
4280
4281 /* strip trailing spaces */
4282 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4283
4284 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004285 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004286 else
4287 start[len] = 0;
4288
Simon Kelley611ebc52012-07-16 16:23:46 +01004289 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004290 arg = start;
4291 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004292 {
4293 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004294 for (arg = p+1; *arg == ' '; arg++);
4295 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004296 *p = 0;
4297 }
4298 else
4299 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004300
Simon Kelley611ebc52012-07-16 16:23:46 +01004301 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004302 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004303 for (option = 0, i = 0; opts[i].name; i++)
4304 if (strcmp(opts[i].name, start) == 0)
4305 {
4306 option = opts[i].val;
4307 break;
4308 }
4309
4310 if (!option)
4311 errmess = _("bad option");
4312 else if (opts[i].has_arg == 0 && arg)
4313 errmess = _("extraneous parameter");
4314 else if (opts[i].has_arg == 1 && !arg)
4315 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004316 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4317 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004318 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004319
4320 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004321 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004322 strcpy(daemon->namebuff, errmess);
4323
Simon Kelley7b1eae42014-02-20 13:43:28 +00004324 if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004325 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004326 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004327 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004328 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004329 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004330 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004331 }
Simon Kelley849a8352006-06-09 21:02:31 +01004332 }
4333
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004334 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004335 fclose(f);
4336}
4337
Simon Kelley4f7bb572018-03-08 18:47:08 +00004338#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004339int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004340{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004341 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4342
Simon Kelley70d18732015-01-31 19:59:29 +00004343 if (flags & AH_DHCP_HST)
4344 return one_file(file, LOPT_BANK);
4345 else if (flags & AH_DHCP_OPT)
4346 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004347
Simon Kelley70d18732015-01-31 19:59:29 +00004348 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004349}
4350#endif
4351
Simon Kelley395eb712012-07-06 22:07:05 +01004352static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004353{
4354 FILE *f;
4355 int nofile_ok = 0;
4356 static int read_stdin = 0;
4357 static struct fileread {
4358 dev_t dev;
4359 ino_t ino;
4360 struct fileread *next;
4361 } *filesread = NULL;
4362
4363 if (hard_opt == '7')
4364 {
4365 /* default conf-file reading */
4366 hard_opt = 0;
4367 nofile_ok = 1;
4368 }
4369
4370 if (hard_opt == 0 && strcmp(file, "-") == 0)
4371 {
4372 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004373 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004374 read_stdin = 1;
4375 file = "stdin";
4376 f = stdin;
4377 }
4378 else
4379 {
4380 /* ignore repeated files. */
4381 struct stat statbuf;
4382
4383 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4384 {
4385 struct fileread *r;
4386
4387 for (r = filesread; r; r = r->next)
4388 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004389 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004390
4391 r = safe_malloc(sizeof(struct fileread));
4392 r->next = filesread;
4393 filesread = r;
4394 r->dev = statbuf.st_dev;
4395 r->ino = statbuf.st_ino;
4396 }
4397
4398 if (!(f = fopen(file, "r")))
4399 {
4400 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004401 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004402 else
4403 {
4404 char *str = _("cannot read %s: %s");
4405 if (hard_opt != 0)
4406 {
4407 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004408 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004409 }
4410 else
4411 die(str, file, EC_FILE);
4412 }
4413 }
4414 }
4415
4416 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004417 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004418}
4419
4420/* expand any name which is a directory */
4421struct hostsfile *expand_filelist(struct hostsfile *list)
4422{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004423 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004424 struct hostsfile *ah;
4425
Simon Kelley19c51cf2014-03-18 22:38:30 +00004426 /* find largest used index */
4427 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004428 {
4429 if (i <= ah->index)
4430 i = ah->index + 1;
4431
4432 if (ah->flags & AH_DIR)
4433 ah->flags |= AH_INACTIVE;
4434 else
4435 ah->flags &= ~AH_INACTIVE;
4436 }
4437
4438 for (ah = list; ah; ah = ah->next)
4439 if (!(ah->flags & AH_INACTIVE))
4440 {
4441 struct stat buf;
4442 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4443 {
4444 DIR *dir_stream;
4445 struct dirent *ent;
4446
4447 /* don't read this as a file */
4448 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004449
Simon Kelley28866e92011-02-14 20:19:14 +00004450 if (!(dir_stream = opendir(ah->fname)))
4451 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4452 ah->fname, strerror(errno));
4453 else
4454 {
4455 while ((ent = readdir(dir_stream)))
4456 {
4457 size_t lendir = strlen(ah->fname);
4458 size_t lenfile = strlen(ent->d_name);
4459 struct hostsfile *ah1;
4460 char *path;
4461
4462 /* ignore emacs backups and dotfiles */
4463 if (lenfile == 0 ||
4464 ent->d_name[lenfile - 1] == '~' ||
4465 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4466 ent->d_name[0] == '.')
4467 continue;
4468
4469 /* see if we have an existing record.
4470 dir is ah->fname
4471 file is ent->d_name
4472 path to match is ah1->fname */
4473
4474 for (ah1 = list; ah1; ah1 = ah1->next)
4475 {
4476 if (lendir < strlen(ah1->fname) &&
4477 strstr(ah1->fname, ah->fname) == ah1->fname &&
4478 ah1->fname[lendir] == '/' &&
4479 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4480 {
4481 ah1->flags &= ~AH_INACTIVE;
4482 break;
4483 }
4484 }
4485
4486 /* make new record */
4487 if (!ah1)
4488 {
4489 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4490 continue;
4491
4492 if (!(path = whine_malloc(lendir + lenfile + 2)))
4493 {
4494 free(ah1);
4495 continue;
4496 }
4497
4498 strcpy(path, ah->fname);
4499 strcat(path, "/");
4500 strcat(path, ent->d_name);
4501 ah1->fname = path;
4502 ah1->index = i++;
4503 ah1->flags = AH_DIR;
4504 ah1->next = list;
4505 list = ah1;
4506 }
4507
4508 /* inactivate record if not regular file */
4509 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4510 ah1->flags |= AH_INACTIVE;
4511
4512 }
4513 closedir(dir_stream);
4514 }
4515 }
4516 }
4517
4518 return list;
4519}
4520
Simon Kelley7b1eae42014-02-20 13:43:28 +00004521void read_servers_file(void)
4522{
4523 FILE *f;
4524
4525 if (!(f = fopen(daemon->servers_file, "r")))
4526 {
4527 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4528 return;
4529 }
4530
4531 mark_servers(SERV_FROM_FILE);
4532 cleanup_servers();
4533
4534 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4535}
4536
Simon Kelley28866e92011-02-14 20:19:14 +00004537
Simon Kelley7622fc02009-06-04 20:32:05 +01004538#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004539static void clear_dynamic_conf(void)
4540{
4541 struct dhcp_config *configs, *cp, **up;
4542
4543 /* remove existing... */
4544 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4545 {
4546 cp = configs->next;
4547
4548 if (configs->flags & CONFIG_BANK)
4549 {
4550 struct hwaddr_config *mac, *tmp;
4551 struct dhcp_netid_list *list, *tmplist;
4552
4553 for (mac = configs->hwaddr; mac; mac = tmp)
4554 {
4555 tmp = mac->next;
4556 free(mac);
4557 }
4558
4559 if (configs->flags & CONFIG_CLID)
4560 free(configs->clid);
4561
4562 for (list = configs->netid; list; list = tmplist)
4563 {
4564 free(list->list);
4565 tmplist = list->next;
4566 free(list);
4567 }
4568
4569 if (configs->flags & CONFIG_NAME)
4570 free(configs->hostname);
4571
4572 *up = configs->next;
4573 free(configs);
4574 }
4575 else
4576 up = &configs->next;
4577 }
4578}
4579
4580static void clear_dynamic_opt(void)
4581{
4582 struct dhcp_opt *opts, *cp, **up;
4583 struct dhcp_netid *id, *next;
4584
4585 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4586 {
4587 cp = opts->next;
4588
4589 if (opts->flags & DHOPT_BANK)
4590 {
4591 if ((opts->flags & DHOPT_VENDOR))
4592 free(opts->u.vendor_class);
4593 free(opts->val);
4594 for (id = opts->netid; id; id = next)
4595 {
4596 next = id->next;
4597 free(id->net);
4598 free(id);
4599 }
4600 *up = opts->next;
4601 free(opts);
4602 }
4603 else
4604 up = &opts->next;
4605 }
4606}
4607
Simon Kelley824af852008-02-12 20:43:05 +00004608void reread_dhcp(void)
4609{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004610 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004611
Simon Kelley4f7bb572018-03-08 18:47:08 +00004612 /* Do these even if there is no daemon->dhcp_hosts_file or
4613 daemon->dhcp_opts_file since entries may have been created by the
4614 inotify dynamic file reading system. */
4615
4616 clear_dynamic_conf();
4617 clear_dynamic_opt();
4618
4619 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004620 {
Simon Kelley28866e92011-02-14 20:19:14 +00004621 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4622 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004623 if (!(hf->flags & AH_INACTIVE))
4624 {
4625 if (one_file(hf->fname, LOPT_BANK))
4626 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4627 }
Simon Kelley824af852008-02-12 20:43:05 +00004628 }
4629
4630 if (daemon->dhcp_opts_file)
4631 {
Simon Kelley28866e92011-02-14 20:19:14 +00004632 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4633 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4634 if (!(hf->flags & AH_INACTIVE))
4635 {
Simon Kelley395eb712012-07-06 22:07:05 +01004636 if (one_file(hf->fname, LOPT_OPTS))
4637 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004638 }
Simon Kelley824af852008-02-12 20:43:05 +00004639 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004640
4641# ifdef HAVE_INOTIFY
4642 /* Setup notify and read pre-existing files. */
4643 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
4644# endif
Simon Kelley824af852008-02-12 20:43:05 +00004645}
Simon Kelley7622fc02009-06-04 20:32:05 +01004646#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00004647
Simon Kelley5aabfc72007-08-29 11:24:47 +01004648void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004649{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004650 size_t argbuf_size = MAXDNAME;
4651 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004652 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004653 int option, conffile_opt = '7', testmode = 0;
Simon Kelley90cb2222015-07-05 21:59:10 +01004654 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004655
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004656 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004657
Simon Kelley824af852008-02-12 20:43:05 +00004658 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004659 memset(daemon, 0, sizeof(struct daemon));
4660 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004661
Simon Kelley3be34542004-09-11 19:12:13 +01004662 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004663 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004664 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004665 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004666 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4667 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004668 daemon->default_resolv.is_default = 1;
4669 daemon->default_resolv.name = RESOLVFILE;
4670 daemon->resolv_files = &daemon->default_resolv;
4671 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004672 daemon->runfile = RUNFILE;
4673 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004674 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004675 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004676 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004677 daemon->auth_ttl = AUTH_TTL;
4678 daemon->soa_refresh = SOA_REFRESH;
4679 daemon->soa_retry = SOA_RETRY;
4680 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00004681 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00004682 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01004683
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004684#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00004685 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
4686 add_txt("authors.bind", "Simon Kelley", 0);
4687 add_txt("copyright.bind", COPYRIGHT, 0);
4688 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
4689 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
4690 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
4691 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
4692 add_txt("hits.bind", NULL, TXT_STAT_HITS);
4693#ifdef HAVE_AUTH
4694 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
4695#endif
4696 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004697#endif
Simon Kelley0a852542005-03-23 20:28:59 +00004698
Simon Kelley849a8352006-06-09 21:02:31 +01004699 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004700 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004701#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004702 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004703#else
Simon Kelley849a8352006-06-09 21:02:31 +01004704 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004705#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004706
4707 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004708 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004709 for (; optind < argc; optind++)
4710 {
4711 unsigned char *c = (unsigned char *)argv[optind];
4712 for (; *c != 0; c++)
4713 if (!isspace(*c))
4714 die(_("junk found in command line"), NULL, EC_BADCONF);
4715 }
Simon Kelley28866e92011-02-14 20:19:14 +00004716 break;
4717 }
4718
Simon Kelley849a8352006-06-09 21:02:31 +01004719 /* Copy optarg so that argv doesn't get changed */
4720 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004721 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00004722 if (strlen(optarg) >= argbuf_size)
4723 {
4724 free(argbuf);
4725 argbuf_size = strlen(optarg) + 1;
4726 argbuf = opt_malloc(argbuf_size);
4727 }
4728 strncpy(argbuf, optarg, argbuf_size);
4729 argbuf[argbuf_size-1] = 0;
4730 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01004731 }
4732 else
4733 arg = NULL;
4734
4735 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004736 if (option == LOPT_TEST)
4737 testmode = 1;
4738 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004739 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004740#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004741 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004742 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004743#ifdef HAVE_DHCP6
4744 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4745 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004746#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004747 else
4748#endif
4749 do_usage();
4750
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004751 exit(0);
4752 }
Simon Kelley849a8352006-06-09 21:02:31 +01004753 else if (option == 'v')
4754 {
4755 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004756 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004757 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4758 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004759 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004760 exit(0);
4761 }
Simon Kelley849a8352006-06-09 21:02:31 +01004762 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004763 {
Simon Kelley28866e92011-02-14 20:19:14 +00004764 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004765 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004766 }
Simon Kelley849a8352006-06-09 21:02:31 +01004767 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004768 {
Simon Kelley26128d22004-11-14 16:43:54 +00004769#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00004770 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004771#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00004772 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004773#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004774 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004775 }
4776 }
Simon Kelley849a8352006-06-09 21:02:31 +01004777
Neil Jerram3bd4c472018-01-18 22:49:38 +00004778 free(argbuf);
4779
Simon Kelley849a8352006-06-09 21:02:31 +01004780 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00004781 {
4782 one_file(conffile, conffile_opt);
Simon Kelley90cb2222015-07-05 21:59:10 +01004783 if (conffile_opt == 0)
4784 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00004785 }
Simon Kelley849a8352006-06-09 21:02:31 +01004786
Simon Kelley1a6bca82008-07-11 11:11:42 +01004787 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004788 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004789 {
4790 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004791 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01004792 if (!(tmp->flags & SERV_HAS_SOURCE))
4793 {
4794 if (tmp->source_addr.sa.sa_family == AF_INET)
4795 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004796#ifdef HAVE_IPV6
Simon Kelley14ffa072016-04-25 16:36:44 +01004797 else if (tmp->source_addr.sa.sa_family == AF_INET6)
4798 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004799#endif
Simon Kelley14ffa072016-04-25 16:36:44 +01004800 }
4801 }
4802
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004803 if (daemon->host_records)
4804 {
4805 struct host_record *hr;
4806
4807 for (hr = daemon->host_records; hr; hr = hr->next)
4808 if (hr->ttl == -1)
4809 hr->ttl = daemon->local_ttl;
4810 }
4811
4812 if (daemon->cnames)
4813 {
Simon Kelley903df072017-01-19 17:22:00 +00004814 struct cname *cn, *cn2, *cn3;
4815
4816#define NOLOOP 1
4817#define TESTLOOP 2
4818
4819 /* Fill in TTL for CNAMES noe we have local_ttl.
4820 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004821 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00004822 {
4823 if (cn->ttl == -1)
4824 cn->ttl = daemon->local_ttl;
4825 cn->flag = 0;
4826 cn->targetp = NULL;
4827 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
4828 if (hostname_isequal(cn->target, cn2->alias))
4829 {
4830 cn->targetp = cn2;
4831 break;
4832 }
4833 }
4834
4835 /* Find any CNAME loops.*/
4836 for (cn = daemon->cnames; cn; cn = cn->next)
4837 {
4838 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
4839 {
4840 if (cn2->flag == NOLOOP)
4841 break;
4842
4843 if (cn2->flag == TESTLOOP)
4844 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
4845
4846 cn2->flag = TESTLOOP;
4847 }
4848
4849 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
4850 cn3->flag = NOLOOP;
4851 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004852 }
4853
Simon Kelley3be34542004-09-11 19:12:13 +01004854 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004855 {
4856 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004857 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004858 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004859 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004860#ifdef HAVE_IPV6
4861 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004862 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004863#endif /* IPv6 */
4864 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004865
4866 /* create default, if not specified */
4867 if (daemon->authserver && !daemon->hostmaster)
4868 {
4869 strcpy(buff, "hostmaster.");
4870 strcat(buff, daemon->authserver);
4871 daemon->hostmaster = opt_string_alloc(buff);
4872 }
4873
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004874 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004875 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004876 {
Simon Kelley0a852542005-03-23 20:28:59 +00004877 struct mx_srv_record *mx;
4878
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004879 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004880 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004881
Simon Kelley0a852542005-03-23 20:28:59 +00004882 for (mx = daemon->mxnames; mx; mx = mx->next)
4883 if (!mx->issrv && hostname_isequal(mx->name, buff))
4884 break;
4885
Simon Kelley28866e92011-02-14 20:19:14 +00004886 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004887 {
Simon Kelley824af852008-02-12 20:43:05 +00004888 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004889 mx->next = daemon->mxnames;
4890 mx->issrv = 0;
4891 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004892 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004893 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004894 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004895
Simon Kelley3be34542004-09-11 19:12:13 +01004896 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004897 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004898
4899 for (mx = daemon->mxnames; mx; mx = mx->next)
4900 if (!mx->issrv && !mx->target)
4901 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004902 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004903
Simon Kelley28866e92011-02-14 20:19:14 +00004904 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004905 daemon->resolv_files &&
4906 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004907 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004908 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004909
Simon Kelley28866e92011-02-14 20:19:14 +00004910 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004911 {
4912 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004913 FILE *f;
4914
Simon Kelley28866e92011-02-14 20:19:14 +00004915 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004916 !daemon->resolv_files ||
4917 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004918 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004919
Simon Kelley3be34542004-09-11 19:12:13 +01004920 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004921 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004922
4923 while ((line = fgets(buff, MAXDNAME, f)))
4924 {
4925 char *token = strtok(line, " \t\n\r");
4926
4927 if (!token || strcmp(token, "search") != 0)
4928 continue;
4929
4930 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004931 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004932 break;
4933 }
Simon Kelley3be34542004-09-11 19:12:13 +01004934
Simon Kelleyde379512004-06-22 20:23:33 +01004935 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004936
Simon Kelley3be34542004-09-11 19:12:13 +01004937 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004938 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004939 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004940
4941 if (daemon->domain_suffix)
4942 {
4943 /* add domain for any srv record without one. */
4944 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004945
Simon Kelley3d8df262005-08-29 12:19:27 +01004946 for (srv = daemon->mxnames; srv; srv = srv->next)
4947 if (srv->issrv &&
4948 strchr(srv->name, '.') &&
4949 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4950 {
4951 strcpy(buff, srv->name);
4952 strcat(buff, ".");
4953 strcat(buff, daemon->domain_suffix);
4954 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004955 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004956 }
4957 }
Simon Kelley28866e92011-02-14 20:19:14 +00004958 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004959 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004960
Simon Kelleyc8a80482014-03-05 14:29:54 +00004961 /* If there's access-control config, then ignore --local-service, it's intended
4962 as a system default to keep otherwise unconfigured installations safe. */
4963 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
4964 reset_option_bool(OPT_LOCAL_SERVICE);
4965
Simon Kelley7622fc02009-06-04 20:32:05 +01004966 if (testmode)
4967 {
4968 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
4969 exit(0);
4970 }
Simon Kelley849a8352006-06-09 21:02:31 +01004971}