blob: bfda212ee220598603ab899c8008340dc8c5cd30 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 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 Kelleyb7cf7542021-03-04 16:54:14 +0000171#define LOPT_DYNHOST 362
Simon Kelleyb260d222021-03-12 21:57:57 +0000172#define LOPT_LOG_DEBUG 363
Simon Kelleybec366b2016-02-24 22:03:26 +0000173
Simon Kelley849a8352006-06-09 21:02:31 +0100174#ifdef HAVE_GETOPT_LONG
175static const struct option opts[] =
176#else
177static const struct myoption opts[] =
178#endif
179 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100180 { "version", 0, 0, 'v' },
181 { "no-hosts", 0, 0, 'h' },
182 { "no-poll", 0, 0, 'n' },
183 { "help", 0, 0, 'w' },
184 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000185 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100186 { "user", 2, 0, 'u' },
187 { "group", 2, 0, 'g' },
188 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000189 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100190 { "mx-host", 1, 0, 'm' },
191 { "mx-target", 1, 0, 't' },
192 { "cache-size", 2, 0, 'c' },
193 { "port", 1, 0, 'p' },
194 { "dhcp-leasefile", 2, 0, 'l' },
195 { "dhcp-lease", 1, 0, 'l' },
196 { "dhcp-host", 1, 0, 'G' },
197 { "dhcp-range", 1, 0, 'F' },
198 { "dhcp-option", 1, 0, 'O' },
199 { "dhcp-boot", 1, 0, 'M' },
200 { "domain", 1, 0, 's' },
201 { "domain-suffix", 1, 0, 's' },
202 { "interface", 1, 0, 'i' },
203 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000204 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100205 { "bogus-priv", 0, 0, 'b' },
206 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000207 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100208 { "selfmx", 0, 0, 'e' },
209 { "filterwin2k", 0, 0, 'f' },
210 { "pid-file", 2, 0, 'x' },
211 { "strict-order", 0, 0, 'o' },
212 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000213 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 { "local", 1, 0, LOPT_LOCAL },
215 { "address", 1, 0, 'A' },
216 { "conf-file", 2, 0, 'C' },
217 { "no-resolv", 0, 0, 'R' },
218 { "expand-hosts", 0, 0, 'E' },
219 { "localmx", 0, 0, 'L' },
220 { "local-ttl", 1, 0, 'T' },
221 { "no-negcache", 0, 0, 'N' },
222 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000223 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100224 { "query-port", 1, 0, 'Q' },
225 { "except-interface", 1, 0, 'I' },
226 { "no-dhcp-interface", 1, 0, '2' },
227 { "domain-needed", 0, 0, 'D' },
228 { "dhcp-lease-max", 1, 0, 'X' },
229 { "bind-interfaces", 0, 0, 'z' },
230 { "read-ethers", 0, 0, 'Z' },
231 { "alias", 1, 0, 'V' },
232 { "dhcp-vendorclass", 1, 0, 'U' },
233 { "dhcp-userclass", 1, 0, 'j' },
234 { "dhcp-ignore", 1, 0, 'J' },
235 { "edns-packet-max", 1, 0, 'P' },
236 { "keep-in-foreground", 0, 0, 'k' },
237 { "dhcp-authoritative", 0, 0, 'K' },
238 { "srv-host", 1, 0, 'W' },
239 { "localise-queries", 0, 0, 'y' },
240 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100241 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100242 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100243 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100244 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100245 { "bootp-dynamic", 2, 0, '3' },
246 { "dhcp-mac", 1, 0, '4' },
247 { "no-ping", 0, 0, '5' },
248 { "dhcp-script", 1, 0, '6' },
249 { "conf-dir", 1, 0, '7' },
250 { "log-facility", 1, 0 ,'8' },
251 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000252 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100253 { "dns-forward-max", 1, 0, '0' },
254 { "clear-on-reload", 0, 0, LOPT_RELOAD },
255 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100256 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100257 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100258 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100259 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100260 { "tftp-root", 1, 0, LOPT_PREFIX },
261 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000262 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100263 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000264 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100265 { "ptr-record", 1, 0, LOPT_PTR },
266 { "naptr-record", 1, 0, LOPT_NAPTR },
267 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000268 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100269 { "dhcp-option-force", 1, 0, LOPT_FORCE },
270 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
271 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
272 { "log-async", 2, 0, LOPT_MAX_LOGS },
273 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
274 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
275 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800276 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100277 { "interface-name", 1, 0, LOPT_INTNAME },
278 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
279 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000280 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000281 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100282 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
283 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
284 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100285 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100286 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100287 { "dhcp-match", 1, 0, LOPT_MATCH },
288 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100289 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100290 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100291 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000292 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100293 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100294 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
295 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
296 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000297 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100298 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
299 { "cname", 1, 0, LOPT_CNAME },
300 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
301 { "pxe-service", 1, 0, LOPT_PXE_SERV },
302 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100303 { "tag-if", 1, 0, LOPT_TAG_IF },
304 { "dhcp-proxy", 2, 0, LOPT_PROXY },
305 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
306 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000307 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100308 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000309 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000310 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100311 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
312 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000313 { "dhcp-client-update", 0, 0, LOPT_FQDN },
314 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000315 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000316 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000317 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100318 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000319 { "auth-zone", 1, 0, LOPT_AUTHZONE },
320 { "auth-server", 1, 0, LOPT_AUTHSERV },
321 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
322 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000323 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000324 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000325 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100326 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200327 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000328 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000329 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100330 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000331 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000332 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100333 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100334 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100335 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
336 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
337 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100338 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000339 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000340 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100341 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000342 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100343 { "dumpfile", 1, 0, LOPT_DUMPFILE },
344 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200345 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000346 { "dynamic-host", 1, 0, LOPT_DYNHOST },
Simon Kelleyb260d222021-03-12 21:57:57 +0000347 { "log-debug", 0, 0, LOPT_LOG_DEBUG },
Simon Kelley849a8352006-06-09 21:02:31 +0100348 { NULL, 0, 0, 0 }
349 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000350
Simon Kelley28866e92011-02-14 20:19:14 +0000351
352#define ARG_DUP OPT_LAST
353#define ARG_ONE OPT_LAST + 1
354#define ARG_USED_CL OPT_LAST + 2
355#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356
Simon Kelley1a6bca82008-07-11 11:11:42 +0100357static struct {
358 int opt;
359 unsigned int rept;
360 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000361 char * const desc;
362 char * const arg;
363} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000364 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
365 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100366 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000367 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
368 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
369 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100370 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
371 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
372 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
373 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
374 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000375 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
376 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
379 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000380 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000381 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100382 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100383 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000384 { '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 +0000385 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000386 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
387 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100388 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
389 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
390 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
391 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800392 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100393 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
394 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100395 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
396 { '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 +0000397 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000399 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100400 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
401 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
402 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
403 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
404 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
405 { 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 +0000406 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
407 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000408 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000409 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000411 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000412 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000413 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000414 { 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 +0000415 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000416 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000417 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
418 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
419 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
420 { 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 +0000421 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
422 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000423 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100424 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100425 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000426 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
427 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100428 { '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 +0000429 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
430 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100431 { '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 +0000432 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
433 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
434 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100435 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
436 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100437 { '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 +0100438 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000439 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100440 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
441 { '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 +0000442 { 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 +0000443 { 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 +0100444 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000445 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
446 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
447 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000448 { 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 +0000449 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000450 { '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 +0100451 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000452 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100453 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100454 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100455 { 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 +0100456 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100457 { 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 +0100458 { 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 +0100459 { 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 +0100460 { 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 +0000461 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000462 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100463 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100464 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100465 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000466 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100467 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000468 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100469 { 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 +0100470 { 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 +0000471 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100472 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100473 { 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 +0100474 { 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 +0100475 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100476 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
477 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000478 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000479 { 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 +0000480 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
481 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100482 { 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 +0000483 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100484 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
485 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
486 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000487 { 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 +0100488 { 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 +0000489 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100490 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100491 { 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 +0200492 { 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 +0100493 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000494 { 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 +0000495 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000496 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000497 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000498 { LOPT_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), NULL },
Simon Kelley974a6d02018-08-23 23:01:16 +0100499 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100500 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000501 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000502 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000503 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000504 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000505 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000506 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
507 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100508 { 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 +0100509 { 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 +0000510 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000511 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000512 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100513 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000514 { 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 +0000515 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100516 { 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 +0100517 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
518 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
519 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelleyb260d222021-03-12 21:57:57 +0000520 { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000521 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
522 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000523 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000524 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100525 { 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 +0000526 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100527 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
528 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000529 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100530 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000531};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000532
Josh Soref730c6742017-02-06 16:14:04 +0000533/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100534 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 +0100535 following sequence so that they map to themselves: it is therefore possible to call
536 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000537 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100538 couple of other places.
539 Note that space is included here so that
540 --dhcp-option=3, string
541 has five characters, whilst
542 --dhcp-option=3," string"
543 has six.
544*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100545
Simon Kelleyf2621c72007-04-29 19:47:21 +0100546static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100547
548static char hide_meta(char c)
549{
550 unsigned int i;
551
552 for (i = 0; i < (sizeof(meta) - 1); i++)
553 if (c == meta[i])
554 return (char)i;
555
556 return c;
557}
558
559static char unhide_meta(char cr)
560{
561 unsigned int c = cr;
562
563 if (c < (sizeof(meta) - 1))
564 cr = meta[c];
565
566 return cr;
567}
568
569static void unhide_metas(char *cp)
570{
571 if (cp)
572 for(; *cp; cp++)
573 *cp = unhide_meta(*cp);
574}
575
Simon Kelley824af852008-02-12 20:43:05 +0000576static void *opt_malloc(size_t size)
577{
578 void *ret;
579
580 if (mem_recover)
581 {
582 ret = whine_malloc(size);
583 if (!ret)
584 longjmp(mem_jmp, 1);
585 }
586 else
587 ret = safe_malloc(size);
588
589 return ret;
590}
591
Petr Menšík59e47032018-11-02 22:39:39 +0000592static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100593{
594 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000595 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100596
Petr Menšík59e47032018-11-02 22:39:39 +0000597 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100598 {
Petr Menšík59e47032018-11-02 22:39:39 +0000599 ret = opt_malloc(len+1);
600 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100601
602 /* restore hidden metachars */
603 unhide_metas(ret);
604 }
605
606 return ret;
607}
608
Simon Kelley3d8df262005-08-29 12:19:27 +0100609
Simon Kelleyf2621c72007-04-29 19:47:21 +0100610/* find next comma, split string with zero and eliminate spaces.
611 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000612
613static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100614{
615 char *comma, *p;
616
Simon Kelley73a08a22009-02-05 20:28:08 +0000617 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100618 return NULL;
619
620 p = comma;
621 *comma = ' ';
622
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100623 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100624
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100625 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100626 *p = 0;
627
628 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100629}
630
Simon Kelley73a08a22009-02-05 20:28:08 +0000631static char *split(char *s)
632{
633 return split_chr(s, ',');
634}
635
Simon Kelley1f15b812009-10-13 17:49:32 +0100636static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100637{
Simon Kelley1f15b812009-10-13 17:49:32 +0100638 char *ret;
639 int nomem;
640
Simon Kelley3d8df262005-08-29 12:19:27 +0100641 if (!s)
642 return 0;
643
644 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100645 if (!(ret = canonicalise(s, &nomem)) && nomem)
646 {
647 if (mem_recover)
648 longjmp(mem_jmp, 1);
649 else
650 die(_("could not get memory"), NULL, EC_NOMEM);
651 }
652
653 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100654}
655
656static int atoi_check(char *a, int *res)
657{
658 char *p;
659
660 if (!a)
661 return 0;
662
663 unhide_metas(a);
664
665 for (p = a; *p; p++)
666 if (*p < '0' || *p > '9')
667 return 0;
668
669 *res = atoi(a);
670 return 1;
671}
672
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100673static int atoi_check16(char *a, int *res)
674{
675 if (!(atoi_check(a, res)) ||
676 *res < 0 ||
677 *res > 0xffff)
678 return 0;
679
680 return 1;
681}
Simon Kelleyee415862014-02-11 11:07:22 +0000682
Simon Kelleyde73a492014-02-17 21:43:27 +0000683#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000684static int atoi_check8(char *a, int *res)
685{
686 if (!(atoi_check(a, res)) ||
687 *res < 0 ||
688 *res > 0xff)
689 return 0;
690
691 return 1;
692}
Simon Kelleyde73a492014-02-17 21:43:27 +0000693#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100694
695#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000696static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000697{
Simon Kelley824af852008-02-12 20:43:05 +0000698 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000699
700 if (txt)
701 {
702 size_t len = strlen(txt);
703 r->txt = opt_malloc(len+1);
704 r->len = len+1;
705 *(r->txt) = len;
706 memcpy((r->txt)+1, txt, len);
707 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100708
Simon Kelleyfec216d2014-03-27 20:54:34 +0000709 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000710 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000711 r->next = daemon->txt;
712 daemon->txt = r;
713 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000714}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100715#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000716
Simon Kelley849a8352006-06-09 21:02:31 +0100717static void do_usage(void)
718{
719 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000720 int i, j;
721
722 struct {
723 char handle;
724 int val;
725 } tab[] = {
726 { '$', CACHESIZ },
727 { '*', EDNS_PKTSZ },
728 { '&', MAXLEASES },
729 { '!', FTABSIZ },
730 { '#', TFTP_MAX_CONNECTIONS },
731 { '\0', 0 }
732 };
Simon Kelley849a8352006-06-09 21:02:31 +0100733
734 printf(_("Usage: dnsmasq [options]\n\n"));
735#ifndef HAVE_GETOPT_LONG
736 printf(_("Use short options only on the command line.\n"));
737#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100738 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100739
Simon Kelley1a6bca82008-07-11 11:11:42 +0100740 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100741 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100742 char *desc = usage[i].flagdesc;
743 char *eq = "=";
744
745 if (!desc || *desc == '[')
746 eq = "";
747
748 if (!desc)
749 desc = "";
750
751 for ( j = 0; opts[j].name; j++)
752 if (opts[j].val == usage[i].opt)
753 break;
754 if (usage[i].opt < 256)
755 sprintf(buff, "-%c, ", usage[i].opt);
756 else
757 sprintf(buff, " ");
758
759 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100760 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100761
Simon Kelley849a8352006-06-09 21:02:31 +0100762 if (usage[i].arg)
763 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000764 strcpy(buff, usage[i].arg);
765 for (j = 0; tab[j].handle; j++)
766 if (tab[j].handle == *(usage[i].arg))
767 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100768 }
Simon Kelley849a8352006-06-09 21:02:31 +0100769 printf(_(usage[i].desc), buff);
770 printf("\n");
771 }
772}
773
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100774#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000775#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
776#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100777
Ed Bardsleya7369be2015-08-05 21:17:18 +0100778static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
779{
780 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
781 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100782 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
783 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100784 else
785 return _("bad address");
786
787 return NULL;
788}
789
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100790char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
791{
792 int source_port = 0, serv_port = NAMESERVER_PORT;
793 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000794 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100795 int scope_index = 0;
796 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100797
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000798 if (!arg || strlen(arg) == 0)
799 {
800 *flags |= SERV_NO_ADDR;
801 *interface = 0;
802 return NULL;
803 }
804
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100805 if ((source = split_chr(arg, '@')) && /* is there a source. */
806 (portno = split_chr(source, '#')) &&
807 !atoi_check16(portno, &source_port))
808 return _("bad port");
809
810 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
811 !atoi_check16(portno, &serv_port))
812 return _("bad port");
813
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100814 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100815
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000816 if (source) {
817 interface_opt = split_chr(source, '@');
818
819 if (interface_opt)
820 {
821#if defined(SO_BINDTODEVICE)
Petr Menšík47b45b22018-08-15 18:17:00 +0200822 safe_strncpy(interface, interface_opt, IF_NAMESIZE);
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000823#else
824 return _("interface binding not supported");
825#endif
826 }
827 }
828
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100829 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100830 {
831 addr->in.sin_port = htons(serv_port);
832 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
833#ifdef HAVE_SOCKADDR_SA_LEN
834 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
835#endif
836 source_addr->in.sin_addr.s_addr = INADDR_ANY;
837 source_addr->in.sin_port = htons(daemon->query_port);
838
839 if (source)
840 {
841 if (flags)
842 *flags |= SERV_HAS_SOURCE;
843 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100844 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100845 {
846#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000847 if (interface_opt)
848 return _("interface can only be specified once");
849
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100850 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200851 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100852#else
853 return _("interface binding not supported");
854#endif
855 }
856 }
857 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100858 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
859 {
860 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
861 return _("bad interface name");
862
863 addr->in6.sin6_port = htons(serv_port);
864 addr->in6.sin6_scope_id = scope_index;
865 source_addr->in6.sin6_addr = in6addr_any;
866 source_addr->in6.sin6_port = htons(daemon->query_port);
867 source_addr->in6.sin6_scope_id = 0;
868 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
869 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
870#ifdef HAVE_SOCKADDR_SA_LEN
871 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
872#endif
873 if (source)
874 {
875 if (flags)
876 *flags |= SERV_HAS_SOURCE;
877 source_addr->in6.sin6_port = htons(source_port);
878 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
879 {
880#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000881 if (interface_opt)
882 return _("interface can only be specified once");
883
884 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200885 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100886#else
887 return _("interface binding not supported");
888#endif
889 }
890 }
891 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100892 else
893 return _("bad address");
894
895 return NULL;
896}
897
Simon Kelleyde73a492014-02-17 21:43:27 +0000898static struct server *add_rev4(struct in_addr addr, int msize)
899{
900 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000901 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000902 char *p;
903
904 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000905 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
906
907 switch (msize)
908 {
909 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100910 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000911 /* fall through */
912 case 24:
913 p += sprintf(p, "%d.", (a >> 8) & 0xff);
914 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000915 case 16:
916 p += sprintf(p, "%d.", (a >> 16) & 0xff);
917 /* fall through */
918 case 8:
919 p += sprintf(p, "%d.", (a >> 24) & 0xff);
920 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000921 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000922 free(serv->domain);
923 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000924 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000925 }
926
927 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000928
929 serv->flags = SERV_HAS_DOMAIN;
930 serv->next = daemon->servers;
931 daemon->servers = serv;
932
933 return serv;
934
935}
936
937static struct server *add_rev6(struct in6_addr *addr, int msize)
938{
939 struct server *serv = opt_malloc(sizeof(struct server));
940 char *p;
941 int i;
942
943 memset(serv, 0, sizeof(struct server));
944 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
945
946 for (i = msize-1; i >= 0; i -= 4)
947 {
948 int dig = ((unsigned char *)addr)[i>>3];
949 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
950 }
951 p += sprintf(p, "ip6.arpa");
952
953 serv->flags = SERV_HAS_DOMAIN;
954 serv->next = daemon->servers;
955 daemon->servers = serv;
956
957 return serv;
958}
959
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000960#ifdef HAVE_DHCP
961
962static int is_tag_prefix(char *arg)
963{
964 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
965 return 1;
966
967 return 0;
968}
969
970static char *set_prefix(char *arg)
971{
972 if (strstr(arg, "set:") == arg)
973 return arg+4;
974
975 return arg;
976}
977
Simon Kelley52ec7832020-02-07 21:05:54 +0000978static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000979{
980 struct dhcp_netid *tt;
981 tt = opt_malloc(sizeof (struct dhcp_netid));
982 tt->net = opt_string_alloc(net);
983 tt->next = next;
984 return tt;
985}
986
987static void dhcp_netid_free(struct dhcp_netid *nid)
988{
989 while (nid)
990 {
991 struct dhcp_netid *tmp = nid;
992 nid = nid->next;
993 free(tmp->net);
994 free(tmp);
995 }
996}
997
998/* Parse one or more tag:s before parameters.
999 * Moves arg to the end of tags. */
1000static struct dhcp_netid * dhcp_tags(char **arg)
1001{
1002 struct dhcp_netid *id = NULL;
1003
1004 while (is_tag_prefix(*arg))
1005 {
1006 char *comma = split(*arg);
1007 id = dhcp_netid_create((*arg)+4, id);
1008 *arg = comma;
1009 };
1010 if (!*arg)
1011 {
1012 dhcp_netid_free(id);
1013 id = NULL;
1014 }
1015 return id;
1016}
1017
1018static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1019{
1020 while (netid)
1021 {
1022 struct dhcp_netid_list *tmplist = netid;
1023 netid = netid->next;
1024 dhcp_netid_free(tmplist->list);
1025 free(tmplist);
1026 }
1027}
1028
1029static void dhcp_config_free(struct dhcp_config *config)
1030{
1031 if (config)
1032 {
1033 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001034
Petr Menšík59e47032018-11-02 22:39:39 +00001035 while (hwaddr)
1036 {
1037 struct hwaddr_config *tmp = hwaddr;
1038 hwaddr = hwaddr->next;
1039 free(tmp);
1040 }
Simon Kelley137286e2020-02-06 22:09:30 +00001041
Petr Menšík59e47032018-11-02 22:39:39 +00001042 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001043 dhcp_netid_free(config->filter);
1044
Petr Menšík59e47032018-11-02 22:39:39 +00001045 if (config->flags & CONFIG_CLID)
1046 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001047
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001048#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001049 if (config->flags & CONFIG_ADDR6)
1050 {
1051 struct addrlist *addr, *tmp;
1052
1053 for (addr = config->addr6; addr; addr = tmp)
1054 {
1055 tmp = addr->next;
1056 free(addr);
1057 }
1058 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001059#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001060
Petr Menšík59e47032018-11-02 22:39:39 +00001061 free(config);
1062 }
1063}
1064
1065static void dhcp_context_free(struct dhcp_context *ctx)
1066{
1067 if (ctx)
1068 {
1069 dhcp_netid_free(ctx->filter);
1070 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001071#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001072 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001073#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001074 free(ctx);
1075 }
1076}
1077
1078static void dhcp_opt_free(struct dhcp_opt *opt)
1079{
1080 if (opt->flags & DHOPT_VENDOR)
1081 free(opt->u.vendor_class);
1082 dhcp_netid_free(opt->netid);
1083 free(opt->val);
1084 free(opt);
1085}
1086
1087
Simon Kelley832af0b2007-01-21 20:01:28 +00001088/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001089static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001090{
Simon Kelley824af852008-02-12 20:43:05 +00001091 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001092 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001093 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001094 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001095 u16 opt_len = 0;
1096 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001097 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001098
1099 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001100 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001101 new->netid = NULL;
1102 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001103 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001104
Simon Kelleyf2621c72007-04-29 19:47:21 +01001105 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001106 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001107 comma = split(arg);
1108
1109 for (cp = arg; *cp; cp++)
1110 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001111 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112
1113 if (!*cp)
1114 {
1115 new->opt = atoi(arg);
1116 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001117 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001118 break;
1119 }
1120
1121 if (strstr(arg, "option:") == arg)
1122 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001123 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1124 {
1125 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1126 /* option:<optname> must follow tag and vendor string. */
1127 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1128 option_ok = 1;
1129 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001130 break;
1131 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001132#ifdef HAVE_DHCP6
1133 else if (strstr(arg, "option6:") == arg)
1134 {
1135 for (cp = arg+8; *cp; cp++)
1136 if (*cp < '0' || *cp > '9')
1137 break;
1138
1139 if (!*cp)
1140 {
1141 new->opt = atoi(arg+8);
1142 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001143 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001144 }
1145 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001146 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001147 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1148 {
1149 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1150 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1151 option_ok = 1;
1152 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001153 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001154 /* option6:<opt>|<optname> must follow tag and vendor string. */
1155 is6 = 1;
1156 break;
1157 }
1158#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001159 else if (strstr(arg, "vendor:") == arg)
1160 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001161 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1162 new->flags |= DHOPT_VENDOR;
1163 }
1164 else if (strstr(arg, "encap:") == arg)
1165 {
1166 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001167 new->flags |= DHOPT_ENCAPSULATE;
1168 }
Simon Kelley316e2732010-01-22 20:16:09 +00001169 else if (strstr(arg, "vi-encap:") == arg)
1170 {
1171 new->u.encap = atoi(arg+9);
1172 new->flags |= DHOPT_RFC3925;
1173 if (flags == DHOPT_MATCH)
1174 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001175 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001176 break;
1177 }
1178 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001179 else
1180 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001181 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001182 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1183 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001184 }
1185
1186 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001187 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001188
1189#ifdef HAVE_DHCP6
1190 if (is6)
1191 {
1192 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001193 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001194
1195 if (opt_len == 0 &&
1196 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001197 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001198 }
1199 else
1200#endif
1201 if (opt_len == 0 &&
1202 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001203 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001204
Simon Kelley316e2732010-01-22 20:16:09 +00001205 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001206 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001207 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001208
1209 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001210 {
1211 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001212 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001213 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001214 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001215 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001216 dots = 0;
1217 for (cp = comma; (c = *cp); cp++)
1218 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001219 {
1220 addrs++;
1221 is_dec = is_hex = 0;
1222 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001223 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001224 {
1225 digs++;
1226 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001227 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001228 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001229 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001230 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001231 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001232 if (cp == comma) /* leading / means a pathname */
1233 is_addr = 0;
1234 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001235 else if (c == '.')
1236 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001237 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001238 dots++;
1239 }
1240 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001241 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001242 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001243 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001244 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001245 {
1246 is_addr = 0;
1247 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001248 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001249 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001250 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001251 *cp = 0;
1252 }
1253 else
1254 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001255 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001256 (c >='a' && c <= 'f') ||
1257 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001258 {
1259 is_hex = 0;
1260 if (c != '[' && c != ']')
1261 is_addr6 = 0;
1262 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001263 }
Simon Kelley28866e92011-02-14 20:19:14 +00001264 else
1265 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001266
Simon Kelley28866e92011-02-14 20:19:14 +00001267 if (!found_dig)
1268 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001269
1270 if (!found_colon)
1271 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001272
1273#ifdef HAVE_DHCP6
1274 /* NTP server option takes hex, addresses or FQDN */
1275 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1276 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1277#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001278
Simon Kelleyf2621c72007-04-29 19:47:21 +01001279 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001280 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001281 {
1282 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001283
1284 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001285 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001286
1287 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001288 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001289 }
Simon Kelley28866e92011-02-14 20:19:14 +00001290 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001291 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1292 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001293
1294 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1295 {
1296 int val, fac = 1;
1297
1298 switch (comma[strlen(comma) - 1])
1299 {
Simon Kelley42243212012-07-20 15:19:18 +01001300 case 'w':
1301 case 'W':
1302 fac *= 7;
1303 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001304 case 'd':
1305 case 'D':
1306 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001307 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001308 case 'h':
1309 case 'H':
1310 fac *= 60;
1311 /* fall through */
1312 case 'm':
1313 case 'M':
1314 fac *= 60;
1315 /* fall through */
1316 case 's':
1317 case 'S':
1318 comma[strlen(comma) - 1] = 0;
1319 }
1320
1321 new->len = 4;
1322 new->val = opt_malloc(4);
1323 val = atoi(comma);
1324 *((int *)new->val) = htonl(val * fac);
1325 }
1326 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001327 {
1328 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001329 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001330 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1331 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001332 }
1333 else if (is_dec)
1334 {
1335 int i, val = atoi(comma);
1336 /* assume numeric arg is 1 byte except for
1337 options where it is known otherwise.
1338 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001339 if (opt_len != 0)
1340 new->len = opt_len;
1341 else if (val & 0xffff0000)
1342 new->len = 4;
1343 else if (val & 0xff00)
1344 new->len = 2;
1345 else
1346 new->len = 1;
1347
Simon Kelley832af0b2007-01-21 20:01:28 +00001348 if (lenchar == 'b')
1349 new->len = 1;
1350 else if (lenchar == 's')
1351 new->len = 2;
1352 else if (lenchar == 'i')
1353 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001354
Simon Kelley824af852008-02-12 20:43:05 +00001355 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001356 for (i=0; i<new->len; i++)
1357 new->val[i] = val>>((new->len - i - 1)*8);
1358 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001359 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001360 {
1361 struct in_addr in;
1362 unsigned char *op;
1363 char *slash;
1364 /* max length of address/subnet descriptor is five bytes,
1365 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001366 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001367 new->flags |= DHOPT_ADDR;
1368
Simon Kelley572b41e2011-02-18 18:11:18 +00001369 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1370 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001371 {
Simon Kelley6b010842007-02-12 20:32:07 +00001372 *(op++) = 1; /* RFC 3361 "enc byte" */
1373 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001374 }
1375 while (addrs--)
1376 {
1377 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001378 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001379 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001380 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001381 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001382 if (!slash)
1383 {
1384 memcpy(op, &in, INADDRSZ);
1385 op += INADDRSZ;
1386 }
1387 else
1388 {
1389 unsigned char *p = (unsigned char *)&in;
1390 int netsize = atoi(slash);
1391 *op++ = netsize;
1392 if (netsize > 0)
1393 *op++ = *p++;
1394 if (netsize > 8)
1395 *op++ = *p++;
1396 if (netsize > 16)
1397 *op++ = *p++;
1398 if (netsize > 24)
1399 *op++ = *p++;
1400 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1401 }
1402 }
1403 new->len = op - new->val;
1404 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001405 else if (is_addr6 && is6)
1406 {
1407 unsigned char *op;
1408 new->val = op = opt_malloc(16 * addrs);
1409 new->flags |= DHOPT_ADDR6;
1410 while (addrs--)
1411 {
1412 cp = comma;
1413 comma = split(cp);
1414
1415 /* check for [1234::7] */
1416 if (*cp == '[')
1417 cp++;
1418 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1419 cp[strlen(cp)-1] = 0;
1420
1421 if (inet_pton(AF_INET6, cp, op))
1422 {
1423 op += IN6ADDRSZ;
1424 continue;
1425 }
Petr Menšík59e47032018-11-02 22:39:39 +00001426
1427 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001428 }
1429 new->len = op - new->val;
1430 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001431 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001432 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001433 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001434 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001435 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001436 {
1437 /* dns search, RFC 3397, or SIP, RFC 3361 */
1438 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001439 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001440 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001441 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001442
1443 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001444 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001445
1446 while (arg && *arg)
1447 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001448 char *in, *dom = NULL;
1449 size_t domlen = 1;
1450 /* Allow "." as an empty domain */
1451 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001452 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001453 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001454 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001455
Simon Kelleyc52e1892010-06-07 22:01:39 +01001456 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001457 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001458
1459 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001460 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001461 {
1462 memcpy(newp, m, header_size + len);
1463 free(m);
1464 }
Simon Kelley824af852008-02-12 20:43:05 +00001465 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001466 p = m + header_size;
1467 q = p + len;
1468
1469 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001470 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001471 {
1472 unsigned char *cp = q++;
1473 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001474 for (j = 0; *in && (*in != '.'); in++, j++)
1475 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001476 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001477 if (*in)
1478 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001479 }
1480 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001481 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001482
Simon Kelley832af0b2007-01-21 20:01:28 +00001483 /* Now tail-compress using earlier names. */
1484 newlen = q - p;
1485 for (tail = p + len; *tail; tail += (*tail) + 1)
1486 for (r = p; r - p < (int)len; r += (*r) + 1)
1487 if (strcmp((char *)r, (char *)tail) == 0)
1488 {
1489 PUTSHORT((r - p) | 0xc000, tail);
1490 newlen = tail - p;
1491 goto end;
1492 }
1493 end:
1494 len = newlen;
1495
1496 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001497 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001498 }
1499
1500 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001501 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001502 m[0] = 0;
1503 new->len = (int) len + header_size;
1504 new->val = m;
1505 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001506#ifdef HAVE_DHCP6
1507 else if (comma && (opt_len & OT_CSTRING))
1508 {
1509 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001510 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001511 unsigned char *p, *newp;
1512
Simon Kelley40ef23b2012-03-13 21:59:28 +00001513 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001514 if (comma[i] == ',')
1515 commas++;
1516
1517 newp = opt_malloc(strlen(comma)+(2*commas));
1518 p = newp;
1519 arg = comma;
1520 comma = split(arg);
1521
1522 while (arg && *arg)
1523 {
1524 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001525 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001526 PUTSHORT(len, p);
1527 memcpy(p, arg, len);
1528 p += len;
1529
1530 arg = comma;
1531 comma = split(arg);
1532 }
1533
1534 new->val = newp;
1535 new->len = p - newp;
1536 }
1537 else if (comma && (opt_len & OT_RFC1035_NAME))
1538 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001539 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001540 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001541 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001542 arg = comma;
1543 comma = split(arg);
1544
1545 while (arg && *arg)
1546 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001547 char *dom = canonicalise_opt(arg);
1548 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001549 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001550
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001551 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001552
1553 if (p)
1554 {
1555 memcpy(newp, p, len);
1556 free(p);
1557 }
1558
1559 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001560 q = p + len;
1561 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001562 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001563 if (is6 && new->opt == OPTION6_NTP_SERVER)
1564 {
1565 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1566 PUTSHORT(end - q - 2, q);
1567 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001568 len = end - p;
1569 free(dom);
1570
Simon Kelley4cb1b322012-02-06 14:30:41 +00001571 arg = comma;
1572 comma = split(arg);
1573 }
1574
Simon Kelley18f0fb02012-03-31 21:18:55 +01001575 new->val = p;
1576 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001577 }
1578#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001579 else
1580 {
1581 new->len = strlen(comma);
1582 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001583 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001584 new->flags |= DHOPT_STRING;
1585 }
1586 }
1587 }
1588
Simon Kelley4cb1b322012-02-06 14:30:41 +00001589 if (!is6 &&
1590 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001591 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001592 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001593 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001594
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001595 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001596 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001597 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1598 !new->netid ||
1599 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001600 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001601
1602 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001603 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001604 new->next = daemon->dhcp_match6;
1605 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001606 }
1607 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001608 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001609 new->next = daemon->dhcp_match;
1610 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001611 }
Simon Kelley824af852008-02-12 20:43:05 +00001612 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001613 else if (is6)
1614 {
1615 new->next = daemon->dhcp_opts6;
1616 daemon->dhcp_opts6 = new;
1617 }
1618 else
1619 {
1620 new->next = daemon->dhcp_opts;
1621 daemon->dhcp_opts = new;
1622 }
1623
1624 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001625on_error:
1626 dhcp_opt_free(new);
1627 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001628}
1629
Simon Kelley7622fc02009-06-04 20:32:05 +01001630#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001631
Simon Kelley28866e92011-02-14 20:19:14 +00001632void set_option_bool(unsigned int opt)
1633{
Petr Menšík24b87602018-10-24 22:30:18 +01001634 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001635}
1636
Simon Kelley2b5bae92012-06-26 16:55:23 +01001637void reset_option_bool(unsigned int opt)
1638{
Petr Menšík24b87602018-10-24 22:30:18 +01001639 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001640}
1641
Petr Menšík59e47032018-11-02 22:39:39 +00001642static void server_list_free(struct server *list)
1643{
1644 while (list)
1645 {
1646 struct server *tmp = list;
1647 list = list->next;
1648 free(tmp);
1649 }
1650}
1651
Simon Kelley7b1eae42014-02-20 13:43:28 +00001652static 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 +01001653{
1654 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001655 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001656
Simon Kelley832af0b2007-01-21 20:01:28 +00001657 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001658 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001659
Simon Kelley1a6bca82008-07-11 11:11:42 +01001660 for (i=0; usage[i].opt != 0; i++)
1661 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001662 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001663 int rept = usage[i].rept;
1664
Simon Kelley28866e92011-02-14 20:19:14 +00001665 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001666 {
1667 /* command line */
1668 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001669 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001670 if (rept == ARG_ONE)
1671 usage[i].rept = ARG_USED_CL;
1672 }
1673 else
1674 {
1675 /* allow file to override command line */
1676 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001677 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001678 if (rept == ARG_USED_CL || rept == ARG_ONE)
1679 usage[i].rept = ARG_USED_FILE;
1680 }
1681
1682 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1683 {
Simon Kelley28866e92011-02-14 20:19:14 +00001684 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001685 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001686 }
1687
1688 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001689 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001690
Simon Kelley849a8352006-06-09 21:02:31 +01001691 switch (option)
1692 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001693 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001694 {
Simon Kelley824af852008-02-12 20:43:05 +00001695 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001696 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001697 {
Simon Kelley28866e92011-02-14 20:19:14 +00001698 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001699 free(file);
1700 }
Simon Kelley849a8352006-06-09 21:02:31 +01001701 break;
1702 }
1703
Simon Kelleyf2621c72007-04-29 19:47:21 +01001704 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001705 {
1706 DIR *dir_stream;
1707 struct dirent *ent;
1708 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001709 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001710 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001711 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001712 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001713
Simon Kelley1f15b812009-10-13 17:49:32 +01001714 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001715 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001716 break;
1717
Simon Kelley1f15b812009-10-13 17:49:32 +01001718 for (arg = comma; arg; arg = comma)
1719 {
1720 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001721 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001722 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001723 li = opt_malloc(sizeof(struct list));
1724 if (*arg == '*')
1725 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001726 /* "*" with no suffix is a no-op */
1727 if (arg[1] == 0)
1728 free(li);
1729 else
1730 {
1731 li->next = match_suffix;
1732 match_suffix = li;
1733 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001734 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001735 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001736 }
1737 else
1738 {
1739 li->next = ignore_suffix;
1740 ignore_suffix = li;
1741 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001742 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001743 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001744 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001745 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001746
Simon Kelley849a8352006-06-09 21:02:31 +01001747 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001748 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001749
Simon Kelley849a8352006-06-09 21:02:31 +01001750 while ((ent = readdir(dir_stream)))
1751 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001752 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001753 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001754
1755 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001756 if (len == 0 ||
1757 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001758 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1759 ent->d_name[0] == '.')
1760 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001761
Simon Kelley3e1551a2014-09-09 21:46:07 +01001762 if (match_suffix)
1763 {
1764 for (li = match_suffix; li; li = li->next)
1765 {
1766 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001767 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001768 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001769 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001770 break;
1771 }
1772 if (!li)
1773 continue;
1774 }
1775
Simon Kelley1f15b812009-10-13 17:49:32 +01001776 for (li = ignore_suffix; li; li = li->next)
1777 {
1778 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001779 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001780 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001781 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001782 break;
1783 }
1784 if (li)
1785 continue;
1786
Simon Kelley824af852008-02-12 20:43:05 +00001787 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001788 strcpy(path, directory);
1789 strcat(path, "/");
1790 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001791
Simon Kelley39595cf2013-02-04 21:40:07 +00001792 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001793 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001794 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001795
Simon Kelley39595cf2013-02-04 21:40:07 +00001796 /* only reg files allowed. */
1797 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001798 {
1799 /* sort files into order. */
1800 struct list **up, *new = opt_malloc(sizeof(struct list));
1801 new->name = path;
1802
1803 for (up = &files, li = files; li; up = &li->next, li = li->next)
1804 if (strcmp(li->name, path) >=0)
1805 break;
1806
1807 new->next = li;
1808 *up = new;
1809 }
1810
Simon Kelley849a8352006-06-09 21:02:31 +01001811 }
Simon Kelleyab538832020-01-10 20:44:48 +00001812
1813 for (li = files; li; li = li->next)
1814 one_file(li->name, 0);
1815
Simon Kelley849a8352006-06-09 21:02:31 +01001816 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001817 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001818 for(; ignore_suffix; ignore_suffix = li)
1819 {
1820 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001821 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001822 free(ignore_suffix);
1823 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001824 for(; match_suffix; match_suffix = li)
1825 {
1826 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001827 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001828 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001829 }
Simon Kelleyab538832020-01-10 20:44:48 +00001830 for(; files; files = li)
1831 {
1832 li = files->next;
1833 free(files->name);
1834 free(files);
1835 }
Simon Kelley849a8352006-06-09 21:02:31 +01001836 break;
1837 }
1838
Simon Kelleyed4c0762013-10-08 20:46:34 +01001839 case LOPT_ADD_SBNET: /* --add-subnet */
1840 set_option_bool(OPT_CLIENT_SUBNET);
1841 if (arg)
1842 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001843 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001844 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001845
1846 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1847 if ((end = split_chr(arg, '/')))
1848 {
1849 /* has subnet+len */
1850 err = parse_mysockaddr(arg, &new->addr);
1851 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001852 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001853 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001854 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001855 new->addr_used = 1;
1856 }
1857 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001858 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001859
1860 daemon->add_subnet4 = new;
1861
Ed Bardsleya7369be2015-08-05 21:17:18 +01001862 if (comma)
1863 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001864 new = opt_malloc(sizeof(struct mysubnet));
1865 if ((end = split_chr(comma, '/')))
1866 {
1867 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001868 err = parse_mysockaddr(comma, &new->addr);
1869 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001870 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001871 if (!atoi_check(end, &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 new->addr_used = 1;
1874 }
1875 else
1876 {
1877 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001878 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001879 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001880
1881 daemon->add_subnet6 = new;
1882 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001883 }
1884 break;
1885
Simon Kelleyad094272012-08-10 17:10:54 +01001886 case '1': /* --enable-dbus */
1887 set_option_bool(OPT_DBUS);
1888 if (arg)
1889 daemon->dbus_name = opt_string_alloc(arg);
1890 else
1891 daemon->dbus_name = DNSMASQ_SERVICE;
1892 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001893
1894 case LOPT_UBUS: /* --enable-ubus */
1895 set_option_bool(OPT_UBUS);
1896 if (arg)
1897 daemon->ubus_name = opt_string_alloc(arg);
1898 else
1899 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1900 break;
1901
Simon Kelleyf2621c72007-04-29 19:47:21 +01001902 case '8': /* --log-facility */
1903 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001904 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001905 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001906 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001907 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001908#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001909 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001910#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001911 for (i = 0; facilitynames[i].c_name; i++)
1912 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1913 break;
1914
1915 if (facilitynames[i].c_name)
1916 daemon->log_fac = facilitynames[i].c_val;
1917 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001918 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001919#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001920 }
1921 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001922
Simon Kelleyf2621c72007-04-29 19:47:21 +01001923 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001924 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001925 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001926
Simon Kelleyf2621c72007-04-29 19:47:21 +01001927 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001928 {
Simon Kelley824af852008-02-12 20:43:05 +00001929 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001930 struct resolvc *new, *list = daemon->resolv_files;
1931
1932 if (list && list->is_default)
1933 {
1934 /* replace default resolv file - possibly with nothing */
1935 if (name)
1936 {
1937 list->is_default = 0;
1938 list->name = name;
1939 }
1940 else
1941 list = NULL;
1942 }
1943 else if (name)
1944 {
Simon Kelley824af852008-02-12 20:43:05 +00001945 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001946 new->next = list;
1947 new->name = name;
1948 new->is_default = 0;
1949 new->mtime = 0;
1950 new->logged = 0;
1951 list = new;
1952 }
1953 daemon->resolv_files = list;
1954 break;
1955 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001956
1957 case LOPT_SERVERS_FILE:
1958 daemon->servers_file = opt_string_alloc(arg);
1959 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001960
Simon Kelleyf2621c72007-04-29 19:47:21 +01001961 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001962 {
1963 int pref = 1;
1964 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001965 char *name, *target = NULL;
1966
Simon Kelleyf2621c72007-04-29 19:47:21 +01001967 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001968 {
1969 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001970 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001971 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001972 }
1973
Simon Kelley1f15b812009-10-13 17:49:32 +01001974 if (!(name = canonicalise_opt(arg)) ||
1975 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001976 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001977
Simon Kelley824af852008-02-12 20:43:05 +00001978 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001979 new->next = daemon->mxnames;
1980 daemon->mxnames = new;
1981 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001982 new->name = name;
1983 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001984 new->weight = pref;
1985 break;
1986 }
1987
Simon Kelleyf2621c72007-04-29 19:47:21 +01001988 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001989 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001990 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001991 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001992
Simon Kelley6b173352018-05-08 18:32:14 +01001993 case LOPT_DUMPFILE: /* --dumpfile */
1994 daemon->dump_file = opt_string_alloc(arg);
1995 break;
1996
1997 case LOPT_DUMPMASK: /* --dumpmask */
1998 daemon->dump_mask = strtol(arg, NULL, 0);
1999 break;
2000
Simon Kelley7622fc02009-06-04 20:32:05 +01002001#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002002 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002003 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002004 break;
2005
Simon Kelleyc72daea2012-01-05 21:33:27 +00002006 /* Sorry about the gross pre-processor abuse */
2007 case '6': /* --dhcp-script */
2008 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002009# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002010 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002011# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002012 if (option == LOPT_LUASCRIPT)
2013# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002014 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002015# else
2016 daemon->luascript = opt_string_alloc(arg);
2017# endif
2018 else
2019 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002020# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002021 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002022#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002023
Simon Kelley70d18732015-01-31 19:59:29 +00002024 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2025 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2026 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2027 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2028 case LOPT_HOST_INOTIFY: /* --hostsdir */
2029 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002030 {
Simon Kelley824af852008-02-12 20:43:05 +00002031 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002032 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002033 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002034 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002035 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002036 if (option == 'H')
2037 {
2038 new->next = daemon->addn_hosts;
2039 daemon->addn_hosts = new;
2040 }
2041 else if (option == LOPT_DHCP_HOST)
2042 {
2043 new->next = daemon->dhcp_hosts_file;
2044 daemon->dhcp_hosts_file = new;
2045 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002046 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002047 {
2048 new->next = daemon->dhcp_opts_file;
2049 daemon->dhcp_opts_file = new;
2050 }
Simon Kelley70d18732015-01-31 19:59:29 +00002051 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002052 {
Simon Kelley70d18732015-01-31 19:59:29 +00002053 new->next = daemon->dynamic_dirs;
2054 daemon->dynamic_dirs = new;
2055 if (option == LOPT_DHCP_INOTIFY)
2056 new->flags |= AH_DHCP_HST;
2057 else if (option == LOPT_DHOPT_INOTIFY)
2058 new->flags |= AH_DHCP_OPT;
2059 else if (option == LOPT_HOST_INOTIFY)
2060 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002061 }
2062
Simon Kelley849a8352006-06-09 21:02:31 +01002063 break;
2064 }
2065
Simon Kelley4f7b3042012-11-28 21:27:02 +00002066 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002067 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002068
Simon Kelley4f7b3042012-11-28 21:27:02 +00002069 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002070
2071 while ((arg = comma))
2072 {
2073 struct iname *new = opt_malloc(sizeof(struct iname));
2074 comma = split(arg);
2075 new->name = NULL;
2076 unhide_metas(arg);
2077 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2078 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002079 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2080 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002081 else
2082 {
2083 char *fam = split_chr(arg, '/');
2084 new->name = opt_string_alloc(arg);
2085 new->addr.sa.sa_family = 0;
2086 if (fam)
2087 {
2088 if (strcmp(fam, "4") == 0)
2089 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002090 else if (strcmp(fam, "6") == 0)
2091 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002092 else
Petr Menšík59e47032018-11-02 22:39:39 +00002093 {
2094 free(new->name);
2095 ret_err_free(gen_err, new);
2096 }
Simon Kelley08933472018-10-05 16:34:35 +01002097 }
2098 }
2099 new->next = daemon->authinterface;
2100 daemon->authinterface = new;
2101 };
Simon Kelley429798f2012-12-10 20:45:53 +00002102
Simon Kelley4f7b3042012-11-28 21:27:02 +00002103 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002104
2105 case LOPT_AUTHSFS: /* --auth-sec-servers */
2106 {
2107 struct name_list *new;
2108
2109 do {
2110 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002111 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002112 new->name = opt_string_alloc(arg);
2113 new->next = daemon->secondary_forward_server;
2114 daemon->secondary_forward_server = new;
2115 arg = comma;
2116 } while (arg);
2117 break;
2118 }
2119
Simon Kelley4f7b3042012-11-28 21:27:02 +00002120 case LOPT_AUTHZONE: /* --auth-zone */
2121 {
2122 struct auth_zone *new;
2123
2124 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002125
Simon Kelley429798f2012-12-10 20:45:53 +00002126 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002127 new->domain = opt_string_alloc(arg);
2128 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002129 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002130 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002131 new->next = daemon->auth_zones;
2132 daemon->auth_zones = new;
2133
2134 while ((arg = comma))
2135 {
2136 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002137 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002138 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002139 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002140 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002141
2142 comma = split(arg);
2143 prefix = split_chr(arg, '/');
2144
2145 if (prefix && !atoi_check(prefix, &prefixlen))
2146 ret_err(gen_err);
2147
Mathias Kresin094bfae2016-07-24 14:15:22 +01002148 if (strstr(arg, "exclude:") == arg)
2149 {
2150 is_exclude = 1;
2151 arg = arg+8;
2152 }
2153
Simon Kelleycc921df2019-01-02 22:48:59 +00002154 if (inet_pton(AF_INET, arg, &addr.addr4))
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) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002158 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002159 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002160 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002161 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002162 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002163 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002164 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002165 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002166 else
2167 {
2168 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2169 name->name = opt_string_alloc(arg);
2170 name->flags = AUTH4 | AUTH6;
2171 name->next = new->interface_names;
2172 new->interface_names = name;
2173 if (prefix)
2174 {
2175 if (prefixlen == 4)
2176 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002177 else if (prefixlen == 6)
2178 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002179 else
2180 ret_err(gen_err);
2181 }
2182 }
2183
2184 if (subnet)
2185 {
2186 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002187
2188 if (is_exclude)
2189 {
2190 subnet->next = new->exclude;
2191 new->exclude = subnet;
2192 }
2193 else
2194 {
2195 subnet->next = new->subnet;
2196 new->subnet = subnet;
2197 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002198 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002199 }
2200 break;
2201 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002202
Simon Kelley4f7b3042012-11-28 21:27:02 +00002203 case LOPT_AUTHSOA: /* --auth-soa */
2204 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002205 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002206 if (comma)
2207 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002208 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002209 arg = comma;
2210 comma = split(arg);
2211 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002212 for (cp = daemon->hostmaster; *cp; cp++)
2213 if (*cp == '@')
2214 *cp = '.';
2215
Simon Kelley4f7b3042012-11-28 21:27:02 +00002216 if (comma)
2217 {
2218 arg = comma;
2219 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002220 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002221 if (comma)
2222 {
2223 arg = comma;
2224 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002225 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002226 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002227 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002228 }
2229 }
2230 }
2231
2232 break;
2233
Simon Kelley2bb73af2013-04-24 17:38:19 +01002234 case 's': /* --domain */
2235 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002236 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002237 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002238 else
Simon Kelley9009d742008-11-14 20:04:27 +00002239 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002240 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002241 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002242 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002243 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002244 else
2245 {
Simon Kelley9009d742008-11-14 20:04:27 +00002246 if (comma)
2247 {
Simon Kelley429798f2012-12-10 20:45:53 +00002248 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002249 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002250
Simon Kelley48fd1c42013-04-25 09:49:38 +01002251 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002252 new->indexed = 0;
2253
Simon Kelley9009d742008-11-14 20:04:27 +00002254 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002255 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002256 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002257 int msize;
2258
Simon Kelley28866e92011-02-14 20:19:14 +00002259 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002260 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002261 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002262 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002263 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002264 int mask = (1 << (32 - msize)) - 1;
2265 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002266 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2267 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002268 if (arg)
2269 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002270 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002271 {
2272 if (!(new->prefix = canonicalise_opt(arg)) ||
2273 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002274 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002275 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002276 else if (strcmp(arg, "local") != 0 ||
2277 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002278 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002279 else
2280 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002281 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002282 local=/xxx.yyy.zzz.in-addr.arpa/ */
2283 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002284 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002285 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002286
Simon Kelleyde73a492014-02-17 21:43:27 +00002287 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002288
2289 /* local=/<domain>/ */
2290 serv = opt_malloc(sizeof(struct server));
2291 memset(serv, 0, sizeof(struct server));
2292 serv->domain = d;
2293 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2294 serv->next = daemon->servers;
2295 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002296 }
2297 }
Simon Kelley9009d742008-11-14 20:04:27 +00002298 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002299 else if (inet_pton(AF_INET6, comma, &new->start6))
2300 {
2301 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2302 u64 addrpart = addr6part(&new->start6);
2303 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002304
Simon Kelleyd74942a2012-02-07 20:51:56 +00002305 /* prefix==64 overflows the mask calculation above */
2306 if (msize == 64)
2307 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002308
Simon Kelleyd74942a2012-02-07 20:51:56 +00002309 new->end6 = new->start6;
2310 setaddr6part(&new->start6, addrpart & ~mask);
2311 setaddr6part(&new->end6, addrpart | mask);
2312
2313 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002314 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002315 else if (arg)
2316 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002317 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002318 {
2319 if (!(new->prefix = canonicalise_opt(arg)) ||
2320 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002321 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002322 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002323 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002324 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002325 else
2326 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002327 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002328 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002329 struct server *serv = add_rev6(&new->start6, msize);
2330 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002331
2332 /* local=/<domain>/ */
2333 serv = opt_malloc(sizeof(struct server));
2334 memset(serv, 0, sizeof(struct server));
2335 serv->domain = d;
2336 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2337 serv->next = daemon->servers;
2338 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002339 }
2340 }
2341 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002342 else
Petr Menšík59e47032018-11-02 22:39:39 +00002343 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002344 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002345 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002346 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002347 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002348 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002349 prefstr = split(arg);
2350
Simon Kelleyd74942a2012-02-07 20:51:56 +00002351 if (inet_pton(AF_INET, comma, &new->start))
2352 {
2353 new->is6 = 0;
2354 if (!arg)
2355 new->end.s_addr = new->start.s_addr;
2356 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002357 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002358 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002359 else if (inet_pton(AF_INET6, comma, &new->start6))
2360 {
2361 new->is6 = 1;
2362 if (!arg)
2363 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2364 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002365 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002366 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002367 else
Petr Menšík59e47032018-11-02 22:39:39 +00002368 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002369
2370 if (option != 's' && prefstr)
2371 {
2372 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2373 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002374 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002375 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002376 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002377
2378 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002379 if (option == 's')
2380 {
2381 new->next = daemon->cond_domain;
2382 daemon->cond_domain = new;
2383 }
2384 else
2385 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002386 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002387 new->next = daemon->synth_domains;
2388 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002389 if (new->prefix &&
2390 (star = strrchr(new->prefix, '*'))
2391 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002392 {
2393 *star = 0;
2394 new->indexed = 1;
2395 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002396 }
Simon Kelley9009d742008-11-14 20:04:27 +00002397 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002398 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002399 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002400 else
2401 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002402 }
2403 }
Simon Kelley849a8352006-06-09 21:02:31 +01002404 break;
2405
Simon Kelley1e505122016-01-25 21:29:23 +00002406 case LOPT_CPE_ID: /* --add-dns-client */
2407 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002408 daemon->dns_client_id = opt_string_alloc(arg);
2409 break;
2410
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002411 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002412 if (!arg)
2413 set_option_bool(OPT_ADD_MAC);
2414 else
2415 {
2416 unhide_metas(arg);
2417 if (strcmp(arg, "base64") == 0)
2418 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002419 else if (strcmp(arg, "text") == 0)
2420 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002421 else
2422 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002423 }
2424 break;
2425
Simon Kelleyf2621c72007-04-29 19:47:21 +01002426 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002427 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002428 break;
2429
Simon Kelleyf2621c72007-04-29 19:47:21 +01002430 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002431 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002432 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002433 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002434
Simon Kelley7622fc02009-06-04 20:32:05 +01002435#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002436 case LOPT_SCRIPTUSR: /* --scriptuser */
2437 daemon->scriptuser = opt_string_alloc(arg);
2438 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002439#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002440
Simon Kelleyf2621c72007-04-29 19:47:21 +01002441 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002442 do {
Simon Kelley824af852008-02-12 20:43:05 +00002443 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002444 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002445 new->next = daemon->if_names;
2446 daemon->if_names = new;
2447 /* new->name may be NULL if someone does
2448 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002449 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002450 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002451 arg = comma;
2452 } while (arg);
2453 break;
2454
Simon Kelley2937f8a2013-07-29 19:49:07 +01002455 case LOPT_TFTP: /* --enable-tftp */
2456 set_option_bool(OPT_TFTP);
2457 if (!arg)
2458 break;
2459 /* fall through */
2460
Simon Kelleyf2621c72007-04-29 19:47:21 +01002461 case 'I': /* --except-interface */
2462 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002463 do {
Simon Kelley824af852008-02-12 20:43:05 +00002464 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002465 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002466 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002467 if (option == 'I')
2468 {
2469 new->next = daemon->if_except;
2470 daemon->if_except = new;
2471 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002472 else if (option == LOPT_TFTP)
2473 {
2474 new->next = daemon->tftp_interfaces;
2475 daemon->tftp_interfaces = new;
2476 }
Simon Kelley849a8352006-06-09 21:02:31 +01002477 else
2478 {
2479 new->next = daemon->dhcp_except;
2480 daemon->dhcp_except = new;
2481 }
2482 arg = comma;
2483 } while (arg);
2484 break;
2485
Simon Kelleyf2621c72007-04-29 19:47:21 +01002486 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002487 case LOPT_IGNORE_ADDR: /* --ignore-address */
2488 {
Simon Kelley849a8352006-06-09 21:02:31 +01002489 struct in_addr addr;
2490 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002491 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002492 {
Simon Kelley824af852008-02-12 20:43:05 +00002493 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002494 if (option == 'B')
2495 {
2496 baddr->next = daemon->bogus_addr;
2497 daemon->bogus_addr = baddr;
2498 }
2499 else
2500 {
2501 baddr->next = daemon->ignore_addr;
2502 daemon->ignore_addr = baddr;
2503 }
Simon Kelley849a8352006-06-09 21:02:31 +01002504 baddr->addr = addr;
2505 }
2506 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002507 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002508 break;
2509 }
2510
Simon Kelleyf2621c72007-04-29 19:47:21 +01002511 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002512 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002513 do {
Simon Kelley824af852008-02-12 20:43:05 +00002514 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002515 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002516 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002517 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002518 {
2519 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002520 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002521#ifdef HAVE_SOCKADDR_SA_LEN
2522 new->addr.in.sin_len = sizeof(new->addr.in);
2523#endif
2524 }
Simon Kelley849a8352006-06-09 21:02:31 +01002525 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2526 {
2527 new->addr.sa.sa_family = AF_INET6;
2528 new->addr.in6.sin6_flowinfo = 0;
2529 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002530 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002531#ifdef HAVE_SOCKADDR_SA_LEN
2532 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2533#endif
2534 }
Simon Kelley849a8352006-06-09 21:02:31 +01002535 else
Petr Menšík59e47032018-11-02 22:39:39 +00002536 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002537
2538 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002539 if (option == 'a')
2540 {
2541 new->next = daemon->if_addrs;
2542 daemon->if_addrs = new;
2543 }
2544 else
2545 {
2546 new->next = daemon->auth_peers;
2547 daemon->auth_peers = new;
2548 }
Simon Kelley849a8352006-06-09 21:02:31 +01002549 arg = comma;
2550 } while (arg);
2551 break;
2552
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002553 case 'S': /* --server */
2554 case LOPT_LOCAL: /* --local */
2555 case 'A': /* --address */
2556 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002557 {
2558 struct server *serv, *newlist = NULL;
2559
2560 unhide_metas(arg);
2561
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002562 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002563 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002564 int rebind = !(*arg == '/');
2565 char *end = NULL;
2566 if (!rebind)
2567 arg++;
2568 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002569 {
2570 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002571 /* elide leading dots - they are implied in the search algorithm */
2572 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002573 /* # matches everything and becomes a zero length domain string */
2574 if (strcmp(arg, "#") == 0)
2575 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002576 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002577 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002578 serv = opt_malloc(sizeof(struct server));
2579 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002580 serv->next = newlist;
2581 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002582 serv->domain = domain;
2583 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002584 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002585 if (rebind)
2586 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002587 }
2588 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002589 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002590 }
2591 else
2592 {
Simon Kelley824af852008-02-12 20:43:05 +00002593 newlist = opt_malloc(sizeof(struct server));
2594 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002595#ifdef HAVE_LOOP
2596 newlist->uid = rand32();
2597#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002598 }
2599
Simon Kelley7b1eae42014-02-20 13:43:28 +00002600 if (servers_only && option == 'S')
2601 newlist->flags |= SERV_FROM_FILE;
2602
Simon Kelley849a8352006-06-09 21:02:31 +01002603 if (option == 'A')
2604 {
2605 newlist->flags |= SERV_LITERAL_ADDRESS;
2606 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002607 {
2608 server_list_free(newlist);
2609 ret_err(gen_err);
2610 }
Simon Kelley849a8352006-06-09 21:02:31 +01002611 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002612 else if (option == LOPT_NO_REBIND)
2613 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002614
2615 if (!arg || !*arg)
2616 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002617 if (!(newlist->flags & SERV_NO_REBIND))
2618 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002619 }
2620
2621 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002622 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002623 else
2624 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002625 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2626 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002627 {
2628 server_list_free(newlist);
2629 ret_err(err);
2630 }
Simon Kelley849a8352006-06-09 21:02:31 +01002631 }
2632
Simon Kelleyf2621c72007-04-29 19:47:21 +01002633 serv = newlist;
2634 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002635 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002636 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002637 serv->next->addr = serv->addr;
2638 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002639 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002640 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002641 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002642 serv->next = daemon->servers;
2643 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002644 break;
2645 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002646
Simon Kelleyde73a492014-02-17 21:43:27 +00002647 case LOPT_REV_SERV: /* --rev-server */
2648 {
2649 char *string;
2650 int size;
2651 struct server *serv;
2652 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002653 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002654
2655 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002656 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002657 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002658
2659 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002660
Simon Kelleya9b022a2020-02-11 21:58:59 +00002661 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2662 ret_err(gen_err);
2663
Simon Kelleyde73a492014-02-17 21:43:27 +00002664 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002665 {
2666 serv = add_rev4(addr4, size);
2667 if (!serv)
2668 ret_err(_("bad prefix"));
2669 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002670 else if (inet_pton(AF_INET6, arg, &addr6))
2671 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002672 else
2673 ret_err(gen_err);
2674
2675 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2676
2677 if (string)
2678 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002679
2680 if (servers_only)
2681 serv->flags |= SERV_FROM_FILE;
2682
Simon Kelleyde73a492014-02-17 21:43:27 +00002683 break;
2684 }
2685
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002686 case LOPT_IPSET: /* --ipset */
2687#ifndef HAVE_IPSET
2688 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2689 break;
2690#else
2691 {
2692 struct ipsets ipsets_head;
2693 struct ipsets *ipsets = &ipsets_head;
2694 int size;
2695 char *end;
2696 char **sets, **sets_pos;
2697 memset(ipsets, 0, sizeof(struct ipsets));
2698 unhide_metas(arg);
2699 if (arg && *arg == '/')
2700 {
2701 arg++;
2702 while ((end = split_chr(arg, '/')))
2703 {
2704 char *domain = NULL;
2705 /* elide leading dots - they are implied in the search algorithm */
2706 while (*arg == '.')
2707 arg++;
2708 /* # matches everything and becomes a zero length domain string */
2709 if (strcmp(arg, "#") == 0 || !*arg)
2710 domain = "";
2711 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002712 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002713 ipsets->next = opt_malloc(sizeof(struct ipsets));
2714 ipsets = ipsets->next;
2715 memset(ipsets, 0, sizeof(struct ipsets));
2716 ipsets->domain = domain;
2717 arg = end;
2718 }
2719 }
2720 else
2721 {
2722 ipsets->next = opt_malloc(sizeof(struct ipsets));
2723 ipsets = ipsets->next;
2724 memset(ipsets, 0, sizeof(struct ipsets));
2725 ipsets->domain = "";
2726 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002727
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002728 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002729 ret_err(gen_err);
2730
2731 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002732 if (*end == ',')
2733 ++size;
2734
2735 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2736
2737 do {
2738 end = split(arg);
2739 *sets_pos++ = opt_string_alloc(arg);
2740 arg = end;
2741 } while (end);
2742 *sets_pos = 0;
2743 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2744 ipsets->next->sets = sets;
2745 ipsets->next = daemon->ipsets;
2746 daemon->ipsets = ipsets_head.next;
2747
2748 break;
2749 }
2750#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002751
Simon Kelleyf2621c72007-04-29 19:47:21 +01002752 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002753 {
2754 int size;
2755
2756 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002757 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002758 else
2759 {
2760 /* zero is OK, and means no caching. */
2761
2762 if (size < 0)
2763 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002764
2765 /* Note that for very large cache sizes, the malloc()
2766 will overflow. For the size of the cache record
2767 at the time this was noted, the value of "very large"
2768 was 46684428. Limit to an order of magnitude less than
2769 that to be safe from changes to the cache record. */
2770 if (size > 5000000)
2771 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002772
2773 daemon->cachesize = size;
2774 }
2775 break;
2776 }
2777
Simon Kelleyf2621c72007-04-29 19:47:21 +01002778 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002779 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002780 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002781 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002782
Simon Kelley1a6bca82008-07-11 11:11:42 +01002783 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002784 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002785 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002786 break;
2787
Hans Dedecker926332a2016-01-23 10:48:12 +00002788 case LOPT_MAXPORT: /* --max-port */
2789 if (!atoi_check16(arg, &daemon->max_port))
2790 ret_err(gen_err);
2791 break;
2792
Simon Kelleyf2621c72007-04-29 19:47:21 +01002793 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002794 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002795 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002796 break;
2797
Simon Kelley25cf5e32015-01-09 15:53:03 +00002798 case 'q': /* --log-queries */
2799 set_option_bool(OPT_LOG);
2800 if (arg && strcmp(arg, "extra") == 0)
2801 set_option_bool(OPT_EXTRALOG);
2802 break;
2803
Simon Kelleyf2621c72007-04-29 19:47:21 +01002804 case LOPT_MAX_LOGS: /* --log-async */
2805 daemon->max_logs = LOG_MAX; /* default */
2806 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002807 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002808 else if (daemon->max_logs > 100)
2809 daemon->max_logs = 100;
2810 break;
2811
2812 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002813 {
2814 int i;
2815 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002816 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002817 daemon->edns_pktsz = (unsigned short)i;
2818 break;
2819 }
2820
Simon Kelleyf2621c72007-04-29 19:47:21 +01002821 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002822 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002823 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002824 /* if explicitly set to zero, use single OS ephemeral port
2825 and disable random ports */
2826 if (daemon->query_port == 0)
2827 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002828 break;
2829
Simon Kelley824af852008-02-12 20:43:05 +00002830 case 'T': /* --local-ttl */
2831 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002832 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002833 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002834 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002835 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002836 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002837 {
2838 int ttl;
2839 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002840 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002841 else if (option == LOPT_NEGTTL)
2842 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002843 else if (option == LOPT_MAXTTL)
2844 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002845 else if (option == LOPT_MINCTTL)
2846 {
2847 if (ttl > TTL_FLOOR_LIMIT)
2848 ttl = TTL_FLOOR_LIMIT;
2849 daemon->min_cache_ttl = (unsigned long)ttl;
2850 }
Simon Kelley1d860412012-09-20 20:48:04 +01002851 else if (option == LOPT_MAXCTTL)
2852 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002853 else if (option == LOPT_AUTHTTL)
2854 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002855 else if (option == LOPT_DHCPTTL)
2856 {
2857 daemon->dhcp_ttl = (unsigned long)ttl;
2858 daemon->use_dhcp_ttl = 1;
2859 }
Simon Kelley849a8352006-06-09 21:02:31 +01002860 else
2861 daemon->local_ttl = (unsigned long)ttl;
2862 break;
2863 }
2864
Simon Kelley7622fc02009-06-04 20:32:05 +01002865#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002866 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002867 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002868 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002869 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002870#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002871
Simon Kelley7622fc02009-06-04 20:32:05 +01002872#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002873 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002874 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002875 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002876 break;
2877
Simon Kelleybec366b2016-02-24 22:03:26 +00002878 case LOPT_TFTP_MTU: /* --tftp-mtu */
2879 if (!atoi_check(arg, &daemon->tftp_mtu))
2880 ret_err(gen_err);
2881 break;
2882
Simon Kelley824af852008-02-12 20:43:05 +00002883 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002884 comma = split(arg);
2885 if (comma)
2886 {
2887 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2888 new->interface = opt_string_alloc(comma);
2889 new->prefix = opt_string_alloc(arg);
2890 new->next = daemon->if_prefix;
2891 daemon->if_prefix = new;
2892 }
2893 else
2894 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002895 break;
2896
Simon Kelley824af852008-02-12 20:43:05 +00002897 case LOPT_TFTPPORTS: /* --tftp-port-range */
2898 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002899 !atoi_check16(arg, &daemon->start_tftp_port) ||
2900 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002901 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002902
2903 if (daemon->start_tftp_port > daemon->end_tftp_port)
2904 {
2905 int tmp = daemon->start_tftp_port;
2906 daemon->start_tftp_port = daemon->end_tftp_port;
2907 daemon->end_tftp_port = tmp;
2908 }
2909
2910 break;
Floris Bos60704f52017-04-09 22:22:49 +01002911
2912 case LOPT_APREF: /* --tftp-unique-root */
2913 if (!arg || strcasecmp(arg, "ip") == 0)
2914 set_option_bool(OPT_TFTP_APREF_IP);
2915 else if (strcasecmp(arg, "mac") == 0)
2916 set_option_bool(OPT_TFTP_APREF_MAC);
2917 else
2918 ret_err(gen_err);
2919 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002920#endif
Simon Kelley824af852008-02-12 20:43:05 +00002921
Simon Kelleyf2621c72007-04-29 19:47:21 +01002922 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002923 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002924 struct dhcp_bridge *new;
2925
Simon Kelley316e2732010-01-22 20:16:09 +00002926 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002927 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002928
Simon Kelley22cd8602018-01-14 22:57:14 +00002929 for (new = daemon->bridges; new; new = new->next)
2930 if (strcmp(new->iface, arg) == 0)
2931 break;
2932
2933 if (!new)
2934 {
2935 new = opt_malloc(sizeof(struct dhcp_bridge));
2936 strcpy(new->iface, arg);
2937 new->alias = NULL;
2938 new->next = daemon->bridges;
2939 daemon->bridges = new;
2940 }
2941
Simon Kelley832af0b2007-01-21 20:01:28 +00002942 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002943 arg = comma;
2944 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002945 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002946 {
Simon Kelley824af852008-02-12 20:43:05 +00002947 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002948 b->next = new->alias;
2949 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002950 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002951 }
2952 } while (comma);
2953
2954 break;
2955 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002956
Simon Kelley7622fc02009-06-04 20:32:05 +01002957#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002958 case LOPT_SHARED_NET: /* --shared-network */
2959 {
2960 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2961
2962#ifdef HAVE_DHCP6
2963 new->shared_addr.s_addr = 0;
2964#endif
2965 new->if_index = 0;
2966
2967 if (!(comma = split(arg)))
2968 {
2969 snerr:
2970 free(new);
2971 ret_err(_("bad shared-network"));
2972 }
2973
2974 if (inet_pton(AF_INET, comma, &new->shared_addr))
2975 {
2976 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2977 !(new->if_index = if_nametoindex(arg)))
2978 goto snerr;
2979 }
2980#ifdef HAVE_DHCP6
2981 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2982 {
2983 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
2984 !(new->if_index = if_nametoindex(arg)))
2985 goto snerr;
2986 }
2987#endif
2988 else
2989 goto snerr;
2990
2991 new->next = daemon->shared_networks;
2992 daemon->shared_networks = new;
2993 break;
2994 }
2995
Simon Kelleyf2621c72007-04-29 19:47:21 +01002996 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002997 {
2998 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002999 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003000 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003001
Simon Kelley52b92f42012-01-22 16:05:15 +00003002 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003003
Simon Kelley849a8352006-06-09 21:02:31 +01003004 while(1)
3005 {
3006 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003007 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3008 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3009 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003010 break;
3011
Simon Kelleyf2621c72007-04-29 19:47:21 +01003012 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003013 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003014 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003015 {
Simon Kelley0c387192013-09-05 10:21:12 +01003016 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003017 if (arg[4])
3018 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003019 }
3020 else
3021 {
3022 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003023 {
3024 dhcp_context_free(new);
3025 ret_err(_("only one tag allowed"));
3026 }
Simon Kelley849a8352006-06-09 21:02:31 +01003027 else
Petr Menšík59e47032018-11-02 22:39:39 +00003028 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003029 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003030 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003031 }
3032 else
3033 {
3034 a[0] = arg;
3035 break;
3036 }
3037 }
3038
Simon Kelley1f776932012-12-16 19:46:08 +00003039 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003040 if (!(a[k] = split(a[k-1])))
3041 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003042
Simon Kelley52b92f42012-01-22 16:05:15 +00003043 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003044 {
3045 dhcp_context_free(new);
3046 ret_err(_("bad dhcp-range"));
3047 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003048
3049 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003050 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003051 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003052 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003053 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003054 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003055 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003056 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003057 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003058 new->flags |= CONTEXT_PROXY;
3059 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003060 {
3061 dhcp_context_free(new);
3062 ret_err(_("bad dhcp-range"));
3063 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003064
3065 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3066 {
3067 struct in_addr tmp = new->start;
3068 new->start = new->end;
3069 new->end = tmp;
3070 }
3071
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003072 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003073 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003074 {
3075 new->flags |= CONTEXT_NETMASK;
3076 leasepos = 3;
3077 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003078 {
3079 dhcp_context_free(new);
3080 ret_err(_("inconsistent DHCP range"));
3081 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003082
Simon Kelley52b92f42012-01-22 16:05:15 +00003083
Simon Kelleyfa794662016-03-03 20:33:54 +00003084 if (k >= 4 && strchr(a[3], '.') &&
3085 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3086 {
3087 new->flags |= CONTEXT_BRDCAST;
3088 leasepos = 4;
3089 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003090 }
Simon Kelley849a8352006-06-09 21:02:31 +01003091 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003092#ifdef HAVE_DHCP6
3093 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003094 {
Petr Menšík59e47032018-11-02 22:39:39 +00003095 const char *err = NULL;
3096
Simon Kelley89500e32013-09-20 16:29:20 +01003097 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003098 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003099 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003100 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003101 new->next = daemon->dhcp6;
3102 daemon->dhcp6 = new;
3103
Simon Kelley30cd9662012-03-25 20:44:38 +01003104 for (leasepos = 1; leasepos < k; leasepos++)
3105 {
3106 if (strcmp(a[leasepos], "static") == 0)
3107 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3108 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003109 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003110 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003111 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003112 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3113 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003114 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003115 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003116 else if (strcmp(a[leasepos], "off-link") == 0)
3117 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003118 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3119 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003120 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3121 {
3122 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3123 new->flags |= CONTEXT_TEMPLATE;
3124 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003125 else
3126 break;
3127 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003128
Simon Kelley52b92f42012-01-22 16:05:15 +00003129 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003130 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003131 {
3132 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003133 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003134 if (!(*cp >= '0' && *cp <= '9'))
3135 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003136 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003137 {
3138 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003139 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003140 }
3141 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003142
Petr Menšík59e47032018-11-02 22:39:39 +00003143 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003144 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003145 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003146 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003147 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003148 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003149 }
Petr Menšík59e47032018-11-02 22:39:39 +00003150 else if (new->prefix < 64)
3151 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003152
Petr Menšík59e47032018-11-02 22:39:39 +00003153 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3154 err=(_("inconsistent DHCPv6 range"));
3155
3156 if (err)
3157 {
3158 dhcp_context_free(new);
3159 ret_err(err);
3160 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003161
3162 /* dhcp-range=:: enables DHCP stateless on any interface */
3163 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3164 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003165
3166 if (new->flags & CONTEXT_TEMPLATE)
3167 {
3168 struct in6_addr zero;
3169 memset(&zero, 0, sizeof(zero));
3170 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003171 {
3172 dhcp_context_free(new);
3173 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3174 }
Simon Kelley66409192013-08-01 20:19:32 +01003175 }
3176
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003177 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003178 {
3179 struct in6_addr tmp = new->start6;
3180 new->start6 = new->end6;
3181 new->end6 = tmp;
3182 }
Simon Kelley849a8352006-06-09 21:02:31 +01003183 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003184#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003185 else
Petr Menšík59e47032018-11-02 22:39:39 +00003186 {
3187 dhcp_context_free(new);
3188 ret_err(_("bad dhcp-range"));
3189 }
Simon Kelley849a8352006-06-09 21:02:31 +01003190
Simon Kelley30cd9662012-03-25 20:44:38 +01003191 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003192 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003193 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003194 {
3195 dhcp_context_free(new);
3196 ret_err(_("bad dhcp-range"));
3197 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003198
Simon Kelley849a8352006-06-09 21:02:31 +01003199 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003200 {
3201 new->lease_time = 0xffffffff;
3202 new->flags |= CONTEXT_SETLEASE;
3203 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003204 else if (strcmp(a[leasepos], "deprecated") == 0)
3205 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003206 else
3207 {
3208 int fac = 1;
3209 if (strlen(a[leasepos]) > 0)
3210 {
3211 switch (a[leasepos][strlen(a[leasepos]) - 1])
3212 {
Simon Kelley42243212012-07-20 15:19:18 +01003213 case 'w':
3214 case 'W':
3215 fac *= 7;
3216 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003217 case 'd':
3218 case 'D':
3219 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003220 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003221 case 'h':
3222 case 'H':
3223 fac *= 60;
3224 /* fall through */
3225 case 'm':
3226 case 'M':
3227 fac *= 60;
3228 /* fall through */
3229 case 's':
3230 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003231 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003232 }
3233
Simon Kelleybe379862012-12-23 12:01:39 +00003234 for (cp = a[leasepos]; *cp; cp++)
3235 if (!(*cp >= '0' && *cp <= '9'))
3236 break;
3237
Simon Kelley54dae552013-02-05 17:55:10 +00003238 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003239 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003240
Simon Kelley849a8352006-06-09 21:02:31 +01003241 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003242 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003243 /* Leases of a minute or less confuse
3244 some clients, notably Apple's */
3245 if (new->lease_time < 120)
3246 new->lease_time = 120;
3247 }
3248 }
3249 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003250
Simon Kelley849a8352006-06-09 21:02:31 +01003251 break;
3252 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003253
Simon Kelley5aabfc72007-08-29 11:24:47 +01003254 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003255 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003256 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003257 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003258 struct in_addr in;
3259
Simon Kelley824af852008-02-12 20:43:05 +00003260 new = opt_malloc(sizeof(struct dhcp_config));
3261
Simon Kelley849a8352006-06-09 21:02:31 +01003262 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003263 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3264 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003265 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003266 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003267 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003268#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003269 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003270#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003271
Simon Kelley137286e2020-02-06 22:09:30 +00003272 while (arg)
3273 {
3274 comma = split(arg);
3275 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3276 {
3277 if ((arg[0] == 'i' || arg[0] == 'I') &&
3278 (arg[1] == 'd' || arg[1] == 'D') &&
3279 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003280 {
Simon Kelley137286e2020-02-06 22:09:30 +00003281 if (arg[3] == '*')
3282 new->flags |= CONFIG_NOCLID;
3283 else
3284 {
3285 int len;
3286 arg += 3; /* dump id: */
3287 if (strchr(arg, ':'))
3288 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3289 else
3290 {
3291 unhide_metas(arg);
3292 len = (int) strlen(arg);
3293 }
3294
3295 if (len == -1)
3296 {
3297 dhcp_config_free(new);
3298 ret_err(_("bad hex constant"));
3299 }
3300 else if ((new->clid = opt_malloc(len)))
3301 {
3302 new->flags |= CONFIG_CLID;
3303 new->clid_len = len;
3304 memcpy(new->clid, arg, len);
3305 }
3306 }
3307 }
3308 /* dhcp-host has strange backwards-compat needs. */
3309 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3310 {
3311 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3312 newlist->next = new->netid;
3313 new->netid = newlist;
3314 newlist->list = dhcp_netid_create(arg+4, NULL);
3315 }
3316 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003317 new->filter = dhcp_netid_create(arg+4, new->filter);
3318
Simon Kelley137286e2020-02-06 22:09:30 +00003319#ifdef HAVE_DHCP6
3320 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3321 {
3322 char *pref;
3323 struct in6_addr in6;
3324 struct addrlist *new_addr;
3325
3326 arg[strlen(arg)-1] = 0;
3327 arg++;
3328 pref = split_chr(arg, '/');
3329
3330 if (!inet_pton(AF_INET6, arg, &in6))
3331 {
3332 dhcp_config_free(new);
3333 ret_err(_("bad IPv6 address"));
3334 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003335
Simon Kelley137286e2020-02-06 22:09:30 +00003336 new_addr = opt_malloc(sizeof(struct addrlist));
3337 new_addr->next = new->addr6;
3338 new_addr->flags = 0;
3339 new_addr->addr.addr6 = in6;
3340 new->addr6 = new_addr;
3341
3342 if (pref)
3343 {
3344 u64 addrpart = addr6part(&in6);
3345
3346 if (!atoi_check(pref, &new_addr->prefixlen) ||
3347 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003348 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003349 {
3350 dhcp_config_free(new);
3351 ret_err(_("bad IPv6 prefix"));
3352 }
3353
3354 new_addr->flags |= ADDRLIST_PREFIX;
3355 }
3356
3357 for (i= 0; i < 8; i++)
3358 if (in6.s6_addr[i] != 0)
3359 break;
3360
3361 /* set WILDCARD if network part all zeros */
3362 if (i == 8)
3363 new_addr->flags |= ADDRLIST_WILDCARD;
3364
3365 new->flags |= CONFIG_ADDR6;
3366 }
3367#endif
3368 else
3369 {
3370 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3371 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3372 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3373 {
3374 free(newhw);
3375 dhcp_config_free(new);
3376 ret_err(_("bad hex constant"));
3377 }
3378 else
3379 {
3380 newhw->next = new->hwaddr;
3381 new->hwaddr = newhw;
3382 }
3383 }
3384 }
3385 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3386 {
3387 struct dhcp_config *configs;
3388
3389 new->addr = in;
3390 new->flags |= CONFIG_ADDR;
3391
3392 /* If the same IP appears in more than one host config, then DISCOVER
3393 for one of the hosts will get the address, but REQUEST will be NAKed,
3394 since the address is reserved by the other one -> protocol loop. */
3395 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3396 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003397 {
Simon Kelley137286e2020-02-06 22:09:30 +00003398 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3399 return 0;
3400 }
3401 }
3402 else
3403 {
3404 char *cp, *lastp = NULL, last = 0;
3405 int fac = 1, isdig = 0;
3406
3407 if (strlen(arg) > 1)
3408 {
3409 lastp = arg + strlen(arg) - 1;
3410 last = *lastp;
3411 switch (last)
3412 {
3413 case 'w':
3414 case 'W':
3415 fac *= 7;
3416 /* fall through */
3417 case 'd':
3418 case 'D':
3419 fac *= 24;
3420 /* fall through */
3421 case 'h':
3422 case 'H':
3423 fac *= 60;
3424 /* fall through */
3425 case 'm':
3426 case 'M':
3427 fac *= 60;
3428 /* fall through */
3429 case 's':
3430 case 'S':
3431 *lastp = 0;
3432 }
3433 }
3434
3435 for (cp = arg; *cp; cp++)
3436 if (isdigit((unsigned char)*cp))
3437 isdig = 1;
3438 else if (*cp != ' ')
3439 break;
3440
3441 if (*cp)
3442 {
3443 if (lastp)
3444 *lastp = last;
3445 if (strcmp(arg, "infinite") == 0)
3446 {
3447 new->lease_time = 0xffffffff;
3448 new->flags |= CONFIG_TIME;
3449 }
3450 else if (strcmp(arg, "ignore") == 0)
3451 new->flags |= CONFIG_DISABLE;
3452 else
3453 {
3454 if (!(new->hostname = canonicalise_opt(arg)) ||
3455 !legal_hostname(new->hostname))
3456 {
3457 dhcp_config_free(new);
3458 ret_err(_("bad DHCP host name"));
3459 }
3460
3461 new->flags |= CONFIG_NAME;
3462 new->domain = strip_hostname(new->hostname);
3463 }
3464 }
3465 else if (isdig)
3466 {
3467 new->lease_time = atoi(arg) * fac;
3468 /* Leases of a minute or less confuse
3469 some clients, notably Apple's */
3470 if (new->lease_time < 120)
3471 new->lease_time = 120;
3472 new->flags |= CONFIG_TIME;
3473 }
3474 }
3475
3476 arg = comma;
3477 }
3478
Simon Kelley5aabfc72007-08-29 11:24:47 +01003479 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003480 break;
3481 }
Simon Kelley137286e2020-02-06 22:09:30 +00003482
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003483 case LOPT_TAG_IF: /* --tag-if */
3484 {
3485 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3486
3487 new->tag = NULL;
3488 new->set = NULL;
3489 new->next = NULL;
3490
3491 /* preserve order */
3492 if (!daemon->tag_if)
3493 daemon->tag_if = new;
3494 else
3495 {
3496 struct tag_if *tmp;
3497 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3498 tmp->next = new;
3499 }
3500
3501 while (arg)
3502 {
3503 size_t len;
3504
3505 comma = split(arg);
3506 len = strlen(arg);
3507
3508 if (len < 5)
3509 {
3510 new->set = NULL;
3511 break;
3512 }
3513 else
3514 {
Petr Menšík59e47032018-11-02 22:39:39 +00003515 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003516
3517 if (strstr(arg, "set:") == arg)
3518 {
3519 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3520 newlist->next = new->set;
3521 new->set = newlist;
3522 newlist->list = newtag;
3523 }
3524 else if (strstr(arg, "tag:") == arg)
3525 {
3526 newtag->next = new->tag;
3527 new->tag = newtag;
3528 }
3529 else
3530 {
3531 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003532 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003533 break;
3534 }
3535 }
3536
3537 arg = comma;
3538 }
3539
3540 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003541 {
3542 dhcp_netid_free(new->tag);
3543 dhcp_netid_list_free(new->set);
3544 ret_err_free(_("bad tag-if"), new);
3545 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003546
3547 break;
3548 }
3549
Simon Kelley849a8352006-06-09 21:02:31 +01003550
Simon Kelley73a08a22009-02-05 20:28:08 +00003551 case 'O': /* --dhcp-option */
3552 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003553 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003554 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003555 return parse_dhcp_opt(errstr, arg,
3556 option == LOPT_FORCE ? DHOPT_FORCE :
3557 (option == LOPT_MATCH ? DHOPT_MATCH :
3558 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003559
3560 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3561 {
3562 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3563 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3564 ssize_t len;
3565
3566 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3567 ret_err(gen_err);
3568
3569 new->wildcard = 0;
3570 new->netid = id;
3571 id->net = opt_string_alloc(set_prefix(arg));
3572
3573 if (comma[len-1] == '*')
3574 {
3575 comma[len-1] = 0;
3576 new->wildcard = 1;
3577 }
3578 new->name = opt_string_alloc(comma);
3579
3580 new->next = daemon->dhcp_name_match;
3581 daemon->dhcp_name_match = new;
3582
3583 break;
3584 }
3585
Simon Kelleyf2621c72007-04-29 19:47:21 +01003586 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003587 {
Petr Menšík59e47032018-11-02 22:39:39 +00003588 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003589
Petr Menšík137e9f82018-12-16 21:25:29 +00003590 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003591 {
3592 ret_err(gen_err);
3593 }
Simon Kelley849a8352006-06-09 21:02:31 +01003594 else
3595 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003596 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003597 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003598 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003599 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003600 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003601 dhcp_next_server.s_addr = 0;
3602 if (comma)
3603 {
3604 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003605 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003606 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003607 if (comma)
3608 {
3609 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003610 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3611 {
3612 /*
3613 * The user may have specified the tftp hostname here.
3614 * save it so that it can be resolved/looked up during
3615 * actual dhcp_reply().
3616 */
3617
3618 tftp_sname = opt_string_alloc(comma);
3619 dhcp_next_server.s_addr = 0;
3620 }
Simon Kelley849a8352006-06-09 21:02:31 +01003621 }
3622 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003623
3624 new = opt_malloc(sizeof(struct dhcp_boot));
3625 new->file = dhcp_file;
3626 new->sname = dhcp_sname;
3627 new->tftp_sname = tftp_sname;
3628 new->next_server = dhcp_next_server;
3629 new->netid = id;
3630 new->next = daemon->boot_config;
3631 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003632 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003633
Simon Kelley849a8352006-06-09 21:02:31 +01003634 break;
3635 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003636
Floris Bos503c6092017-04-09 23:07:13 +01003637 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3638 {
Petr Menšík59e47032018-11-02 22:39:39 +00003639 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003640
Petr Menšík137e9f82018-12-16 21:25:29 +00003641 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003642 {
3643 ret_err(gen_err);
3644 }
Floris Bos503c6092017-04-09 23:07:13 +01003645 else
3646 {
3647 struct delay_config *new;
3648 int delay;
3649 if (!atoi_check(arg, &delay))
3650 ret_err(gen_err);
3651
3652 new = opt_malloc(sizeof(struct delay_config));
3653 new->delay = delay;
3654 new->netid = id;
3655 new->next = daemon->delay_conf;
3656 daemon->delay_conf = new;
3657 }
3658
3659 break;
3660 }
3661
Simon Kelley7622fc02009-06-04 20:32:05 +01003662 case LOPT_PXE_PROMT: /* --pxe-prompt */
3663 {
3664 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3665 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003666
Simon Kelley7622fc02009-06-04 20:32:05 +01003667 new->netid = NULL;
3668 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003669 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003670
Petr Menšík137e9f82018-12-16 21:25:29 +00003671 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003672 {
3673 dhcp_opt_free(new);
3674 ret_err(gen_err);
3675 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003676 else
3677 {
3678 comma = split(arg);
3679 unhide_metas(arg);
3680 new->len = strlen(arg) + 1;
3681 new->val = opt_malloc(new->len);
3682 memcpy(new->val + 1, arg, new->len - 1);
3683
Wang Shanker4ded9622020-12-04 10:17:35 +08003684 new->u.vendor_class = NULL;
3685 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003686
3687 if (comma && atoi_check(comma, &timeout))
3688 *(new->val) = timeout;
3689 else
3690 *(new->val) = 255;
3691
3692 new->next = daemon->dhcp_opts;
3693 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003694 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003695 }
3696
3697 break;
3698 }
3699
3700 case LOPT_PXE_SERV: /* --pxe-service */
3701 {
3702 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3703 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003704 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3705 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003706 static int boottype = 32768;
3707
3708 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003709 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003710 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003711 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003712
Simon Kelley7622fc02009-06-04 20:32:05 +01003713 if (arg && (comma = split(arg)))
3714 {
3715 for (i = 0; CSA[i]; i++)
3716 if (strcasecmp(CSA[i], arg) == 0)
3717 break;
3718
3719 if (CSA[i] || atoi_check(arg, &i))
3720 {
3721 arg = comma;
3722 comma = split(arg);
3723
3724 new->CSA = i;
3725 new->menu = opt_string_alloc(arg);
3726
Simon Kelley316e2732010-01-22 20:16:09 +00003727 if (!comma)
3728 {
3729 new->type = 0; /* local boot */
3730 new->basename = NULL;
3731 }
3732 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003733 {
3734 arg = comma;
3735 comma = split(arg);
3736 if (atoi_check(arg, &i))
3737 {
3738 new->type = i;
3739 new->basename = NULL;
3740 }
3741 else
3742 {
3743 new->type = boottype++;
3744 new->basename = opt_string_alloc(arg);
3745 }
3746
Simon Kelley751d6f42012-02-10 15:24:51 +00003747 if (comma)
3748 {
3749 if (!inet_pton(AF_INET, comma, &new->server))
3750 {
3751 new->server.s_addr = 0;
3752 new->sname = opt_string_alloc(comma);
3753 }
3754
3755 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003756 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003757
Simon Kelley316e2732010-01-22 20:16:09 +00003758 /* Order matters */
3759 new->next = NULL;
3760 if (!daemon->pxe_services)
3761 daemon->pxe_services = new;
3762 else
3763 {
3764 struct pxe_service *s;
3765 for (s = daemon->pxe_services; s->next; s = s->next);
3766 s->next = new;
3767 }
3768
3769 daemon->enable_pxe = 1;
3770 break;
3771
Simon Kelley7622fc02009-06-04 20:32:05 +01003772 }
3773 }
3774
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003775 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003776 }
3777
Simon Kelleyf2621c72007-04-29 19:47:21 +01003778 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003779 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003780 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003781 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003782 else
3783 {
Simon Kelley824af852008-02-12 20:43:05 +00003784 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003785 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003786 unhide_metas(comma);
3787 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003788 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003789 {
3790 free(new->netid.net);
3791 ret_err_free(gen_err, new);
3792 }
Simon Kelley28866e92011-02-14 20:19:14 +00003793 else
3794 {
3795 new->next = daemon->dhcp_macs;
3796 daemon->dhcp_macs = new;
3797 }
Simon Kelley849a8352006-06-09 21:02:31 +01003798 }
3799 }
3800 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003801
Simon Kelleyf2621c72007-04-29 19:47:21 +01003802 case 'U': /* --dhcp-vendorclass */
3803 case 'j': /* --dhcp-userclass */
3804 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3805 case LOPT_REMOTE: /* --dhcp-remoteid */
3806 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003807 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003808 unsigned char *p;
3809 int dig = 0;
3810 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3811
3812 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003813 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003814
3815 new->netid.net = opt_string_alloc(set_prefix(arg));
3816 /* check for hex string - must digits may include : must not have nothing else,
3817 only allowed for agent-options. */
3818
3819 arg = comma;
3820 if ((comma = split(arg)))
3821 {
3822 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003823 {
3824 free(new->netid.net);
3825 ret_err_free(gen_err, new);
3826 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003827 else
3828 new->enterprise = atoi(arg+11);
3829 }
3830 else
3831 comma = arg;
3832
3833 for (p = (unsigned char *)comma; *p; p++)
3834 if (isxdigit(*p))
3835 dig = 1;
3836 else if (*p != ':')
3837 break;
3838 unhide_metas(comma);
3839 if (option == 'U' || option == 'j' || *p || !dig)
3840 {
3841 new->len = strlen(comma);
3842 new->data = opt_malloc(new->len);
3843 memcpy(new->data, comma, new->len);
3844 }
3845 else
3846 {
3847 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3848 new->data = opt_malloc(new->len);
3849 memcpy(new->data, comma, new->len);
3850 }
3851
3852 switch (option)
3853 {
3854 case 'j':
3855 new->match_type = MATCH_USER;
3856 break;
3857 case 'U':
3858 new->match_type = MATCH_VENDOR;
3859 break;
3860 case LOPT_CIRCUIT:
3861 new->match_type = MATCH_CIRCUIT;
3862 break;
3863 case LOPT_REMOTE:
3864 new->match_type = MATCH_REMOTE;
3865 break;
3866 case LOPT_SUBSCR:
3867 new->match_type = MATCH_SUBSCRIBER;
3868 break;
3869 }
3870 new->next = daemon->dhcp_vendors;
3871 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003872
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003873 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003874 }
3875
Simon Kelley9e038942008-05-30 20:06:34 +01003876 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3877 if (!arg)
3878 {
3879 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3880 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3881 }
3882 else
3883 {
3884 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003885 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3886 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003887 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003888 if (!comma)
3889 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3890 }
3891 break;
3892
Simon Kelley824af852008-02-12 20:43:05 +00003893 case 'J': /* --dhcp-ignore */
3894 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3895 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003896 case '3': /* --bootp-dynamic */
3897 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003898 {
Simon Kelley824af852008-02-12 20:43:05 +00003899 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003900 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003901 if (option == 'J')
3902 {
3903 new->next = daemon->dhcp_ignore;
3904 daemon->dhcp_ignore = new;
3905 }
Simon Kelley824af852008-02-12 20:43:05 +00003906 else if (option == LOPT_BROADCAST)
3907 {
3908 new->next = daemon->force_broadcast;
3909 daemon->force_broadcast = new;
3910 }
Simon Kelley9009d742008-11-14 20:04:27 +00003911 else if (option == '3')
3912 {
3913 new->next = daemon->bootp_dynamic;
3914 daemon->bootp_dynamic = new;
3915 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003916 else if (option == LOPT_GEN_NAMES)
3917 {
3918 new->next = daemon->dhcp_gen_names;
3919 daemon->dhcp_gen_names = new;
3920 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003921 else
3922 {
3923 new->next = daemon->dhcp_ignore_names;
3924 daemon->dhcp_ignore_names = new;
3925 }
3926
3927 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003928 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003929 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003930 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003931 }
Simon Kelley849a8352006-06-09 21:02:31 +01003932
3933 new->list = list;
3934 break;
3935 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003936
3937 case LOPT_PROXY: /* --dhcp-proxy */
3938 daemon->override = 1;
3939 while (arg) {
3940 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3941 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003942 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003943 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003944 new->next = daemon->override_relays;
3945 daemon->override_relays = new;
3946 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003947 }
3948 break;
3949
3950 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3951 {
3952 while (arg) {
3953 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3954 comma = split(arg);
3955 new->data = opt_string_alloc(arg);
3956 new->next = daemon->dhcp_pxe_vendors;
3957 daemon->dhcp_pxe_vendors = new;
3958 arg = comma;
3959 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003960 }
3961 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003962
3963 case LOPT_RELAY: /* --dhcp-relay */
3964 {
3965 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3966 comma = split(arg);
3967 new->interface = opt_string_alloc(split(comma));
3968 new->iface_index = 0;
3969 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3970 {
3971 new->next = daemon->relay4;
3972 daemon->relay4 = new;
3973 }
3974#ifdef HAVE_DHCP6
3975 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3976 {
3977 new->next = daemon->relay6;
3978 daemon->relay6 = new;
3979 }
3980#endif
3981 else
Petr Menšík59e47032018-11-02 22:39:39 +00003982 {
3983 free(new->interface);
3984 ret_err_free(_("Bad dhcp-relay"), new);
3985 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003986
3987 break;
3988 }
3989
Simon Kelley7622fc02009-06-04 20:32:05 +01003990#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003991
Simon Kelley8b372702012-03-09 17:45:10 +00003992#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003993 case LOPT_RA_PARAM: /* --ra-param */
3994 if ((comma = split(arg)))
3995 {
3996 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3997 new->lifetime = -1;
3998 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003999 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004000 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004001 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004002 if (strcasestr(comma, "mtu:") == comma)
4003 {
4004 arg = comma + 4;
4005 if (!(comma = split(comma)))
4006 goto err;
4007 if (!strcasecmp(arg, "off"))
4008 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004009 else if (!atoi_check(arg, &new->mtu))
4010 new->mtu_name = opt_string_alloc(arg);
4011 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004012 goto err;
4013 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004014 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4015 {
4016 if (*comma == 'l' || *comma == 'L')
4017 new->prio = 0x18;
4018 else
4019 new->prio = 0x08;
4020 comma = split(comma);
4021 }
4022 arg = split(comma);
4023 if (!atoi_check(comma, &new->interval) ||
4024 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004025 {
David Flamand005c46d2017-04-11 11:49:54 +01004026err:
Petr Menšík59e47032018-11-02 22:39:39 +00004027 free(new->name);
4028 ret_err_free(_("bad RA-params"), new);
4029 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004030
4031 new->next = daemon->ra_interfaces;
4032 daemon->ra_interfaces = new;
4033 }
4034 break;
4035
Simon Kelley8b372702012-03-09 17:45:10 +00004036 case LOPT_DUID: /* --dhcp-duid */
4037 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004038 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004039 else
4040 {
4041 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4042 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4043 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4044 }
4045 break;
4046#endif
4047
Simon Kelleyf2621c72007-04-29 19:47:21 +01004048 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004049 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004050 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004051 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004052 struct doctor *new = opt_malloc(sizeof(struct doctor));
4053 new->next = daemon->doctors;
4054 daemon->doctors = new;
4055 new->mask.s_addr = 0xffffffff;
4056 new->end.s_addr = 0;
4057
Simon Kelley849a8352006-06-09 21:02:31 +01004058 if ((a[0] = arg))
4059 for (k = 1; k < 3; k++)
4060 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004061 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004062 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004063 unhide_metas(a[k]);
4064 }
Simon Kelley849a8352006-06-09 21:02:31 +01004065
Simon Kelley73a08a22009-02-05 20:28:08 +00004066 dash = split_chr(a[0], '-');
4067
Simon Kelley849a8352006-06-09 21:02:31 +01004068 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004069 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004070 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4071 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4072 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004073
Simon Kelley73a08a22009-02-05 20:28:08 +00004074 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004075 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004076 !is_same_net(new->in, new->end, new->mask) ||
4077 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004078 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004079
4080 break;
4081 }
4082
Simon Kelleyf2621c72007-04-29 19:47:21 +01004083 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004084 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004085 {
4086 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004087 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004088
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004089 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004090
Simon Kelley824af852008-02-12 20:43:05 +00004091 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004092 memset(new, 0, sizeof(struct interface_name));
4093 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004094
Simon Kelleyf2621c72007-04-29 19:47:21 +01004095 /* Add to the end of the list, so that first name
4096 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004097 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004098 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004099
4100 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004101 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004102 if (inet_pton(AF_INET, arg, &new->proto4))
4103 new->flags |= INP4;
4104 else if (inet_pton(AF_INET6, arg, &new->proto6))
4105 new->flags |= INP6;
4106 else
4107 break;
4108
4109 arg = comma;
4110 }
4111
4112 if ((comma = split_chr(arg, '/')))
4113 {
4114 if (strcmp(comma, "4") == 0)
4115 new->flags &= ~IN6;
4116 else if (strcmp(comma, "6") == 0)
4117 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004118 else
Petr Menšík59e47032018-11-02 22:39:39 +00004119 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004120 }
4121
4122 new->intr = opt_string_alloc(arg);
4123
4124 if (option == LOPT_DYNHOST)
4125 {
4126 if (!(new->flags & (INP4 | INP6)))
4127 ret_err(_("missing address in dynamic host"));
4128
4129 if (!(new->flags & IN4) || !(new->flags & IN6))
4130 arg = NULL; /* provoke error below */
4131
4132 new->flags &= ~(IN4 | IN6);
4133 }
4134 else
4135 {
4136 if (new->flags & (INP4 | INP6))
4137 arg = NULL; /* provoke error below */
4138 }
4139
4140 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4141 ret_err(option == LOPT_DYNHOST ?
4142 _("bad dynamic host") : _("bad interface name"));
4143
Simon Kelleyf2621c72007-04-29 19:47:21 +01004144 break;
4145 }
Simon Kelley9009d742008-11-14 20:04:27 +00004146
4147 case LOPT_CNAME: /* --cname */
4148 {
4149 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004150 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004151 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004152
Simon Kelleya1d973f2016-12-22 22:09:50 +00004153 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004154 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004155 pen = last;
4156 last = comma;
4157 }
4158
4159 if (!pen)
4160 ret_err(_("bad CNAME"));
4161
4162 if (pen != arg && atoi_check(last, &ttl))
4163 last = pen;
4164
4165 target = canonicalise_opt(last);
4166
4167 while (arg != last)
4168 {
Petr Menšík56f06232018-03-06 23:13:32 +00004169 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004170 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004171
4172 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004173 {
4174 free(target);
4175 free(alias);
4176 ret_err(_("bad CNAME"));
4177 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004178
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004179 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004180 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004181 {
4182 free(target);
4183 free(alias);
4184 ret_err(_("duplicate CNAME"));
4185 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004186 new = opt_malloc(sizeof(struct cname));
4187 new->next = daemon->cnames;
4188 daemon->cnames = new;
4189 new->alias = alias;
4190 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004191 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004192
Petr Menšík56f06232018-03-06 23:13:32 +00004193 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004194 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004195
Simon Kelley9009d742008-11-14 20:04:27 +00004196 break;
4197 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004198
4199 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004200 {
4201 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004202 char *dom, *target = NULL;
4203
Simon Kelleyf2621c72007-04-29 19:47:21 +01004204 comma = split(arg);
4205
Simon Kelley1f15b812009-10-13 17:49:32 +01004206 if (!(dom = canonicalise_opt(arg)) ||
4207 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004208 {
4209 free(dom);
4210 free(target);
4211 ret_err(_("bad PTR record"));
4212 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004213 else
4214 {
4215 new = opt_malloc(sizeof(struct ptr_record));
4216 new->next = daemon->ptr;
4217 daemon->ptr = new;
4218 new->name = dom;
4219 new->ptr = target;
4220 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004221 break;
4222 }
4223
Simon Kelley1a6bca82008-07-11 11:11:42 +01004224 case LOPT_NAPTR: /* --naptr-record */
4225 {
4226 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4227 int k = 0;
4228 struct naptr *new;
4229 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004230 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004231
4232 if ((a[0] = arg))
4233 for (k = 1; k < 7; k++)
4234 if (!(a[k] = split(a[k-1])))
4235 break;
4236
4237
4238 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004239 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004240 !atoi_check16(a[1], &order) ||
4241 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004242 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004243 {
4244 free(name);
4245 free(replace);
4246 ret_err(_("bad NAPTR record"));
4247 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004248 else
4249 {
4250 new = opt_malloc(sizeof(struct naptr));
4251 new->next = daemon->naptr;
4252 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004253 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004254 new->flags = opt_string_alloc(a[3]);
4255 new->services = opt_string_alloc(a[4]);
4256 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004257 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004258 new->order = order;
4259 new->pref = pref;
4260 }
4261 break;
4262 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004263
4264 case LOPT_RR: /* dns-rr */
4265 {
4266 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004267 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004268 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004269 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004270
4271 comma = split(arg);
4272 data = split(comma);
4273
4274 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004275 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004276
Petr Menšík59e47032018-11-02 22:39:39 +00004277 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004278 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004279 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004280 {
4281 free(new->name);
4282 ret_err_free(_("bad RR record"), new);
4283 }
4284
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004285 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004286 new->class = class;
4287 new->next = daemon->rr;
4288 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004289
4290 if (data)
4291 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004292 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004293 new->len = len;
4294 memcpy(new->txt, data, len);
4295 }
4296
4297 break;
4298 }
4299
Simon Kelley974a6d02018-08-23 23:01:16 +01004300 case LOPT_CAA: /* --caa-record */
4301 {
4302 struct txt_record *new;
4303 char *tag, *value;
4304 int flags;
4305
4306 comma = split(arg);
4307 tag = split(comma);
4308 value = split(tag);
4309
4310 new = opt_malloc(sizeof(struct txt_record));
4311 new->next = daemon->rr;
4312 daemon->rr = new;
4313
4314 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4315 ret_err(_("bad CAA record"));
4316
4317 unhide_metas(tag);
4318 unhide_metas(value);
4319
4320 new->len = strlen(tag) + strlen(value) + 2;
4321 new->txt = opt_malloc(new->len);
4322 new->txt[0] = flags;
4323 new->txt[1] = strlen(tag);
4324 memcpy(&new->txt[2], tag, strlen(tag));
4325 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4326 new->class = T_CAA;
4327
4328 break;
4329 }
4330
Simon Kelleyf2621c72007-04-29 19:47:21 +01004331 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004332 {
4333 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004334 unsigned char *p, *cnt;
4335 size_t len;
4336
4337 comma = split(arg);
4338
Simon Kelley824af852008-02-12 20:43:05 +00004339 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004340 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004341 new->stat = 0;
4342
Simon Kelley1f15b812009-10-13 17:49:32 +01004343 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004344 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004345
Petr Menšík59e47032018-11-02 22:39:39 +00004346 new->next = daemon->txt;
4347 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004348 len = comma ? strlen(comma) : 0;
4349 len += (len/255) + 1; /* room for extra counts */
4350 new->txt = p = opt_malloc(len);
4351
4352 cnt = p++;
4353 *cnt = 0;
4354
4355 while (comma && *comma)
4356 {
4357 unsigned char c = (unsigned char)*comma++;
4358
4359 if (c == ',' || *cnt == 255)
4360 {
4361 if (c != ',')
4362 comma--;
4363 cnt = p++;
4364 *cnt = 0;
4365 }
4366 else
4367 {
4368 *p++ = unhide_meta(c);
4369 (*cnt)++;
4370 }
4371 }
4372
4373 new->len = p - new->txt;
4374
Simon Kelley849a8352006-06-09 21:02:31 +01004375 break;
4376 }
4377
Simon Kelleyf2621c72007-04-29 19:47:21 +01004378 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004379 {
4380 int port = 1, priority = 0, weight = 0;
4381 char *name, *target = NULL;
4382 struct mx_srv_record *new;
4383
Simon Kelleyf2621c72007-04-29 19:47:21 +01004384 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004385
Simon Kelley1f15b812009-10-13 17:49:32 +01004386 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004387 ret_err(_("bad SRV record"));
4388
Simon Kelley849a8352006-06-09 21:02:31 +01004389 if (comma)
4390 {
4391 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004392 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004393 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004394 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004395
Simon Kelley849a8352006-06-09 21:02:31 +01004396 if (comma)
4397 {
4398 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004399 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004400 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004401 {
4402 free(name);
4403 ret_err_free(_("invalid port number"), target);
4404 }
Simon Kelley824af852008-02-12 20:43:05 +00004405
Simon Kelley849a8352006-06-09 21:02:31 +01004406 if (comma)
4407 {
4408 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004409 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004410 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004411 {
4412 free(name);
4413 ret_err_free(_("invalid priority"), target);
4414 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004415 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004416 {
4417 free(name);
4418 ret_err_free(_("invalid weight"), target);
4419 }
Simon Kelley849a8352006-06-09 21:02:31 +01004420 }
4421 }
4422 }
4423
Simon Kelley824af852008-02-12 20:43:05 +00004424 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004425 new->next = daemon->mxnames;
4426 daemon->mxnames = new;
4427 new->issrv = 1;
4428 new->name = name;
4429 new->target = target;
4430 new->srvport = port;
4431 new->priority = priority;
4432 new->weight = weight;
4433 break;
4434 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004435
Simon Kelleye759d422012-03-16 13:18:57 +00004436 case LOPT_HOST_REC: /* --host-record */
4437 {
Petr Menšík59e47032018-11-02 22:39:39 +00004438 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004439
Simon Kelleye759d422012-03-16 13:18:57 +00004440 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004441 ret_err(_("Bad host-record"));
4442
Petr Menšík59e47032018-11-02 22:39:39 +00004443 new = opt_malloc(sizeof(struct host_record));
4444 memset(new, 0, sizeof(struct host_record));
4445 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004446 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004447
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004448 while (arg)
4449 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004450 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004451 char *dig;
4452
4453 for (dig = arg; *dig != 0; dig++)
4454 if (*dig < '0' || *dig > '9')
4455 break;
4456 if (*dig == 0)
4457 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004458 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004459 {
4460 new->addr = addr.addr4;
4461 new->flags |= HR_4;
4462 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004463 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004464 {
4465 new->addr6 = addr.addr6;
4466 new->flags |= HR_6;
4467 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004468 else
4469 {
4470 int nomem;
4471 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004472 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004473 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004474 {
4475 struct name_list *tmp = new->names, *next;
4476 for (tmp = new->names; tmp; tmp = next)
4477 {
4478 next = tmp->next;
4479 free(tmp);
4480 }
4481 ret_err_free(_("Bad name in host-record"), new);
4482 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004483
Petr Menšík59e47032018-11-02 22:39:39 +00004484 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004485 nl->name = canon;
4486 /* keep order, so that PTR record goes to first name */
4487 nl->next = NULL;
4488 if (!new->names)
4489 new->names = nl;
4490 else
4491 {
4492 struct name_list *tmp;
4493 for (tmp = new->names; tmp->next; tmp = tmp->next);
4494 tmp->next = nl;
4495 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004496 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004497
4498 arg = comma;
4499 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004500 }
Simon Kelleye759d422012-03-16 13:18:57 +00004501
4502 /* Keep list order */
4503 if (!daemon->host_records_tail)
4504 daemon->host_records = new;
4505 else
4506 daemon->host_records_tail->next = new;
4507 new->next = NULL;
4508 daemon->host_records_tail = new;
4509 break;
4510 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004511
4512#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004513 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004514 daemon->timestamp_file = opt_string_alloc(arg);
4515 break;
4516
Simon Kelleyf3e57872018-07-20 21:10:48 +01004517 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004518 if (arg)
4519 {
4520 if (strcmp(arg, "no") == 0)
4521 set_option_bool(OPT_DNSSEC_IGN_NS);
4522 else
4523 ret_err(_("bad value for dnssec-check-unsigned"));
4524 }
4525 break;
4526
Simon Kelleyf3e57872018-07-20 21:10:48 +01004527 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004528 {
Simon Kelleyee415862014-02-11 11:07:22 +00004529 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4530 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4531 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004532
4533 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004534 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004535
Simon Kelleycbf13a22014-01-25 17:59:14 +00004536 if ((comma = split(arg)) && (algo = split(comma)))
4537 {
4538 int class = 0;
4539 if (strcmp(comma, "IN") == 0)
4540 class = C_IN;
4541 else if (strcmp(comma, "CH") == 0)
4542 class = C_CHAOS;
4543 else if (strcmp(comma, "HS") == 0)
4544 class = C_HESIOD;
4545
4546 if (class != 0)
4547 {
4548 new->class = class;
4549 comma = algo;
4550 algo = split(comma);
4551 }
4552 }
4553
Simon Kelleyee415862014-02-11 11:07:22 +00004554 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4555 !atoi_check16(comma, &new->keytag) ||
4556 !atoi_check8(algo, &new->algo) ||
4557 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004558 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004559 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004560
Simon Kelley0fc2f312014-01-08 10:26:58 +00004561 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004562 len = (2*strlen(keyhex))+1;
4563 new->digest = opt_malloc(len);
4564 unhide_metas(keyhex);
4565 /* 4034: "Whitespace is allowed within digits" */
4566 for (cp = keyhex; *cp; )
4567 if (isspace(*cp))
4568 for (cp1 = cp; *cp1; cp1++)
4569 *cp1 = *(cp1+1);
4570 else
4571 cp++;
4572 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004573 {
4574 free(new->name);
4575 ret_err_free(_("bad HEX in trust anchor"), new);
4576 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004577
Simon Kelleyee415862014-02-11 11:07:22 +00004578 new->next = daemon->ds;
4579 daemon->ds = new;
4580
Simon Kelley0fc2f312014-01-08 10:26:58 +00004581 break;
4582 }
4583#endif
4584
Simon Kelley7622fc02009-06-04 20:32:05 +01004585 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004586 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004587
Simon Kelley849a8352006-06-09 21:02:31 +01004588 }
Simon Kelley824af852008-02-12 20:43:05 +00004589
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004590 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004591}
4592
Simon Kelley28866e92011-02-14 20:19:14 +00004593static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004594{
Simon Kelley824af852008-02-12 20:43:05 +00004595 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004596 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004597
4598 while (fgets(buff, MAXDNAME, f))
4599 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004600 int white, i;
4601 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004602 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004603 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004604
Simon Kelley824af852008-02-12 20:43:05 +00004605 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004606 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004607 {
4608 if (setjmp(mem_jmp))
4609 continue;
4610 mem_recover = 1;
4611 }
4612
Simon Kelley13dee6f2017-02-28 16:51:58 +00004613 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004614 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004615 errmess = NULL;
4616
Simon Kelley849a8352006-06-09 21:02:31 +01004617 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4618 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004619 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004620 {
4621 if (*p == '"')
4622 {
4623 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004624
Simon Kelley849a8352006-06-09 21:02:31 +01004625 for(; *p && *p != '"'; p++)
4626 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004627 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004628 {
4629 if (p[1] == 't')
4630 p[1] = '\t';
4631 else if (p[1] == 'n')
4632 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004633 else if (p[1] == 'b')
4634 p[1] = '\b';
4635 else if (p[1] == 'r')
4636 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004637 else if (p[1] == 'e') /* escape */
4638 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004639 memmove(p, p+1, strlen(p+1)+1);
4640 }
4641 *p = hide_meta(*p);
4642 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004643
4644 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004645 {
4646 errmess = _("missing \"");
4647 goto oops;
4648 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004649
4650 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004651 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004652
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004653 if (isspace(*p))
4654 {
4655 *p = ' ';
4656 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004657 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004658 else
4659 {
4660 if (white && *p == '#')
4661 {
4662 *p = 0;
4663 break;
4664 }
4665 white = 0;
4666 }
Simon Kelley849a8352006-06-09 21:02:31 +01004667 }
4668
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004669
4670 /* strip leading spaces */
4671 for (start = buff; *start && *start == ' '; start++);
4672
4673 /* strip trailing spaces */
4674 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4675
4676 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004677 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004678 else
4679 start[len] = 0;
4680
Simon Kelley611ebc52012-07-16 16:23:46 +01004681 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004682 arg = start;
4683 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004684 {
4685 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004686 for (arg = p+1; *arg == ' '; arg++);
4687 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004688 *p = 0;
4689 }
4690 else
4691 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004692
Simon Kelley611ebc52012-07-16 16:23:46 +01004693 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004694 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004695 for (option = 0, i = 0; opts[i].name; i++)
4696 if (strcmp(opts[i].name, start) == 0)
4697 {
4698 option = opts[i].val;
4699 break;
4700 }
4701
4702 if (!option)
4703 errmess = _("bad option");
4704 else if (opts[i].has_arg == 0 && arg)
4705 errmess = _("extraneous parameter");
4706 else if (opts[i].has_arg == 1 && !arg)
4707 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004708 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4709 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004710 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004711
4712 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004713 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004714 strcpy(daemon->namebuff, errmess);
4715
Simon Kelley9bafdc62018-08-21 22:53:38 +01004716 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004717 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004718 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004719 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004720 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004721 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004722 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004723 }
Simon Kelley849a8352006-06-09 21:02:31 +01004724 }
4725
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004726 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004727 fclose(f);
4728}
4729
Simon Kelley4f7bb572018-03-08 18:47:08 +00004730#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004731int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004732{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004733 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4734
Simon Kelley70d18732015-01-31 19:59:29 +00004735 if (flags & AH_DHCP_HST)
4736 return one_file(file, LOPT_BANK);
4737 else if (flags & AH_DHCP_OPT)
4738 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004739
Simon Kelley70d18732015-01-31 19:59:29 +00004740 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004741}
4742#endif
4743
Simon Kelley395eb712012-07-06 22:07:05 +01004744static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004745{
4746 FILE *f;
4747 int nofile_ok = 0;
4748 static int read_stdin = 0;
4749 static struct fileread {
4750 dev_t dev;
4751 ino_t ino;
4752 struct fileread *next;
4753 } *filesread = NULL;
4754
4755 if (hard_opt == '7')
4756 {
4757 /* default conf-file reading */
4758 hard_opt = 0;
4759 nofile_ok = 1;
4760 }
4761
4762 if (hard_opt == 0 && strcmp(file, "-") == 0)
4763 {
4764 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004765 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004766 read_stdin = 1;
4767 file = "stdin";
4768 f = stdin;
4769 }
4770 else
4771 {
4772 /* ignore repeated files. */
4773 struct stat statbuf;
4774
4775 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4776 {
4777 struct fileread *r;
4778
4779 for (r = filesread; r; r = r->next)
4780 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004781 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004782
4783 r = safe_malloc(sizeof(struct fileread));
4784 r->next = filesread;
4785 filesread = r;
4786 r->dev = statbuf.st_dev;
4787 r->ino = statbuf.st_ino;
4788 }
4789
4790 if (!(f = fopen(file, "r")))
4791 {
4792 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004793 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004794 else
4795 {
4796 char *str = _("cannot read %s: %s");
4797 if (hard_opt != 0)
4798 {
4799 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004800 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004801 }
4802 else
4803 die(str, file, EC_FILE);
4804 }
4805 }
4806 }
4807
4808 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004809 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004810}
4811
4812/* expand any name which is a directory */
4813struct hostsfile *expand_filelist(struct hostsfile *list)
4814{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004815 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004816 struct hostsfile *ah;
4817
Simon Kelley19c51cf2014-03-18 22:38:30 +00004818 /* find largest used index */
4819 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004820 {
4821 if (i <= ah->index)
4822 i = ah->index + 1;
4823
4824 if (ah->flags & AH_DIR)
4825 ah->flags |= AH_INACTIVE;
4826 else
4827 ah->flags &= ~AH_INACTIVE;
4828 }
4829
4830 for (ah = list; ah; ah = ah->next)
4831 if (!(ah->flags & AH_INACTIVE))
4832 {
4833 struct stat buf;
4834 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4835 {
4836 DIR *dir_stream;
4837 struct dirent *ent;
4838
4839 /* don't read this as a file */
4840 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004841
Simon Kelley28866e92011-02-14 20:19:14 +00004842 if (!(dir_stream = opendir(ah->fname)))
4843 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4844 ah->fname, strerror(errno));
4845 else
4846 {
4847 while ((ent = readdir(dir_stream)))
4848 {
4849 size_t lendir = strlen(ah->fname);
4850 size_t lenfile = strlen(ent->d_name);
4851 struct hostsfile *ah1;
4852 char *path;
4853
4854 /* ignore emacs backups and dotfiles */
4855 if (lenfile == 0 ||
4856 ent->d_name[lenfile - 1] == '~' ||
4857 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4858 ent->d_name[0] == '.')
4859 continue;
4860
4861 /* see if we have an existing record.
4862 dir is ah->fname
4863 file is ent->d_name
4864 path to match is ah1->fname */
4865
4866 for (ah1 = list; ah1; ah1 = ah1->next)
4867 {
4868 if (lendir < strlen(ah1->fname) &&
4869 strstr(ah1->fname, ah->fname) == ah1->fname &&
4870 ah1->fname[lendir] == '/' &&
4871 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4872 {
4873 ah1->flags &= ~AH_INACTIVE;
4874 break;
4875 }
4876 }
4877
4878 /* make new record */
4879 if (!ah1)
4880 {
4881 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4882 continue;
4883
4884 if (!(path = whine_malloc(lendir + lenfile + 2)))
4885 {
4886 free(ah1);
4887 continue;
4888 }
4889
4890 strcpy(path, ah->fname);
4891 strcat(path, "/");
4892 strcat(path, ent->d_name);
4893 ah1->fname = path;
4894 ah1->index = i++;
4895 ah1->flags = AH_DIR;
4896 ah1->next = list;
4897 list = ah1;
4898 }
4899
4900 /* inactivate record if not regular file */
4901 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4902 ah1->flags |= AH_INACTIVE;
4903
4904 }
4905 closedir(dir_stream);
4906 }
4907 }
4908 }
4909
4910 return list;
4911}
4912
Simon Kelley7b1eae42014-02-20 13:43:28 +00004913void read_servers_file(void)
4914{
4915 FILE *f;
4916
4917 if (!(f = fopen(daemon->servers_file, "r")))
4918 {
4919 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4920 return;
4921 }
4922
4923 mark_servers(SERV_FROM_FILE);
4924 cleanup_servers();
4925
4926 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4927}
4928
Simon Kelley28866e92011-02-14 20:19:14 +00004929
Simon Kelley7622fc02009-06-04 20:32:05 +01004930#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004931static void clear_dynamic_conf(void)
4932{
4933 struct dhcp_config *configs, *cp, **up;
4934
4935 /* remove existing... */
4936 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4937 {
4938 cp = configs->next;
4939
4940 if (configs->flags & CONFIG_BANK)
4941 {
4942 struct hwaddr_config *mac, *tmp;
4943 struct dhcp_netid_list *list, *tmplist;
4944
4945 for (mac = configs->hwaddr; mac; mac = tmp)
4946 {
4947 tmp = mac->next;
4948 free(mac);
4949 }
4950
4951 if (configs->flags & CONFIG_CLID)
4952 free(configs->clid);
4953
4954 for (list = configs->netid; list; list = tmplist)
4955 {
4956 free(list->list);
4957 tmplist = list->next;
4958 free(list);
4959 }
4960
4961 if (configs->flags & CONFIG_NAME)
4962 free(configs->hostname);
4963
4964 *up = configs->next;
4965 free(configs);
4966 }
4967 else
4968 up = &configs->next;
4969 }
4970}
4971
4972static void clear_dynamic_opt(void)
4973{
4974 struct dhcp_opt *opts, *cp, **up;
4975 struct dhcp_netid *id, *next;
4976
4977 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4978 {
4979 cp = opts->next;
4980
4981 if (opts->flags & DHOPT_BANK)
4982 {
4983 if ((opts->flags & DHOPT_VENDOR))
4984 free(opts->u.vendor_class);
4985 free(opts->val);
4986 for (id = opts->netid; id; id = next)
4987 {
4988 next = id->next;
4989 free(id->net);
4990 free(id);
4991 }
4992 *up = opts->next;
4993 free(opts);
4994 }
4995 else
4996 up = &opts->next;
4997 }
4998}
4999
Simon Kelley824af852008-02-12 20:43:05 +00005000void reread_dhcp(void)
5001{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005002 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005003
Simon Kelley4f7bb572018-03-08 18:47:08 +00005004 /* Do these even if there is no daemon->dhcp_hosts_file or
5005 daemon->dhcp_opts_file since entries may have been created by the
5006 inotify dynamic file reading system. */
5007
5008 clear_dynamic_conf();
5009 clear_dynamic_opt();
5010
5011 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005012 {
Simon Kelley28866e92011-02-14 20:19:14 +00005013 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5014 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005015 if (!(hf->flags & AH_INACTIVE))
5016 {
5017 if (one_file(hf->fname, LOPT_BANK))
5018 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5019 }
Simon Kelley824af852008-02-12 20:43:05 +00005020 }
5021
5022 if (daemon->dhcp_opts_file)
5023 {
Simon Kelley28866e92011-02-14 20:19:14 +00005024 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5025 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5026 if (!(hf->flags & AH_INACTIVE))
5027 {
Simon Kelley395eb712012-07-06 22:07:05 +01005028 if (one_file(hf->fname, LOPT_OPTS))
5029 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005030 }
Simon Kelley824af852008-02-12 20:43:05 +00005031 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005032
5033# ifdef HAVE_INOTIFY
5034 /* Setup notify and read pre-existing files. */
5035 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5036# endif
Simon Kelley824af852008-02-12 20:43:05 +00005037}
Simon Kelley7622fc02009-06-04 20:32:05 +01005038#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005039
Simon Kelley5aabfc72007-08-29 11:24:47 +01005040void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005041{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005042 size_t argbuf_size = MAXDNAME;
5043 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005044 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005045 int option, testmode = 0;
5046 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005047
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005048 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005049
Simon Kelley824af852008-02-12 20:43:05 +00005050 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005051 memset(daemon, 0, sizeof(struct daemon));
5052 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005053
Simon Kelley3be34542004-09-11 19:12:13 +01005054 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005055 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005056 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005057 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005058 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5059 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005060 daemon->default_resolv.is_default = 1;
5061 daemon->default_resolv.name = RESOLVFILE;
5062 daemon->resolv_files = &daemon->default_resolv;
5063 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005064 daemon->runfile = RUNFILE;
5065 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005066 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005067 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005068 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005069 daemon->auth_ttl = AUTH_TTL;
5070 daemon->soa_refresh = SOA_REFRESH;
5071 daemon->soa_retry = SOA_RETRY;
5072 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00005073 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00005074 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01005075
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005076#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005077 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5078 add_txt("authors.bind", "Simon Kelley", 0);
5079 add_txt("copyright.bind", COPYRIGHT, 0);
5080 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5081 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5082 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5083 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5084 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5085#ifdef HAVE_AUTH
5086 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5087#endif
5088 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005089#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005090
Simon Kelley849a8352006-06-09 21:02:31 +01005091 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005092 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005093#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005094 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005095#else
Simon Kelley849a8352006-06-09 21:02:31 +01005096 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005097#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005098
5099 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005100 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005101 for (; optind < argc; optind++)
5102 {
5103 unsigned char *c = (unsigned char *)argv[optind];
5104 for (; *c != 0; c++)
5105 if (!isspace(*c))
5106 die(_("junk found in command line"), NULL, EC_BADCONF);
5107 }
Simon Kelley28866e92011-02-14 20:19:14 +00005108 break;
5109 }
5110
Simon Kelley849a8352006-06-09 21:02:31 +01005111 /* Copy optarg so that argv doesn't get changed */
5112 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005113 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005114 if (strlen(optarg) >= argbuf_size)
5115 {
5116 free(argbuf);
5117 argbuf_size = strlen(optarg) + 1;
5118 argbuf = opt_malloc(argbuf_size);
5119 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005120 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005121 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005122 }
5123 else
5124 arg = NULL;
5125
5126 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005127 if (option == LOPT_TEST)
5128 testmode = 1;
5129 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005130 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005131#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005132 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005133 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005134#ifdef HAVE_DHCP6
5135 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5136 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005137#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005138 else
5139#endif
5140 do_usage();
5141
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005142 exit(0);
5143 }
Simon Kelley849a8352006-06-09 21:02:31 +01005144 else if (option == 'v')
5145 {
5146 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005147 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005148 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5149 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005150 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005151 exit(0);
5152 }
Simon Kelley849a8352006-06-09 21:02:31 +01005153 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005154 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005155 if (!conffile)
5156 conffile = opt_string_alloc(arg);
5157 else
5158 {
5159 char *extra = opt_string_alloc(arg);
5160 one_file(extra, 0);
5161 free(extra);
5162 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005163 }
Simon Kelley849a8352006-06-09 21:02:31 +01005164 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005165 {
Simon Kelley26128d22004-11-14 16:43:54 +00005166#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005167 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005168#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005169 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005170#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005171 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005172 }
5173 }
Simon Kelley849a8352006-06-09 21:02:31 +01005174
Neil Jerram3bd4c472018-01-18 22:49:38 +00005175 free(argbuf);
5176
Simon Kelley849a8352006-06-09 21:02:31 +01005177 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005178 {
Petr Menšík59e47032018-11-02 22:39:39 +00005179 one_file(conffile, 0);
5180 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005181 }
Petr Menšík59e47032018-11-02 22:39:39 +00005182 else
5183 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005184
Simon Kelley1a6bca82008-07-11 11:11:42 +01005185 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005186 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005187 {
5188 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005189 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005190 if (!(tmp->flags & SERV_HAS_SOURCE))
5191 {
5192 if (tmp->source_addr.sa.sa_family == AF_INET)
5193 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005194 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5195 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005196 }
5197 }
5198
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005199 if (daemon->host_records)
5200 {
5201 struct host_record *hr;
5202
5203 for (hr = daemon->host_records; hr; hr = hr->next)
5204 if (hr->ttl == -1)
5205 hr->ttl = daemon->local_ttl;
5206 }
5207
5208 if (daemon->cnames)
5209 {
Simon Kelley903df072017-01-19 17:22:00 +00005210 struct cname *cn, *cn2, *cn3;
5211
5212#define NOLOOP 1
5213#define TESTLOOP 2
5214
Simon Kelley157d8cf2019-10-25 17:46:49 +01005215 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005216 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005217 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005218 {
5219 if (cn->ttl == -1)
5220 cn->ttl = daemon->local_ttl;
5221 cn->flag = 0;
5222 cn->targetp = NULL;
5223 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5224 if (hostname_isequal(cn->target, cn2->alias))
5225 {
5226 cn->targetp = cn2;
5227 break;
5228 }
5229 }
5230
5231 /* Find any CNAME loops.*/
5232 for (cn = daemon->cnames; cn; cn = cn->next)
5233 {
5234 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5235 {
5236 if (cn2->flag == NOLOOP)
5237 break;
5238
5239 if (cn2->flag == TESTLOOP)
5240 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5241
5242 cn2->flag = TESTLOOP;
5243 }
5244
5245 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5246 cn3->flag = NOLOOP;
5247 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005248 }
5249
Simon Kelley3be34542004-09-11 19:12:13 +01005250 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005251 {
5252 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005253 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005254 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005255 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005256 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005257 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005258 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005259
5260 /* create default, if not specified */
5261 if (daemon->authserver && !daemon->hostmaster)
5262 {
5263 strcpy(buff, "hostmaster.");
5264 strcat(buff, daemon->authserver);
5265 daemon->hostmaster = opt_string_alloc(buff);
5266 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005267
5268 if (!daemon->dhcp_pxe_vendors)
5269 {
5270 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5271 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5272 daemon->dhcp_pxe_vendors->next = NULL;
5273 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005274
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005275 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005276 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005277 {
Simon Kelley0a852542005-03-23 20:28:59 +00005278 struct mx_srv_record *mx;
5279
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005280 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005281 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005282
Simon Kelley0a852542005-03-23 20:28:59 +00005283 for (mx = daemon->mxnames; mx; mx = mx->next)
5284 if (!mx->issrv && hostname_isequal(mx->name, buff))
5285 break;
5286
Simon Kelley28866e92011-02-14 20:19:14 +00005287 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005288 {
Simon Kelley824af852008-02-12 20:43:05 +00005289 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005290 mx->next = daemon->mxnames;
5291 mx->issrv = 0;
5292 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005293 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005294 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005295 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005296
Simon Kelley3be34542004-09-11 19:12:13 +01005297 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005298 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005299
5300 for (mx = daemon->mxnames; mx; mx = mx->next)
5301 if (!mx->issrv && !mx->target)
5302 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005303 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005304
Simon Kelley28866e92011-02-14 20:19:14 +00005305 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005306 daemon->resolv_files &&
5307 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005308 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005309 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005310
Simon Kelley28866e92011-02-14 20:19:14 +00005311 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005312 {
5313 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005314 FILE *f;
5315
Simon Kelley28866e92011-02-14 20:19:14 +00005316 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005317 !daemon->resolv_files ||
5318 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005319 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005320
Simon Kelley3be34542004-09-11 19:12:13 +01005321 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005322 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005323
5324 while ((line = fgets(buff, MAXDNAME, f)))
5325 {
5326 char *token = strtok(line, " \t\n\r");
5327
5328 if (!token || strcmp(token, "search") != 0)
5329 continue;
5330
5331 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005332 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005333 break;
5334 }
Simon Kelley3be34542004-09-11 19:12:13 +01005335
Simon Kelleyde379512004-06-22 20:23:33 +01005336 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005337
Simon Kelley3be34542004-09-11 19:12:13 +01005338 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005339 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005340 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005341
5342 if (daemon->domain_suffix)
5343 {
5344 /* add domain for any srv record without one. */
5345 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005346
Simon Kelley3d8df262005-08-29 12:19:27 +01005347 for (srv = daemon->mxnames; srv; srv = srv->next)
5348 if (srv->issrv &&
5349 strchr(srv->name, '.') &&
5350 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5351 {
5352 strcpy(buff, srv->name);
5353 strcat(buff, ".");
5354 strcat(buff, daemon->domain_suffix);
5355 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005356 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005357 }
5358 }
Simon Kelley28866e92011-02-14 20:19:14 +00005359 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005360 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005361
Simon Kelleyc8a80482014-03-05 14:29:54 +00005362 /* If there's access-control config, then ignore --local-service, it's intended
5363 as a system default to keep otherwise unconfigured installations safe. */
5364 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5365 reset_option_bool(OPT_LOCAL_SERVICE);
5366
Simon Kelley7622fc02009-06-04 20:32:05 +01005367 if (testmode)
5368 {
5369 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5370 exit(0);
5371 }
Simon Kelley849a8352006-06-09 21:02:31 +01005372}