blob: 316d9c892054a17fc40646fd641f605b274175ba [file] [log] [blame]
Simon Kelley2a8710a2020-01-05 16:40:06 +00001/* dnsmasq is Copyright (c) 2000-2020 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 Kelleye98bd522014-03-28 20:41:23 +0000132#define LOPT_RELAY 323
133#define LOPT_RA_PARAM 324
134#define LOPT_ADD_SBNET 325
135#define LOPT_QUIET_DHCP 326
136#define LOPT_QUIET_DHCP6 327
137#define LOPT_QUIET_RA 328
138#define LOPT_SEC_VALID 329
139#define LOPT_TRUST_ANCHOR 330
140#define LOPT_DNSSEC_DEBUG 331
141#define LOPT_REV_SERV 332
142#define LOPT_SERVERS_FILE 333
143#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000144#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000145#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100146#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000147#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000148#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000149#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000150#define LOPT_DHOPT_INOTIFY 341
151#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000152#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100153#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000154#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000155#define LOPT_CPE_ID 346
156#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000157#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000158#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100159#define LOPT_REPLY_DELAY 350
Simon Kelley734d5312018-03-23 23:09:53 +0000160#define LOPT_RAPID_COMMIT 351
Simon Kelley6b173352018-05-08 18:32:14 +0100161#define LOPT_DUMPFILE 352
162#define LOPT_DUMPMASK 353
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100163#define LOPT_UBUS 354
Simon Kelleyc8226202018-08-08 23:46:03 +0100164#define LOPT_NAME_MATCH 355
Simon Kelley974a6d02018-08-23 23:01:16 +0100165#define LOPT_CAA 356
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000166#define LOPT_SHARED_NET 357
Florent Fourcot13a58f92019-06-20 10:26:40 +0200167#define LOPT_IGNORE_CLID 358
Simon Kelley66f62652020-01-05 16:21:24 +0000168#define LOPT_SINGLE_PORT 359
Simon Kelleyee645822020-02-27 16:34:14 +0000169#define LOPT_SCRIPT_TIME 360
Wang Shanker4ded9622020-12-04 10:17:35 +0800170#define LOPT_PXE_VENDOR 361
Simon Kelleybec366b2016-02-24 22:03:26 +0000171
Simon Kelley849a8352006-06-09 21:02:31 +0100172#ifdef HAVE_GETOPT_LONG
173static const struct option opts[] =
174#else
175static const struct myoption opts[] =
176#endif
177 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100178 { "version", 0, 0, 'v' },
179 { "no-hosts", 0, 0, 'h' },
180 { "no-poll", 0, 0, 'n' },
181 { "help", 0, 0, 'w' },
182 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000183 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100184 { "user", 2, 0, 'u' },
185 { "group", 2, 0, 'g' },
186 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000187 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100188 { "mx-host", 1, 0, 'm' },
189 { "mx-target", 1, 0, 't' },
190 { "cache-size", 2, 0, 'c' },
191 { "port", 1, 0, 'p' },
192 { "dhcp-leasefile", 2, 0, 'l' },
193 { "dhcp-lease", 1, 0, 'l' },
194 { "dhcp-host", 1, 0, 'G' },
195 { "dhcp-range", 1, 0, 'F' },
196 { "dhcp-option", 1, 0, 'O' },
197 { "dhcp-boot", 1, 0, 'M' },
198 { "domain", 1, 0, 's' },
199 { "domain-suffix", 1, 0, 's' },
200 { "interface", 1, 0, 'i' },
201 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000202 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100203 { "bogus-priv", 0, 0, 'b' },
204 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000205 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100206 { "selfmx", 0, 0, 'e' },
207 { "filterwin2k", 0, 0, 'f' },
208 { "pid-file", 2, 0, 'x' },
209 { "strict-order", 0, 0, 'o' },
210 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000211 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100212 { "local", 1, 0, LOPT_LOCAL },
213 { "address", 1, 0, 'A' },
214 { "conf-file", 2, 0, 'C' },
215 { "no-resolv", 0, 0, 'R' },
216 { "expand-hosts", 0, 0, 'E' },
217 { "localmx", 0, 0, 'L' },
218 { "local-ttl", 1, 0, 'T' },
219 { "no-negcache", 0, 0, 'N' },
220 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000221 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100222 { "query-port", 1, 0, 'Q' },
223 { "except-interface", 1, 0, 'I' },
224 { "no-dhcp-interface", 1, 0, '2' },
225 { "domain-needed", 0, 0, 'D' },
226 { "dhcp-lease-max", 1, 0, 'X' },
227 { "bind-interfaces", 0, 0, 'z' },
228 { "read-ethers", 0, 0, 'Z' },
229 { "alias", 1, 0, 'V' },
230 { "dhcp-vendorclass", 1, 0, 'U' },
231 { "dhcp-userclass", 1, 0, 'j' },
232 { "dhcp-ignore", 1, 0, 'J' },
233 { "edns-packet-max", 1, 0, 'P' },
234 { "keep-in-foreground", 0, 0, 'k' },
235 { "dhcp-authoritative", 0, 0, 'K' },
236 { "srv-host", 1, 0, 'W' },
237 { "localise-queries", 0, 0, 'y' },
238 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100239 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100240 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100241 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100242 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100243 { "bootp-dynamic", 2, 0, '3' },
244 { "dhcp-mac", 1, 0, '4' },
245 { "no-ping", 0, 0, '5' },
246 { "dhcp-script", 1, 0, '6' },
247 { "conf-dir", 1, 0, '7' },
248 { "log-facility", 1, 0 ,'8' },
249 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000250 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100251 { "dns-forward-max", 1, 0, '0' },
252 { "clear-on-reload", 0, 0, LOPT_RELOAD },
253 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100254 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100255 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100256 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100257 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100258 { "tftp-root", 1, 0, LOPT_PREFIX },
259 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000260 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100261 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000262 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100263 { "ptr-record", 1, 0, LOPT_PTR },
264 { "naptr-record", 1, 0, LOPT_NAPTR },
265 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000266 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100267 { "dhcp-option-force", 1, 0, LOPT_FORCE },
268 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
269 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
270 { "log-async", 2, 0, LOPT_MAX_LOGS },
271 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
272 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
273 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800274 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100275 { "interface-name", 1, 0, LOPT_INTNAME },
276 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
277 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000278 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000279 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100280 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
281 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
282 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100283 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100284 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100285 { "dhcp-match", 1, 0, LOPT_MATCH },
286 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100287 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100288 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100289 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000290 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100291 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100292 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
293 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
294 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000295 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100296 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
297 { "cname", 1, 0, LOPT_CNAME },
298 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
299 { "pxe-service", 1, 0, LOPT_PXE_SERV },
300 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100301 { "tag-if", 1, 0, LOPT_TAG_IF },
302 { "dhcp-proxy", 2, 0, LOPT_PROXY },
303 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
304 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000305 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100306 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000307 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000308 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100309 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
310 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000311 { "dhcp-client-update", 0, 0, LOPT_FQDN },
312 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000313 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000314 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000315 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100316 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000317 { "auth-zone", 1, 0, LOPT_AUTHZONE },
318 { "auth-server", 1, 0, LOPT_AUTHSERV },
319 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
320 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000321 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000322 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000323 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100324 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200325 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000326 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000327 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100328 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000329 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000330 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100331 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100332 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100333 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
334 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
335 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100336 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000337 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000338 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100339 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000340 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100341 { "dumpfile", 1, 0, LOPT_DUMPFILE },
342 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200343 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelley849a8352006-06-09 21:02:31 +0100344 { NULL, 0, 0, 0 }
345 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000346
Simon Kelley28866e92011-02-14 20:19:14 +0000347
348#define ARG_DUP OPT_LAST
349#define ARG_ONE OPT_LAST + 1
350#define ARG_USED_CL OPT_LAST + 2
351#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000352
Simon Kelley1a6bca82008-07-11 11:11:42 +0100353static struct {
354 int opt;
355 unsigned int rept;
356 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000357 char * const desc;
358 char * const arg;
359} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000360 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
361 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100362 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000363 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
364 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
365 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100366 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
367 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
368 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
369 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
370 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000371 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
372 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100373 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000374 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
375 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000376 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000377 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100378 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100379 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000380 { '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 +0000381 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000382 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
383 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100384 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
385 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
386 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
387 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800388 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100389 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
390 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100391 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
392 { '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 +0000393 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100394 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000395 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100396 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
397 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
398 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
399 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
400 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
401 { 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 +0000402 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
403 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000404 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000405 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100406 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000407 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000408 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000409 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000410 { 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 +0000411 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000412 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000413 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
414 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
415 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
416 { 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 +0000417 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
418 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000419 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100420 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100421 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000422 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
423 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100424 { '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 +0000425 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
426 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100427 { '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 +0000428 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
429 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
430 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100431 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
432 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100433 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100434 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000435 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100436 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
437 { '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 +0000438 { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000439 { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
Simon Kelley1a6bca82008-07-11 11:11:42 +0100440 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000441 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
442 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
443 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000444 { 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 +0000445 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000446 { '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 +0100447 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000448 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100449 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100450 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { 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 +0100452 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100453 { 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 +0100454 { 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 +0100455 { 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 +0100456 { 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 +0000457 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000458 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100459 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100460 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100461 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000462 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100463 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000464 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100465 { 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 +0100466 { 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 +0000467 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100468 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100469 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelleyc8226202018-08-08 23:46:03 +0100470 { LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100471 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100472 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
473 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000474 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000475 { 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 +0000476 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
477 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100478 { 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 +0000479 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100480 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
481 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
482 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000483 { 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 +0100484 { 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 +0000485 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100486 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100487 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200488 { LOPT_IGNORE_CLID, OPT_IGNORE_CLID, NULL, gettext_noop("Ignore client identifier option sent by DHCP clients."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100489 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000490 { 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 +0000491 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000492 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000493 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley974a6d02018-08-23 23:01:16 +0100494 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100495 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000496 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000497 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000498 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000499 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000500 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000501 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
502 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100503 { 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 +0100504 { 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 +0000505 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000506 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000507 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100508 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000509 { 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 +0000510 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100511 { 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 +0100512 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
513 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
514 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000515 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
516 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000517 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000518 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100519 { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
Simon Kelley734d5312018-03-23 23:09:53 +0000520 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100521 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
522 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000523 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100524 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000525};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000526
Josh Soref730c6742017-02-06 16:14:04 +0000527/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100528 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 +0100529 following sequence so that they map to themselves: it is therefore possible to call
530 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000531 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100532 couple of other places.
533 Note that space is included here so that
534 --dhcp-option=3, string
535 has five characters, whilst
536 --dhcp-option=3," string"
537 has six.
538*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100539
Simon Kelleyf2621c72007-04-29 19:47:21 +0100540static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100541
542static char hide_meta(char c)
543{
544 unsigned int i;
545
546 for (i = 0; i < (sizeof(meta) - 1); i++)
547 if (c == meta[i])
548 return (char)i;
549
550 return c;
551}
552
553static char unhide_meta(char cr)
554{
555 unsigned int c = cr;
556
557 if (c < (sizeof(meta) - 1))
558 cr = meta[c];
559
560 return cr;
561}
562
563static void unhide_metas(char *cp)
564{
565 if (cp)
566 for(; *cp; cp++)
567 *cp = unhide_meta(*cp);
568}
569
Simon Kelley824af852008-02-12 20:43:05 +0000570static void *opt_malloc(size_t size)
571{
572 void *ret;
573
574 if (mem_recover)
575 {
576 ret = whine_malloc(size);
577 if (!ret)
578 longjmp(mem_jmp, 1);
579 }
580 else
581 ret = safe_malloc(size);
582
583 return ret;
584}
585
Petr Menšík59e47032018-11-02 22:39:39 +0000586static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100587{
588 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000589 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100590
Petr Menšík59e47032018-11-02 22:39:39 +0000591 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100592 {
Petr Menšík59e47032018-11-02 22:39:39 +0000593 ret = opt_malloc(len+1);
594 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100595
596 /* restore hidden metachars */
597 unhide_metas(ret);
598 }
599
600 return ret;
601}
602
Simon Kelley3d8df262005-08-29 12:19:27 +0100603
Simon Kelleyf2621c72007-04-29 19:47:21 +0100604/* find next comma, split string with zero and eliminate spaces.
605 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000606
607static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100608{
609 char *comma, *p;
610
Simon Kelley73a08a22009-02-05 20:28:08 +0000611 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100612 return NULL;
613
614 p = comma;
615 *comma = ' ';
616
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100617 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100618
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100619 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100620 *p = 0;
621
622 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100623}
624
Simon Kelley73a08a22009-02-05 20:28:08 +0000625static char *split(char *s)
626{
627 return split_chr(s, ',');
628}
629
Simon Kelley1f15b812009-10-13 17:49:32 +0100630static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100631{
Simon Kelley1f15b812009-10-13 17:49:32 +0100632 char *ret;
633 int nomem;
634
Simon Kelley3d8df262005-08-29 12:19:27 +0100635 if (!s)
636 return 0;
637
638 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100639 if (!(ret = canonicalise(s, &nomem)) && nomem)
640 {
641 if (mem_recover)
642 longjmp(mem_jmp, 1);
643 else
644 die(_("could not get memory"), NULL, EC_NOMEM);
645 }
646
647 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100648}
649
650static int atoi_check(char *a, int *res)
651{
652 char *p;
653
654 if (!a)
655 return 0;
656
657 unhide_metas(a);
658
659 for (p = a; *p; p++)
660 if (*p < '0' || *p > '9')
661 return 0;
662
663 *res = atoi(a);
664 return 1;
665}
666
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100667static int atoi_check16(char *a, int *res)
668{
669 if (!(atoi_check(a, res)) ||
670 *res < 0 ||
671 *res > 0xffff)
672 return 0;
673
674 return 1;
675}
Simon Kelleyee415862014-02-11 11:07:22 +0000676
Simon Kelleyde73a492014-02-17 21:43:27 +0000677#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000678static int atoi_check8(char *a, int *res)
679{
680 if (!(atoi_check(a, res)) ||
681 *res < 0 ||
682 *res > 0xff)
683 return 0;
684
685 return 1;
686}
Simon Kelleyde73a492014-02-17 21:43:27 +0000687#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100688
689#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000690static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000691{
Simon Kelley824af852008-02-12 20:43:05 +0000692 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000693
694 if (txt)
695 {
696 size_t len = strlen(txt);
697 r->txt = opt_malloc(len+1);
698 r->len = len+1;
699 *(r->txt) = len;
700 memcpy((r->txt)+1, txt, len);
701 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100702
Simon Kelleyfec216d2014-03-27 20:54:34 +0000703 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000704 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000705 r->next = daemon->txt;
706 daemon->txt = r;
707 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000708}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100709#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000710
Simon Kelley849a8352006-06-09 21:02:31 +0100711static void do_usage(void)
712{
713 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000714 int i, j;
715
716 struct {
717 char handle;
718 int val;
719 } tab[] = {
720 { '$', CACHESIZ },
721 { '*', EDNS_PKTSZ },
722 { '&', MAXLEASES },
723 { '!', FTABSIZ },
724 { '#', TFTP_MAX_CONNECTIONS },
725 { '\0', 0 }
726 };
Simon Kelley849a8352006-06-09 21:02:31 +0100727
728 printf(_("Usage: dnsmasq [options]\n\n"));
729#ifndef HAVE_GETOPT_LONG
730 printf(_("Use short options only on the command line.\n"));
731#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100732 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100733
Simon Kelley1a6bca82008-07-11 11:11:42 +0100734 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100735 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100736 char *desc = usage[i].flagdesc;
737 char *eq = "=";
738
739 if (!desc || *desc == '[')
740 eq = "";
741
742 if (!desc)
743 desc = "";
744
745 for ( j = 0; opts[j].name; j++)
746 if (opts[j].val == usage[i].opt)
747 break;
748 if (usage[i].opt < 256)
749 sprintf(buff, "-%c, ", usage[i].opt);
750 else
751 sprintf(buff, " ");
752
753 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100754 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100755
Simon Kelley849a8352006-06-09 21:02:31 +0100756 if (usage[i].arg)
757 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000758 strcpy(buff, usage[i].arg);
759 for (j = 0; tab[j].handle; j++)
760 if (tab[j].handle == *(usage[i].arg))
761 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100762 }
Simon Kelley849a8352006-06-09 21:02:31 +0100763 printf(_(usage[i].desc), buff);
764 printf("\n");
765 }
766}
767
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100768#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000769#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
770#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100771
Ed Bardsleya7369be2015-08-05 21:17:18 +0100772static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
773{
774 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
775 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100776 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
777 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100778 else
779 return _("bad address");
780
781 return NULL;
782}
783
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100784char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
785{
786 int source_port = 0, serv_port = NAMESERVER_PORT;
787 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000788 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100789 int scope_index = 0;
790 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100791
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000792 if (!arg || strlen(arg) == 0)
793 {
794 *flags |= SERV_NO_ADDR;
795 *interface = 0;
796 return NULL;
797 }
798
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100799 if ((source = split_chr(arg, '@')) && /* is there a source. */
800 (portno = split_chr(source, '#')) &&
801 !atoi_check16(portno, &source_port))
802 return _("bad port");
803
804 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
805 !atoi_check16(portno, &serv_port))
806 return _("bad port");
807
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100808 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100809
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000810 if (source) {
811 interface_opt = split_chr(source, '@');
812
813 if (interface_opt)
814 {
815#if defined(SO_BINDTODEVICE)
Petr Menšík47b45b22018-08-15 18:17:00 +0200816 safe_strncpy(interface, interface_opt, IF_NAMESIZE);
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000817#else
818 return _("interface binding not supported");
819#endif
820 }
821 }
822
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100823 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100824 {
825 addr->in.sin_port = htons(serv_port);
826 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
827#ifdef HAVE_SOCKADDR_SA_LEN
828 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
829#endif
830 source_addr->in.sin_addr.s_addr = INADDR_ANY;
831 source_addr->in.sin_port = htons(daemon->query_port);
832
833 if (source)
834 {
835 if (flags)
836 *flags |= SERV_HAS_SOURCE;
837 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100838 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100839 {
840#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000841 if (interface_opt)
842 return _("interface can only be specified once");
843
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100844 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200845 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100846#else
847 return _("interface binding not supported");
848#endif
849 }
850 }
851 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100852 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
853 {
854 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
855 return _("bad interface name");
856
857 addr->in6.sin6_port = htons(serv_port);
858 addr->in6.sin6_scope_id = scope_index;
859 source_addr->in6.sin6_addr = in6addr_any;
860 source_addr->in6.sin6_port = htons(daemon->query_port);
861 source_addr->in6.sin6_scope_id = 0;
862 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
863 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
864#ifdef HAVE_SOCKADDR_SA_LEN
865 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
866#endif
867 if (source)
868 {
869 if (flags)
870 *flags |= SERV_HAS_SOURCE;
871 source_addr->in6.sin6_port = htons(source_port);
872 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
873 {
874#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000875 if (interface_opt)
876 return _("interface can only be specified once");
877
878 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200879 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100880#else
881 return _("interface binding not supported");
882#endif
883 }
884 }
885 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100886 else
887 return _("bad address");
888
889 return NULL;
890}
891
Simon Kelleyde73a492014-02-17 21:43:27 +0000892static struct server *add_rev4(struct in_addr addr, int msize)
893{
894 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000895 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000896 char *p;
897
898 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000899 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
900
901 switch (msize)
902 {
903 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100904 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000905 /* fall through */
906 case 24:
907 p += sprintf(p, "%d.", (a >> 8) & 0xff);
908 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000909 case 16:
910 p += sprintf(p, "%d.", (a >> 16) & 0xff);
911 /* fall through */
912 case 8:
913 p += sprintf(p, "%d.", (a >> 24) & 0xff);
914 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000915 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000916 free(serv->domain);
917 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000918 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000919 }
920
921 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000922
923 serv->flags = SERV_HAS_DOMAIN;
924 serv->next = daemon->servers;
925 daemon->servers = serv;
926
927 return serv;
928
929}
930
931static struct server *add_rev6(struct in6_addr *addr, int msize)
932{
933 struct server *serv = opt_malloc(sizeof(struct server));
934 char *p;
935 int i;
936
937 memset(serv, 0, sizeof(struct server));
938 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
939
940 for (i = msize-1; i >= 0; i -= 4)
941 {
942 int dig = ((unsigned char *)addr)[i>>3];
943 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
944 }
945 p += sprintf(p, "ip6.arpa");
946
947 serv->flags = SERV_HAS_DOMAIN;
948 serv->next = daemon->servers;
949 daemon->servers = serv;
950
951 return serv;
952}
953
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000954#ifdef HAVE_DHCP
955
956static int is_tag_prefix(char *arg)
957{
958 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
959 return 1;
960
961 return 0;
962}
963
964static char *set_prefix(char *arg)
965{
966 if (strstr(arg, "set:") == arg)
967 return arg+4;
968
969 return arg;
970}
971
Simon Kelley52ec7832020-02-07 21:05:54 +0000972static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000973{
974 struct dhcp_netid *tt;
975 tt = opt_malloc(sizeof (struct dhcp_netid));
976 tt->net = opt_string_alloc(net);
977 tt->next = next;
978 return tt;
979}
980
981static void dhcp_netid_free(struct dhcp_netid *nid)
982{
983 while (nid)
984 {
985 struct dhcp_netid *tmp = nid;
986 nid = nid->next;
987 free(tmp->net);
988 free(tmp);
989 }
990}
991
992/* Parse one or more tag:s before parameters.
993 * Moves arg to the end of tags. */
994static struct dhcp_netid * dhcp_tags(char **arg)
995{
996 struct dhcp_netid *id = NULL;
997
998 while (is_tag_prefix(*arg))
999 {
1000 char *comma = split(*arg);
1001 id = dhcp_netid_create((*arg)+4, id);
1002 *arg = comma;
1003 };
1004 if (!*arg)
1005 {
1006 dhcp_netid_free(id);
1007 id = NULL;
1008 }
1009 return id;
1010}
1011
1012static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1013{
1014 while (netid)
1015 {
1016 struct dhcp_netid_list *tmplist = netid;
1017 netid = netid->next;
1018 dhcp_netid_free(tmplist->list);
1019 free(tmplist);
1020 }
1021}
1022
1023static void dhcp_config_free(struct dhcp_config *config)
1024{
1025 if (config)
1026 {
1027 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001028
Petr Menšík59e47032018-11-02 22:39:39 +00001029 while (hwaddr)
1030 {
1031 struct hwaddr_config *tmp = hwaddr;
1032 hwaddr = hwaddr->next;
1033 free(tmp);
1034 }
Simon Kelley137286e2020-02-06 22:09:30 +00001035
Petr Menšík59e47032018-11-02 22:39:39 +00001036 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001037 dhcp_netid_free(config->filter);
1038
Petr Menšík59e47032018-11-02 22:39:39 +00001039 if (config->flags & CONFIG_CLID)
1040 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001041
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001042#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001043 if (config->flags & CONFIG_ADDR6)
1044 {
1045 struct addrlist *addr, *tmp;
1046
1047 for (addr = config->addr6; addr; addr = tmp)
1048 {
1049 tmp = addr->next;
1050 free(addr);
1051 }
1052 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001053#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001054
Petr Menšík59e47032018-11-02 22:39:39 +00001055 free(config);
1056 }
1057}
1058
1059static void dhcp_context_free(struct dhcp_context *ctx)
1060{
1061 if (ctx)
1062 {
1063 dhcp_netid_free(ctx->filter);
1064 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001065#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001066 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001067#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001068 free(ctx);
1069 }
1070}
1071
1072static void dhcp_opt_free(struct dhcp_opt *opt)
1073{
1074 if (opt->flags & DHOPT_VENDOR)
1075 free(opt->u.vendor_class);
1076 dhcp_netid_free(opt->netid);
1077 free(opt->val);
1078 free(opt);
1079}
1080
1081
Simon Kelley832af0b2007-01-21 20:01:28 +00001082/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001083static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001084{
Simon Kelley824af852008-02-12 20:43:05 +00001085 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001086 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001087 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001088 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001089 u16 opt_len = 0;
1090 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001091 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001092
1093 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001094 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001095 new->netid = NULL;
1096 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001097 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001098
Simon Kelleyf2621c72007-04-29 19:47:21 +01001099 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001100 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001101 comma = split(arg);
1102
1103 for (cp = arg; *cp; cp++)
1104 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001105 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001106
1107 if (!*cp)
1108 {
1109 new->opt = atoi(arg);
1110 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001111 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112 break;
1113 }
1114
1115 if (strstr(arg, "option:") == arg)
1116 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001117 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1118 {
1119 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1120 /* option:<optname> must follow tag and vendor string. */
1121 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1122 option_ok = 1;
1123 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001124 break;
1125 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001126#ifdef HAVE_DHCP6
1127 else if (strstr(arg, "option6:") == arg)
1128 {
1129 for (cp = arg+8; *cp; cp++)
1130 if (*cp < '0' || *cp > '9')
1131 break;
1132
1133 if (!*cp)
1134 {
1135 new->opt = atoi(arg+8);
1136 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001137 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001138 }
1139 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001140 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001141 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1142 {
1143 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1144 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1145 option_ok = 1;
1146 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001147 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001148 /* option6:<opt>|<optname> must follow tag and vendor string. */
1149 is6 = 1;
1150 break;
1151 }
1152#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001153 else if (strstr(arg, "vendor:") == arg)
1154 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001155 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1156 new->flags |= DHOPT_VENDOR;
1157 }
1158 else if (strstr(arg, "encap:") == arg)
1159 {
1160 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001161 new->flags |= DHOPT_ENCAPSULATE;
1162 }
Simon Kelley316e2732010-01-22 20:16:09 +00001163 else if (strstr(arg, "vi-encap:") == arg)
1164 {
1165 new->u.encap = atoi(arg+9);
1166 new->flags |= DHOPT_RFC3925;
1167 if (flags == DHOPT_MATCH)
1168 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001169 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001170 break;
1171 }
1172 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001173 else
1174 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001175 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001176 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1177 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001178 }
1179
1180 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001181 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001182
1183#ifdef HAVE_DHCP6
1184 if (is6)
1185 {
1186 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001187 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001188
1189 if (opt_len == 0 &&
1190 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001191 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001192 }
1193 else
1194#endif
1195 if (opt_len == 0 &&
1196 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001197 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001198
Simon Kelley316e2732010-01-22 20:16:09 +00001199 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001200 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001201 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001202
1203 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001204 {
1205 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001206 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001207 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001208 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001209 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001210 dots = 0;
1211 for (cp = comma; (c = *cp); cp++)
1212 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001213 {
1214 addrs++;
1215 is_dec = is_hex = 0;
1216 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001217 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001218 {
1219 digs++;
1220 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001221 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001222 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001223 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001224 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001225 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001226 if (cp == comma) /* leading / means a pathname */
1227 is_addr = 0;
1228 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001229 else if (c == '.')
1230 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001231 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001232 dots++;
1233 }
1234 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001235 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001236 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001237 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001238 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001239 {
1240 is_addr = 0;
1241 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001242 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001243 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001244 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001245 *cp = 0;
1246 }
1247 else
1248 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001249 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001250 (c >='a' && c <= 'f') ||
1251 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001252 {
1253 is_hex = 0;
1254 if (c != '[' && c != ']')
1255 is_addr6 = 0;
1256 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001257 }
Simon Kelley28866e92011-02-14 20:19:14 +00001258 else
1259 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001260
Simon Kelley28866e92011-02-14 20:19:14 +00001261 if (!found_dig)
1262 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001263
1264 if (!found_colon)
1265 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001266
1267#ifdef HAVE_DHCP6
1268 /* NTP server option takes hex, addresses or FQDN */
1269 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1270 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1271#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001272
Simon Kelleyf2621c72007-04-29 19:47:21 +01001273 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001274 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001275 {
1276 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001277
1278 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001279 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001280
1281 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001282 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001283 }
Simon Kelley28866e92011-02-14 20:19:14 +00001284 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001285 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1286 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001287
1288 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1289 {
1290 int val, fac = 1;
1291
1292 switch (comma[strlen(comma) - 1])
1293 {
Simon Kelley42243212012-07-20 15:19:18 +01001294 case 'w':
1295 case 'W':
1296 fac *= 7;
1297 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001298 case 'd':
1299 case 'D':
1300 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001301 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001302 case 'h':
1303 case 'H':
1304 fac *= 60;
1305 /* fall through */
1306 case 'm':
1307 case 'M':
1308 fac *= 60;
1309 /* fall through */
1310 case 's':
1311 case 'S':
1312 comma[strlen(comma) - 1] = 0;
1313 }
1314
1315 new->len = 4;
1316 new->val = opt_malloc(4);
1317 val = atoi(comma);
1318 *((int *)new->val) = htonl(val * fac);
1319 }
1320 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001321 {
1322 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001323 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001324 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1325 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001326 }
1327 else if (is_dec)
1328 {
1329 int i, val = atoi(comma);
1330 /* assume numeric arg is 1 byte except for
1331 options where it is known otherwise.
1332 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001333 if (opt_len != 0)
1334 new->len = opt_len;
1335 else if (val & 0xffff0000)
1336 new->len = 4;
1337 else if (val & 0xff00)
1338 new->len = 2;
1339 else
1340 new->len = 1;
1341
Simon Kelley832af0b2007-01-21 20:01:28 +00001342 if (lenchar == 'b')
1343 new->len = 1;
1344 else if (lenchar == 's')
1345 new->len = 2;
1346 else if (lenchar == 'i')
1347 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001348
Simon Kelley824af852008-02-12 20:43:05 +00001349 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001350 for (i=0; i<new->len; i++)
1351 new->val[i] = val>>((new->len - i - 1)*8);
1352 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001353 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001354 {
1355 struct in_addr in;
1356 unsigned char *op;
1357 char *slash;
1358 /* max length of address/subnet descriptor is five bytes,
1359 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001360 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001361 new->flags |= DHOPT_ADDR;
1362
Simon Kelley572b41e2011-02-18 18:11:18 +00001363 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1364 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001365 {
Simon Kelley6b010842007-02-12 20:32:07 +00001366 *(op++) = 1; /* RFC 3361 "enc byte" */
1367 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001368 }
1369 while (addrs--)
1370 {
1371 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001372 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001373 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001374 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001375 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001376 if (!slash)
1377 {
1378 memcpy(op, &in, INADDRSZ);
1379 op += INADDRSZ;
1380 }
1381 else
1382 {
1383 unsigned char *p = (unsigned char *)&in;
1384 int netsize = atoi(slash);
1385 *op++ = netsize;
1386 if (netsize > 0)
1387 *op++ = *p++;
1388 if (netsize > 8)
1389 *op++ = *p++;
1390 if (netsize > 16)
1391 *op++ = *p++;
1392 if (netsize > 24)
1393 *op++ = *p++;
1394 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1395 }
1396 }
1397 new->len = op - new->val;
1398 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001399 else if (is_addr6 && is6)
1400 {
1401 unsigned char *op;
1402 new->val = op = opt_malloc(16 * addrs);
1403 new->flags |= DHOPT_ADDR6;
1404 while (addrs--)
1405 {
1406 cp = comma;
1407 comma = split(cp);
1408
1409 /* check for [1234::7] */
1410 if (*cp == '[')
1411 cp++;
1412 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1413 cp[strlen(cp)-1] = 0;
1414
1415 if (inet_pton(AF_INET6, cp, op))
1416 {
1417 op += IN6ADDRSZ;
1418 continue;
1419 }
Petr Menšík59e47032018-11-02 22:39:39 +00001420
1421 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001422 }
1423 new->len = op - new->val;
1424 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001425 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001426 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001427 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001428 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001429 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001430 {
1431 /* dns search, RFC 3397, or SIP, RFC 3361 */
1432 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001433 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001434 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001435 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001436
1437 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001438 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001439
1440 while (arg && *arg)
1441 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001442 char *in, *dom = NULL;
1443 size_t domlen = 1;
1444 /* Allow "." as an empty domain */
1445 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001446 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001447 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001448 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001449
Simon Kelleyc52e1892010-06-07 22:01:39 +01001450 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001451 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001452
1453 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001454 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001455 {
1456 memcpy(newp, m, header_size + len);
1457 free(m);
1458 }
Simon Kelley824af852008-02-12 20:43:05 +00001459 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001460 p = m + header_size;
1461 q = p + len;
1462
1463 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001464 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001465 {
1466 unsigned char *cp = q++;
1467 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001468 for (j = 0; *in && (*in != '.'); in++, j++)
1469 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001470 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001471 if (*in)
1472 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001473 }
1474 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001475 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001476
Simon Kelley832af0b2007-01-21 20:01:28 +00001477 /* Now tail-compress using earlier names. */
1478 newlen = q - p;
1479 for (tail = p + len; *tail; tail += (*tail) + 1)
1480 for (r = p; r - p < (int)len; r += (*r) + 1)
1481 if (strcmp((char *)r, (char *)tail) == 0)
1482 {
1483 PUTSHORT((r - p) | 0xc000, tail);
1484 newlen = tail - p;
1485 goto end;
1486 }
1487 end:
1488 len = newlen;
1489
1490 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001491 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001492 }
1493
1494 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001495 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001496 m[0] = 0;
1497 new->len = (int) len + header_size;
1498 new->val = m;
1499 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001500#ifdef HAVE_DHCP6
1501 else if (comma && (opt_len & OT_CSTRING))
1502 {
1503 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001504 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001505 unsigned char *p, *newp;
1506
Simon Kelley40ef23b2012-03-13 21:59:28 +00001507 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001508 if (comma[i] == ',')
1509 commas++;
1510
1511 newp = opt_malloc(strlen(comma)+(2*commas));
1512 p = newp;
1513 arg = comma;
1514 comma = split(arg);
1515
1516 while (arg && *arg)
1517 {
1518 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001519 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001520 PUTSHORT(len, p);
1521 memcpy(p, arg, len);
1522 p += len;
1523
1524 arg = comma;
1525 comma = split(arg);
1526 }
1527
1528 new->val = newp;
1529 new->len = p - newp;
1530 }
1531 else if (comma && (opt_len & OT_RFC1035_NAME))
1532 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001533 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001534 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001535 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001536 arg = comma;
1537 comma = split(arg);
1538
1539 while (arg && *arg)
1540 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001541 char *dom = canonicalise_opt(arg);
1542 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001543 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001544
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001545 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001546
1547 if (p)
1548 {
1549 memcpy(newp, p, len);
1550 free(p);
1551 }
1552
1553 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001554 q = p + len;
1555 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001556 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001557 if (is6 && new->opt == OPTION6_NTP_SERVER)
1558 {
1559 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1560 PUTSHORT(end - q - 2, q);
1561 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001562 len = end - p;
1563 free(dom);
1564
Simon Kelley4cb1b322012-02-06 14:30:41 +00001565 arg = comma;
1566 comma = split(arg);
1567 }
1568
Simon Kelley18f0fb02012-03-31 21:18:55 +01001569 new->val = p;
1570 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001571 }
1572#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001573 else
1574 {
1575 new->len = strlen(comma);
1576 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001577 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001578 new->flags |= DHOPT_STRING;
1579 }
1580 }
1581 }
1582
Simon Kelley4cb1b322012-02-06 14:30:41 +00001583 if (!is6 &&
1584 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001585 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001586 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001587 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001588
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001589 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001590 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001591 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1592 !new->netid ||
1593 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001594 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001595
1596 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001597 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001598 new->next = daemon->dhcp_match6;
1599 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001600 }
1601 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001602 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001603 new->next = daemon->dhcp_match;
1604 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001605 }
Simon Kelley824af852008-02-12 20:43:05 +00001606 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001607 else if (is6)
1608 {
1609 new->next = daemon->dhcp_opts6;
1610 daemon->dhcp_opts6 = new;
1611 }
1612 else
1613 {
1614 new->next = daemon->dhcp_opts;
1615 daemon->dhcp_opts = new;
1616 }
1617
1618 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001619on_error:
1620 dhcp_opt_free(new);
1621 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001622}
1623
Simon Kelley7622fc02009-06-04 20:32:05 +01001624#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001625
Simon Kelley28866e92011-02-14 20:19:14 +00001626void set_option_bool(unsigned int opt)
1627{
Petr Menšík24b87602018-10-24 22:30:18 +01001628 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001629}
1630
Simon Kelley2b5bae92012-06-26 16:55:23 +01001631void reset_option_bool(unsigned int opt)
1632{
Petr Menšík24b87602018-10-24 22:30:18 +01001633 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001634}
1635
Petr Menšík59e47032018-11-02 22:39:39 +00001636static void server_list_free(struct server *list)
1637{
1638 while (list)
1639 {
1640 struct server *tmp = list;
1641 list = list->next;
1642 free(tmp);
1643 }
1644}
1645
Simon Kelley7b1eae42014-02-20 13:43:28 +00001646static 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 +01001647{
1648 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001649 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001650
Simon Kelley832af0b2007-01-21 20:01:28 +00001651 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001652 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001653
Simon Kelley1a6bca82008-07-11 11:11:42 +01001654 for (i=0; usage[i].opt != 0; i++)
1655 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001656 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001657 int rept = usage[i].rept;
1658
Simon Kelley28866e92011-02-14 20:19:14 +00001659 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001660 {
1661 /* command line */
1662 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001663 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001664 if (rept == ARG_ONE)
1665 usage[i].rept = ARG_USED_CL;
1666 }
1667 else
1668 {
1669 /* allow file to override command line */
1670 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001671 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001672 if (rept == ARG_USED_CL || rept == ARG_ONE)
1673 usage[i].rept = ARG_USED_FILE;
1674 }
1675
1676 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1677 {
Simon Kelley28866e92011-02-14 20:19:14 +00001678 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001679 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001680 }
1681
1682 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001683 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001684
Simon Kelley849a8352006-06-09 21:02:31 +01001685 switch (option)
1686 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001687 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001688 {
Simon Kelley824af852008-02-12 20:43:05 +00001689 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001690 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001691 {
Simon Kelley28866e92011-02-14 20:19:14 +00001692 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001693 free(file);
1694 }
Simon Kelley849a8352006-06-09 21:02:31 +01001695 break;
1696 }
1697
Simon Kelleyf2621c72007-04-29 19:47:21 +01001698 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001699 {
1700 DIR *dir_stream;
1701 struct dirent *ent;
1702 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001703 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001704 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001705 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001706 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001707
Simon Kelley1f15b812009-10-13 17:49:32 +01001708 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001709 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001710 break;
1711
Simon Kelley1f15b812009-10-13 17:49:32 +01001712 for (arg = comma; arg; arg = comma)
1713 {
1714 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001715 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001716 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001717 li = opt_malloc(sizeof(struct list));
1718 if (*arg == '*')
1719 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001720 /* "*" with no suffix is a no-op */
1721 if (arg[1] == 0)
1722 free(li);
1723 else
1724 {
1725 li->next = match_suffix;
1726 match_suffix = li;
1727 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001728 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001729 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001730 }
1731 else
1732 {
1733 li->next = ignore_suffix;
1734 ignore_suffix = li;
1735 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001736 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001737 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001738 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001739 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001740
Simon Kelley849a8352006-06-09 21:02:31 +01001741 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001742 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001743
Simon Kelley849a8352006-06-09 21:02:31 +01001744 while ((ent = readdir(dir_stream)))
1745 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001746 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001747 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001748
1749 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001750 if (len == 0 ||
1751 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001752 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1753 ent->d_name[0] == '.')
1754 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001755
Simon Kelley3e1551a2014-09-09 21:46:07 +01001756 if (match_suffix)
1757 {
1758 for (li = match_suffix; li; li = li->next)
1759 {
1760 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001761 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001762 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001763 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001764 break;
1765 }
1766 if (!li)
1767 continue;
1768 }
1769
Simon Kelley1f15b812009-10-13 17:49:32 +01001770 for (li = ignore_suffix; li; li = li->next)
1771 {
1772 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001773 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001774 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001775 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001776 break;
1777 }
1778 if (li)
1779 continue;
1780
Simon Kelley824af852008-02-12 20:43:05 +00001781 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001782 strcpy(path, directory);
1783 strcat(path, "/");
1784 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001785
Simon Kelley39595cf2013-02-04 21:40:07 +00001786 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001787 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001788 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001789
Simon Kelley39595cf2013-02-04 21:40:07 +00001790 /* only reg files allowed. */
1791 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001792 {
1793 /* sort files into order. */
1794 struct list **up, *new = opt_malloc(sizeof(struct list));
1795 new->name = path;
1796
1797 for (up = &files, li = files; li; up = &li->next, li = li->next)
1798 if (strcmp(li->name, path) >=0)
1799 break;
1800
1801 new->next = li;
1802 *up = new;
1803 }
1804
Simon Kelley849a8352006-06-09 21:02:31 +01001805 }
Simon Kelleyab538832020-01-10 20:44:48 +00001806
1807 for (li = files; li; li = li->next)
1808 one_file(li->name, 0);
1809
Simon Kelley849a8352006-06-09 21:02:31 +01001810 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001811 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001812 for(; ignore_suffix; ignore_suffix = li)
1813 {
1814 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001815 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001816 free(ignore_suffix);
1817 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001818 for(; match_suffix; match_suffix = li)
1819 {
1820 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001821 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001822 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001823 }
Simon Kelleyab538832020-01-10 20:44:48 +00001824 for(; files; files = li)
1825 {
1826 li = files->next;
1827 free(files->name);
1828 free(files);
1829 }
Simon Kelley849a8352006-06-09 21:02:31 +01001830 break;
1831 }
1832
Simon Kelleyed4c0762013-10-08 20:46:34 +01001833 case LOPT_ADD_SBNET: /* --add-subnet */
1834 set_option_bool(OPT_CLIENT_SUBNET);
1835 if (arg)
1836 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001837 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001838 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001839
1840 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1841 if ((end = split_chr(arg, '/')))
1842 {
1843 /* has subnet+len */
1844 err = parse_mysockaddr(arg, &new->addr);
1845 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001846 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001847 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001848 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001849 new->addr_used = 1;
1850 }
1851 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001852 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001853
1854 daemon->add_subnet4 = new;
1855
Ed Bardsleya7369be2015-08-05 21:17:18 +01001856 if (comma)
1857 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001858 new = opt_malloc(sizeof(struct mysubnet));
1859 if ((end = split_chr(comma, '/')))
1860 {
1861 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001862 err = parse_mysockaddr(comma, &new->addr);
1863 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001864 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001865 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001866 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001867 new->addr_used = 1;
1868 }
1869 else
1870 {
1871 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001872 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001873 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001874
1875 daemon->add_subnet6 = new;
1876 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001877 }
1878 break;
1879
Simon Kelleyad094272012-08-10 17:10:54 +01001880 case '1': /* --enable-dbus */
1881 set_option_bool(OPT_DBUS);
1882 if (arg)
1883 daemon->dbus_name = opt_string_alloc(arg);
1884 else
1885 daemon->dbus_name = DNSMASQ_SERVICE;
1886 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001887
1888 case LOPT_UBUS: /* --enable-ubus */
1889 set_option_bool(OPT_UBUS);
1890 if (arg)
1891 daemon->ubus_name = opt_string_alloc(arg);
1892 else
1893 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1894 break;
1895
Simon Kelleyf2621c72007-04-29 19:47:21 +01001896 case '8': /* --log-facility */
1897 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001898 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001899 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001900 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001901 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001902#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001903 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001904#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001905 for (i = 0; facilitynames[i].c_name; i++)
1906 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1907 break;
1908
1909 if (facilitynames[i].c_name)
1910 daemon->log_fac = facilitynames[i].c_val;
1911 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001912 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001913#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001914 }
1915 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001916
Simon Kelleyf2621c72007-04-29 19:47:21 +01001917 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001918 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001919 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001920
Simon Kelleyf2621c72007-04-29 19:47:21 +01001921 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001922 {
Simon Kelley824af852008-02-12 20:43:05 +00001923 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001924 struct resolvc *new, *list = daemon->resolv_files;
1925
1926 if (list && list->is_default)
1927 {
1928 /* replace default resolv file - possibly with nothing */
1929 if (name)
1930 {
1931 list->is_default = 0;
1932 list->name = name;
1933 }
1934 else
1935 list = NULL;
1936 }
1937 else if (name)
1938 {
Simon Kelley824af852008-02-12 20:43:05 +00001939 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001940 new->next = list;
1941 new->name = name;
1942 new->is_default = 0;
1943 new->mtime = 0;
1944 new->logged = 0;
1945 list = new;
1946 }
1947 daemon->resolv_files = list;
1948 break;
1949 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001950
1951 case LOPT_SERVERS_FILE:
1952 daemon->servers_file = opt_string_alloc(arg);
1953 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001954
Simon Kelleyf2621c72007-04-29 19:47:21 +01001955 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001956 {
1957 int pref = 1;
1958 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001959 char *name, *target = NULL;
1960
Simon Kelleyf2621c72007-04-29 19:47:21 +01001961 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001962 {
1963 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001964 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001965 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001966 }
1967
Simon Kelley1f15b812009-10-13 17:49:32 +01001968 if (!(name = canonicalise_opt(arg)) ||
1969 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001970 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001971
Simon Kelley824af852008-02-12 20:43:05 +00001972 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001973 new->next = daemon->mxnames;
1974 daemon->mxnames = new;
1975 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001976 new->name = name;
1977 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001978 new->weight = pref;
1979 break;
1980 }
1981
Simon Kelleyf2621c72007-04-29 19:47:21 +01001982 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001983 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001984 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001985 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001986
Simon Kelley6b173352018-05-08 18:32:14 +01001987 case LOPT_DUMPFILE: /* --dumpfile */
1988 daemon->dump_file = opt_string_alloc(arg);
1989 break;
1990
1991 case LOPT_DUMPMASK: /* --dumpmask */
1992 daemon->dump_mask = strtol(arg, NULL, 0);
1993 break;
1994
Simon Kelley7622fc02009-06-04 20:32:05 +01001995#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001996 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001997 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001998 break;
1999
Simon Kelleyc72daea2012-01-05 21:33:27 +00002000 /* Sorry about the gross pre-processor abuse */
2001 case '6': /* --dhcp-script */
2002 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002003# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002004 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002005# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002006 if (option == LOPT_LUASCRIPT)
2007# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002008 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002009# else
2010 daemon->luascript = opt_string_alloc(arg);
2011# endif
2012 else
2013 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002014# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002015 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002016#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002017
Simon Kelley70d18732015-01-31 19:59:29 +00002018 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2019 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2020 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2021 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2022 case LOPT_HOST_INOTIFY: /* --hostsdir */
2023 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002024 {
Simon Kelley824af852008-02-12 20:43:05 +00002025 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002026 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002027 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002028 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002029 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002030 if (option == 'H')
2031 {
2032 new->next = daemon->addn_hosts;
2033 daemon->addn_hosts = new;
2034 }
2035 else if (option == LOPT_DHCP_HOST)
2036 {
2037 new->next = daemon->dhcp_hosts_file;
2038 daemon->dhcp_hosts_file = new;
2039 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002040 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002041 {
2042 new->next = daemon->dhcp_opts_file;
2043 daemon->dhcp_opts_file = new;
2044 }
Simon Kelley70d18732015-01-31 19:59:29 +00002045 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002046 {
Simon Kelley70d18732015-01-31 19:59:29 +00002047 new->next = daemon->dynamic_dirs;
2048 daemon->dynamic_dirs = new;
2049 if (option == LOPT_DHCP_INOTIFY)
2050 new->flags |= AH_DHCP_HST;
2051 else if (option == LOPT_DHOPT_INOTIFY)
2052 new->flags |= AH_DHCP_OPT;
2053 else if (option == LOPT_HOST_INOTIFY)
2054 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002055 }
2056
Simon Kelley849a8352006-06-09 21:02:31 +01002057 break;
2058 }
2059
Simon Kelley4f7b3042012-11-28 21:27:02 +00002060 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002061 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002062
Simon Kelley4f7b3042012-11-28 21:27:02 +00002063 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002064
2065 while ((arg = comma))
2066 {
2067 struct iname *new = opt_malloc(sizeof(struct iname));
2068 comma = split(arg);
2069 new->name = NULL;
2070 unhide_metas(arg);
2071 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2072 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002073 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2074 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002075 else
2076 {
2077 char *fam = split_chr(arg, '/');
2078 new->name = opt_string_alloc(arg);
2079 new->addr.sa.sa_family = 0;
2080 if (fam)
2081 {
2082 if (strcmp(fam, "4") == 0)
2083 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002084 else if (strcmp(fam, "6") == 0)
2085 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002086 else
Petr Menšík59e47032018-11-02 22:39:39 +00002087 {
2088 free(new->name);
2089 ret_err_free(gen_err, new);
2090 }
Simon Kelley08933472018-10-05 16:34:35 +01002091 }
2092 }
2093 new->next = daemon->authinterface;
2094 daemon->authinterface = new;
2095 };
Simon Kelley429798f2012-12-10 20:45:53 +00002096
Simon Kelley4f7b3042012-11-28 21:27:02 +00002097 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002098
2099 case LOPT_AUTHSFS: /* --auth-sec-servers */
2100 {
2101 struct name_list *new;
2102
2103 do {
2104 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002105 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002106 new->name = opt_string_alloc(arg);
2107 new->next = daemon->secondary_forward_server;
2108 daemon->secondary_forward_server = new;
2109 arg = comma;
2110 } while (arg);
2111 break;
2112 }
2113
Simon Kelley4f7b3042012-11-28 21:27:02 +00002114 case LOPT_AUTHZONE: /* --auth-zone */
2115 {
2116 struct auth_zone *new;
2117
2118 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002119
Simon Kelley429798f2012-12-10 20:45:53 +00002120 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002121 new->domain = opt_string_alloc(arg);
2122 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002123 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002124 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002125 new->next = daemon->auth_zones;
2126 daemon->auth_zones = new;
2127
2128 while ((arg = comma))
2129 {
2130 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002131 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002132 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002133 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002134 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002135
2136 comma = split(arg);
2137 prefix = split_chr(arg, '/');
2138
2139 if (prefix && !atoi_check(prefix, &prefixlen))
2140 ret_err(gen_err);
2141
Mathias Kresin094bfae2016-07-24 14:15:22 +01002142 if (strstr(arg, "exclude:") == arg)
2143 {
2144 is_exclude = 1;
2145 arg = arg+8;
2146 }
2147
Simon Kelleycc921df2019-01-02 22:48:59 +00002148 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002149 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002150 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002151 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002152 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002153 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002154 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002155 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002156 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002157 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002158 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002159 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002160 else
2161 {
2162 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2163 name->name = opt_string_alloc(arg);
2164 name->flags = AUTH4 | AUTH6;
2165 name->next = new->interface_names;
2166 new->interface_names = name;
2167 if (prefix)
2168 {
2169 if (prefixlen == 4)
2170 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002171 else if (prefixlen == 6)
2172 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002173 else
2174 ret_err(gen_err);
2175 }
2176 }
2177
2178 if (subnet)
2179 {
2180 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002181
2182 if (is_exclude)
2183 {
2184 subnet->next = new->exclude;
2185 new->exclude = subnet;
2186 }
2187 else
2188 {
2189 subnet->next = new->subnet;
2190 new->subnet = subnet;
2191 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002192 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002193 }
2194 break;
2195 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002196
Simon Kelley4f7b3042012-11-28 21:27:02 +00002197 case LOPT_AUTHSOA: /* --auth-soa */
2198 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002199 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002200 if (comma)
2201 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002202 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002203 arg = comma;
2204 comma = split(arg);
2205 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002206 for (cp = daemon->hostmaster; *cp; cp++)
2207 if (*cp == '@')
2208 *cp = '.';
2209
Simon Kelley4f7b3042012-11-28 21:27:02 +00002210 if (comma)
2211 {
2212 arg = comma;
2213 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002214 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002215 if (comma)
2216 {
2217 arg = comma;
2218 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002219 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002220 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002221 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002222 }
2223 }
2224 }
2225
2226 break;
2227
Simon Kelley2bb73af2013-04-24 17:38:19 +01002228 case 's': /* --domain */
2229 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002230 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002231 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002232 else
Simon Kelley9009d742008-11-14 20:04:27 +00002233 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002234 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002235 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002236 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002237 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002238 else
2239 {
Simon Kelley9009d742008-11-14 20:04:27 +00002240 if (comma)
2241 {
Simon Kelley429798f2012-12-10 20:45:53 +00002242 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002243 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002244
Simon Kelley48fd1c42013-04-25 09:49:38 +01002245 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002246 new->indexed = 0;
2247
Simon Kelley9009d742008-11-14 20:04:27 +00002248 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002249 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002250 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002251 int msize;
2252
Simon Kelley28866e92011-02-14 20:19:14 +00002253 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002254 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002255 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002256 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002257 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002258 int mask = (1 << (32 - msize)) - 1;
2259 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002260 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2261 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002262 if (arg)
2263 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002264 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002265 {
2266 if (!(new->prefix = canonicalise_opt(arg)) ||
2267 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002268 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002269 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002270 else if (strcmp(arg, "local") != 0 ||
2271 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002272 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002273 else
2274 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002275 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002276 local=/xxx.yyy.zzz.in-addr.arpa/ */
2277 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002278 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002279 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002280
Simon Kelleyde73a492014-02-17 21:43:27 +00002281 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002282
2283 /* local=/<domain>/ */
2284 serv = opt_malloc(sizeof(struct server));
2285 memset(serv, 0, sizeof(struct server));
2286 serv->domain = d;
2287 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2288 serv->next = daemon->servers;
2289 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002290 }
2291 }
Simon Kelley9009d742008-11-14 20:04:27 +00002292 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002293 else if (inet_pton(AF_INET6, comma, &new->start6))
2294 {
2295 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2296 u64 addrpart = addr6part(&new->start6);
2297 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002298
Simon Kelleyd74942a2012-02-07 20:51:56 +00002299 /* prefix==64 overflows the mask calculation above */
2300 if (msize == 64)
2301 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002302
Simon Kelleyd74942a2012-02-07 20:51:56 +00002303 new->end6 = new->start6;
2304 setaddr6part(&new->start6, addrpart & ~mask);
2305 setaddr6part(&new->end6, addrpart | mask);
2306
2307 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002308 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002309 else if (arg)
2310 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002311 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002312 {
2313 if (!(new->prefix = canonicalise_opt(arg)) ||
2314 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002315 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002316 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002317 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002318 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002319 else
2320 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002321 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002322 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002323 struct server *serv = add_rev6(&new->start6, msize);
2324 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002325
2326 /* local=/<domain>/ */
2327 serv = opt_malloc(sizeof(struct server));
2328 memset(serv, 0, sizeof(struct server));
2329 serv->domain = d;
2330 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2331 serv->next = daemon->servers;
2332 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002333 }
2334 }
2335 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002336 else
Petr Menšík59e47032018-11-02 22:39:39 +00002337 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002338 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002339 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002340 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002341 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002342 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002343 prefstr = split(arg);
2344
Simon Kelleyd74942a2012-02-07 20:51:56 +00002345 if (inet_pton(AF_INET, comma, &new->start))
2346 {
2347 new->is6 = 0;
2348 if (!arg)
2349 new->end.s_addr = new->start.s_addr;
2350 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002351 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002352 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002353 else if (inet_pton(AF_INET6, comma, &new->start6))
2354 {
2355 new->is6 = 1;
2356 if (!arg)
2357 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2358 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002359 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002360 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002361 else
Petr Menšík59e47032018-11-02 22:39:39 +00002362 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002363
2364 if (option != 's' && prefstr)
2365 {
2366 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2367 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002368 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002369 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002370 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002371
2372 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002373 if (option == 's')
2374 {
2375 new->next = daemon->cond_domain;
2376 daemon->cond_domain = new;
2377 }
2378 else
2379 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002380 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002381 new->next = daemon->synth_domains;
2382 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002383 if (new->prefix &&
2384 (star = strrchr(new->prefix, '*'))
2385 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002386 {
2387 *star = 0;
2388 new->indexed = 1;
2389 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002390 }
Simon Kelley9009d742008-11-14 20:04:27 +00002391 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002392 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002393 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002394 else
2395 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002396 }
2397 }
Simon Kelley849a8352006-06-09 21:02:31 +01002398 break;
2399
Simon Kelley1e505122016-01-25 21:29:23 +00002400 case LOPT_CPE_ID: /* --add-dns-client */
2401 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002402 daemon->dns_client_id = opt_string_alloc(arg);
2403 break;
2404
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002405 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002406 if (!arg)
2407 set_option_bool(OPT_ADD_MAC);
2408 else
2409 {
2410 unhide_metas(arg);
2411 if (strcmp(arg, "base64") == 0)
2412 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002413 else if (strcmp(arg, "text") == 0)
2414 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002415 else
2416 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002417 }
2418 break;
2419
Simon Kelleyf2621c72007-04-29 19:47:21 +01002420 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002421 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002422 break;
2423
Simon Kelleyf2621c72007-04-29 19:47:21 +01002424 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002425 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002426 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002427 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002428
Simon Kelley7622fc02009-06-04 20:32:05 +01002429#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002430 case LOPT_SCRIPTUSR: /* --scriptuser */
2431 daemon->scriptuser = opt_string_alloc(arg);
2432 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002433#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002434
Simon Kelleyf2621c72007-04-29 19:47:21 +01002435 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002436 do {
Simon Kelley824af852008-02-12 20:43:05 +00002437 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002438 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002439 new->next = daemon->if_names;
2440 daemon->if_names = new;
2441 /* new->name may be NULL if someone does
2442 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002443 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002444 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002445 arg = comma;
2446 } while (arg);
2447 break;
2448
Simon Kelley2937f8a2013-07-29 19:49:07 +01002449 case LOPT_TFTP: /* --enable-tftp */
2450 set_option_bool(OPT_TFTP);
2451 if (!arg)
2452 break;
2453 /* fall through */
2454
Simon Kelleyf2621c72007-04-29 19:47:21 +01002455 case 'I': /* --except-interface */
2456 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002457 do {
Simon Kelley824af852008-02-12 20:43:05 +00002458 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002459 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002460 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002461 if (option == 'I')
2462 {
2463 new->next = daemon->if_except;
2464 daemon->if_except = new;
2465 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002466 else if (option == LOPT_TFTP)
2467 {
2468 new->next = daemon->tftp_interfaces;
2469 daemon->tftp_interfaces = new;
2470 }
Simon Kelley849a8352006-06-09 21:02:31 +01002471 else
2472 {
2473 new->next = daemon->dhcp_except;
2474 daemon->dhcp_except = new;
2475 }
2476 arg = comma;
2477 } while (arg);
2478 break;
2479
Simon Kelleyf2621c72007-04-29 19:47:21 +01002480 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002481 case LOPT_IGNORE_ADDR: /* --ignore-address */
2482 {
Simon Kelley849a8352006-06-09 21:02:31 +01002483 struct in_addr addr;
2484 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002485 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002486 {
Simon Kelley824af852008-02-12 20:43:05 +00002487 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002488 if (option == 'B')
2489 {
2490 baddr->next = daemon->bogus_addr;
2491 daemon->bogus_addr = baddr;
2492 }
2493 else
2494 {
2495 baddr->next = daemon->ignore_addr;
2496 daemon->ignore_addr = baddr;
2497 }
Simon Kelley849a8352006-06-09 21:02:31 +01002498 baddr->addr = addr;
2499 }
2500 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002501 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002502 break;
2503 }
2504
Simon Kelleyf2621c72007-04-29 19:47:21 +01002505 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002506 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002507 do {
Simon Kelley824af852008-02-12 20:43:05 +00002508 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002509 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002510 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002511 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002512 {
2513 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002514 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002515#ifdef HAVE_SOCKADDR_SA_LEN
2516 new->addr.in.sin_len = sizeof(new->addr.in);
2517#endif
2518 }
Simon Kelley849a8352006-06-09 21:02:31 +01002519 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2520 {
2521 new->addr.sa.sa_family = AF_INET6;
2522 new->addr.in6.sin6_flowinfo = 0;
2523 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002524 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002525#ifdef HAVE_SOCKADDR_SA_LEN
2526 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2527#endif
2528 }
Simon Kelley849a8352006-06-09 21:02:31 +01002529 else
Petr Menšík59e47032018-11-02 22:39:39 +00002530 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002531
2532 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002533 if (option == 'a')
2534 {
2535 new->next = daemon->if_addrs;
2536 daemon->if_addrs = new;
2537 }
2538 else
2539 {
2540 new->next = daemon->auth_peers;
2541 daemon->auth_peers = new;
2542 }
Simon Kelley849a8352006-06-09 21:02:31 +01002543 arg = comma;
2544 } while (arg);
2545 break;
2546
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002547 case 'S': /* --server */
2548 case LOPT_LOCAL: /* --local */
2549 case 'A': /* --address */
2550 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002551 {
2552 struct server *serv, *newlist = NULL;
2553
2554 unhide_metas(arg);
2555
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002556 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002557 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002558 int rebind = !(*arg == '/');
2559 char *end = NULL;
2560 if (!rebind)
2561 arg++;
2562 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002563 {
2564 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002565 /* elide leading dots - they are implied in the search algorithm */
2566 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002567 /* # matches everything and becomes a zero length domain string */
2568 if (strcmp(arg, "#") == 0)
2569 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002570 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002571 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002572 serv = opt_malloc(sizeof(struct server));
2573 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002574 serv->next = newlist;
2575 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002576 serv->domain = domain;
2577 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002578 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002579 if (rebind)
2580 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002581 }
2582 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002583 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002584 }
2585 else
2586 {
Simon Kelley824af852008-02-12 20:43:05 +00002587 newlist = opt_malloc(sizeof(struct server));
2588 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002589#ifdef HAVE_LOOP
2590 newlist->uid = rand32();
2591#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002592 }
2593
Simon Kelley7b1eae42014-02-20 13:43:28 +00002594 if (servers_only && option == 'S')
2595 newlist->flags |= SERV_FROM_FILE;
2596
Simon Kelley849a8352006-06-09 21:02:31 +01002597 if (option == 'A')
2598 {
2599 newlist->flags |= SERV_LITERAL_ADDRESS;
2600 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002601 {
2602 server_list_free(newlist);
2603 ret_err(gen_err);
2604 }
Simon Kelley849a8352006-06-09 21:02:31 +01002605 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002606 else if (option == LOPT_NO_REBIND)
2607 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002608
2609 if (!arg || !*arg)
2610 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002611 if (!(newlist->flags & SERV_NO_REBIND))
2612 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002613 }
2614
2615 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002616 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002617 else
2618 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002619 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2620 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002621 {
2622 server_list_free(newlist);
2623 ret_err(err);
2624 }
Simon Kelley849a8352006-06-09 21:02:31 +01002625 }
2626
Simon Kelleyf2621c72007-04-29 19:47:21 +01002627 serv = newlist;
2628 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002629 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002630 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002631 serv->next->addr = serv->addr;
2632 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002633 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002634 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002635 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002636 serv->next = daemon->servers;
2637 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002638 break;
2639 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002640
Simon Kelleyde73a492014-02-17 21:43:27 +00002641 case LOPT_REV_SERV: /* --rev-server */
2642 {
2643 char *string;
2644 int size;
2645 struct server *serv;
2646 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002647 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002648
2649 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002650 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002651 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002652
2653 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002654
Simon Kelleya9b022a2020-02-11 21:58:59 +00002655 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2656 ret_err(gen_err);
2657
Simon Kelleyde73a492014-02-17 21:43:27 +00002658 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002659 {
2660 serv = add_rev4(addr4, size);
2661 if (!serv)
2662 ret_err(_("bad prefix"));
2663 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002664 else if (inet_pton(AF_INET6, arg, &addr6))
2665 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002666 else
2667 ret_err(gen_err);
2668
2669 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2670
2671 if (string)
2672 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002673
2674 if (servers_only)
2675 serv->flags |= SERV_FROM_FILE;
2676
Simon Kelleyde73a492014-02-17 21:43:27 +00002677 break;
2678 }
2679
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002680 case LOPT_IPSET: /* --ipset */
2681#ifndef HAVE_IPSET
2682 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2683 break;
2684#else
2685 {
2686 struct ipsets ipsets_head;
2687 struct ipsets *ipsets = &ipsets_head;
2688 int size;
2689 char *end;
2690 char **sets, **sets_pos;
2691 memset(ipsets, 0, sizeof(struct ipsets));
2692 unhide_metas(arg);
2693 if (arg && *arg == '/')
2694 {
2695 arg++;
2696 while ((end = split_chr(arg, '/')))
2697 {
2698 char *domain = NULL;
2699 /* elide leading dots - they are implied in the search algorithm */
2700 while (*arg == '.')
2701 arg++;
2702 /* # matches everything and becomes a zero length domain string */
2703 if (strcmp(arg, "#") == 0 || !*arg)
2704 domain = "";
2705 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002706 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002707 ipsets->next = opt_malloc(sizeof(struct ipsets));
2708 ipsets = ipsets->next;
2709 memset(ipsets, 0, sizeof(struct ipsets));
2710 ipsets->domain = domain;
2711 arg = end;
2712 }
2713 }
2714 else
2715 {
2716 ipsets->next = opt_malloc(sizeof(struct ipsets));
2717 ipsets = ipsets->next;
2718 memset(ipsets, 0, sizeof(struct ipsets));
2719 ipsets->domain = "";
2720 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002721
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002722 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002723 ret_err(gen_err);
2724
2725 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002726 if (*end == ',')
2727 ++size;
2728
2729 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2730
2731 do {
2732 end = split(arg);
2733 *sets_pos++ = opt_string_alloc(arg);
2734 arg = end;
2735 } while (end);
2736 *sets_pos = 0;
2737 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2738 ipsets->next->sets = sets;
2739 ipsets->next = daemon->ipsets;
2740 daemon->ipsets = ipsets_head.next;
2741
2742 break;
2743 }
2744#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002745
Simon Kelleyf2621c72007-04-29 19:47:21 +01002746 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002747 {
2748 int size;
2749
2750 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002751 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002752 else
2753 {
2754 /* zero is OK, and means no caching. */
2755
2756 if (size < 0)
2757 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002758
2759 /* Note that for very large cache sizes, the malloc()
2760 will overflow. For the size of the cache record
2761 at the time this was noted, the value of "very large"
2762 was 46684428. Limit to an order of magnitude less than
2763 that to be safe from changes to the cache record. */
2764 if (size > 5000000)
2765 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002766
2767 daemon->cachesize = size;
2768 }
2769 break;
2770 }
2771
Simon Kelleyf2621c72007-04-29 19:47:21 +01002772 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002773 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002774 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002775 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002776
Simon Kelley1a6bca82008-07-11 11:11:42 +01002777 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002778 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002779 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002780 break;
2781
Hans Dedecker926332a2016-01-23 10:48:12 +00002782 case LOPT_MAXPORT: /* --max-port */
2783 if (!atoi_check16(arg, &daemon->max_port))
2784 ret_err(gen_err);
2785 break;
2786
Simon Kelleyf2621c72007-04-29 19:47:21 +01002787 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002788 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002789 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002790 break;
2791
Simon Kelley25cf5e32015-01-09 15:53:03 +00002792 case 'q': /* --log-queries */
2793 set_option_bool(OPT_LOG);
2794 if (arg && strcmp(arg, "extra") == 0)
2795 set_option_bool(OPT_EXTRALOG);
2796 break;
2797
Simon Kelleyf2621c72007-04-29 19:47:21 +01002798 case LOPT_MAX_LOGS: /* --log-async */
2799 daemon->max_logs = LOG_MAX; /* default */
2800 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002801 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002802 else if (daemon->max_logs > 100)
2803 daemon->max_logs = 100;
2804 break;
2805
2806 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002807 {
2808 int i;
2809 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002810 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002811 daemon->edns_pktsz = (unsigned short)i;
2812 break;
2813 }
2814
Simon Kelleyf2621c72007-04-29 19:47:21 +01002815 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002816 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002817 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002818 /* if explicitly set to zero, use single OS ephemeral port
2819 and disable random ports */
2820 if (daemon->query_port == 0)
2821 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002822 break;
2823
Simon Kelley824af852008-02-12 20:43:05 +00002824 case 'T': /* --local-ttl */
2825 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002826 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002827 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002828 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002829 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002830 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002831 {
2832 int ttl;
2833 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002834 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002835 else if (option == LOPT_NEGTTL)
2836 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002837 else if (option == LOPT_MAXTTL)
2838 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002839 else if (option == LOPT_MINCTTL)
2840 {
2841 if (ttl > TTL_FLOOR_LIMIT)
2842 ttl = TTL_FLOOR_LIMIT;
2843 daemon->min_cache_ttl = (unsigned long)ttl;
2844 }
Simon Kelley1d860412012-09-20 20:48:04 +01002845 else if (option == LOPT_MAXCTTL)
2846 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002847 else if (option == LOPT_AUTHTTL)
2848 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002849 else if (option == LOPT_DHCPTTL)
2850 {
2851 daemon->dhcp_ttl = (unsigned long)ttl;
2852 daemon->use_dhcp_ttl = 1;
2853 }
Simon Kelley849a8352006-06-09 21:02:31 +01002854 else
2855 daemon->local_ttl = (unsigned long)ttl;
2856 break;
2857 }
2858
Simon Kelley7622fc02009-06-04 20:32:05 +01002859#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002860 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002861 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002862 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002863 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002864#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002865
Simon Kelley7622fc02009-06-04 20:32:05 +01002866#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002867 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002868 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002869 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002870 break;
2871
Simon Kelleybec366b2016-02-24 22:03:26 +00002872 case LOPT_TFTP_MTU: /* --tftp-mtu */
2873 if (!atoi_check(arg, &daemon->tftp_mtu))
2874 ret_err(gen_err);
2875 break;
2876
Simon Kelley824af852008-02-12 20:43:05 +00002877 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002878 comma = split(arg);
2879 if (comma)
2880 {
2881 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2882 new->interface = opt_string_alloc(comma);
2883 new->prefix = opt_string_alloc(arg);
2884 new->next = daemon->if_prefix;
2885 daemon->if_prefix = new;
2886 }
2887 else
2888 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002889 break;
2890
Simon Kelley824af852008-02-12 20:43:05 +00002891 case LOPT_TFTPPORTS: /* --tftp-port-range */
2892 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002893 !atoi_check16(arg, &daemon->start_tftp_port) ||
2894 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002895 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002896
2897 if (daemon->start_tftp_port > daemon->end_tftp_port)
2898 {
2899 int tmp = daemon->start_tftp_port;
2900 daemon->start_tftp_port = daemon->end_tftp_port;
2901 daemon->end_tftp_port = tmp;
2902 }
2903
2904 break;
Floris Bos60704f52017-04-09 22:22:49 +01002905
2906 case LOPT_APREF: /* --tftp-unique-root */
2907 if (!arg || strcasecmp(arg, "ip") == 0)
2908 set_option_bool(OPT_TFTP_APREF_IP);
2909 else if (strcasecmp(arg, "mac") == 0)
2910 set_option_bool(OPT_TFTP_APREF_MAC);
2911 else
2912 ret_err(gen_err);
2913 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002914#endif
Simon Kelley824af852008-02-12 20:43:05 +00002915
Simon Kelleyf2621c72007-04-29 19:47:21 +01002916 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002917 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002918 struct dhcp_bridge *new;
2919
Simon Kelley316e2732010-01-22 20:16:09 +00002920 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002921 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002922
Simon Kelley22cd8602018-01-14 22:57:14 +00002923 for (new = daemon->bridges; new; new = new->next)
2924 if (strcmp(new->iface, arg) == 0)
2925 break;
2926
2927 if (!new)
2928 {
2929 new = opt_malloc(sizeof(struct dhcp_bridge));
2930 strcpy(new->iface, arg);
2931 new->alias = NULL;
2932 new->next = daemon->bridges;
2933 daemon->bridges = new;
2934 }
2935
Simon Kelley832af0b2007-01-21 20:01:28 +00002936 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002937 arg = comma;
2938 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002939 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002940 {
Simon Kelley824af852008-02-12 20:43:05 +00002941 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002942 b->next = new->alias;
2943 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002944 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002945 }
2946 } while (comma);
2947
2948 break;
2949 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002950
Simon Kelley7622fc02009-06-04 20:32:05 +01002951#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002952 case LOPT_SHARED_NET: /* --shared-network */
2953 {
2954 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2955
2956#ifdef HAVE_DHCP6
2957 new->shared_addr.s_addr = 0;
2958#endif
2959 new->if_index = 0;
2960
2961 if (!(comma = split(arg)))
2962 {
2963 snerr:
2964 free(new);
2965 ret_err(_("bad shared-network"));
2966 }
2967
2968 if (inet_pton(AF_INET, comma, &new->shared_addr))
2969 {
2970 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2971 !(new->if_index = if_nametoindex(arg)))
2972 goto snerr;
2973 }
2974#ifdef HAVE_DHCP6
2975 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2976 {
2977 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
2978 !(new->if_index = if_nametoindex(arg)))
2979 goto snerr;
2980 }
2981#endif
2982 else
2983 goto snerr;
2984
2985 new->next = daemon->shared_networks;
2986 daemon->shared_networks = new;
2987 break;
2988 }
2989
Simon Kelleyf2621c72007-04-29 19:47:21 +01002990 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002991 {
2992 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002993 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002994 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002995
Simon Kelley52b92f42012-01-22 16:05:15 +00002996 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00002997
Simon Kelley849a8352006-06-09 21:02:31 +01002998 while(1)
2999 {
3000 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003001 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3002 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3003 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003004 break;
3005
Simon Kelleyf2621c72007-04-29 19:47:21 +01003006 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003007 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003008 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003009 {
Simon Kelley0c387192013-09-05 10:21:12 +01003010 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003011 if (arg[4])
3012 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003013 }
3014 else
3015 {
3016 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003017 {
3018 dhcp_context_free(new);
3019 ret_err(_("only one tag allowed"));
3020 }
Simon Kelley849a8352006-06-09 21:02:31 +01003021 else
Petr Menšík59e47032018-11-02 22:39:39 +00003022 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003023 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003024 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003025 }
3026 else
3027 {
3028 a[0] = arg;
3029 break;
3030 }
3031 }
3032
Simon Kelley1f776932012-12-16 19:46:08 +00003033 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003034 if (!(a[k] = split(a[k-1])))
3035 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003036
Simon Kelley52b92f42012-01-22 16:05:15 +00003037 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003038 {
3039 dhcp_context_free(new);
3040 ret_err(_("bad dhcp-range"));
3041 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003042
3043 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003044 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003045 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003046 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003047 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003048 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003049 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003050 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003051 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003052 new->flags |= CONTEXT_PROXY;
3053 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003054 {
3055 dhcp_context_free(new);
3056 ret_err(_("bad dhcp-range"));
3057 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003058
3059 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3060 {
3061 struct in_addr tmp = new->start;
3062 new->start = new->end;
3063 new->end = tmp;
3064 }
3065
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003066 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003067 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003068 {
3069 new->flags |= CONTEXT_NETMASK;
3070 leasepos = 3;
3071 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003072 {
3073 dhcp_context_free(new);
3074 ret_err(_("inconsistent DHCP range"));
3075 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003076
Simon Kelley52b92f42012-01-22 16:05:15 +00003077
Simon Kelleyfa794662016-03-03 20:33:54 +00003078 if (k >= 4 && strchr(a[3], '.') &&
3079 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3080 {
3081 new->flags |= CONTEXT_BRDCAST;
3082 leasepos = 4;
3083 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003084 }
Simon Kelley849a8352006-06-09 21:02:31 +01003085 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003086#ifdef HAVE_DHCP6
3087 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003088 {
Petr Menšík59e47032018-11-02 22:39:39 +00003089 const char *err = NULL;
3090
Simon Kelley89500e32013-09-20 16:29:20 +01003091 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003092 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003093 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003094 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003095 new->next = daemon->dhcp6;
3096 daemon->dhcp6 = new;
3097
Simon Kelley30cd9662012-03-25 20:44:38 +01003098 for (leasepos = 1; leasepos < k; leasepos++)
3099 {
3100 if (strcmp(a[leasepos], "static") == 0)
3101 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3102 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003103 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003104 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003105 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003106 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3107 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003108 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003109 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003110 else if (strcmp(a[leasepos], "off-link") == 0)
3111 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003112 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3113 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003114 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3115 {
3116 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3117 new->flags |= CONTEXT_TEMPLATE;
3118 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003119 else
3120 break;
3121 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003122
Simon Kelley52b92f42012-01-22 16:05:15 +00003123 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003124 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003125 {
3126 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003127 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003128 if (!(*cp >= '0' && *cp <= '9'))
3129 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003130 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003131 {
3132 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003133 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003134 }
3135 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003136
Petr Menšík59e47032018-11-02 22:39:39 +00003137 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003138 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003139 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003140 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003141 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003142 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003143 }
Petr Menšík59e47032018-11-02 22:39:39 +00003144 else if (new->prefix < 64)
3145 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003146
Petr Menšík59e47032018-11-02 22:39:39 +00003147 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3148 err=(_("inconsistent DHCPv6 range"));
3149
3150 if (err)
3151 {
3152 dhcp_context_free(new);
3153 ret_err(err);
3154 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003155
3156 /* dhcp-range=:: enables DHCP stateless on any interface */
3157 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3158 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003159
3160 if (new->flags & CONTEXT_TEMPLATE)
3161 {
3162 struct in6_addr zero;
3163 memset(&zero, 0, sizeof(zero));
3164 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003165 {
3166 dhcp_context_free(new);
3167 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3168 }
Simon Kelley66409192013-08-01 20:19:32 +01003169 }
3170
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003171 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003172 {
3173 struct in6_addr tmp = new->start6;
3174 new->start6 = new->end6;
3175 new->end6 = tmp;
3176 }
Simon Kelley849a8352006-06-09 21:02:31 +01003177 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003178#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003179 else
Petr Menšík59e47032018-11-02 22:39:39 +00003180 {
3181 dhcp_context_free(new);
3182 ret_err(_("bad dhcp-range"));
3183 }
Simon Kelley849a8352006-06-09 21:02:31 +01003184
Simon Kelley30cd9662012-03-25 20:44:38 +01003185 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003186 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003187 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003188 {
3189 dhcp_context_free(new);
3190 ret_err(_("bad dhcp-range"));
3191 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003192
Simon Kelley849a8352006-06-09 21:02:31 +01003193 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003194 {
3195 new->lease_time = 0xffffffff;
3196 new->flags |= CONTEXT_SETLEASE;
3197 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003198 else if (strcmp(a[leasepos], "deprecated") == 0)
3199 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003200 else
3201 {
3202 int fac = 1;
3203 if (strlen(a[leasepos]) > 0)
3204 {
3205 switch (a[leasepos][strlen(a[leasepos]) - 1])
3206 {
Simon Kelley42243212012-07-20 15:19:18 +01003207 case 'w':
3208 case 'W':
3209 fac *= 7;
3210 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003211 case 'd':
3212 case 'D':
3213 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003214 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003215 case 'h':
3216 case 'H':
3217 fac *= 60;
3218 /* fall through */
3219 case 'm':
3220 case 'M':
3221 fac *= 60;
3222 /* fall through */
3223 case 's':
3224 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003225 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003226 }
3227
Simon Kelleybe379862012-12-23 12:01:39 +00003228 for (cp = a[leasepos]; *cp; cp++)
3229 if (!(*cp >= '0' && *cp <= '9'))
3230 break;
3231
Simon Kelley54dae552013-02-05 17:55:10 +00003232 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003233 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003234
Simon Kelley849a8352006-06-09 21:02:31 +01003235 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003236 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003237 /* Leases of a minute or less confuse
3238 some clients, notably Apple's */
3239 if (new->lease_time < 120)
3240 new->lease_time = 120;
3241 }
3242 }
3243 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003244
Simon Kelley849a8352006-06-09 21:02:31 +01003245 break;
3246 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003247
Simon Kelley5aabfc72007-08-29 11:24:47 +01003248 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003249 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003250 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003251 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003252 struct in_addr in;
3253
Simon Kelley824af852008-02-12 20:43:05 +00003254 new = opt_malloc(sizeof(struct dhcp_config));
3255
Simon Kelley849a8352006-06-09 21:02:31 +01003256 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003257 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3258 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003259 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003260 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003261 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003262#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003263 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003264#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003265
Simon Kelley137286e2020-02-06 22:09:30 +00003266 while (arg)
3267 {
3268 comma = split(arg);
3269 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3270 {
3271 if ((arg[0] == 'i' || arg[0] == 'I') &&
3272 (arg[1] == 'd' || arg[1] == 'D') &&
3273 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003274 {
Simon Kelley137286e2020-02-06 22:09:30 +00003275 if (arg[3] == '*')
3276 new->flags |= CONFIG_NOCLID;
3277 else
3278 {
3279 int len;
3280 arg += 3; /* dump id: */
3281 if (strchr(arg, ':'))
3282 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3283 else
3284 {
3285 unhide_metas(arg);
3286 len = (int) strlen(arg);
3287 }
3288
3289 if (len == -1)
3290 {
3291 dhcp_config_free(new);
3292 ret_err(_("bad hex constant"));
3293 }
3294 else if ((new->clid = opt_malloc(len)))
3295 {
3296 new->flags |= CONFIG_CLID;
3297 new->clid_len = len;
3298 memcpy(new->clid, arg, len);
3299 }
3300 }
3301 }
3302 /* dhcp-host has strange backwards-compat needs. */
3303 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3304 {
3305 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3306 newlist->next = new->netid;
3307 new->netid = newlist;
3308 newlist->list = dhcp_netid_create(arg+4, NULL);
3309 }
3310 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003311 new->filter = dhcp_netid_create(arg+4, new->filter);
3312
Simon Kelley137286e2020-02-06 22:09:30 +00003313#ifdef HAVE_DHCP6
3314 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3315 {
3316 char *pref;
3317 struct in6_addr in6;
3318 struct addrlist *new_addr;
3319
3320 arg[strlen(arg)-1] = 0;
3321 arg++;
3322 pref = split_chr(arg, '/');
3323
3324 if (!inet_pton(AF_INET6, arg, &in6))
3325 {
3326 dhcp_config_free(new);
3327 ret_err(_("bad IPv6 address"));
3328 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003329
Simon Kelley137286e2020-02-06 22:09:30 +00003330 new_addr = opt_malloc(sizeof(struct addrlist));
3331 new_addr->next = new->addr6;
3332 new_addr->flags = 0;
3333 new_addr->addr.addr6 = in6;
3334 new->addr6 = new_addr;
3335
3336 if (pref)
3337 {
3338 u64 addrpart = addr6part(&in6);
3339
3340 if (!atoi_check(pref, &new_addr->prefixlen) ||
3341 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003342 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003343 {
3344 dhcp_config_free(new);
3345 ret_err(_("bad IPv6 prefix"));
3346 }
3347
3348 new_addr->flags |= ADDRLIST_PREFIX;
3349 }
3350
3351 for (i= 0; i < 8; i++)
3352 if (in6.s6_addr[i] != 0)
3353 break;
3354
3355 /* set WILDCARD if network part all zeros */
3356 if (i == 8)
3357 new_addr->flags |= ADDRLIST_WILDCARD;
3358
3359 new->flags |= CONFIG_ADDR6;
3360 }
3361#endif
3362 else
3363 {
3364 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3365 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3366 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3367 {
3368 free(newhw);
3369 dhcp_config_free(new);
3370 ret_err(_("bad hex constant"));
3371 }
3372 else
3373 {
3374 newhw->next = new->hwaddr;
3375 new->hwaddr = newhw;
3376 }
3377 }
3378 }
3379 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3380 {
3381 struct dhcp_config *configs;
3382
3383 new->addr = in;
3384 new->flags |= CONFIG_ADDR;
3385
3386 /* If the same IP appears in more than one host config, then DISCOVER
3387 for one of the hosts will get the address, but REQUEST will be NAKed,
3388 since the address is reserved by the other one -> protocol loop. */
3389 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3390 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003391 {
Simon Kelley137286e2020-02-06 22:09:30 +00003392 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3393 return 0;
3394 }
3395 }
3396 else
3397 {
3398 char *cp, *lastp = NULL, last = 0;
3399 int fac = 1, isdig = 0;
3400
3401 if (strlen(arg) > 1)
3402 {
3403 lastp = arg + strlen(arg) - 1;
3404 last = *lastp;
3405 switch (last)
3406 {
3407 case 'w':
3408 case 'W':
3409 fac *= 7;
3410 /* fall through */
3411 case 'd':
3412 case 'D':
3413 fac *= 24;
3414 /* fall through */
3415 case 'h':
3416 case 'H':
3417 fac *= 60;
3418 /* fall through */
3419 case 'm':
3420 case 'M':
3421 fac *= 60;
3422 /* fall through */
3423 case 's':
3424 case 'S':
3425 *lastp = 0;
3426 }
3427 }
3428
3429 for (cp = arg; *cp; cp++)
3430 if (isdigit((unsigned char)*cp))
3431 isdig = 1;
3432 else if (*cp != ' ')
3433 break;
3434
3435 if (*cp)
3436 {
3437 if (lastp)
3438 *lastp = last;
3439 if (strcmp(arg, "infinite") == 0)
3440 {
3441 new->lease_time = 0xffffffff;
3442 new->flags |= CONFIG_TIME;
3443 }
3444 else if (strcmp(arg, "ignore") == 0)
3445 new->flags |= CONFIG_DISABLE;
3446 else
3447 {
3448 if (!(new->hostname = canonicalise_opt(arg)) ||
3449 !legal_hostname(new->hostname))
3450 {
3451 dhcp_config_free(new);
3452 ret_err(_("bad DHCP host name"));
3453 }
3454
3455 new->flags |= CONFIG_NAME;
3456 new->domain = strip_hostname(new->hostname);
3457 }
3458 }
3459 else if (isdig)
3460 {
3461 new->lease_time = atoi(arg) * fac;
3462 /* Leases of a minute or less confuse
3463 some clients, notably Apple's */
3464 if (new->lease_time < 120)
3465 new->lease_time = 120;
3466 new->flags |= CONFIG_TIME;
3467 }
3468 }
3469
3470 arg = comma;
3471 }
3472
Simon Kelley5aabfc72007-08-29 11:24:47 +01003473 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003474 break;
3475 }
Simon Kelley137286e2020-02-06 22:09:30 +00003476
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003477 case LOPT_TAG_IF: /* --tag-if */
3478 {
3479 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3480
3481 new->tag = NULL;
3482 new->set = NULL;
3483 new->next = NULL;
3484
3485 /* preserve order */
3486 if (!daemon->tag_if)
3487 daemon->tag_if = new;
3488 else
3489 {
3490 struct tag_if *tmp;
3491 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3492 tmp->next = new;
3493 }
3494
3495 while (arg)
3496 {
3497 size_t len;
3498
3499 comma = split(arg);
3500 len = strlen(arg);
3501
3502 if (len < 5)
3503 {
3504 new->set = NULL;
3505 break;
3506 }
3507 else
3508 {
Petr Menšík59e47032018-11-02 22:39:39 +00003509 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003510
3511 if (strstr(arg, "set:") == arg)
3512 {
3513 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3514 newlist->next = new->set;
3515 new->set = newlist;
3516 newlist->list = newtag;
3517 }
3518 else if (strstr(arg, "tag:") == arg)
3519 {
3520 newtag->next = new->tag;
3521 new->tag = newtag;
3522 }
3523 else
3524 {
3525 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003526 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003527 break;
3528 }
3529 }
3530
3531 arg = comma;
3532 }
3533
3534 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003535 {
3536 dhcp_netid_free(new->tag);
3537 dhcp_netid_list_free(new->set);
3538 ret_err_free(_("bad tag-if"), new);
3539 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003540
3541 break;
3542 }
3543
Simon Kelley849a8352006-06-09 21:02:31 +01003544
Simon Kelley73a08a22009-02-05 20:28:08 +00003545 case 'O': /* --dhcp-option */
3546 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003547 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003548 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003549 return parse_dhcp_opt(errstr, arg,
3550 option == LOPT_FORCE ? DHOPT_FORCE :
3551 (option == LOPT_MATCH ? DHOPT_MATCH :
3552 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003553
3554 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3555 {
3556 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3557 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3558 ssize_t len;
3559
3560 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3561 ret_err(gen_err);
3562
3563 new->wildcard = 0;
3564 new->netid = id;
3565 id->net = opt_string_alloc(set_prefix(arg));
3566
3567 if (comma[len-1] == '*')
3568 {
3569 comma[len-1] = 0;
3570 new->wildcard = 1;
3571 }
3572 new->name = opt_string_alloc(comma);
3573
3574 new->next = daemon->dhcp_name_match;
3575 daemon->dhcp_name_match = new;
3576
3577 break;
3578 }
3579
Simon Kelleyf2621c72007-04-29 19:47:21 +01003580 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003581 {
Petr Menšík59e47032018-11-02 22:39:39 +00003582 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003583
Petr Menšík137e9f82018-12-16 21:25:29 +00003584 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003585 {
3586 ret_err(gen_err);
3587 }
Simon Kelley849a8352006-06-09 21:02:31 +01003588 else
3589 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003590 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003591 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003592 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003593 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003594 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003595 dhcp_next_server.s_addr = 0;
3596 if (comma)
3597 {
3598 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003599 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003600 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003601 if (comma)
3602 {
3603 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003604 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3605 {
3606 /*
3607 * The user may have specified the tftp hostname here.
3608 * save it so that it can be resolved/looked up during
3609 * actual dhcp_reply().
3610 */
3611
3612 tftp_sname = opt_string_alloc(comma);
3613 dhcp_next_server.s_addr = 0;
3614 }
Simon Kelley849a8352006-06-09 21:02:31 +01003615 }
3616 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003617
3618 new = opt_malloc(sizeof(struct dhcp_boot));
3619 new->file = dhcp_file;
3620 new->sname = dhcp_sname;
3621 new->tftp_sname = tftp_sname;
3622 new->next_server = dhcp_next_server;
3623 new->netid = id;
3624 new->next = daemon->boot_config;
3625 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003626 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003627
Simon Kelley849a8352006-06-09 21:02:31 +01003628 break;
3629 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003630
Floris Bos503c6092017-04-09 23:07:13 +01003631 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3632 {
Petr Menšík59e47032018-11-02 22:39:39 +00003633 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003634
Petr Menšík137e9f82018-12-16 21:25:29 +00003635 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003636 {
3637 ret_err(gen_err);
3638 }
Floris Bos503c6092017-04-09 23:07:13 +01003639 else
3640 {
3641 struct delay_config *new;
3642 int delay;
3643 if (!atoi_check(arg, &delay))
3644 ret_err(gen_err);
3645
3646 new = opt_malloc(sizeof(struct delay_config));
3647 new->delay = delay;
3648 new->netid = id;
3649 new->next = daemon->delay_conf;
3650 daemon->delay_conf = new;
3651 }
3652
3653 break;
3654 }
3655
Simon Kelley7622fc02009-06-04 20:32:05 +01003656 case LOPT_PXE_PROMT: /* --pxe-prompt */
3657 {
3658 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3659 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003660
Simon Kelley7622fc02009-06-04 20:32:05 +01003661 new->netid = NULL;
3662 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003663 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003664
Petr Menšík137e9f82018-12-16 21:25:29 +00003665 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003666 {
3667 dhcp_opt_free(new);
3668 ret_err(gen_err);
3669 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003670 else
3671 {
3672 comma = split(arg);
3673 unhide_metas(arg);
3674 new->len = strlen(arg) + 1;
3675 new->val = opt_malloc(new->len);
3676 memcpy(new->val + 1, arg, new->len - 1);
3677
Wang Shanker4ded9622020-12-04 10:17:35 +08003678 new->u.vendor_class = NULL;
3679 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003680
3681 if (comma && atoi_check(comma, &timeout))
3682 *(new->val) = timeout;
3683 else
3684 *(new->val) = 255;
3685
3686 new->next = daemon->dhcp_opts;
3687 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003688 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003689 }
3690
3691 break;
3692 }
3693
3694 case LOPT_PXE_SERV: /* --pxe-service */
3695 {
3696 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3697 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003698 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3699 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003700 static int boottype = 32768;
3701
3702 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003703 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003704 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003705 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003706
Simon Kelley7622fc02009-06-04 20:32:05 +01003707 if (arg && (comma = split(arg)))
3708 {
3709 for (i = 0; CSA[i]; i++)
3710 if (strcasecmp(CSA[i], arg) == 0)
3711 break;
3712
3713 if (CSA[i] || atoi_check(arg, &i))
3714 {
3715 arg = comma;
3716 comma = split(arg);
3717
3718 new->CSA = i;
3719 new->menu = opt_string_alloc(arg);
3720
Simon Kelley316e2732010-01-22 20:16:09 +00003721 if (!comma)
3722 {
3723 new->type = 0; /* local boot */
3724 new->basename = NULL;
3725 }
3726 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003727 {
3728 arg = comma;
3729 comma = split(arg);
3730 if (atoi_check(arg, &i))
3731 {
3732 new->type = i;
3733 new->basename = NULL;
3734 }
3735 else
3736 {
3737 new->type = boottype++;
3738 new->basename = opt_string_alloc(arg);
3739 }
3740
Simon Kelley751d6f42012-02-10 15:24:51 +00003741 if (comma)
3742 {
3743 if (!inet_pton(AF_INET, comma, &new->server))
3744 {
3745 new->server.s_addr = 0;
3746 new->sname = opt_string_alloc(comma);
3747 }
3748
3749 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003750 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003751
Simon Kelley316e2732010-01-22 20:16:09 +00003752 /* Order matters */
3753 new->next = NULL;
3754 if (!daemon->pxe_services)
3755 daemon->pxe_services = new;
3756 else
3757 {
3758 struct pxe_service *s;
3759 for (s = daemon->pxe_services; s->next; s = s->next);
3760 s->next = new;
3761 }
3762
3763 daemon->enable_pxe = 1;
3764 break;
3765
Simon Kelley7622fc02009-06-04 20:32:05 +01003766 }
3767 }
3768
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003769 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003770 }
3771
Simon Kelleyf2621c72007-04-29 19:47:21 +01003772 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003773 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003774 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003775 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003776 else
3777 {
Simon Kelley824af852008-02-12 20:43:05 +00003778 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003779 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003780 unhide_metas(comma);
3781 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003782 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003783 {
3784 free(new->netid.net);
3785 ret_err_free(gen_err, new);
3786 }
Simon Kelley28866e92011-02-14 20:19:14 +00003787 else
3788 {
3789 new->next = daemon->dhcp_macs;
3790 daemon->dhcp_macs = new;
3791 }
Simon Kelley849a8352006-06-09 21:02:31 +01003792 }
3793 }
3794 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003795
Simon Kelleyf2621c72007-04-29 19:47:21 +01003796 case 'U': /* --dhcp-vendorclass */
3797 case 'j': /* --dhcp-userclass */
3798 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3799 case LOPT_REMOTE: /* --dhcp-remoteid */
3800 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003801 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003802 unsigned char *p;
3803 int dig = 0;
3804 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3805
3806 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003807 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003808
3809 new->netid.net = opt_string_alloc(set_prefix(arg));
3810 /* check for hex string - must digits may include : must not have nothing else,
3811 only allowed for agent-options. */
3812
3813 arg = comma;
3814 if ((comma = split(arg)))
3815 {
3816 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003817 {
3818 free(new->netid.net);
3819 ret_err_free(gen_err, new);
3820 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003821 else
3822 new->enterprise = atoi(arg+11);
3823 }
3824 else
3825 comma = arg;
3826
3827 for (p = (unsigned char *)comma; *p; p++)
3828 if (isxdigit(*p))
3829 dig = 1;
3830 else if (*p != ':')
3831 break;
3832 unhide_metas(comma);
3833 if (option == 'U' || option == 'j' || *p || !dig)
3834 {
3835 new->len = strlen(comma);
3836 new->data = opt_malloc(new->len);
3837 memcpy(new->data, comma, new->len);
3838 }
3839 else
3840 {
3841 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3842 new->data = opt_malloc(new->len);
3843 memcpy(new->data, comma, new->len);
3844 }
3845
3846 switch (option)
3847 {
3848 case 'j':
3849 new->match_type = MATCH_USER;
3850 break;
3851 case 'U':
3852 new->match_type = MATCH_VENDOR;
3853 break;
3854 case LOPT_CIRCUIT:
3855 new->match_type = MATCH_CIRCUIT;
3856 break;
3857 case LOPT_REMOTE:
3858 new->match_type = MATCH_REMOTE;
3859 break;
3860 case LOPT_SUBSCR:
3861 new->match_type = MATCH_SUBSCRIBER;
3862 break;
3863 }
3864 new->next = daemon->dhcp_vendors;
3865 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003866
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003867 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003868 }
3869
Simon Kelley9e038942008-05-30 20:06:34 +01003870 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3871 if (!arg)
3872 {
3873 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3874 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3875 }
3876 else
3877 {
3878 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003879 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3880 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003881 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003882 if (!comma)
3883 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3884 }
3885 break;
3886
Simon Kelley824af852008-02-12 20:43:05 +00003887 case 'J': /* --dhcp-ignore */
3888 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3889 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003890 case '3': /* --bootp-dynamic */
3891 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003892 {
Simon Kelley824af852008-02-12 20:43:05 +00003893 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003894 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003895 if (option == 'J')
3896 {
3897 new->next = daemon->dhcp_ignore;
3898 daemon->dhcp_ignore = new;
3899 }
Simon Kelley824af852008-02-12 20:43:05 +00003900 else if (option == LOPT_BROADCAST)
3901 {
3902 new->next = daemon->force_broadcast;
3903 daemon->force_broadcast = new;
3904 }
Simon Kelley9009d742008-11-14 20:04:27 +00003905 else if (option == '3')
3906 {
3907 new->next = daemon->bootp_dynamic;
3908 daemon->bootp_dynamic = new;
3909 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003910 else if (option == LOPT_GEN_NAMES)
3911 {
3912 new->next = daemon->dhcp_gen_names;
3913 daemon->dhcp_gen_names = new;
3914 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003915 else
3916 {
3917 new->next = daemon->dhcp_ignore_names;
3918 daemon->dhcp_ignore_names = new;
3919 }
3920
3921 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003922 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003923 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003924 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003925 }
Simon Kelley849a8352006-06-09 21:02:31 +01003926
3927 new->list = list;
3928 break;
3929 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003930
3931 case LOPT_PROXY: /* --dhcp-proxy */
3932 daemon->override = 1;
3933 while (arg) {
3934 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3935 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003936 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003937 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003938 new->next = daemon->override_relays;
3939 daemon->override_relays = new;
3940 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003941 }
3942 break;
3943
3944 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3945 {
3946 while (arg) {
3947 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3948 comma = split(arg);
3949 new->data = opt_string_alloc(arg);
3950 new->next = daemon->dhcp_pxe_vendors;
3951 daemon->dhcp_pxe_vendors = new;
3952 arg = comma;
3953 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003954 }
3955 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003956
3957 case LOPT_RELAY: /* --dhcp-relay */
3958 {
3959 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3960 comma = split(arg);
3961 new->interface = opt_string_alloc(split(comma));
3962 new->iface_index = 0;
3963 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3964 {
3965 new->next = daemon->relay4;
3966 daemon->relay4 = new;
3967 }
3968#ifdef HAVE_DHCP6
3969 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3970 {
3971 new->next = daemon->relay6;
3972 daemon->relay6 = new;
3973 }
3974#endif
3975 else
Petr Menšík59e47032018-11-02 22:39:39 +00003976 {
3977 free(new->interface);
3978 ret_err_free(_("Bad dhcp-relay"), new);
3979 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003980
3981 break;
3982 }
3983
Simon Kelley7622fc02009-06-04 20:32:05 +01003984#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003985
Simon Kelley8b372702012-03-09 17:45:10 +00003986#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003987 case LOPT_RA_PARAM: /* --ra-param */
3988 if ((comma = split(arg)))
3989 {
3990 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3991 new->lifetime = -1;
3992 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003993 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003994 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003995 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003996 if (strcasestr(comma, "mtu:") == comma)
3997 {
3998 arg = comma + 4;
3999 if (!(comma = split(comma)))
4000 goto err;
4001 if (!strcasecmp(arg, "off"))
4002 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004003 else if (!atoi_check(arg, &new->mtu))
4004 new->mtu_name = opt_string_alloc(arg);
4005 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004006 goto err;
4007 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004008 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4009 {
4010 if (*comma == 'l' || *comma == 'L')
4011 new->prio = 0x18;
4012 else
4013 new->prio = 0x08;
4014 comma = split(comma);
4015 }
4016 arg = split(comma);
4017 if (!atoi_check(comma, &new->interval) ||
4018 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004019 {
David Flamand005c46d2017-04-11 11:49:54 +01004020err:
Petr Menšík59e47032018-11-02 22:39:39 +00004021 free(new->name);
4022 ret_err_free(_("bad RA-params"), new);
4023 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004024
4025 new->next = daemon->ra_interfaces;
4026 daemon->ra_interfaces = new;
4027 }
4028 break;
4029
Simon Kelley8b372702012-03-09 17:45:10 +00004030 case LOPT_DUID: /* --dhcp-duid */
4031 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004032 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004033 else
4034 {
4035 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4036 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4037 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4038 }
4039 break;
4040#endif
4041
Simon Kelleyf2621c72007-04-29 19:47:21 +01004042 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004043 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004044 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004045 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004046 struct doctor *new = opt_malloc(sizeof(struct doctor));
4047 new->next = daemon->doctors;
4048 daemon->doctors = new;
4049 new->mask.s_addr = 0xffffffff;
4050 new->end.s_addr = 0;
4051
Simon Kelley849a8352006-06-09 21:02:31 +01004052 if ((a[0] = arg))
4053 for (k = 1; k < 3; k++)
4054 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004055 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004056 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004057 unhide_metas(a[k]);
4058 }
Simon Kelley849a8352006-06-09 21:02:31 +01004059
Simon Kelley73a08a22009-02-05 20:28:08 +00004060 dash = split_chr(a[0], '-');
4061
Simon Kelley849a8352006-06-09 21:02:31 +01004062 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004063 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004064 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4065 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4066 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004067
Simon Kelley73a08a22009-02-05 20:28:08 +00004068 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004069 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004070 !is_same_net(new->in, new->end, new->mask) ||
4071 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004072 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004073
4074 break;
4075 }
4076
Simon Kelleyf2621c72007-04-29 19:47:21 +01004077 case LOPT_INTNAME: /* --interface-name */
4078 {
4079 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01004080 char *domain = NULL;
4081
Simon Kelleyf2621c72007-04-29 19:47:21 +01004082 comma = split(arg);
4083
Simon Kelley1f15b812009-10-13 17:49:32 +01004084 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004085 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01004086
Simon Kelley824af852008-02-12 20:43:05 +00004087 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004088 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00004089 new->addr = NULL;
4090
Simon Kelleyf2621c72007-04-29 19:47:21 +01004091 /* Add to the end of the list, so that first name
4092 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004093 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004094 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004095 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004096 new->family = 0;
4097 arg = split_chr(comma, '/');
4098 if (arg)
4099 {
4100 if (strcmp(arg, "4") == 0)
4101 new->family = AF_INET;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004102 else if (strcmp(arg, "6") == 0)
4103 new->family = AF_INET6;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004104 else
Petr Menšík59e47032018-11-02 22:39:39 +00004105 ret_err_free(gen_err, new);
Simon Kelleyf7029f52013-11-21 15:09:09 +00004106 }
Simon Kelley824af852008-02-12 20:43:05 +00004107 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004108 break;
4109 }
Simon Kelley9009d742008-11-14 20:04:27 +00004110
4111 case LOPT_CNAME: /* --cname */
4112 {
4113 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004114 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004115 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004116
Simon Kelleya1d973f2016-12-22 22:09:50 +00004117 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004118 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004119 pen = last;
4120 last = comma;
4121 }
4122
4123 if (!pen)
4124 ret_err(_("bad CNAME"));
4125
4126 if (pen != arg && atoi_check(last, &ttl))
4127 last = pen;
4128
4129 target = canonicalise_opt(last);
4130
4131 while (arg != last)
4132 {
Petr Menšík56f06232018-03-06 23:13:32 +00004133 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004134 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004135
4136 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004137 {
4138 free(target);
4139 free(alias);
4140 ret_err(_("bad CNAME"));
4141 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004142
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004143 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004144 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004145 {
4146 free(target);
4147 free(alias);
4148 ret_err(_("duplicate CNAME"));
4149 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004150 new = opt_malloc(sizeof(struct cname));
4151 new->next = daemon->cnames;
4152 daemon->cnames = new;
4153 new->alias = alias;
4154 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004155 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004156
Petr Menšík56f06232018-03-06 23:13:32 +00004157 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004158 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004159
Simon Kelley9009d742008-11-14 20:04:27 +00004160 break;
4161 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004162
4163 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004164 {
4165 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004166 char *dom, *target = NULL;
4167
Simon Kelleyf2621c72007-04-29 19:47:21 +01004168 comma = split(arg);
4169
Simon Kelley1f15b812009-10-13 17:49:32 +01004170 if (!(dom = canonicalise_opt(arg)) ||
4171 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004172 {
4173 free(dom);
4174 free(target);
4175 ret_err(_("bad PTR record"));
4176 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004177 else
4178 {
4179 new = opt_malloc(sizeof(struct ptr_record));
4180 new->next = daemon->ptr;
4181 daemon->ptr = new;
4182 new->name = dom;
4183 new->ptr = target;
4184 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004185 break;
4186 }
4187
Simon Kelley1a6bca82008-07-11 11:11:42 +01004188 case LOPT_NAPTR: /* --naptr-record */
4189 {
4190 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4191 int k = 0;
4192 struct naptr *new;
4193 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004194 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004195
4196 if ((a[0] = arg))
4197 for (k = 1; k < 7; k++)
4198 if (!(a[k] = split(a[k-1])))
4199 break;
4200
4201
4202 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004203 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004204 !atoi_check16(a[1], &order) ||
4205 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004206 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004207 {
4208 free(name);
4209 free(replace);
4210 ret_err(_("bad NAPTR record"));
4211 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004212 else
4213 {
4214 new = opt_malloc(sizeof(struct naptr));
4215 new->next = daemon->naptr;
4216 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004217 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004218 new->flags = opt_string_alloc(a[3]);
4219 new->services = opt_string_alloc(a[4]);
4220 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004221 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004222 new->order = order;
4223 new->pref = pref;
4224 }
4225 break;
4226 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004227
4228 case LOPT_RR: /* dns-rr */
4229 {
4230 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004231 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004232 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004233 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004234
4235 comma = split(arg);
4236 data = split(comma);
4237
4238 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004239 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004240
Petr Menšík59e47032018-11-02 22:39:39 +00004241 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004242 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004243 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004244 {
4245 free(new->name);
4246 ret_err_free(_("bad RR record"), new);
4247 }
4248
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004249 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004250 new->class = class;
4251 new->next = daemon->rr;
4252 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004253
4254 if (data)
4255 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004256 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004257 new->len = len;
4258 memcpy(new->txt, data, len);
4259 }
4260
4261 break;
4262 }
4263
Simon Kelley974a6d02018-08-23 23:01:16 +01004264 case LOPT_CAA: /* --caa-record */
4265 {
4266 struct txt_record *new;
4267 char *tag, *value;
4268 int flags;
4269
4270 comma = split(arg);
4271 tag = split(comma);
4272 value = split(tag);
4273
4274 new = opt_malloc(sizeof(struct txt_record));
4275 new->next = daemon->rr;
4276 daemon->rr = new;
4277
4278 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4279 ret_err(_("bad CAA record"));
4280
4281 unhide_metas(tag);
4282 unhide_metas(value);
4283
4284 new->len = strlen(tag) + strlen(value) + 2;
4285 new->txt = opt_malloc(new->len);
4286 new->txt[0] = flags;
4287 new->txt[1] = strlen(tag);
4288 memcpy(&new->txt[2], tag, strlen(tag));
4289 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4290 new->class = T_CAA;
4291
4292 break;
4293 }
4294
Simon Kelleyf2621c72007-04-29 19:47:21 +01004295 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004296 {
4297 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004298 unsigned char *p, *cnt;
4299 size_t len;
4300
4301 comma = split(arg);
4302
Simon Kelley824af852008-02-12 20:43:05 +00004303 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004304 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004305 new->stat = 0;
4306
Simon Kelley1f15b812009-10-13 17:49:32 +01004307 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004308 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004309
Petr Menšík59e47032018-11-02 22:39:39 +00004310 new->next = daemon->txt;
4311 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004312 len = comma ? strlen(comma) : 0;
4313 len += (len/255) + 1; /* room for extra counts */
4314 new->txt = p = opt_malloc(len);
4315
4316 cnt = p++;
4317 *cnt = 0;
4318
4319 while (comma && *comma)
4320 {
4321 unsigned char c = (unsigned char)*comma++;
4322
4323 if (c == ',' || *cnt == 255)
4324 {
4325 if (c != ',')
4326 comma--;
4327 cnt = p++;
4328 *cnt = 0;
4329 }
4330 else
4331 {
4332 *p++ = unhide_meta(c);
4333 (*cnt)++;
4334 }
4335 }
4336
4337 new->len = p - new->txt;
4338
Simon Kelley849a8352006-06-09 21:02:31 +01004339 break;
4340 }
4341
Simon Kelleyf2621c72007-04-29 19:47:21 +01004342 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004343 {
4344 int port = 1, priority = 0, weight = 0;
4345 char *name, *target = NULL;
4346 struct mx_srv_record *new;
4347
Simon Kelleyf2621c72007-04-29 19:47:21 +01004348 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004349
Simon Kelley1f15b812009-10-13 17:49:32 +01004350 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004351 ret_err(_("bad SRV record"));
4352
Simon Kelley849a8352006-06-09 21:02:31 +01004353 if (comma)
4354 {
4355 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004356 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004357 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004358 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004359
Simon Kelley849a8352006-06-09 21:02:31 +01004360 if (comma)
4361 {
4362 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004363 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004364 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004365 {
4366 free(name);
4367 ret_err_free(_("invalid port number"), target);
4368 }
Simon Kelley824af852008-02-12 20:43:05 +00004369
Simon Kelley849a8352006-06-09 21:02:31 +01004370 if (comma)
4371 {
4372 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004373 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004374 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004375 {
4376 free(name);
4377 ret_err_free(_("invalid priority"), target);
4378 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004379 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004380 {
4381 free(name);
4382 ret_err_free(_("invalid weight"), target);
4383 }
Simon Kelley849a8352006-06-09 21:02:31 +01004384 }
4385 }
4386 }
4387
Simon Kelley824af852008-02-12 20:43:05 +00004388 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004389 new->next = daemon->mxnames;
4390 daemon->mxnames = new;
4391 new->issrv = 1;
4392 new->name = name;
4393 new->target = target;
4394 new->srvport = port;
4395 new->priority = priority;
4396 new->weight = weight;
4397 break;
4398 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004399
Simon Kelleye759d422012-03-16 13:18:57 +00004400 case LOPT_HOST_REC: /* --host-record */
4401 {
Petr Menšík59e47032018-11-02 22:39:39 +00004402 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004403
Simon Kelleye759d422012-03-16 13:18:57 +00004404 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004405 ret_err(_("Bad host-record"));
4406
Petr Menšík59e47032018-11-02 22:39:39 +00004407 new = opt_malloc(sizeof(struct host_record));
4408 memset(new, 0, sizeof(struct host_record));
4409 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004410 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004411
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004412 while (arg)
4413 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004414 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004415 char *dig;
4416
4417 for (dig = arg; *dig != 0; dig++)
4418 if (*dig < '0' || *dig > '9')
4419 break;
4420 if (*dig == 0)
4421 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004422 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004423 {
4424 new->addr = addr.addr4;
4425 new->flags |= HR_4;
4426 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004427 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004428 {
4429 new->addr6 = addr.addr6;
4430 new->flags |= HR_6;
4431 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004432 else
4433 {
4434 int nomem;
4435 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004436 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004437 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004438 {
4439 struct name_list *tmp = new->names, *next;
4440 for (tmp = new->names; tmp; tmp = next)
4441 {
4442 next = tmp->next;
4443 free(tmp);
4444 }
4445 ret_err_free(_("Bad name in host-record"), new);
4446 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004447
Petr Menšík59e47032018-11-02 22:39:39 +00004448 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004449 nl->name = canon;
4450 /* keep order, so that PTR record goes to first name */
4451 nl->next = NULL;
4452 if (!new->names)
4453 new->names = nl;
4454 else
4455 {
4456 struct name_list *tmp;
4457 for (tmp = new->names; tmp->next; tmp = tmp->next);
4458 tmp->next = nl;
4459 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004460 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004461
4462 arg = comma;
4463 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004464 }
Simon Kelleye759d422012-03-16 13:18:57 +00004465
4466 /* Keep list order */
4467 if (!daemon->host_records_tail)
4468 daemon->host_records = new;
4469 else
4470 daemon->host_records_tail->next = new;
4471 new->next = NULL;
4472 daemon->host_records_tail = new;
4473 break;
4474 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004475
4476#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004477 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004478 daemon->timestamp_file = opt_string_alloc(arg);
4479 break;
4480
Simon Kelleyf3e57872018-07-20 21:10:48 +01004481 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004482 if (arg)
4483 {
4484 if (strcmp(arg, "no") == 0)
4485 set_option_bool(OPT_DNSSEC_IGN_NS);
4486 else
4487 ret_err(_("bad value for dnssec-check-unsigned"));
4488 }
4489 break;
4490
Simon Kelleyf3e57872018-07-20 21:10:48 +01004491 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004492 {
Simon Kelleyee415862014-02-11 11:07:22 +00004493 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4494 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4495 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004496
4497 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004498 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004499
Simon Kelleycbf13a22014-01-25 17:59:14 +00004500 if ((comma = split(arg)) && (algo = split(comma)))
4501 {
4502 int class = 0;
4503 if (strcmp(comma, "IN") == 0)
4504 class = C_IN;
4505 else if (strcmp(comma, "CH") == 0)
4506 class = C_CHAOS;
4507 else if (strcmp(comma, "HS") == 0)
4508 class = C_HESIOD;
4509
4510 if (class != 0)
4511 {
4512 new->class = class;
4513 comma = algo;
4514 algo = split(comma);
4515 }
4516 }
4517
Simon Kelleyee415862014-02-11 11:07:22 +00004518 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4519 !atoi_check16(comma, &new->keytag) ||
4520 !atoi_check8(algo, &new->algo) ||
4521 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004522 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004523 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004524
Simon Kelley0fc2f312014-01-08 10:26:58 +00004525 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004526 len = (2*strlen(keyhex))+1;
4527 new->digest = opt_malloc(len);
4528 unhide_metas(keyhex);
4529 /* 4034: "Whitespace is allowed within digits" */
4530 for (cp = keyhex; *cp; )
4531 if (isspace(*cp))
4532 for (cp1 = cp; *cp1; cp1++)
4533 *cp1 = *(cp1+1);
4534 else
4535 cp++;
4536 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004537 {
4538 free(new->name);
4539 ret_err_free(_("bad HEX in trust anchor"), new);
4540 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004541
Simon Kelleyee415862014-02-11 11:07:22 +00004542 new->next = daemon->ds;
4543 daemon->ds = new;
4544
Simon Kelley0fc2f312014-01-08 10:26:58 +00004545 break;
4546 }
4547#endif
4548
Simon Kelley7622fc02009-06-04 20:32:05 +01004549 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004550 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004551
Simon Kelley849a8352006-06-09 21:02:31 +01004552 }
Simon Kelley824af852008-02-12 20:43:05 +00004553
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004554 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004555}
4556
Simon Kelley28866e92011-02-14 20:19:14 +00004557static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004558{
Simon Kelley824af852008-02-12 20:43:05 +00004559 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004560 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004561
4562 while (fgets(buff, MAXDNAME, f))
4563 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004564 int white, i;
4565 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004566 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004567 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004568
Simon Kelley824af852008-02-12 20:43:05 +00004569 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004570 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004571 {
4572 if (setjmp(mem_jmp))
4573 continue;
4574 mem_recover = 1;
4575 }
4576
Simon Kelley13dee6f2017-02-28 16:51:58 +00004577 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004578 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004579 errmess = NULL;
4580
Simon Kelley849a8352006-06-09 21:02:31 +01004581 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4582 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004583 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004584 {
4585 if (*p == '"')
4586 {
4587 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004588
Simon Kelley849a8352006-06-09 21:02:31 +01004589 for(; *p && *p != '"'; p++)
4590 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004591 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004592 {
4593 if (p[1] == 't')
4594 p[1] = '\t';
4595 else if (p[1] == 'n')
4596 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004597 else if (p[1] == 'b')
4598 p[1] = '\b';
4599 else if (p[1] == 'r')
4600 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004601 else if (p[1] == 'e') /* escape */
4602 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004603 memmove(p, p+1, strlen(p+1)+1);
4604 }
4605 *p = hide_meta(*p);
4606 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004607
4608 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004609 {
4610 errmess = _("missing \"");
4611 goto oops;
4612 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004613
4614 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004615 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004616
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004617 if (isspace(*p))
4618 {
4619 *p = ' ';
4620 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004621 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004622 else
4623 {
4624 if (white && *p == '#')
4625 {
4626 *p = 0;
4627 break;
4628 }
4629 white = 0;
4630 }
Simon Kelley849a8352006-06-09 21:02:31 +01004631 }
4632
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004633
4634 /* strip leading spaces */
4635 for (start = buff; *start && *start == ' '; start++);
4636
4637 /* strip trailing spaces */
4638 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4639
4640 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004641 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004642 else
4643 start[len] = 0;
4644
Simon Kelley611ebc52012-07-16 16:23:46 +01004645 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004646 arg = start;
4647 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004648 {
4649 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004650 for (arg = p+1; *arg == ' '; arg++);
4651 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004652 *p = 0;
4653 }
4654 else
4655 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004656
Simon Kelley611ebc52012-07-16 16:23:46 +01004657 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004658 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004659 for (option = 0, i = 0; opts[i].name; i++)
4660 if (strcmp(opts[i].name, start) == 0)
4661 {
4662 option = opts[i].val;
4663 break;
4664 }
4665
4666 if (!option)
4667 errmess = _("bad option");
4668 else if (opts[i].has_arg == 0 && arg)
4669 errmess = _("extraneous parameter");
4670 else if (opts[i].has_arg == 1 && !arg)
4671 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004672 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4673 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004674 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004675
4676 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004677 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004678 strcpy(daemon->namebuff, errmess);
4679
Simon Kelley9bafdc62018-08-21 22:53:38 +01004680 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004681 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004682 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004683 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004684 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004685 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004686 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004687 }
Simon Kelley849a8352006-06-09 21:02:31 +01004688 }
4689
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004690 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004691 fclose(f);
4692}
4693
Simon Kelley4f7bb572018-03-08 18:47:08 +00004694#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004695int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004696{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004697 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4698
Simon Kelley70d18732015-01-31 19:59:29 +00004699 if (flags & AH_DHCP_HST)
4700 return one_file(file, LOPT_BANK);
4701 else if (flags & AH_DHCP_OPT)
4702 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004703
Simon Kelley70d18732015-01-31 19:59:29 +00004704 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004705}
4706#endif
4707
Simon Kelley395eb712012-07-06 22:07:05 +01004708static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004709{
4710 FILE *f;
4711 int nofile_ok = 0;
4712 static int read_stdin = 0;
4713 static struct fileread {
4714 dev_t dev;
4715 ino_t ino;
4716 struct fileread *next;
4717 } *filesread = NULL;
4718
4719 if (hard_opt == '7')
4720 {
4721 /* default conf-file reading */
4722 hard_opt = 0;
4723 nofile_ok = 1;
4724 }
4725
4726 if (hard_opt == 0 && strcmp(file, "-") == 0)
4727 {
4728 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004729 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004730 read_stdin = 1;
4731 file = "stdin";
4732 f = stdin;
4733 }
4734 else
4735 {
4736 /* ignore repeated files. */
4737 struct stat statbuf;
4738
4739 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4740 {
4741 struct fileread *r;
4742
4743 for (r = filesread; r; r = r->next)
4744 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004745 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004746
4747 r = safe_malloc(sizeof(struct fileread));
4748 r->next = filesread;
4749 filesread = r;
4750 r->dev = statbuf.st_dev;
4751 r->ino = statbuf.st_ino;
4752 }
4753
4754 if (!(f = fopen(file, "r")))
4755 {
4756 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004757 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004758 else
4759 {
4760 char *str = _("cannot read %s: %s");
4761 if (hard_opt != 0)
4762 {
4763 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004764 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004765 }
4766 else
4767 die(str, file, EC_FILE);
4768 }
4769 }
4770 }
4771
4772 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004773 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004774}
4775
4776/* expand any name which is a directory */
4777struct hostsfile *expand_filelist(struct hostsfile *list)
4778{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004779 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004780 struct hostsfile *ah;
4781
Simon Kelley19c51cf2014-03-18 22:38:30 +00004782 /* find largest used index */
4783 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004784 {
4785 if (i <= ah->index)
4786 i = ah->index + 1;
4787
4788 if (ah->flags & AH_DIR)
4789 ah->flags |= AH_INACTIVE;
4790 else
4791 ah->flags &= ~AH_INACTIVE;
4792 }
4793
4794 for (ah = list; ah; ah = ah->next)
4795 if (!(ah->flags & AH_INACTIVE))
4796 {
4797 struct stat buf;
4798 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4799 {
4800 DIR *dir_stream;
4801 struct dirent *ent;
4802
4803 /* don't read this as a file */
4804 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004805
Simon Kelley28866e92011-02-14 20:19:14 +00004806 if (!(dir_stream = opendir(ah->fname)))
4807 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4808 ah->fname, strerror(errno));
4809 else
4810 {
4811 while ((ent = readdir(dir_stream)))
4812 {
4813 size_t lendir = strlen(ah->fname);
4814 size_t lenfile = strlen(ent->d_name);
4815 struct hostsfile *ah1;
4816 char *path;
4817
4818 /* ignore emacs backups and dotfiles */
4819 if (lenfile == 0 ||
4820 ent->d_name[lenfile - 1] == '~' ||
4821 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4822 ent->d_name[0] == '.')
4823 continue;
4824
4825 /* see if we have an existing record.
4826 dir is ah->fname
4827 file is ent->d_name
4828 path to match is ah1->fname */
4829
4830 for (ah1 = list; ah1; ah1 = ah1->next)
4831 {
4832 if (lendir < strlen(ah1->fname) &&
4833 strstr(ah1->fname, ah->fname) == ah1->fname &&
4834 ah1->fname[lendir] == '/' &&
4835 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4836 {
4837 ah1->flags &= ~AH_INACTIVE;
4838 break;
4839 }
4840 }
4841
4842 /* make new record */
4843 if (!ah1)
4844 {
4845 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4846 continue;
4847
4848 if (!(path = whine_malloc(lendir + lenfile + 2)))
4849 {
4850 free(ah1);
4851 continue;
4852 }
4853
4854 strcpy(path, ah->fname);
4855 strcat(path, "/");
4856 strcat(path, ent->d_name);
4857 ah1->fname = path;
4858 ah1->index = i++;
4859 ah1->flags = AH_DIR;
4860 ah1->next = list;
4861 list = ah1;
4862 }
4863
4864 /* inactivate record if not regular file */
4865 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4866 ah1->flags |= AH_INACTIVE;
4867
4868 }
4869 closedir(dir_stream);
4870 }
4871 }
4872 }
4873
4874 return list;
4875}
4876
Simon Kelley7b1eae42014-02-20 13:43:28 +00004877void read_servers_file(void)
4878{
4879 FILE *f;
4880
4881 if (!(f = fopen(daemon->servers_file, "r")))
4882 {
4883 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4884 return;
4885 }
4886
4887 mark_servers(SERV_FROM_FILE);
4888 cleanup_servers();
4889
4890 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4891}
4892
Simon Kelley28866e92011-02-14 20:19:14 +00004893
Simon Kelley7622fc02009-06-04 20:32:05 +01004894#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004895static void clear_dynamic_conf(void)
4896{
4897 struct dhcp_config *configs, *cp, **up;
4898
4899 /* remove existing... */
4900 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4901 {
4902 cp = configs->next;
4903
4904 if (configs->flags & CONFIG_BANK)
4905 {
4906 struct hwaddr_config *mac, *tmp;
4907 struct dhcp_netid_list *list, *tmplist;
4908
4909 for (mac = configs->hwaddr; mac; mac = tmp)
4910 {
4911 tmp = mac->next;
4912 free(mac);
4913 }
4914
4915 if (configs->flags & CONFIG_CLID)
4916 free(configs->clid);
4917
4918 for (list = configs->netid; list; list = tmplist)
4919 {
4920 free(list->list);
4921 tmplist = list->next;
4922 free(list);
4923 }
4924
4925 if (configs->flags & CONFIG_NAME)
4926 free(configs->hostname);
4927
4928 *up = configs->next;
4929 free(configs);
4930 }
4931 else
4932 up = &configs->next;
4933 }
4934}
4935
4936static void clear_dynamic_opt(void)
4937{
4938 struct dhcp_opt *opts, *cp, **up;
4939 struct dhcp_netid *id, *next;
4940
4941 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4942 {
4943 cp = opts->next;
4944
4945 if (opts->flags & DHOPT_BANK)
4946 {
4947 if ((opts->flags & DHOPT_VENDOR))
4948 free(opts->u.vendor_class);
4949 free(opts->val);
4950 for (id = opts->netid; id; id = next)
4951 {
4952 next = id->next;
4953 free(id->net);
4954 free(id);
4955 }
4956 *up = opts->next;
4957 free(opts);
4958 }
4959 else
4960 up = &opts->next;
4961 }
4962}
4963
Simon Kelley824af852008-02-12 20:43:05 +00004964void reread_dhcp(void)
4965{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004966 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004967
Simon Kelley4f7bb572018-03-08 18:47:08 +00004968 /* Do these even if there is no daemon->dhcp_hosts_file or
4969 daemon->dhcp_opts_file since entries may have been created by the
4970 inotify dynamic file reading system. */
4971
4972 clear_dynamic_conf();
4973 clear_dynamic_opt();
4974
4975 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004976 {
Simon Kelley28866e92011-02-14 20:19:14 +00004977 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4978 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004979 if (!(hf->flags & AH_INACTIVE))
4980 {
4981 if (one_file(hf->fname, LOPT_BANK))
4982 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4983 }
Simon Kelley824af852008-02-12 20:43:05 +00004984 }
4985
4986 if (daemon->dhcp_opts_file)
4987 {
Simon Kelley28866e92011-02-14 20:19:14 +00004988 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4989 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4990 if (!(hf->flags & AH_INACTIVE))
4991 {
Simon Kelley395eb712012-07-06 22:07:05 +01004992 if (one_file(hf->fname, LOPT_OPTS))
4993 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004994 }
Simon Kelley824af852008-02-12 20:43:05 +00004995 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004996
4997# ifdef HAVE_INOTIFY
4998 /* Setup notify and read pre-existing files. */
4999 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5000# endif
Simon Kelley824af852008-02-12 20:43:05 +00005001}
Simon Kelley7622fc02009-06-04 20:32:05 +01005002#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005003
Simon Kelley5aabfc72007-08-29 11:24:47 +01005004void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005005{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005006 size_t argbuf_size = MAXDNAME;
5007 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005008 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005009 int option, testmode = 0;
5010 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005011
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005012 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005013
Simon Kelley824af852008-02-12 20:43:05 +00005014 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005015 memset(daemon, 0, sizeof(struct daemon));
5016 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005017
Simon Kelley3be34542004-09-11 19:12:13 +01005018 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005019 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005020 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005021 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005022 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5023 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005024 daemon->default_resolv.is_default = 1;
5025 daemon->default_resolv.name = RESOLVFILE;
5026 daemon->resolv_files = &daemon->default_resolv;
5027 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005028 daemon->runfile = RUNFILE;
5029 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005030 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005031 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005032 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005033 daemon->auth_ttl = AUTH_TTL;
5034 daemon->soa_refresh = SOA_REFRESH;
5035 daemon->soa_retry = SOA_RETRY;
5036 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00005037 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00005038 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01005039
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005040#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005041 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5042 add_txt("authors.bind", "Simon Kelley", 0);
5043 add_txt("copyright.bind", COPYRIGHT, 0);
5044 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5045 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5046 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5047 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5048 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5049#ifdef HAVE_AUTH
5050 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5051#endif
5052 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005053#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005054
Simon Kelley849a8352006-06-09 21:02:31 +01005055 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005056 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005057#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005058 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005059#else
Simon Kelley849a8352006-06-09 21:02:31 +01005060 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005061#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005062
5063 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005064 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005065 for (; optind < argc; optind++)
5066 {
5067 unsigned char *c = (unsigned char *)argv[optind];
5068 for (; *c != 0; c++)
5069 if (!isspace(*c))
5070 die(_("junk found in command line"), NULL, EC_BADCONF);
5071 }
Simon Kelley28866e92011-02-14 20:19:14 +00005072 break;
5073 }
5074
Simon Kelley849a8352006-06-09 21:02:31 +01005075 /* Copy optarg so that argv doesn't get changed */
5076 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005077 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005078 if (strlen(optarg) >= argbuf_size)
5079 {
5080 free(argbuf);
5081 argbuf_size = strlen(optarg) + 1;
5082 argbuf = opt_malloc(argbuf_size);
5083 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005084 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005085 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005086 }
5087 else
5088 arg = NULL;
5089
5090 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005091 if (option == LOPT_TEST)
5092 testmode = 1;
5093 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005094 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005095#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005096 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005097 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005098#ifdef HAVE_DHCP6
5099 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5100 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005101#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005102 else
5103#endif
5104 do_usage();
5105
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005106 exit(0);
5107 }
Simon Kelley849a8352006-06-09 21:02:31 +01005108 else if (option == 'v')
5109 {
5110 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005111 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005112 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5113 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005114 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005115 exit(0);
5116 }
Simon Kelley849a8352006-06-09 21:02:31 +01005117 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005118 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005119 if (!conffile)
5120 conffile = opt_string_alloc(arg);
5121 else
5122 {
5123 char *extra = opt_string_alloc(arg);
5124 one_file(extra, 0);
5125 free(extra);
5126 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005127 }
Simon Kelley849a8352006-06-09 21:02:31 +01005128 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005129 {
Simon Kelley26128d22004-11-14 16:43:54 +00005130#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005131 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005132#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005133 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005134#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005135 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005136 }
5137 }
Simon Kelley849a8352006-06-09 21:02:31 +01005138
Neil Jerram3bd4c472018-01-18 22:49:38 +00005139 free(argbuf);
5140
Simon Kelley849a8352006-06-09 21:02:31 +01005141 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005142 {
Petr Menšík59e47032018-11-02 22:39:39 +00005143 one_file(conffile, 0);
5144 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005145 }
Petr Menšík59e47032018-11-02 22:39:39 +00005146 else
5147 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005148
Simon Kelley1a6bca82008-07-11 11:11:42 +01005149 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005150 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005151 {
5152 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005153 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005154 if (!(tmp->flags & SERV_HAS_SOURCE))
5155 {
5156 if (tmp->source_addr.sa.sa_family == AF_INET)
5157 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005158 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5159 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005160 }
5161 }
5162
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005163 if (daemon->host_records)
5164 {
5165 struct host_record *hr;
5166
5167 for (hr = daemon->host_records; hr; hr = hr->next)
5168 if (hr->ttl == -1)
5169 hr->ttl = daemon->local_ttl;
5170 }
5171
5172 if (daemon->cnames)
5173 {
Simon Kelley903df072017-01-19 17:22:00 +00005174 struct cname *cn, *cn2, *cn3;
5175
5176#define NOLOOP 1
5177#define TESTLOOP 2
5178
Simon Kelley157d8cf2019-10-25 17:46:49 +01005179 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005180 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005181 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005182 {
5183 if (cn->ttl == -1)
5184 cn->ttl = daemon->local_ttl;
5185 cn->flag = 0;
5186 cn->targetp = NULL;
5187 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5188 if (hostname_isequal(cn->target, cn2->alias))
5189 {
5190 cn->targetp = cn2;
5191 break;
5192 }
5193 }
5194
5195 /* Find any CNAME loops.*/
5196 for (cn = daemon->cnames; cn; cn = cn->next)
5197 {
5198 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5199 {
5200 if (cn2->flag == NOLOOP)
5201 break;
5202
5203 if (cn2->flag == TESTLOOP)
5204 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5205
5206 cn2->flag = TESTLOOP;
5207 }
5208
5209 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5210 cn3->flag = NOLOOP;
5211 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005212 }
5213
Simon Kelley3be34542004-09-11 19:12:13 +01005214 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005215 {
5216 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005217 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005218 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005219 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005220 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005221 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005222 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005223
5224 /* create default, if not specified */
5225 if (daemon->authserver && !daemon->hostmaster)
5226 {
5227 strcpy(buff, "hostmaster.");
5228 strcat(buff, daemon->authserver);
5229 daemon->hostmaster = opt_string_alloc(buff);
5230 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005231
5232 if (!daemon->dhcp_pxe_vendors)
5233 {
5234 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5235 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5236 daemon->dhcp_pxe_vendors->next = NULL;
5237 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005238
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005239 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005240 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005241 {
Simon Kelley0a852542005-03-23 20:28:59 +00005242 struct mx_srv_record *mx;
5243
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005244 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005245 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005246
Simon Kelley0a852542005-03-23 20:28:59 +00005247 for (mx = daemon->mxnames; mx; mx = mx->next)
5248 if (!mx->issrv && hostname_isequal(mx->name, buff))
5249 break;
5250
Simon Kelley28866e92011-02-14 20:19:14 +00005251 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005252 {
Simon Kelley824af852008-02-12 20:43:05 +00005253 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005254 mx->next = daemon->mxnames;
5255 mx->issrv = 0;
5256 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005257 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005258 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005259 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005260
Simon Kelley3be34542004-09-11 19:12:13 +01005261 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005262 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005263
5264 for (mx = daemon->mxnames; mx; mx = mx->next)
5265 if (!mx->issrv && !mx->target)
5266 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005267 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005268
Simon Kelley28866e92011-02-14 20:19:14 +00005269 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005270 daemon->resolv_files &&
5271 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005272 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005273 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005274
Simon Kelley28866e92011-02-14 20:19:14 +00005275 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005276 {
5277 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005278 FILE *f;
5279
Simon Kelley28866e92011-02-14 20:19:14 +00005280 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005281 !daemon->resolv_files ||
5282 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005283 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005284
Simon Kelley3be34542004-09-11 19:12:13 +01005285 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005286 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005287
5288 while ((line = fgets(buff, MAXDNAME, f)))
5289 {
5290 char *token = strtok(line, " \t\n\r");
5291
5292 if (!token || strcmp(token, "search") != 0)
5293 continue;
5294
5295 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005296 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005297 break;
5298 }
Simon Kelley3be34542004-09-11 19:12:13 +01005299
Simon Kelleyde379512004-06-22 20:23:33 +01005300 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005301
Simon Kelley3be34542004-09-11 19:12:13 +01005302 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005303 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005304 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005305
5306 if (daemon->domain_suffix)
5307 {
5308 /* add domain for any srv record without one. */
5309 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005310
Simon Kelley3d8df262005-08-29 12:19:27 +01005311 for (srv = daemon->mxnames; srv; srv = srv->next)
5312 if (srv->issrv &&
5313 strchr(srv->name, '.') &&
5314 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5315 {
5316 strcpy(buff, srv->name);
5317 strcat(buff, ".");
5318 strcat(buff, daemon->domain_suffix);
5319 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005320 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005321 }
5322 }
Simon Kelley28866e92011-02-14 20:19:14 +00005323 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005324 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005325
Simon Kelleyc8a80482014-03-05 14:29:54 +00005326 /* If there's access-control config, then ignore --local-service, it's intended
5327 as a system default to keep otherwise unconfigured installations safe. */
5328 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5329 reset_option_bool(OPT_LOCAL_SERVICE);
5330
Simon Kelley7622fc02009-06-04 20:32:05 +01005331 if (testmode)
5332 {
5333 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5334 exit(0);
5335 }
Simon Kelley849a8352006-06-09 21:02:31 +01005336}