blob: 159d1d40331371cf5e1e5cc3904d794390570836 [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)
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000822 safe_strncpy(interface, source, IF_NAMESIZE);
823 source = interface_opt;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000824#else
825 return _("interface binding not supported");
826#endif
827 }
828 }
829
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100830 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100831 {
832 addr->in.sin_port = htons(serv_port);
833 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
834#ifdef HAVE_SOCKADDR_SA_LEN
835 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
836#endif
837 source_addr->in.sin_addr.s_addr = INADDR_ANY;
838 source_addr->in.sin_port = htons(daemon->query_port);
839
840 if (source)
841 {
842 if (flags)
843 *flags |= SERV_HAS_SOURCE;
844 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100845 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100846 {
847#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000848 if (interface_opt)
849 return _("interface can only be specified once");
850
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100851 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200852 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100853#else
854 return _("interface binding not supported");
855#endif
856 }
857 }
858 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100859 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
860 {
861 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
862 return _("bad interface name");
863
864 addr->in6.sin6_port = htons(serv_port);
865 addr->in6.sin6_scope_id = scope_index;
866 source_addr->in6.sin6_addr = in6addr_any;
867 source_addr->in6.sin6_port = htons(daemon->query_port);
868 source_addr->in6.sin6_scope_id = 0;
869 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
870 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
871#ifdef HAVE_SOCKADDR_SA_LEN
872 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
873#endif
874 if (source)
875 {
876 if (flags)
877 *flags |= SERV_HAS_SOURCE;
878 source_addr->in6.sin6_port = htons(source_port);
879 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
880 {
881#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000882 if (interface_opt)
883 return _("interface can only be specified once");
884
885 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200886 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100887#else
888 return _("interface binding not supported");
889#endif
890 }
891 }
892 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100893 else
894 return _("bad address");
895
896 return NULL;
897}
898
Simon Kelleyde73a492014-02-17 21:43:27 +0000899static struct server *add_rev4(struct in_addr addr, int msize)
900{
901 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000902 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000903 char *p;
904
905 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000906 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
907
908 switch (msize)
909 {
910 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100911 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000912 /* fall through */
913 case 24:
914 p += sprintf(p, "%d.", (a >> 8) & 0xff);
915 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000916 case 16:
917 p += sprintf(p, "%d.", (a >> 16) & 0xff);
918 /* fall through */
919 case 8:
920 p += sprintf(p, "%d.", (a >> 24) & 0xff);
921 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000922 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000923 free(serv->domain);
924 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000925 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000926 }
927
928 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000929
930 serv->flags = SERV_HAS_DOMAIN;
931 serv->next = daemon->servers;
932 daemon->servers = serv;
933
934 return serv;
935
936}
937
938static struct server *add_rev6(struct in6_addr *addr, int msize)
939{
940 struct server *serv = opt_malloc(sizeof(struct server));
941 char *p;
942 int i;
943
944 memset(serv, 0, sizeof(struct server));
945 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
946
947 for (i = msize-1; i >= 0; i -= 4)
948 {
949 int dig = ((unsigned char *)addr)[i>>3];
950 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
951 }
952 p += sprintf(p, "ip6.arpa");
953
954 serv->flags = SERV_HAS_DOMAIN;
955 serv->next = daemon->servers;
956 daemon->servers = serv;
957
958 return serv;
959}
960
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000961#ifdef HAVE_DHCP
962
963static int is_tag_prefix(char *arg)
964{
965 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
966 return 1;
967
968 return 0;
969}
970
971static char *set_prefix(char *arg)
972{
973 if (strstr(arg, "set:") == arg)
974 return arg+4;
975
976 return arg;
977}
978
Simon Kelley52ec7832020-02-07 21:05:54 +0000979static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000980{
981 struct dhcp_netid *tt;
982 tt = opt_malloc(sizeof (struct dhcp_netid));
983 tt->net = opt_string_alloc(net);
984 tt->next = next;
985 return tt;
986}
987
988static void dhcp_netid_free(struct dhcp_netid *nid)
989{
990 while (nid)
991 {
992 struct dhcp_netid *tmp = nid;
993 nid = nid->next;
994 free(tmp->net);
995 free(tmp);
996 }
997}
998
999/* Parse one or more tag:s before parameters.
1000 * Moves arg to the end of tags. */
1001static struct dhcp_netid * dhcp_tags(char **arg)
1002{
1003 struct dhcp_netid *id = NULL;
1004
1005 while (is_tag_prefix(*arg))
1006 {
1007 char *comma = split(*arg);
1008 id = dhcp_netid_create((*arg)+4, id);
1009 *arg = comma;
1010 };
1011 if (!*arg)
1012 {
1013 dhcp_netid_free(id);
1014 id = NULL;
1015 }
1016 return id;
1017}
1018
1019static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1020{
1021 while (netid)
1022 {
1023 struct dhcp_netid_list *tmplist = netid;
1024 netid = netid->next;
1025 dhcp_netid_free(tmplist->list);
1026 free(tmplist);
1027 }
1028}
1029
1030static void dhcp_config_free(struct dhcp_config *config)
1031{
1032 if (config)
1033 {
1034 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001035
Petr Menšík59e47032018-11-02 22:39:39 +00001036 while (hwaddr)
1037 {
1038 struct hwaddr_config *tmp = hwaddr;
1039 hwaddr = hwaddr->next;
1040 free(tmp);
1041 }
Simon Kelley137286e2020-02-06 22:09:30 +00001042
Petr Menšík59e47032018-11-02 22:39:39 +00001043 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001044 dhcp_netid_free(config->filter);
1045
Petr Menšík59e47032018-11-02 22:39:39 +00001046 if (config->flags & CONFIG_CLID)
1047 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001048
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001049#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001050 if (config->flags & CONFIG_ADDR6)
1051 {
1052 struct addrlist *addr, *tmp;
1053
1054 for (addr = config->addr6; addr; addr = tmp)
1055 {
1056 tmp = addr->next;
1057 free(addr);
1058 }
1059 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001060#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001061
Petr Menšík59e47032018-11-02 22:39:39 +00001062 free(config);
1063 }
1064}
1065
1066static void dhcp_context_free(struct dhcp_context *ctx)
1067{
1068 if (ctx)
1069 {
1070 dhcp_netid_free(ctx->filter);
1071 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001072#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001073 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001074#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001075 free(ctx);
1076 }
1077}
1078
1079static void dhcp_opt_free(struct dhcp_opt *opt)
1080{
1081 if (opt->flags & DHOPT_VENDOR)
1082 free(opt->u.vendor_class);
1083 dhcp_netid_free(opt->netid);
1084 free(opt->val);
1085 free(opt);
1086}
1087
1088
Simon Kelley832af0b2007-01-21 20:01:28 +00001089/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001090static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001091{
Simon Kelley824af852008-02-12 20:43:05 +00001092 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001093 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001094 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001095 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001096 u16 opt_len = 0;
1097 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001098 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001099
1100 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001101 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001102 new->netid = NULL;
1103 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001104 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001105
Simon Kelleyf2621c72007-04-29 19:47:21 +01001106 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001107 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001108 comma = split(arg);
1109
1110 for (cp = arg; *cp; cp++)
1111 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001112 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001113
1114 if (!*cp)
1115 {
1116 new->opt = atoi(arg);
1117 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001118 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001119 break;
1120 }
1121
1122 if (strstr(arg, "option:") == arg)
1123 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001124 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1125 {
1126 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1127 /* option:<optname> must follow tag and vendor string. */
1128 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1129 option_ok = 1;
1130 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001131 break;
1132 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001133#ifdef HAVE_DHCP6
1134 else if (strstr(arg, "option6:") == arg)
1135 {
1136 for (cp = arg+8; *cp; cp++)
1137 if (*cp < '0' || *cp > '9')
1138 break;
1139
1140 if (!*cp)
1141 {
1142 new->opt = atoi(arg+8);
1143 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001144 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001145 }
1146 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001147 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001148 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1149 {
1150 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1151 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1152 option_ok = 1;
1153 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001154 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001155 /* option6:<opt>|<optname> must follow tag and vendor string. */
1156 is6 = 1;
1157 break;
1158 }
1159#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001160 else if (strstr(arg, "vendor:") == arg)
1161 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001162 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1163 new->flags |= DHOPT_VENDOR;
1164 }
1165 else if (strstr(arg, "encap:") == arg)
1166 {
1167 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001168 new->flags |= DHOPT_ENCAPSULATE;
1169 }
Simon Kelley316e2732010-01-22 20:16:09 +00001170 else if (strstr(arg, "vi-encap:") == arg)
1171 {
1172 new->u.encap = atoi(arg+9);
1173 new->flags |= DHOPT_RFC3925;
1174 if (flags == DHOPT_MATCH)
1175 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001176 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001177 break;
1178 }
1179 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001180 else
1181 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001182 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001183 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1184 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001185 }
1186
1187 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001188 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001189
1190#ifdef HAVE_DHCP6
1191 if (is6)
1192 {
1193 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001194 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001195
1196 if (opt_len == 0 &&
1197 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001198 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001199 }
1200 else
1201#endif
1202 if (opt_len == 0 &&
1203 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001204 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001205
Simon Kelley316e2732010-01-22 20:16:09 +00001206 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001207 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001208 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001209
1210 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001211 {
1212 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001213 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001214 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001215 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001216 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001217 dots = 0;
1218 for (cp = comma; (c = *cp); cp++)
1219 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001220 {
1221 addrs++;
1222 is_dec = is_hex = 0;
1223 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001224 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001225 {
1226 digs++;
1227 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001228 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001229 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001230 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001231 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001232 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001233 if (cp == comma) /* leading / means a pathname */
1234 is_addr = 0;
1235 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001236 else if (c == '.')
1237 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001238 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001239 dots++;
1240 }
1241 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001242 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001243 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001244 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001245 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001246 {
1247 is_addr = 0;
1248 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001249 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001250 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001251 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001252 *cp = 0;
1253 }
1254 else
1255 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001256 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001257 (c >='a' && c <= 'f') ||
1258 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001259 {
1260 is_hex = 0;
1261 if (c != '[' && c != ']')
1262 is_addr6 = 0;
1263 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001264 }
Simon Kelley28866e92011-02-14 20:19:14 +00001265 else
1266 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001267
Simon Kelley28866e92011-02-14 20:19:14 +00001268 if (!found_dig)
1269 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001270
1271 if (!found_colon)
1272 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001273
1274#ifdef HAVE_DHCP6
1275 /* NTP server option takes hex, addresses or FQDN */
1276 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1277 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1278#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001279
Simon Kelleyf2621c72007-04-29 19:47:21 +01001280 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001281 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001282 {
1283 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001284
1285 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001286 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001287
1288 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001289 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001290 }
Simon Kelley28866e92011-02-14 20:19:14 +00001291 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001292 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1293 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001294
1295 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1296 {
1297 int val, fac = 1;
1298
1299 switch (comma[strlen(comma) - 1])
1300 {
Simon Kelley42243212012-07-20 15:19:18 +01001301 case 'w':
1302 case 'W':
1303 fac *= 7;
1304 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001305 case 'd':
1306 case 'D':
1307 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001308 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001309 case 'h':
1310 case 'H':
1311 fac *= 60;
1312 /* fall through */
1313 case 'm':
1314 case 'M':
1315 fac *= 60;
1316 /* fall through */
1317 case 's':
1318 case 'S':
1319 comma[strlen(comma) - 1] = 0;
1320 }
1321
1322 new->len = 4;
1323 new->val = opt_malloc(4);
1324 val = atoi(comma);
1325 *((int *)new->val) = htonl(val * fac);
1326 }
1327 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001328 {
1329 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001330 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001331 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1332 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001333 }
1334 else if (is_dec)
1335 {
1336 int i, val = atoi(comma);
1337 /* assume numeric arg is 1 byte except for
1338 options where it is known otherwise.
1339 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001340 if (opt_len != 0)
1341 new->len = opt_len;
1342 else if (val & 0xffff0000)
1343 new->len = 4;
1344 else if (val & 0xff00)
1345 new->len = 2;
1346 else
1347 new->len = 1;
1348
Simon Kelley832af0b2007-01-21 20:01:28 +00001349 if (lenchar == 'b')
1350 new->len = 1;
1351 else if (lenchar == 's')
1352 new->len = 2;
1353 else if (lenchar == 'i')
1354 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001355
Simon Kelley824af852008-02-12 20:43:05 +00001356 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001357 for (i=0; i<new->len; i++)
1358 new->val[i] = val>>((new->len - i - 1)*8);
1359 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001360 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001361 {
1362 struct in_addr in;
1363 unsigned char *op;
1364 char *slash;
1365 /* max length of address/subnet descriptor is five bytes,
1366 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001367 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001368 new->flags |= DHOPT_ADDR;
1369
Simon Kelley572b41e2011-02-18 18:11:18 +00001370 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1371 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001372 {
Simon Kelley6b010842007-02-12 20:32:07 +00001373 *(op++) = 1; /* RFC 3361 "enc byte" */
1374 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001375 }
1376 while (addrs--)
1377 {
1378 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001379 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001380 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001381 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001382 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001383 if (!slash)
1384 {
1385 memcpy(op, &in, INADDRSZ);
1386 op += INADDRSZ;
1387 }
1388 else
1389 {
1390 unsigned char *p = (unsigned char *)&in;
1391 int netsize = atoi(slash);
1392 *op++ = netsize;
1393 if (netsize > 0)
1394 *op++ = *p++;
1395 if (netsize > 8)
1396 *op++ = *p++;
1397 if (netsize > 16)
1398 *op++ = *p++;
1399 if (netsize > 24)
1400 *op++ = *p++;
1401 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1402 }
1403 }
1404 new->len = op - new->val;
1405 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001406 else if (is_addr6 && is6)
1407 {
1408 unsigned char *op;
1409 new->val = op = opt_malloc(16 * addrs);
1410 new->flags |= DHOPT_ADDR6;
1411 while (addrs--)
1412 {
1413 cp = comma;
1414 comma = split(cp);
1415
1416 /* check for [1234::7] */
1417 if (*cp == '[')
1418 cp++;
1419 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1420 cp[strlen(cp)-1] = 0;
1421
1422 if (inet_pton(AF_INET6, cp, op))
1423 {
1424 op += IN6ADDRSZ;
1425 continue;
1426 }
Petr Menšík59e47032018-11-02 22:39:39 +00001427
1428 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001429 }
1430 new->len = op - new->val;
1431 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001432 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001433 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001434 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001435 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001436 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001437 {
1438 /* dns search, RFC 3397, or SIP, RFC 3361 */
1439 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001440 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001441 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001442 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001443
1444 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001445 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001446
1447 while (arg && *arg)
1448 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001449 char *in, *dom = NULL;
1450 size_t domlen = 1;
1451 /* Allow "." as an empty domain */
1452 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001453 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001454 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001455 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001456
Simon Kelleyc52e1892010-06-07 22:01:39 +01001457 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001458 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001459
1460 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001461 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001462 {
1463 memcpy(newp, m, header_size + len);
1464 free(m);
1465 }
Simon Kelley824af852008-02-12 20:43:05 +00001466 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001467 p = m + header_size;
1468 q = p + len;
1469
1470 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001471 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001472 {
1473 unsigned char *cp = q++;
1474 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001475 for (j = 0; *in && (*in != '.'); in++, j++)
1476 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001477 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001478 if (*in)
1479 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001480 }
1481 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001482 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001483
Simon Kelley832af0b2007-01-21 20:01:28 +00001484 /* Now tail-compress using earlier names. */
1485 newlen = q - p;
1486 for (tail = p + len; *tail; tail += (*tail) + 1)
1487 for (r = p; r - p < (int)len; r += (*r) + 1)
1488 if (strcmp((char *)r, (char *)tail) == 0)
1489 {
1490 PUTSHORT((r - p) | 0xc000, tail);
1491 newlen = tail - p;
1492 goto end;
1493 }
1494 end:
1495 len = newlen;
1496
1497 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001498 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001499 }
1500
1501 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001502 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001503 m[0] = 0;
1504 new->len = (int) len + header_size;
1505 new->val = m;
1506 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001507#ifdef HAVE_DHCP6
1508 else if (comma && (opt_len & OT_CSTRING))
1509 {
1510 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001511 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001512 unsigned char *p, *newp;
1513
Simon Kelley40ef23b2012-03-13 21:59:28 +00001514 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001515 if (comma[i] == ',')
1516 commas++;
1517
1518 newp = opt_malloc(strlen(comma)+(2*commas));
1519 p = newp;
1520 arg = comma;
1521 comma = split(arg);
1522
1523 while (arg && *arg)
1524 {
1525 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001526 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001527 PUTSHORT(len, p);
1528 memcpy(p, arg, len);
1529 p += len;
1530
1531 arg = comma;
1532 comma = split(arg);
1533 }
1534
1535 new->val = newp;
1536 new->len = p - newp;
1537 }
1538 else if (comma && (opt_len & OT_RFC1035_NAME))
1539 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001540 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001541 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001542 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001543 arg = comma;
1544 comma = split(arg);
1545
1546 while (arg && *arg)
1547 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001548 char *dom = canonicalise_opt(arg);
1549 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001550 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001551
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001552 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001553
1554 if (p)
1555 {
1556 memcpy(newp, p, len);
1557 free(p);
1558 }
1559
1560 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001561 q = p + len;
1562 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001563 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001564 if (is6 && new->opt == OPTION6_NTP_SERVER)
1565 {
1566 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1567 PUTSHORT(end - q - 2, q);
1568 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001569 len = end - p;
1570 free(dom);
1571
Simon Kelley4cb1b322012-02-06 14:30:41 +00001572 arg = comma;
1573 comma = split(arg);
1574 }
1575
Simon Kelley18f0fb02012-03-31 21:18:55 +01001576 new->val = p;
1577 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001578 }
1579#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001580 else
1581 {
1582 new->len = strlen(comma);
1583 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001584 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001585 new->flags |= DHOPT_STRING;
1586 }
1587 }
1588 }
1589
Simon Kelley4cb1b322012-02-06 14:30:41 +00001590 if (!is6 &&
1591 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001592 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001593 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001594 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001595
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001596 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001597 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001598 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1599 !new->netid ||
1600 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001601 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001602
1603 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001604 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001605 new->next = daemon->dhcp_match6;
1606 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001607 }
1608 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001609 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001610 new->next = daemon->dhcp_match;
1611 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001612 }
Simon Kelley824af852008-02-12 20:43:05 +00001613 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001614 else if (is6)
1615 {
1616 new->next = daemon->dhcp_opts6;
1617 daemon->dhcp_opts6 = new;
1618 }
1619 else
1620 {
1621 new->next = daemon->dhcp_opts;
1622 daemon->dhcp_opts = new;
1623 }
1624
1625 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001626on_error:
1627 dhcp_opt_free(new);
1628 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001629}
1630
Simon Kelley7622fc02009-06-04 20:32:05 +01001631#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001632
Simon Kelley28866e92011-02-14 20:19:14 +00001633void set_option_bool(unsigned int opt)
1634{
Petr Menšík24b87602018-10-24 22:30:18 +01001635 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001636}
1637
Simon Kelley2b5bae92012-06-26 16:55:23 +01001638void reset_option_bool(unsigned int opt)
1639{
Petr Menšík24b87602018-10-24 22:30:18 +01001640 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001641}
1642
Petr Menšík59e47032018-11-02 22:39:39 +00001643static void server_list_free(struct server *list)
1644{
1645 while (list)
1646 {
1647 struct server *tmp = list;
1648 list = list->next;
1649 free(tmp);
1650 }
1651}
1652
Simon Kelley7b1eae42014-02-20 13:43:28 +00001653static 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 +01001654{
1655 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001656 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001657
Simon Kelley832af0b2007-01-21 20:01:28 +00001658 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001659 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001660
Simon Kelley1a6bca82008-07-11 11:11:42 +01001661 for (i=0; usage[i].opt != 0; i++)
1662 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001663 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001664 int rept = usage[i].rept;
1665
Simon Kelley28866e92011-02-14 20:19:14 +00001666 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001667 {
1668 /* command line */
1669 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001670 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001671 if (rept == ARG_ONE)
1672 usage[i].rept = ARG_USED_CL;
1673 }
1674 else
1675 {
1676 /* allow file to override command line */
1677 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001678 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001679 if (rept == ARG_USED_CL || rept == ARG_ONE)
1680 usage[i].rept = ARG_USED_FILE;
1681 }
1682
1683 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1684 {
Simon Kelley28866e92011-02-14 20:19:14 +00001685 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001686 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001687 }
1688
1689 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001690 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001691
Simon Kelley849a8352006-06-09 21:02:31 +01001692 switch (option)
1693 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001694 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001695 {
Simon Kelley824af852008-02-12 20:43:05 +00001696 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001697 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001698 {
Simon Kelley28866e92011-02-14 20:19:14 +00001699 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001700 free(file);
1701 }
Simon Kelley849a8352006-06-09 21:02:31 +01001702 break;
1703 }
1704
Simon Kelleyf2621c72007-04-29 19:47:21 +01001705 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001706 {
1707 DIR *dir_stream;
1708 struct dirent *ent;
1709 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001710 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001711 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001712 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001713 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001714
Simon Kelley1f15b812009-10-13 17:49:32 +01001715 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001716 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001717 break;
1718
Simon Kelley1f15b812009-10-13 17:49:32 +01001719 for (arg = comma; arg; arg = comma)
1720 {
1721 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001722 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001723 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001724 li = opt_malloc(sizeof(struct list));
1725 if (*arg == '*')
1726 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001727 /* "*" with no suffix is a no-op */
1728 if (arg[1] == 0)
1729 free(li);
1730 else
1731 {
1732 li->next = match_suffix;
1733 match_suffix = li;
1734 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001735 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001736 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001737 }
1738 else
1739 {
1740 li->next = ignore_suffix;
1741 ignore_suffix = li;
1742 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001743 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001744 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001745 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001746 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001747
Simon Kelley849a8352006-06-09 21:02:31 +01001748 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001749 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001750
Simon Kelley849a8352006-06-09 21:02:31 +01001751 while ((ent = readdir(dir_stream)))
1752 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001753 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001754 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001755
1756 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001757 if (len == 0 ||
1758 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001759 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1760 ent->d_name[0] == '.')
1761 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001762
Simon Kelley3e1551a2014-09-09 21:46:07 +01001763 if (match_suffix)
1764 {
1765 for (li = match_suffix; li; li = li->next)
1766 {
1767 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001768 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001769 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001770 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001771 break;
1772 }
1773 if (!li)
1774 continue;
1775 }
1776
Simon Kelley1f15b812009-10-13 17:49:32 +01001777 for (li = ignore_suffix; li; li = li->next)
1778 {
1779 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001780 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001781 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001782 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001783 break;
1784 }
1785 if (li)
1786 continue;
1787
Simon Kelley824af852008-02-12 20:43:05 +00001788 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001789 strcpy(path, directory);
1790 strcat(path, "/");
1791 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001792
Simon Kelley39595cf2013-02-04 21:40:07 +00001793 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001794 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001795 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001796
Simon Kelley39595cf2013-02-04 21:40:07 +00001797 /* only reg files allowed. */
1798 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001799 {
1800 /* sort files into order. */
1801 struct list **up, *new = opt_malloc(sizeof(struct list));
1802 new->name = path;
1803
1804 for (up = &files, li = files; li; up = &li->next, li = li->next)
1805 if (strcmp(li->name, path) >=0)
1806 break;
1807
1808 new->next = li;
1809 *up = new;
1810 }
1811
Simon Kelley849a8352006-06-09 21:02:31 +01001812 }
Simon Kelleyab538832020-01-10 20:44:48 +00001813
1814 for (li = files; li; li = li->next)
1815 one_file(li->name, 0);
1816
Simon Kelley849a8352006-06-09 21:02:31 +01001817 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001818 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001819 for(; ignore_suffix; ignore_suffix = li)
1820 {
1821 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001822 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001823 free(ignore_suffix);
1824 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001825 for(; match_suffix; match_suffix = li)
1826 {
1827 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001828 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001829 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001830 }
Simon Kelleyab538832020-01-10 20:44:48 +00001831 for(; files; files = li)
1832 {
1833 li = files->next;
1834 free(files->name);
1835 free(files);
1836 }
Simon Kelley849a8352006-06-09 21:02:31 +01001837 break;
1838 }
1839
Simon Kelleyed4c0762013-10-08 20:46:34 +01001840 case LOPT_ADD_SBNET: /* --add-subnet */
1841 set_option_bool(OPT_CLIENT_SUBNET);
1842 if (arg)
1843 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001844 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001845 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001846
1847 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1848 if ((end = split_chr(arg, '/')))
1849 {
1850 /* has subnet+len */
1851 err = parse_mysockaddr(arg, &new->addr);
1852 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001853 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001854 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001855 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001856 new->addr_used = 1;
1857 }
1858 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001859 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001860
1861 daemon->add_subnet4 = new;
1862
Ed Bardsleya7369be2015-08-05 21:17:18 +01001863 if (comma)
1864 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001865 new = opt_malloc(sizeof(struct mysubnet));
1866 if ((end = split_chr(comma, '/')))
1867 {
1868 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001869 err = parse_mysockaddr(comma, &new->addr);
1870 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001871 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001872 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001873 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001874 new->addr_used = 1;
1875 }
1876 else
1877 {
1878 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001879 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001880 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001881
1882 daemon->add_subnet6 = new;
1883 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001884 }
1885 break;
1886
Simon Kelleyad094272012-08-10 17:10:54 +01001887 case '1': /* --enable-dbus */
1888 set_option_bool(OPT_DBUS);
1889 if (arg)
1890 daemon->dbus_name = opt_string_alloc(arg);
1891 else
1892 daemon->dbus_name = DNSMASQ_SERVICE;
1893 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001894
1895 case LOPT_UBUS: /* --enable-ubus */
1896 set_option_bool(OPT_UBUS);
1897 if (arg)
1898 daemon->ubus_name = opt_string_alloc(arg);
1899 else
1900 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1901 break;
1902
Simon Kelleyf2621c72007-04-29 19:47:21 +01001903 case '8': /* --log-facility */
1904 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001905 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001906 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001907 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001908 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001909#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001910 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001911#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001912 for (i = 0; facilitynames[i].c_name; i++)
1913 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1914 break;
1915
1916 if (facilitynames[i].c_name)
1917 daemon->log_fac = facilitynames[i].c_val;
1918 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001919 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001920#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001921 }
1922 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001923
Simon Kelleyf2621c72007-04-29 19:47:21 +01001924 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001925 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001926 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001927
Simon Kelleyf2621c72007-04-29 19:47:21 +01001928 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001929 {
Simon Kelley824af852008-02-12 20:43:05 +00001930 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001931 struct resolvc *new, *list = daemon->resolv_files;
1932
1933 if (list && list->is_default)
1934 {
1935 /* replace default resolv file - possibly with nothing */
1936 if (name)
1937 {
1938 list->is_default = 0;
1939 list->name = name;
1940 }
1941 else
1942 list = NULL;
1943 }
1944 else if (name)
1945 {
Simon Kelley824af852008-02-12 20:43:05 +00001946 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001947 new->next = list;
1948 new->name = name;
1949 new->is_default = 0;
1950 new->mtime = 0;
1951 new->logged = 0;
1952 list = new;
1953 }
1954 daemon->resolv_files = list;
1955 break;
1956 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001957
1958 case LOPT_SERVERS_FILE:
1959 daemon->servers_file = opt_string_alloc(arg);
1960 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001961
Simon Kelleyf2621c72007-04-29 19:47:21 +01001962 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001963 {
1964 int pref = 1;
1965 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001966 char *name, *target = NULL;
1967
Simon Kelleyf2621c72007-04-29 19:47:21 +01001968 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001969 {
1970 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001971 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001972 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001973 }
1974
Simon Kelley1f15b812009-10-13 17:49:32 +01001975 if (!(name = canonicalise_opt(arg)) ||
1976 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001977 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001978
Simon Kelley824af852008-02-12 20:43:05 +00001979 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001980 new->next = daemon->mxnames;
1981 daemon->mxnames = new;
1982 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001983 new->name = name;
1984 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001985 new->weight = pref;
1986 break;
1987 }
1988
Simon Kelleyf2621c72007-04-29 19:47:21 +01001989 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001990 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001991 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001992 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001993
Simon Kelley6b173352018-05-08 18:32:14 +01001994 case LOPT_DUMPFILE: /* --dumpfile */
1995 daemon->dump_file = opt_string_alloc(arg);
1996 break;
1997
1998 case LOPT_DUMPMASK: /* --dumpmask */
1999 daemon->dump_mask = strtol(arg, NULL, 0);
2000 break;
2001
Simon Kelley7622fc02009-06-04 20:32:05 +01002002#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002003 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002004 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002005 break;
2006
Simon Kelleyc72daea2012-01-05 21:33:27 +00002007 /* Sorry about the gross pre-processor abuse */
2008 case '6': /* --dhcp-script */
2009 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002010# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002011 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002012# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002013 if (option == LOPT_LUASCRIPT)
2014# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002015 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002016# else
2017 daemon->luascript = opt_string_alloc(arg);
2018# endif
2019 else
2020 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002021# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002022 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002023#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002024
Simon Kelley70d18732015-01-31 19:59:29 +00002025 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2026 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2027 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2028 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2029 case LOPT_HOST_INOTIFY: /* --hostsdir */
2030 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002031 {
Simon Kelley824af852008-02-12 20:43:05 +00002032 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002033 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002034 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002035 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002036 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002037 if (option == 'H')
2038 {
2039 new->next = daemon->addn_hosts;
2040 daemon->addn_hosts = new;
2041 }
2042 else if (option == LOPT_DHCP_HOST)
2043 {
2044 new->next = daemon->dhcp_hosts_file;
2045 daemon->dhcp_hosts_file = new;
2046 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002047 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002048 {
2049 new->next = daemon->dhcp_opts_file;
2050 daemon->dhcp_opts_file = new;
2051 }
Simon Kelley70d18732015-01-31 19:59:29 +00002052 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002053 {
Simon Kelley70d18732015-01-31 19:59:29 +00002054 new->next = daemon->dynamic_dirs;
2055 daemon->dynamic_dirs = new;
2056 if (option == LOPT_DHCP_INOTIFY)
2057 new->flags |= AH_DHCP_HST;
2058 else if (option == LOPT_DHOPT_INOTIFY)
2059 new->flags |= AH_DHCP_OPT;
2060 else if (option == LOPT_HOST_INOTIFY)
2061 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002062 }
2063
Simon Kelley849a8352006-06-09 21:02:31 +01002064 break;
2065 }
2066
Simon Kelley4f7b3042012-11-28 21:27:02 +00002067 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002068 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002069
Simon Kelley4f7b3042012-11-28 21:27:02 +00002070 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002071
2072 while ((arg = comma))
2073 {
2074 struct iname *new = opt_malloc(sizeof(struct iname));
2075 comma = split(arg);
2076 new->name = NULL;
2077 unhide_metas(arg);
2078 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2079 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002080 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2081 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002082 else
2083 {
2084 char *fam = split_chr(arg, '/');
2085 new->name = opt_string_alloc(arg);
2086 new->addr.sa.sa_family = 0;
2087 if (fam)
2088 {
2089 if (strcmp(fam, "4") == 0)
2090 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002091 else if (strcmp(fam, "6") == 0)
2092 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002093 else
Petr Menšík59e47032018-11-02 22:39:39 +00002094 {
2095 free(new->name);
2096 ret_err_free(gen_err, new);
2097 }
Simon Kelley08933472018-10-05 16:34:35 +01002098 }
2099 }
2100 new->next = daemon->authinterface;
2101 daemon->authinterface = new;
2102 };
Simon Kelley429798f2012-12-10 20:45:53 +00002103
Simon Kelley4f7b3042012-11-28 21:27:02 +00002104 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002105
2106 case LOPT_AUTHSFS: /* --auth-sec-servers */
2107 {
2108 struct name_list *new;
2109
2110 do {
2111 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002112 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002113 new->name = opt_string_alloc(arg);
2114 new->next = daemon->secondary_forward_server;
2115 daemon->secondary_forward_server = new;
2116 arg = comma;
2117 } while (arg);
2118 break;
2119 }
2120
Simon Kelley4f7b3042012-11-28 21:27:02 +00002121 case LOPT_AUTHZONE: /* --auth-zone */
2122 {
2123 struct auth_zone *new;
2124
2125 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002126
Simon Kelley429798f2012-12-10 20:45:53 +00002127 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002128 new->domain = opt_string_alloc(arg);
2129 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002130 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002131 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002132 new->next = daemon->auth_zones;
2133 daemon->auth_zones = new;
2134
2135 while ((arg = comma))
2136 {
2137 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002138 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002139 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002140 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002141 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002142
2143 comma = split(arg);
2144 prefix = split_chr(arg, '/');
2145
2146 if (prefix && !atoi_check(prefix, &prefixlen))
2147 ret_err(gen_err);
2148
Mathias Kresin094bfae2016-07-24 14:15:22 +01002149 if (strstr(arg, "exclude:") == arg)
2150 {
2151 is_exclude = 1;
2152 arg = arg+8;
2153 }
2154
Simon Kelleycc921df2019-01-02 22:48:59 +00002155 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002156 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002157 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002158 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002159 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002160 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002161 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002162 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002163 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002164 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002165 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002166 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002167 else
2168 {
2169 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2170 name->name = opt_string_alloc(arg);
2171 name->flags = AUTH4 | AUTH6;
2172 name->next = new->interface_names;
2173 new->interface_names = name;
2174 if (prefix)
2175 {
2176 if (prefixlen == 4)
2177 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002178 else if (prefixlen == 6)
2179 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002180 else
2181 ret_err(gen_err);
2182 }
2183 }
2184
2185 if (subnet)
2186 {
2187 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002188
2189 if (is_exclude)
2190 {
2191 subnet->next = new->exclude;
2192 new->exclude = subnet;
2193 }
2194 else
2195 {
2196 subnet->next = new->subnet;
2197 new->subnet = subnet;
2198 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002199 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002200 }
2201 break;
2202 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002203
Simon Kelley4f7b3042012-11-28 21:27:02 +00002204 case LOPT_AUTHSOA: /* --auth-soa */
2205 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002206 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002207 if (comma)
2208 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002209 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002210 arg = comma;
2211 comma = split(arg);
2212 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002213 for (cp = daemon->hostmaster; *cp; cp++)
2214 if (*cp == '@')
2215 *cp = '.';
2216
Simon Kelley4f7b3042012-11-28 21:27:02 +00002217 if (comma)
2218 {
2219 arg = comma;
2220 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002221 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002222 if (comma)
2223 {
2224 arg = comma;
2225 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002226 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002227 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002228 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002229 }
2230 }
2231 }
2232
2233 break;
2234
Simon Kelley2bb73af2013-04-24 17:38:19 +01002235 case 's': /* --domain */
2236 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002237 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002238 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002239 else
Simon Kelley9009d742008-11-14 20:04:27 +00002240 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002241 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002242 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002243 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002244 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002245 else
2246 {
Simon Kelley9009d742008-11-14 20:04:27 +00002247 if (comma)
2248 {
Simon Kelley429798f2012-12-10 20:45:53 +00002249 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002250 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002251
Simon Kelley48fd1c42013-04-25 09:49:38 +01002252 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002253 new->indexed = 0;
2254
Simon Kelley9009d742008-11-14 20:04:27 +00002255 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002256 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002257 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002258 int msize;
2259
Simon Kelley28866e92011-02-14 20:19:14 +00002260 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002261 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002262 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002263 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002264 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002265 int mask = (1 << (32 - msize)) - 1;
2266 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002267 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2268 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002269 if (arg)
2270 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002271 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002272 {
2273 if (!(new->prefix = canonicalise_opt(arg)) ||
2274 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002275 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002276 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002277 else if (strcmp(arg, "local") != 0 ||
2278 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002279 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002280 else
2281 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002282 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002283 local=/xxx.yyy.zzz.in-addr.arpa/ */
2284 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002285 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002286 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002287
Simon Kelleyde73a492014-02-17 21:43:27 +00002288 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002289
2290 /* local=/<domain>/ */
2291 serv = opt_malloc(sizeof(struct server));
2292 memset(serv, 0, sizeof(struct server));
2293 serv->domain = d;
2294 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2295 serv->next = daemon->servers;
2296 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002297 }
2298 }
Simon Kelley9009d742008-11-14 20:04:27 +00002299 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002300 else if (inet_pton(AF_INET6, comma, &new->start6))
2301 {
2302 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2303 u64 addrpart = addr6part(&new->start6);
2304 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002305
Simon Kelleyd74942a2012-02-07 20:51:56 +00002306 /* prefix==64 overflows the mask calculation above */
2307 if (msize == 64)
2308 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002309
Simon Kelleyd74942a2012-02-07 20:51:56 +00002310 new->end6 = new->start6;
2311 setaddr6part(&new->start6, addrpart & ~mask);
2312 setaddr6part(&new->end6, addrpart | mask);
2313
2314 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002315 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002316 else if (arg)
2317 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002318 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002319 {
2320 if (!(new->prefix = canonicalise_opt(arg)) ||
2321 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002322 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002323 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002324 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002325 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002326 else
2327 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002328 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002329 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002330 struct server *serv = add_rev6(&new->start6, msize);
2331 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002332
2333 /* local=/<domain>/ */
2334 serv = opt_malloc(sizeof(struct server));
2335 memset(serv, 0, sizeof(struct server));
2336 serv->domain = d;
2337 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2338 serv->next = daemon->servers;
2339 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002340 }
2341 }
2342 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002343 else
Petr Menšík59e47032018-11-02 22:39:39 +00002344 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002345 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002346 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002347 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002348 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002349 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002350 prefstr = split(arg);
2351
Simon Kelleyd74942a2012-02-07 20:51:56 +00002352 if (inet_pton(AF_INET, comma, &new->start))
2353 {
2354 new->is6 = 0;
2355 if (!arg)
2356 new->end.s_addr = new->start.s_addr;
2357 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002358 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002359 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002360 else if (inet_pton(AF_INET6, comma, &new->start6))
2361 {
2362 new->is6 = 1;
2363 if (!arg)
2364 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2365 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002366 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002367 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002368 else
Petr Menšík59e47032018-11-02 22:39:39 +00002369 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002370
2371 if (option != 's' && prefstr)
2372 {
2373 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2374 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002375 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002376 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002377 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002378
2379 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002380 if (option == 's')
2381 {
2382 new->next = daemon->cond_domain;
2383 daemon->cond_domain = new;
2384 }
2385 else
2386 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002387 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002388 new->next = daemon->synth_domains;
2389 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002390 if (new->prefix &&
2391 (star = strrchr(new->prefix, '*'))
2392 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002393 {
2394 *star = 0;
2395 new->indexed = 1;
2396 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002397 }
Simon Kelley9009d742008-11-14 20:04:27 +00002398 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002399 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002400 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002401 else
2402 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002403 }
2404 }
Simon Kelley849a8352006-06-09 21:02:31 +01002405 break;
2406
Simon Kelley1e505122016-01-25 21:29:23 +00002407 case LOPT_CPE_ID: /* --add-dns-client */
2408 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002409 daemon->dns_client_id = opt_string_alloc(arg);
2410 break;
2411
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002412 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002413 if (!arg)
2414 set_option_bool(OPT_ADD_MAC);
2415 else
2416 {
2417 unhide_metas(arg);
2418 if (strcmp(arg, "base64") == 0)
2419 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002420 else if (strcmp(arg, "text") == 0)
2421 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002422 else
2423 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002424 }
2425 break;
2426
Simon Kelleyf2621c72007-04-29 19:47:21 +01002427 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002428 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002429 break;
2430
Simon Kelleyf2621c72007-04-29 19:47:21 +01002431 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002432 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002433 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002434 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002435
Simon Kelley7622fc02009-06-04 20:32:05 +01002436#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002437 case LOPT_SCRIPTUSR: /* --scriptuser */
2438 daemon->scriptuser = opt_string_alloc(arg);
2439 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002440#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002441
Simon Kelleyf2621c72007-04-29 19:47:21 +01002442 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002443 do {
Simon Kelley824af852008-02-12 20:43:05 +00002444 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002445 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002446 new->next = daemon->if_names;
2447 daemon->if_names = new;
2448 /* new->name may be NULL if someone does
2449 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002450 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002451 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002452 arg = comma;
2453 } while (arg);
2454 break;
2455
Simon Kelley2937f8a2013-07-29 19:49:07 +01002456 case LOPT_TFTP: /* --enable-tftp */
2457 set_option_bool(OPT_TFTP);
2458 if (!arg)
2459 break;
2460 /* fall through */
2461
Simon Kelleyf2621c72007-04-29 19:47:21 +01002462 case 'I': /* --except-interface */
2463 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002464 do {
Simon Kelley824af852008-02-12 20:43:05 +00002465 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002466 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002467 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002468 if (option == 'I')
2469 {
2470 new->next = daemon->if_except;
2471 daemon->if_except = new;
2472 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002473 else if (option == LOPT_TFTP)
2474 {
2475 new->next = daemon->tftp_interfaces;
2476 daemon->tftp_interfaces = new;
2477 }
Simon Kelley849a8352006-06-09 21:02:31 +01002478 else
2479 {
2480 new->next = daemon->dhcp_except;
2481 daemon->dhcp_except = new;
2482 }
2483 arg = comma;
2484 } while (arg);
2485 break;
2486
Simon Kelleyf2621c72007-04-29 19:47:21 +01002487 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002488 case LOPT_IGNORE_ADDR: /* --ignore-address */
2489 {
Simon Kelley849a8352006-06-09 21:02:31 +01002490 struct in_addr addr;
2491 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002492 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002493 {
Simon Kelley824af852008-02-12 20:43:05 +00002494 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002495 if (option == 'B')
2496 {
2497 baddr->next = daemon->bogus_addr;
2498 daemon->bogus_addr = baddr;
2499 }
2500 else
2501 {
2502 baddr->next = daemon->ignore_addr;
2503 daemon->ignore_addr = baddr;
2504 }
Simon Kelley849a8352006-06-09 21:02:31 +01002505 baddr->addr = addr;
2506 }
2507 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002508 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002509 break;
2510 }
2511
Simon Kelleyf2621c72007-04-29 19:47:21 +01002512 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002513 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002514 do {
Simon Kelley824af852008-02-12 20:43:05 +00002515 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002516 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002517 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002518 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002519 {
2520 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002521 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002522#ifdef HAVE_SOCKADDR_SA_LEN
2523 new->addr.in.sin_len = sizeof(new->addr.in);
2524#endif
2525 }
Simon Kelley849a8352006-06-09 21:02:31 +01002526 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2527 {
2528 new->addr.sa.sa_family = AF_INET6;
2529 new->addr.in6.sin6_flowinfo = 0;
2530 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002531 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002532#ifdef HAVE_SOCKADDR_SA_LEN
2533 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2534#endif
2535 }
Simon Kelley849a8352006-06-09 21:02:31 +01002536 else
Petr Menšík59e47032018-11-02 22:39:39 +00002537 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002538
2539 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002540 if (option == 'a')
2541 {
2542 new->next = daemon->if_addrs;
2543 daemon->if_addrs = new;
2544 }
2545 else
2546 {
2547 new->next = daemon->auth_peers;
2548 daemon->auth_peers = new;
2549 }
Simon Kelley849a8352006-06-09 21:02:31 +01002550 arg = comma;
2551 } while (arg);
2552 break;
2553
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002554 case 'S': /* --server */
2555 case LOPT_LOCAL: /* --local */
2556 case 'A': /* --address */
2557 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002558 {
2559 struct server *serv, *newlist = NULL;
2560
2561 unhide_metas(arg);
2562
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002563 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002564 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002565 int rebind = !(*arg == '/');
2566 char *end = NULL;
2567 if (!rebind)
2568 arg++;
2569 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002570 {
2571 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002572 /* elide leading dots - they are implied in the search algorithm */
2573 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002574 /* # matches everything and becomes a zero length domain string */
2575 if (strcmp(arg, "#") == 0)
2576 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002577 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002578 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002579 serv = opt_malloc(sizeof(struct server));
2580 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002581 serv->next = newlist;
2582 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002583 serv->domain = domain;
2584 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002585 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002586 if (rebind)
2587 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002588 }
2589 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002590 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002591 }
2592 else
2593 {
Simon Kelley824af852008-02-12 20:43:05 +00002594 newlist = opt_malloc(sizeof(struct server));
2595 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002596#ifdef HAVE_LOOP
2597 newlist->uid = rand32();
2598#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002599 }
2600
Simon Kelley7b1eae42014-02-20 13:43:28 +00002601 if (servers_only && option == 'S')
2602 newlist->flags |= SERV_FROM_FILE;
2603
Simon Kelley849a8352006-06-09 21:02:31 +01002604 if (option == 'A')
2605 {
2606 newlist->flags |= SERV_LITERAL_ADDRESS;
2607 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002608 {
2609 server_list_free(newlist);
2610 ret_err(gen_err);
2611 }
Simon Kelley849a8352006-06-09 21:02:31 +01002612 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002613 else if (option == LOPT_NO_REBIND)
2614 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002615
2616 if (!arg || !*arg)
2617 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002618 if (!(newlist->flags & SERV_NO_REBIND))
2619 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002620 }
2621
2622 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002623 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002624 else
2625 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002626 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2627 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002628 {
2629 server_list_free(newlist);
2630 ret_err(err);
2631 }
Simon Kelley849a8352006-06-09 21:02:31 +01002632 }
2633
Simon Kelleyf2621c72007-04-29 19:47:21 +01002634 serv = newlist;
2635 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002636 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002637 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002638 serv->next->addr = serv->addr;
2639 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002640 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002641 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002642 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002643 serv->next = daemon->servers;
2644 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002645 break;
2646 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002647
Simon Kelleyde73a492014-02-17 21:43:27 +00002648 case LOPT_REV_SERV: /* --rev-server */
2649 {
2650 char *string;
2651 int size;
2652 struct server *serv;
2653 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002654 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002655
2656 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002657 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002658 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002659
2660 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002661
Simon Kelleya9b022a2020-02-11 21:58:59 +00002662 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2663 ret_err(gen_err);
2664
Simon Kelleyde73a492014-02-17 21:43:27 +00002665 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002666 {
2667 serv = add_rev4(addr4, size);
2668 if (!serv)
2669 ret_err(_("bad prefix"));
2670 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002671 else if (inet_pton(AF_INET6, arg, &addr6))
2672 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002673 else
2674 ret_err(gen_err);
2675
2676 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2677
2678 if (string)
2679 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002680
2681 if (servers_only)
2682 serv->flags |= SERV_FROM_FILE;
2683
Simon Kelleyde73a492014-02-17 21:43:27 +00002684 break;
2685 }
2686
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002687 case LOPT_IPSET: /* --ipset */
2688#ifndef HAVE_IPSET
2689 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2690 break;
2691#else
2692 {
2693 struct ipsets ipsets_head;
2694 struct ipsets *ipsets = &ipsets_head;
2695 int size;
2696 char *end;
2697 char **sets, **sets_pos;
2698 memset(ipsets, 0, sizeof(struct ipsets));
2699 unhide_metas(arg);
2700 if (arg && *arg == '/')
2701 {
2702 arg++;
2703 while ((end = split_chr(arg, '/')))
2704 {
2705 char *domain = NULL;
2706 /* elide leading dots - they are implied in the search algorithm */
2707 while (*arg == '.')
2708 arg++;
2709 /* # matches everything and becomes a zero length domain string */
2710 if (strcmp(arg, "#") == 0 || !*arg)
2711 domain = "";
2712 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002713 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002714 ipsets->next = opt_malloc(sizeof(struct ipsets));
2715 ipsets = ipsets->next;
2716 memset(ipsets, 0, sizeof(struct ipsets));
2717 ipsets->domain = domain;
2718 arg = end;
2719 }
2720 }
2721 else
2722 {
2723 ipsets->next = opt_malloc(sizeof(struct ipsets));
2724 ipsets = ipsets->next;
2725 memset(ipsets, 0, sizeof(struct ipsets));
2726 ipsets->domain = "";
2727 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002728
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002729 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002730 ret_err(gen_err);
2731
2732 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002733 if (*end == ',')
2734 ++size;
2735
2736 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2737
2738 do {
2739 end = split(arg);
2740 *sets_pos++ = opt_string_alloc(arg);
2741 arg = end;
2742 } while (end);
2743 *sets_pos = 0;
2744 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2745 ipsets->next->sets = sets;
2746 ipsets->next = daemon->ipsets;
2747 daemon->ipsets = ipsets_head.next;
2748
2749 break;
2750 }
2751#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002752
Simon Kelleyf2621c72007-04-29 19:47:21 +01002753 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002754 {
2755 int size;
2756
2757 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002758 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002759 else
2760 {
2761 /* zero is OK, and means no caching. */
2762
2763 if (size < 0)
2764 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002765
2766 /* Note that for very large cache sizes, the malloc()
2767 will overflow. For the size of the cache record
2768 at the time this was noted, the value of "very large"
2769 was 46684428. Limit to an order of magnitude less than
2770 that to be safe from changes to the cache record. */
2771 if (size > 5000000)
2772 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002773
2774 daemon->cachesize = size;
2775 }
2776 break;
2777 }
2778
Simon Kelleyf2621c72007-04-29 19:47:21 +01002779 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002780 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002781 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002782 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002783
Simon Kelley1a6bca82008-07-11 11:11:42 +01002784 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002785 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002786 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002787 break;
2788
Hans Dedecker926332a2016-01-23 10:48:12 +00002789 case LOPT_MAXPORT: /* --max-port */
2790 if (!atoi_check16(arg, &daemon->max_port))
2791 ret_err(gen_err);
2792 break;
2793
Simon Kelleyf2621c72007-04-29 19:47:21 +01002794 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002795 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002796 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002797 break;
2798
Simon Kelley25cf5e32015-01-09 15:53:03 +00002799 case 'q': /* --log-queries */
2800 set_option_bool(OPT_LOG);
2801 if (arg && strcmp(arg, "extra") == 0)
2802 set_option_bool(OPT_EXTRALOG);
2803 break;
2804
Simon Kelleyf2621c72007-04-29 19:47:21 +01002805 case LOPT_MAX_LOGS: /* --log-async */
2806 daemon->max_logs = LOG_MAX; /* default */
2807 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002808 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002809 else if (daemon->max_logs > 100)
2810 daemon->max_logs = 100;
2811 break;
2812
2813 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002814 {
2815 int i;
2816 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002817 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002818 daemon->edns_pktsz = (unsigned short)i;
2819 break;
2820 }
2821
Simon Kelleyf2621c72007-04-29 19:47:21 +01002822 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002823 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002824 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002825 /* if explicitly set to zero, use single OS ephemeral port
2826 and disable random ports */
2827 if (daemon->query_port == 0)
2828 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002829 break;
2830
Simon Kelley824af852008-02-12 20:43:05 +00002831 case 'T': /* --local-ttl */
2832 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002833 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002834 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002835 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002836 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002837 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002838 {
2839 int ttl;
2840 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002841 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002842 else if (option == LOPT_NEGTTL)
2843 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002844 else if (option == LOPT_MAXTTL)
2845 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002846 else if (option == LOPT_MINCTTL)
2847 {
2848 if (ttl > TTL_FLOOR_LIMIT)
2849 ttl = TTL_FLOOR_LIMIT;
2850 daemon->min_cache_ttl = (unsigned long)ttl;
2851 }
Simon Kelley1d860412012-09-20 20:48:04 +01002852 else if (option == LOPT_MAXCTTL)
2853 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002854 else if (option == LOPT_AUTHTTL)
2855 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002856 else if (option == LOPT_DHCPTTL)
2857 {
2858 daemon->dhcp_ttl = (unsigned long)ttl;
2859 daemon->use_dhcp_ttl = 1;
2860 }
Simon Kelley849a8352006-06-09 21:02:31 +01002861 else
2862 daemon->local_ttl = (unsigned long)ttl;
2863 break;
2864 }
2865
Simon Kelley7622fc02009-06-04 20:32:05 +01002866#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002867 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002868 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002869 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002870 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002871#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002872
Simon Kelley7622fc02009-06-04 20:32:05 +01002873#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002874 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002875 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002876 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002877 break;
2878
Simon Kelleybec366b2016-02-24 22:03:26 +00002879 case LOPT_TFTP_MTU: /* --tftp-mtu */
2880 if (!atoi_check(arg, &daemon->tftp_mtu))
2881 ret_err(gen_err);
2882 break;
2883
Simon Kelley824af852008-02-12 20:43:05 +00002884 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002885 comma = split(arg);
2886 if (comma)
2887 {
2888 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2889 new->interface = opt_string_alloc(comma);
2890 new->prefix = opt_string_alloc(arg);
2891 new->next = daemon->if_prefix;
2892 daemon->if_prefix = new;
2893 }
2894 else
2895 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002896 break;
2897
Simon Kelley824af852008-02-12 20:43:05 +00002898 case LOPT_TFTPPORTS: /* --tftp-port-range */
2899 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002900 !atoi_check16(arg, &daemon->start_tftp_port) ||
2901 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002902 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002903
2904 if (daemon->start_tftp_port > daemon->end_tftp_port)
2905 {
2906 int tmp = daemon->start_tftp_port;
2907 daemon->start_tftp_port = daemon->end_tftp_port;
2908 daemon->end_tftp_port = tmp;
2909 }
2910
2911 break;
Floris Bos60704f52017-04-09 22:22:49 +01002912
2913 case LOPT_APREF: /* --tftp-unique-root */
2914 if (!arg || strcasecmp(arg, "ip") == 0)
2915 set_option_bool(OPT_TFTP_APREF_IP);
2916 else if (strcasecmp(arg, "mac") == 0)
2917 set_option_bool(OPT_TFTP_APREF_MAC);
2918 else
2919 ret_err(gen_err);
2920 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002921#endif
Simon Kelley824af852008-02-12 20:43:05 +00002922
Simon Kelleyf2621c72007-04-29 19:47:21 +01002923 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002924 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002925 struct dhcp_bridge *new;
2926
Simon Kelley316e2732010-01-22 20:16:09 +00002927 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002928 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002929
Simon Kelley22cd8602018-01-14 22:57:14 +00002930 for (new = daemon->bridges; new; new = new->next)
2931 if (strcmp(new->iface, arg) == 0)
2932 break;
2933
2934 if (!new)
2935 {
2936 new = opt_malloc(sizeof(struct dhcp_bridge));
2937 strcpy(new->iface, arg);
2938 new->alias = NULL;
2939 new->next = daemon->bridges;
2940 daemon->bridges = new;
2941 }
2942
Simon Kelley832af0b2007-01-21 20:01:28 +00002943 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002944 arg = comma;
2945 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002946 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002947 {
Simon Kelley824af852008-02-12 20:43:05 +00002948 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002949 b->next = new->alias;
2950 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002951 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002952 }
2953 } while (comma);
2954
2955 break;
2956 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002957
Simon Kelley7622fc02009-06-04 20:32:05 +01002958#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002959 case LOPT_SHARED_NET: /* --shared-network */
2960 {
2961 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2962
2963#ifdef HAVE_DHCP6
2964 new->shared_addr.s_addr = 0;
2965#endif
2966 new->if_index = 0;
2967
2968 if (!(comma = split(arg)))
2969 {
2970 snerr:
2971 free(new);
2972 ret_err(_("bad shared-network"));
2973 }
2974
2975 if (inet_pton(AF_INET, comma, &new->shared_addr))
2976 {
2977 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2978 !(new->if_index = if_nametoindex(arg)))
2979 goto snerr;
2980 }
2981#ifdef HAVE_DHCP6
2982 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2983 {
2984 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
2985 !(new->if_index = if_nametoindex(arg)))
2986 goto snerr;
2987 }
2988#endif
2989 else
2990 goto snerr;
2991
2992 new->next = daemon->shared_networks;
2993 daemon->shared_networks = new;
2994 break;
2995 }
2996
Simon Kelleyf2621c72007-04-29 19:47:21 +01002997 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002998 {
2999 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00003000 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003001 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003002
Simon Kelley52b92f42012-01-22 16:05:15 +00003003 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003004
Simon Kelley849a8352006-06-09 21:02:31 +01003005 while(1)
3006 {
3007 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003008 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3009 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3010 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003011 break;
3012
Simon Kelleyf2621c72007-04-29 19:47:21 +01003013 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003014 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003015 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003016 {
Simon Kelley0c387192013-09-05 10:21:12 +01003017 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003018 if (arg[4])
3019 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003020 }
3021 else
3022 {
3023 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003024 {
3025 dhcp_context_free(new);
3026 ret_err(_("only one tag allowed"));
3027 }
Simon Kelley849a8352006-06-09 21:02:31 +01003028 else
Petr Menšík59e47032018-11-02 22:39:39 +00003029 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003030 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003031 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003032 }
3033 else
3034 {
3035 a[0] = arg;
3036 break;
3037 }
3038 }
3039
Simon Kelley1f776932012-12-16 19:46:08 +00003040 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003041 if (!(a[k] = split(a[k-1])))
3042 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003043
Simon Kelley52b92f42012-01-22 16:05:15 +00003044 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003045 {
3046 dhcp_context_free(new);
3047 ret_err(_("bad dhcp-range"));
3048 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003049
3050 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003051 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003052 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003053 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003054 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003055 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003056 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003057 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003058 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003059 new->flags |= CONTEXT_PROXY;
3060 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003061 {
3062 dhcp_context_free(new);
3063 ret_err(_("bad dhcp-range"));
3064 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003065
3066 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3067 {
3068 struct in_addr tmp = new->start;
3069 new->start = new->end;
3070 new->end = tmp;
3071 }
3072
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003073 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003074 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003075 {
3076 new->flags |= CONTEXT_NETMASK;
3077 leasepos = 3;
3078 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003079 {
3080 dhcp_context_free(new);
3081 ret_err(_("inconsistent DHCP range"));
3082 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003083
Simon Kelley52b92f42012-01-22 16:05:15 +00003084
Simon Kelleyfa794662016-03-03 20:33:54 +00003085 if (k >= 4 && strchr(a[3], '.') &&
3086 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3087 {
3088 new->flags |= CONTEXT_BRDCAST;
3089 leasepos = 4;
3090 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003091 }
Simon Kelley849a8352006-06-09 21:02:31 +01003092 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003093#ifdef HAVE_DHCP6
3094 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003095 {
Petr Menšík59e47032018-11-02 22:39:39 +00003096 const char *err = NULL;
3097
Simon Kelley89500e32013-09-20 16:29:20 +01003098 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003099 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003100 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003101 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003102 new->next = daemon->dhcp6;
3103 daemon->dhcp6 = new;
3104
Simon Kelley30cd9662012-03-25 20:44:38 +01003105 for (leasepos = 1; leasepos < k; leasepos++)
3106 {
3107 if (strcmp(a[leasepos], "static") == 0)
3108 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3109 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003110 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003111 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003112 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003113 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3114 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003115 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003116 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003117 else if (strcmp(a[leasepos], "off-link") == 0)
3118 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003119 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3120 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003121 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3122 {
3123 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3124 new->flags |= CONTEXT_TEMPLATE;
3125 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003126 else
3127 break;
3128 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003129
Simon Kelley52b92f42012-01-22 16:05:15 +00003130 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003131 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003132 {
3133 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003134 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003135 if (!(*cp >= '0' && *cp <= '9'))
3136 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003137 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003138 {
3139 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003140 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003141 }
3142 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003143
Petr Menšík59e47032018-11-02 22:39:39 +00003144 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003145 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003146 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003147 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003148 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003149 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003150 }
Petr Menšík59e47032018-11-02 22:39:39 +00003151 else if (new->prefix < 64)
3152 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003153
Petr Menšík59e47032018-11-02 22:39:39 +00003154 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3155 err=(_("inconsistent DHCPv6 range"));
3156
3157 if (err)
3158 {
3159 dhcp_context_free(new);
3160 ret_err(err);
3161 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003162
3163 /* dhcp-range=:: enables DHCP stateless on any interface */
3164 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3165 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003166
3167 if (new->flags & CONTEXT_TEMPLATE)
3168 {
3169 struct in6_addr zero;
3170 memset(&zero, 0, sizeof(zero));
3171 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003172 {
3173 dhcp_context_free(new);
3174 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3175 }
Simon Kelley66409192013-08-01 20:19:32 +01003176 }
3177
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003178 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003179 {
3180 struct in6_addr tmp = new->start6;
3181 new->start6 = new->end6;
3182 new->end6 = tmp;
3183 }
Simon Kelley849a8352006-06-09 21:02:31 +01003184 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003185#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003186 else
Petr Menšík59e47032018-11-02 22:39:39 +00003187 {
3188 dhcp_context_free(new);
3189 ret_err(_("bad dhcp-range"));
3190 }
Simon Kelley849a8352006-06-09 21:02:31 +01003191
Simon Kelley30cd9662012-03-25 20:44:38 +01003192 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003193 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003194 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003195 {
3196 dhcp_context_free(new);
3197 ret_err(_("bad dhcp-range"));
3198 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003199
Simon Kelley849a8352006-06-09 21:02:31 +01003200 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003201 {
3202 new->lease_time = 0xffffffff;
3203 new->flags |= CONTEXT_SETLEASE;
3204 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003205 else if (strcmp(a[leasepos], "deprecated") == 0)
3206 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003207 else
3208 {
3209 int fac = 1;
3210 if (strlen(a[leasepos]) > 0)
3211 {
3212 switch (a[leasepos][strlen(a[leasepos]) - 1])
3213 {
Simon Kelley42243212012-07-20 15:19:18 +01003214 case 'w':
3215 case 'W':
3216 fac *= 7;
3217 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003218 case 'd':
3219 case 'D':
3220 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003221 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003222 case 'h':
3223 case 'H':
3224 fac *= 60;
3225 /* fall through */
3226 case 'm':
3227 case 'M':
3228 fac *= 60;
3229 /* fall through */
3230 case 's':
3231 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003232 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003233 }
3234
Simon Kelleybe379862012-12-23 12:01:39 +00003235 for (cp = a[leasepos]; *cp; cp++)
3236 if (!(*cp >= '0' && *cp <= '9'))
3237 break;
3238
Simon Kelley54dae552013-02-05 17:55:10 +00003239 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003240 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003241
Simon Kelley849a8352006-06-09 21:02:31 +01003242 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003243 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003244 /* Leases of a minute or less confuse
3245 some clients, notably Apple's */
3246 if (new->lease_time < 120)
3247 new->lease_time = 120;
3248 }
3249 }
3250 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003251
Simon Kelley849a8352006-06-09 21:02:31 +01003252 break;
3253 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003254
Simon Kelley5aabfc72007-08-29 11:24:47 +01003255 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003256 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003257 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003258 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003259 struct in_addr in;
3260
Simon Kelley824af852008-02-12 20:43:05 +00003261 new = opt_malloc(sizeof(struct dhcp_config));
3262
Simon Kelley849a8352006-06-09 21:02:31 +01003263 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003264 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3265 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003266 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003267 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003268 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003269#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003270 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003271#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003272
Simon Kelley137286e2020-02-06 22:09:30 +00003273 while (arg)
3274 {
3275 comma = split(arg);
3276 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3277 {
3278 if ((arg[0] == 'i' || arg[0] == 'I') &&
3279 (arg[1] == 'd' || arg[1] == 'D') &&
3280 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003281 {
Simon Kelley137286e2020-02-06 22:09:30 +00003282 if (arg[3] == '*')
3283 new->flags |= CONFIG_NOCLID;
3284 else
3285 {
3286 int len;
3287 arg += 3; /* dump id: */
3288 if (strchr(arg, ':'))
3289 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3290 else
3291 {
3292 unhide_metas(arg);
3293 len = (int) strlen(arg);
3294 }
3295
3296 if (len == -1)
3297 {
3298 dhcp_config_free(new);
3299 ret_err(_("bad hex constant"));
3300 }
3301 else if ((new->clid = opt_malloc(len)))
3302 {
3303 new->flags |= CONFIG_CLID;
3304 new->clid_len = len;
3305 memcpy(new->clid, arg, len);
3306 }
3307 }
3308 }
3309 /* dhcp-host has strange backwards-compat needs. */
3310 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3311 {
3312 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3313 newlist->next = new->netid;
3314 new->netid = newlist;
3315 newlist->list = dhcp_netid_create(arg+4, NULL);
3316 }
3317 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003318 new->filter = dhcp_netid_create(arg+4, new->filter);
3319
Simon Kelley137286e2020-02-06 22:09:30 +00003320#ifdef HAVE_DHCP6
3321 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3322 {
3323 char *pref;
3324 struct in6_addr in6;
3325 struct addrlist *new_addr;
3326
3327 arg[strlen(arg)-1] = 0;
3328 arg++;
3329 pref = split_chr(arg, '/');
3330
3331 if (!inet_pton(AF_INET6, arg, &in6))
3332 {
3333 dhcp_config_free(new);
3334 ret_err(_("bad IPv6 address"));
3335 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003336
Simon Kelley137286e2020-02-06 22:09:30 +00003337 new_addr = opt_malloc(sizeof(struct addrlist));
3338 new_addr->next = new->addr6;
3339 new_addr->flags = 0;
3340 new_addr->addr.addr6 = in6;
3341 new->addr6 = new_addr;
3342
3343 if (pref)
3344 {
3345 u64 addrpart = addr6part(&in6);
3346
3347 if (!atoi_check(pref, &new_addr->prefixlen) ||
3348 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003349 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003350 {
3351 dhcp_config_free(new);
3352 ret_err(_("bad IPv6 prefix"));
3353 }
3354
3355 new_addr->flags |= ADDRLIST_PREFIX;
3356 }
3357
3358 for (i= 0; i < 8; i++)
3359 if (in6.s6_addr[i] != 0)
3360 break;
3361
3362 /* set WILDCARD if network part all zeros */
3363 if (i == 8)
3364 new_addr->flags |= ADDRLIST_WILDCARD;
3365
3366 new->flags |= CONFIG_ADDR6;
3367 }
3368#endif
3369 else
3370 {
3371 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3372 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3373 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3374 {
3375 free(newhw);
3376 dhcp_config_free(new);
3377 ret_err(_("bad hex constant"));
3378 }
3379 else
3380 {
3381 newhw->next = new->hwaddr;
3382 new->hwaddr = newhw;
3383 }
3384 }
3385 }
3386 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3387 {
3388 struct dhcp_config *configs;
3389
3390 new->addr = in;
3391 new->flags |= CONFIG_ADDR;
3392
3393 /* If the same IP appears in more than one host config, then DISCOVER
3394 for one of the hosts will get the address, but REQUEST will be NAKed,
3395 since the address is reserved by the other one -> protocol loop. */
3396 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3397 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003398 {
Simon Kelley137286e2020-02-06 22:09:30 +00003399 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3400 return 0;
3401 }
3402 }
3403 else
3404 {
3405 char *cp, *lastp = NULL, last = 0;
3406 int fac = 1, isdig = 0;
3407
3408 if (strlen(arg) > 1)
3409 {
3410 lastp = arg + strlen(arg) - 1;
3411 last = *lastp;
3412 switch (last)
3413 {
3414 case 'w':
3415 case 'W':
3416 fac *= 7;
3417 /* fall through */
3418 case 'd':
3419 case 'D':
3420 fac *= 24;
3421 /* fall through */
3422 case 'h':
3423 case 'H':
3424 fac *= 60;
3425 /* fall through */
3426 case 'm':
3427 case 'M':
3428 fac *= 60;
3429 /* fall through */
3430 case 's':
3431 case 'S':
3432 *lastp = 0;
3433 }
3434 }
3435
3436 for (cp = arg; *cp; cp++)
3437 if (isdigit((unsigned char)*cp))
3438 isdig = 1;
3439 else if (*cp != ' ')
3440 break;
3441
3442 if (*cp)
3443 {
3444 if (lastp)
3445 *lastp = last;
3446 if (strcmp(arg, "infinite") == 0)
3447 {
3448 new->lease_time = 0xffffffff;
3449 new->flags |= CONFIG_TIME;
3450 }
3451 else if (strcmp(arg, "ignore") == 0)
3452 new->flags |= CONFIG_DISABLE;
3453 else
3454 {
3455 if (!(new->hostname = canonicalise_opt(arg)) ||
3456 !legal_hostname(new->hostname))
3457 {
3458 dhcp_config_free(new);
3459 ret_err(_("bad DHCP host name"));
3460 }
3461
3462 new->flags |= CONFIG_NAME;
3463 new->domain = strip_hostname(new->hostname);
3464 }
3465 }
3466 else if (isdig)
3467 {
3468 new->lease_time = atoi(arg) * fac;
3469 /* Leases of a minute or less confuse
3470 some clients, notably Apple's */
3471 if (new->lease_time < 120)
3472 new->lease_time = 120;
3473 new->flags |= CONFIG_TIME;
3474 }
3475 }
3476
3477 arg = comma;
3478 }
3479
Simon Kelley5aabfc72007-08-29 11:24:47 +01003480 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003481 break;
3482 }
Simon Kelley137286e2020-02-06 22:09:30 +00003483
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003484 case LOPT_TAG_IF: /* --tag-if */
3485 {
3486 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3487
3488 new->tag = NULL;
3489 new->set = NULL;
3490 new->next = NULL;
3491
3492 /* preserve order */
3493 if (!daemon->tag_if)
3494 daemon->tag_if = new;
3495 else
3496 {
3497 struct tag_if *tmp;
3498 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3499 tmp->next = new;
3500 }
3501
3502 while (arg)
3503 {
3504 size_t len;
3505
3506 comma = split(arg);
3507 len = strlen(arg);
3508
3509 if (len < 5)
3510 {
3511 new->set = NULL;
3512 break;
3513 }
3514 else
3515 {
Petr Menšík59e47032018-11-02 22:39:39 +00003516 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003517
3518 if (strstr(arg, "set:") == arg)
3519 {
3520 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3521 newlist->next = new->set;
3522 new->set = newlist;
3523 newlist->list = newtag;
3524 }
3525 else if (strstr(arg, "tag:") == arg)
3526 {
3527 newtag->next = new->tag;
3528 new->tag = newtag;
3529 }
3530 else
3531 {
3532 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003533 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003534 break;
3535 }
3536 }
3537
3538 arg = comma;
3539 }
3540
3541 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003542 {
3543 dhcp_netid_free(new->tag);
3544 dhcp_netid_list_free(new->set);
3545 ret_err_free(_("bad tag-if"), new);
3546 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003547
3548 break;
3549 }
3550
Simon Kelley849a8352006-06-09 21:02:31 +01003551
Simon Kelley73a08a22009-02-05 20:28:08 +00003552 case 'O': /* --dhcp-option */
3553 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003554 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003555 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003556 return parse_dhcp_opt(errstr, arg,
3557 option == LOPT_FORCE ? DHOPT_FORCE :
3558 (option == LOPT_MATCH ? DHOPT_MATCH :
3559 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003560
3561 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3562 {
3563 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3564 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3565 ssize_t len;
3566
3567 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3568 ret_err(gen_err);
3569
3570 new->wildcard = 0;
3571 new->netid = id;
3572 id->net = opt_string_alloc(set_prefix(arg));
3573
3574 if (comma[len-1] == '*')
3575 {
3576 comma[len-1] = 0;
3577 new->wildcard = 1;
3578 }
3579 new->name = opt_string_alloc(comma);
3580
3581 new->next = daemon->dhcp_name_match;
3582 daemon->dhcp_name_match = new;
3583
3584 break;
3585 }
3586
Simon Kelleyf2621c72007-04-29 19:47:21 +01003587 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003588 {
Petr Menšík59e47032018-11-02 22:39:39 +00003589 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003590
Petr Menšík137e9f82018-12-16 21:25:29 +00003591 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003592 {
3593 ret_err(gen_err);
3594 }
Simon Kelley849a8352006-06-09 21:02:31 +01003595 else
3596 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003597 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003598 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003599 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003600 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003601 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003602 dhcp_next_server.s_addr = 0;
3603 if (comma)
3604 {
3605 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003606 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003607 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003608 if (comma)
3609 {
3610 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003611 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3612 {
3613 /*
3614 * The user may have specified the tftp hostname here.
3615 * save it so that it can be resolved/looked up during
3616 * actual dhcp_reply().
3617 */
3618
3619 tftp_sname = opt_string_alloc(comma);
3620 dhcp_next_server.s_addr = 0;
3621 }
Simon Kelley849a8352006-06-09 21:02:31 +01003622 }
3623 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003624
3625 new = opt_malloc(sizeof(struct dhcp_boot));
3626 new->file = dhcp_file;
3627 new->sname = dhcp_sname;
3628 new->tftp_sname = tftp_sname;
3629 new->next_server = dhcp_next_server;
3630 new->netid = id;
3631 new->next = daemon->boot_config;
3632 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003633 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003634
Simon Kelley849a8352006-06-09 21:02:31 +01003635 break;
3636 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003637
Floris Bos503c6092017-04-09 23:07:13 +01003638 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3639 {
Petr Menšík59e47032018-11-02 22:39:39 +00003640 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003641
Petr Menšík137e9f82018-12-16 21:25:29 +00003642 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003643 {
3644 ret_err(gen_err);
3645 }
Floris Bos503c6092017-04-09 23:07:13 +01003646 else
3647 {
3648 struct delay_config *new;
3649 int delay;
3650 if (!atoi_check(arg, &delay))
3651 ret_err(gen_err);
3652
3653 new = opt_malloc(sizeof(struct delay_config));
3654 new->delay = delay;
3655 new->netid = id;
3656 new->next = daemon->delay_conf;
3657 daemon->delay_conf = new;
3658 }
3659
3660 break;
3661 }
3662
Simon Kelley7622fc02009-06-04 20:32:05 +01003663 case LOPT_PXE_PROMT: /* --pxe-prompt */
3664 {
3665 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3666 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003667
Simon Kelley7622fc02009-06-04 20:32:05 +01003668 new->netid = NULL;
3669 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003670 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003671
Petr Menšík137e9f82018-12-16 21:25:29 +00003672 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003673 {
3674 dhcp_opt_free(new);
3675 ret_err(gen_err);
3676 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003677 else
3678 {
3679 comma = split(arg);
3680 unhide_metas(arg);
3681 new->len = strlen(arg) + 1;
3682 new->val = opt_malloc(new->len);
3683 memcpy(new->val + 1, arg, new->len - 1);
3684
Wang Shanker4ded9622020-12-04 10:17:35 +08003685 new->u.vendor_class = NULL;
3686 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003687
3688 if (comma && atoi_check(comma, &timeout))
3689 *(new->val) = timeout;
3690 else
3691 *(new->val) = 255;
3692
3693 new->next = daemon->dhcp_opts;
3694 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003695 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003696 }
3697
3698 break;
3699 }
3700
3701 case LOPT_PXE_SERV: /* --pxe-service */
3702 {
3703 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3704 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003705 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3706 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003707 static int boottype = 32768;
3708
3709 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003710 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003711 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003712 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003713
Simon Kelley7622fc02009-06-04 20:32:05 +01003714 if (arg && (comma = split(arg)))
3715 {
3716 for (i = 0; CSA[i]; i++)
3717 if (strcasecmp(CSA[i], arg) == 0)
3718 break;
3719
3720 if (CSA[i] || atoi_check(arg, &i))
3721 {
3722 arg = comma;
3723 comma = split(arg);
3724
3725 new->CSA = i;
3726 new->menu = opt_string_alloc(arg);
3727
Simon Kelley316e2732010-01-22 20:16:09 +00003728 if (!comma)
3729 {
3730 new->type = 0; /* local boot */
3731 new->basename = NULL;
3732 }
3733 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003734 {
3735 arg = comma;
3736 comma = split(arg);
3737 if (atoi_check(arg, &i))
3738 {
3739 new->type = i;
3740 new->basename = NULL;
3741 }
3742 else
3743 {
3744 new->type = boottype++;
3745 new->basename = opt_string_alloc(arg);
3746 }
3747
Simon Kelley751d6f42012-02-10 15:24:51 +00003748 if (comma)
3749 {
3750 if (!inet_pton(AF_INET, comma, &new->server))
3751 {
3752 new->server.s_addr = 0;
3753 new->sname = opt_string_alloc(comma);
3754 }
3755
3756 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003757 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003758
Simon Kelley316e2732010-01-22 20:16:09 +00003759 /* Order matters */
3760 new->next = NULL;
3761 if (!daemon->pxe_services)
3762 daemon->pxe_services = new;
3763 else
3764 {
3765 struct pxe_service *s;
3766 for (s = daemon->pxe_services; s->next; s = s->next);
3767 s->next = new;
3768 }
3769
3770 daemon->enable_pxe = 1;
3771 break;
3772
Simon Kelley7622fc02009-06-04 20:32:05 +01003773 }
3774 }
3775
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003776 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003777 }
3778
Simon Kelleyf2621c72007-04-29 19:47:21 +01003779 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003780 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003781 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003782 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003783 else
3784 {
Simon Kelley824af852008-02-12 20:43:05 +00003785 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003786 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003787 unhide_metas(comma);
3788 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003789 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003790 {
3791 free(new->netid.net);
3792 ret_err_free(gen_err, new);
3793 }
Simon Kelley28866e92011-02-14 20:19:14 +00003794 else
3795 {
3796 new->next = daemon->dhcp_macs;
3797 daemon->dhcp_macs = new;
3798 }
Simon Kelley849a8352006-06-09 21:02:31 +01003799 }
3800 }
3801 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003802
Simon Kelleyf2621c72007-04-29 19:47:21 +01003803 case 'U': /* --dhcp-vendorclass */
3804 case 'j': /* --dhcp-userclass */
3805 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3806 case LOPT_REMOTE: /* --dhcp-remoteid */
3807 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003808 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003809 unsigned char *p;
3810 int dig = 0;
3811 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3812
3813 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003814 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003815
3816 new->netid.net = opt_string_alloc(set_prefix(arg));
3817 /* check for hex string - must digits may include : must not have nothing else,
3818 only allowed for agent-options. */
3819
3820 arg = comma;
3821 if ((comma = split(arg)))
3822 {
3823 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003824 {
3825 free(new->netid.net);
3826 ret_err_free(gen_err, new);
3827 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003828 else
3829 new->enterprise = atoi(arg+11);
3830 }
3831 else
3832 comma = arg;
3833
3834 for (p = (unsigned char *)comma; *p; p++)
3835 if (isxdigit(*p))
3836 dig = 1;
3837 else if (*p != ':')
3838 break;
3839 unhide_metas(comma);
3840 if (option == 'U' || option == 'j' || *p || !dig)
3841 {
3842 new->len = strlen(comma);
3843 new->data = opt_malloc(new->len);
3844 memcpy(new->data, comma, new->len);
3845 }
3846 else
3847 {
3848 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3849 new->data = opt_malloc(new->len);
3850 memcpy(new->data, comma, new->len);
3851 }
3852
3853 switch (option)
3854 {
3855 case 'j':
3856 new->match_type = MATCH_USER;
3857 break;
3858 case 'U':
3859 new->match_type = MATCH_VENDOR;
3860 break;
3861 case LOPT_CIRCUIT:
3862 new->match_type = MATCH_CIRCUIT;
3863 break;
3864 case LOPT_REMOTE:
3865 new->match_type = MATCH_REMOTE;
3866 break;
3867 case LOPT_SUBSCR:
3868 new->match_type = MATCH_SUBSCRIBER;
3869 break;
3870 }
3871 new->next = daemon->dhcp_vendors;
3872 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003873
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003874 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003875 }
3876
Simon Kelley9e038942008-05-30 20:06:34 +01003877 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3878 if (!arg)
3879 {
3880 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3881 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3882 }
3883 else
3884 {
3885 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003886 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3887 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003888 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003889 if (!comma)
3890 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3891 }
3892 break;
3893
Simon Kelley824af852008-02-12 20:43:05 +00003894 case 'J': /* --dhcp-ignore */
3895 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3896 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003897 case '3': /* --bootp-dynamic */
3898 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003899 {
Simon Kelley824af852008-02-12 20:43:05 +00003900 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003901 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003902 if (option == 'J')
3903 {
3904 new->next = daemon->dhcp_ignore;
3905 daemon->dhcp_ignore = new;
3906 }
Simon Kelley824af852008-02-12 20:43:05 +00003907 else if (option == LOPT_BROADCAST)
3908 {
3909 new->next = daemon->force_broadcast;
3910 daemon->force_broadcast = new;
3911 }
Simon Kelley9009d742008-11-14 20:04:27 +00003912 else if (option == '3')
3913 {
3914 new->next = daemon->bootp_dynamic;
3915 daemon->bootp_dynamic = new;
3916 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003917 else if (option == LOPT_GEN_NAMES)
3918 {
3919 new->next = daemon->dhcp_gen_names;
3920 daemon->dhcp_gen_names = new;
3921 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003922 else
3923 {
3924 new->next = daemon->dhcp_ignore_names;
3925 daemon->dhcp_ignore_names = new;
3926 }
3927
3928 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003929 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003930 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003931 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003932 }
Simon Kelley849a8352006-06-09 21:02:31 +01003933
3934 new->list = list;
3935 break;
3936 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003937
3938 case LOPT_PROXY: /* --dhcp-proxy */
3939 daemon->override = 1;
3940 while (arg) {
3941 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3942 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003943 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003944 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003945 new->next = daemon->override_relays;
3946 daemon->override_relays = new;
3947 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003948 }
3949 break;
3950
3951 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3952 {
3953 while (arg) {
3954 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3955 comma = split(arg);
3956 new->data = opt_string_alloc(arg);
3957 new->next = daemon->dhcp_pxe_vendors;
3958 daemon->dhcp_pxe_vendors = new;
3959 arg = comma;
3960 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003961 }
3962 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003963
3964 case LOPT_RELAY: /* --dhcp-relay */
3965 {
3966 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3967 comma = split(arg);
3968 new->interface = opt_string_alloc(split(comma));
3969 new->iface_index = 0;
3970 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3971 {
3972 new->next = daemon->relay4;
3973 daemon->relay4 = new;
3974 }
3975#ifdef HAVE_DHCP6
3976 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3977 {
3978 new->next = daemon->relay6;
3979 daemon->relay6 = new;
3980 }
3981#endif
3982 else
Petr Menšík59e47032018-11-02 22:39:39 +00003983 {
3984 free(new->interface);
3985 ret_err_free(_("Bad dhcp-relay"), new);
3986 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003987
3988 break;
3989 }
3990
Simon Kelley7622fc02009-06-04 20:32:05 +01003991#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003992
Simon Kelley8b372702012-03-09 17:45:10 +00003993#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003994 case LOPT_RA_PARAM: /* --ra-param */
3995 if ((comma = split(arg)))
3996 {
3997 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3998 new->lifetime = -1;
3999 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01004000 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004001 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004002 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004003 if (strcasestr(comma, "mtu:") == comma)
4004 {
4005 arg = comma + 4;
4006 if (!(comma = split(comma)))
4007 goto err;
4008 if (!strcasecmp(arg, "off"))
4009 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004010 else if (!atoi_check(arg, &new->mtu))
4011 new->mtu_name = opt_string_alloc(arg);
4012 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004013 goto err;
4014 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004015 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4016 {
4017 if (*comma == 'l' || *comma == 'L')
4018 new->prio = 0x18;
4019 else
4020 new->prio = 0x08;
4021 comma = split(comma);
4022 }
4023 arg = split(comma);
4024 if (!atoi_check(comma, &new->interval) ||
4025 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004026 {
David Flamand005c46d2017-04-11 11:49:54 +01004027err:
Petr Menšík59e47032018-11-02 22:39:39 +00004028 free(new->name);
4029 ret_err_free(_("bad RA-params"), new);
4030 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004031
4032 new->next = daemon->ra_interfaces;
4033 daemon->ra_interfaces = new;
4034 }
4035 break;
4036
Simon Kelley8b372702012-03-09 17:45:10 +00004037 case LOPT_DUID: /* --dhcp-duid */
4038 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004039 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004040 else
4041 {
4042 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4043 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4044 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4045 }
4046 break;
4047#endif
4048
Simon Kelleyf2621c72007-04-29 19:47:21 +01004049 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004050 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004051 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004052 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004053 struct doctor *new = opt_malloc(sizeof(struct doctor));
4054 new->next = daemon->doctors;
4055 daemon->doctors = new;
4056 new->mask.s_addr = 0xffffffff;
4057 new->end.s_addr = 0;
4058
Simon Kelley849a8352006-06-09 21:02:31 +01004059 if ((a[0] = arg))
4060 for (k = 1; k < 3; k++)
4061 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004062 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004063 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004064 unhide_metas(a[k]);
4065 }
Simon Kelley849a8352006-06-09 21:02:31 +01004066
Simon Kelley73a08a22009-02-05 20:28:08 +00004067 dash = split_chr(a[0], '-');
4068
Simon Kelley849a8352006-06-09 21:02:31 +01004069 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004070 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004071 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4072 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4073 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004074
Simon Kelley73a08a22009-02-05 20:28:08 +00004075 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004076 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004077 !is_same_net(new->in, new->end, new->mask) ||
4078 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004079 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004080
4081 break;
4082 }
4083
Simon Kelleyf2621c72007-04-29 19:47:21 +01004084 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004085 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004086 {
4087 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004088 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004089
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004090 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004091
Simon Kelley824af852008-02-12 20:43:05 +00004092 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004093 memset(new, 0, sizeof(struct interface_name));
4094 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004095
Simon Kelleyf2621c72007-04-29 19:47:21 +01004096 /* Add to the end of the list, so that first name
4097 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004098 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004099 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004100
4101 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004102 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004103 if (inet_pton(AF_INET, arg, &new->proto4))
4104 new->flags |= INP4;
4105 else if (inet_pton(AF_INET6, arg, &new->proto6))
4106 new->flags |= INP6;
4107 else
4108 break;
4109
4110 arg = comma;
4111 }
4112
4113 if ((comma = split_chr(arg, '/')))
4114 {
4115 if (strcmp(comma, "4") == 0)
4116 new->flags &= ~IN6;
4117 else if (strcmp(comma, "6") == 0)
4118 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004119 else
Petr Menšík59e47032018-11-02 22:39:39 +00004120 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004121 }
4122
4123 new->intr = opt_string_alloc(arg);
4124
4125 if (option == LOPT_DYNHOST)
4126 {
4127 if (!(new->flags & (INP4 | INP6)))
4128 ret_err(_("missing address in dynamic host"));
4129
4130 if (!(new->flags & IN4) || !(new->flags & IN6))
4131 arg = NULL; /* provoke error below */
4132
4133 new->flags &= ~(IN4 | IN6);
4134 }
4135 else
4136 {
4137 if (new->flags & (INP4 | INP6))
4138 arg = NULL; /* provoke error below */
4139 }
4140
4141 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4142 ret_err(option == LOPT_DYNHOST ?
4143 _("bad dynamic host") : _("bad interface name"));
4144
Simon Kelleyf2621c72007-04-29 19:47:21 +01004145 break;
4146 }
Simon Kelley9009d742008-11-14 20:04:27 +00004147
4148 case LOPT_CNAME: /* --cname */
4149 {
4150 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004151 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004152 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004153
Simon Kelleya1d973f2016-12-22 22:09:50 +00004154 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004155 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004156 pen = last;
4157 last = comma;
4158 }
4159
4160 if (!pen)
4161 ret_err(_("bad CNAME"));
4162
4163 if (pen != arg && atoi_check(last, &ttl))
4164 last = pen;
4165
4166 target = canonicalise_opt(last);
4167
4168 while (arg != last)
4169 {
Petr Menšík56f06232018-03-06 23:13:32 +00004170 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004171 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004172
4173 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004174 {
4175 free(target);
4176 free(alias);
4177 ret_err(_("bad CNAME"));
4178 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004179
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004180 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004181 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004182 {
4183 free(target);
4184 free(alias);
4185 ret_err(_("duplicate CNAME"));
4186 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004187 new = opt_malloc(sizeof(struct cname));
4188 new->next = daemon->cnames;
4189 daemon->cnames = new;
4190 new->alias = alias;
4191 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004192 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004193
Petr Menšík56f06232018-03-06 23:13:32 +00004194 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004195 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004196
Simon Kelley9009d742008-11-14 20:04:27 +00004197 break;
4198 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004199
4200 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004201 {
4202 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004203 char *dom, *target = NULL;
4204
Simon Kelleyf2621c72007-04-29 19:47:21 +01004205 comma = split(arg);
4206
Simon Kelley1f15b812009-10-13 17:49:32 +01004207 if (!(dom = canonicalise_opt(arg)) ||
4208 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004209 {
4210 free(dom);
4211 free(target);
4212 ret_err(_("bad PTR record"));
4213 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004214 else
4215 {
4216 new = opt_malloc(sizeof(struct ptr_record));
4217 new->next = daemon->ptr;
4218 daemon->ptr = new;
4219 new->name = dom;
4220 new->ptr = target;
4221 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004222 break;
4223 }
4224
Simon Kelley1a6bca82008-07-11 11:11:42 +01004225 case LOPT_NAPTR: /* --naptr-record */
4226 {
4227 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4228 int k = 0;
4229 struct naptr *new;
4230 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004231 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004232
4233 if ((a[0] = arg))
4234 for (k = 1; k < 7; k++)
4235 if (!(a[k] = split(a[k-1])))
4236 break;
4237
4238
4239 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004240 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004241 !atoi_check16(a[1], &order) ||
4242 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004243 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004244 {
4245 free(name);
4246 free(replace);
4247 ret_err(_("bad NAPTR record"));
4248 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004249 else
4250 {
4251 new = opt_malloc(sizeof(struct naptr));
4252 new->next = daemon->naptr;
4253 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004254 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004255 new->flags = opt_string_alloc(a[3]);
4256 new->services = opt_string_alloc(a[4]);
4257 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004258 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004259 new->order = order;
4260 new->pref = pref;
4261 }
4262 break;
4263 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004264
4265 case LOPT_RR: /* dns-rr */
4266 {
4267 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004268 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004269 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004270 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004271
4272 comma = split(arg);
4273 data = split(comma);
4274
4275 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004276 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004277
Petr Menšík59e47032018-11-02 22:39:39 +00004278 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004279 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004280 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004281 {
4282 free(new->name);
4283 ret_err_free(_("bad RR record"), new);
4284 }
4285
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004286 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004287 new->class = class;
4288 new->next = daemon->rr;
4289 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004290
4291 if (data)
4292 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004293 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004294 new->len = len;
4295 memcpy(new->txt, data, len);
4296 }
4297
4298 break;
4299 }
4300
Simon Kelley974a6d02018-08-23 23:01:16 +01004301 case LOPT_CAA: /* --caa-record */
4302 {
4303 struct txt_record *new;
4304 char *tag, *value;
4305 int flags;
4306
4307 comma = split(arg);
4308 tag = split(comma);
4309 value = split(tag);
4310
4311 new = opt_malloc(sizeof(struct txt_record));
4312 new->next = daemon->rr;
4313 daemon->rr = new;
4314
4315 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4316 ret_err(_("bad CAA record"));
4317
4318 unhide_metas(tag);
4319 unhide_metas(value);
4320
4321 new->len = strlen(tag) + strlen(value) + 2;
4322 new->txt = opt_malloc(new->len);
4323 new->txt[0] = flags;
4324 new->txt[1] = strlen(tag);
4325 memcpy(&new->txt[2], tag, strlen(tag));
4326 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4327 new->class = T_CAA;
4328
4329 break;
4330 }
4331
Simon Kelleyf2621c72007-04-29 19:47:21 +01004332 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004333 {
4334 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004335 unsigned char *p, *cnt;
4336 size_t len;
4337
4338 comma = split(arg);
4339
Simon Kelley824af852008-02-12 20:43:05 +00004340 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004341 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004342 new->stat = 0;
4343
Simon Kelley1f15b812009-10-13 17:49:32 +01004344 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004345 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004346
Petr Menšík59e47032018-11-02 22:39:39 +00004347 new->next = daemon->txt;
4348 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004349 len = comma ? strlen(comma) : 0;
4350 len += (len/255) + 1; /* room for extra counts */
4351 new->txt = p = opt_malloc(len);
4352
4353 cnt = p++;
4354 *cnt = 0;
4355
4356 while (comma && *comma)
4357 {
4358 unsigned char c = (unsigned char)*comma++;
4359
4360 if (c == ',' || *cnt == 255)
4361 {
4362 if (c != ',')
4363 comma--;
4364 cnt = p++;
4365 *cnt = 0;
4366 }
4367 else
4368 {
4369 *p++ = unhide_meta(c);
4370 (*cnt)++;
4371 }
4372 }
4373
4374 new->len = p - new->txt;
4375
Simon Kelley849a8352006-06-09 21:02:31 +01004376 break;
4377 }
4378
Simon Kelleyf2621c72007-04-29 19:47:21 +01004379 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004380 {
4381 int port = 1, priority = 0, weight = 0;
4382 char *name, *target = NULL;
4383 struct mx_srv_record *new;
4384
Simon Kelleyf2621c72007-04-29 19:47:21 +01004385 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004386
Simon Kelley1f15b812009-10-13 17:49:32 +01004387 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004388 ret_err(_("bad SRV record"));
4389
Simon Kelley849a8352006-06-09 21:02:31 +01004390 if (comma)
4391 {
4392 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004393 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004394 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004395 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004396
Simon Kelley849a8352006-06-09 21:02:31 +01004397 if (comma)
4398 {
4399 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004400 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004401 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004402 {
4403 free(name);
4404 ret_err_free(_("invalid port number"), target);
4405 }
Simon Kelley824af852008-02-12 20:43:05 +00004406
Simon Kelley849a8352006-06-09 21:02:31 +01004407 if (comma)
4408 {
4409 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004410 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004411 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004412 {
4413 free(name);
4414 ret_err_free(_("invalid priority"), target);
4415 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004416 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004417 {
4418 free(name);
4419 ret_err_free(_("invalid weight"), target);
4420 }
Simon Kelley849a8352006-06-09 21:02:31 +01004421 }
4422 }
4423 }
4424
Simon Kelley824af852008-02-12 20:43:05 +00004425 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004426 new->next = daemon->mxnames;
4427 daemon->mxnames = new;
4428 new->issrv = 1;
4429 new->name = name;
4430 new->target = target;
4431 new->srvport = port;
4432 new->priority = priority;
4433 new->weight = weight;
4434 break;
4435 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004436
Simon Kelleye759d422012-03-16 13:18:57 +00004437 case LOPT_HOST_REC: /* --host-record */
4438 {
Petr Menšík59e47032018-11-02 22:39:39 +00004439 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004440
Simon Kelleye759d422012-03-16 13:18:57 +00004441 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004442 ret_err(_("Bad host-record"));
4443
Petr Menšík59e47032018-11-02 22:39:39 +00004444 new = opt_malloc(sizeof(struct host_record));
4445 memset(new, 0, sizeof(struct host_record));
4446 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004447 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004448
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004449 while (arg)
4450 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004451 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004452 char *dig;
4453
4454 for (dig = arg; *dig != 0; dig++)
4455 if (*dig < '0' || *dig > '9')
4456 break;
4457 if (*dig == 0)
4458 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004459 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004460 {
4461 new->addr = addr.addr4;
4462 new->flags |= HR_4;
4463 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004464 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004465 {
4466 new->addr6 = addr.addr6;
4467 new->flags |= HR_6;
4468 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004469 else
4470 {
4471 int nomem;
4472 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004473 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004474 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004475 {
4476 struct name_list *tmp = new->names, *next;
4477 for (tmp = new->names; tmp; tmp = next)
4478 {
4479 next = tmp->next;
4480 free(tmp);
4481 }
4482 ret_err_free(_("Bad name in host-record"), new);
4483 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004484
Petr Menšík59e47032018-11-02 22:39:39 +00004485 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004486 nl->name = canon;
4487 /* keep order, so that PTR record goes to first name */
4488 nl->next = NULL;
4489 if (!new->names)
4490 new->names = nl;
4491 else
4492 {
4493 struct name_list *tmp;
4494 for (tmp = new->names; tmp->next; tmp = tmp->next);
4495 tmp->next = nl;
4496 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004497 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004498
4499 arg = comma;
4500 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004501 }
Simon Kelleye759d422012-03-16 13:18:57 +00004502
4503 /* Keep list order */
4504 if (!daemon->host_records_tail)
4505 daemon->host_records = new;
4506 else
4507 daemon->host_records_tail->next = new;
4508 new->next = NULL;
4509 daemon->host_records_tail = new;
4510 break;
4511 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004512
4513#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004514 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004515 daemon->timestamp_file = opt_string_alloc(arg);
4516 break;
4517
Simon Kelleyf3e57872018-07-20 21:10:48 +01004518 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004519 if (arg)
4520 {
4521 if (strcmp(arg, "no") == 0)
4522 set_option_bool(OPT_DNSSEC_IGN_NS);
4523 else
4524 ret_err(_("bad value for dnssec-check-unsigned"));
4525 }
4526 break;
4527
Simon Kelleyf3e57872018-07-20 21:10:48 +01004528 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004529 {
Simon Kelleyee415862014-02-11 11:07:22 +00004530 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4531 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4532 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004533
4534 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004535 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004536
Simon Kelleycbf13a22014-01-25 17:59:14 +00004537 if ((comma = split(arg)) && (algo = split(comma)))
4538 {
4539 int class = 0;
4540 if (strcmp(comma, "IN") == 0)
4541 class = C_IN;
4542 else if (strcmp(comma, "CH") == 0)
4543 class = C_CHAOS;
4544 else if (strcmp(comma, "HS") == 0)
4545 class = C_HESIOD;
4546
4547 if (class != 0)
4548 {
4549 new->class = class;
4550 comma = algo;
4551 algo = split(comma);
4552 }
4553 }
4554
Simon Kelleyee415862014-02-11 11:07:22 +00004555 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4556 !atoi_check16(comma, &new->keytag) ||
4557 !atoi_check8(algo, &new->algo) ||
4558 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004559 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004560 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004561
Simon Kelley0fc2f312014-01-08 10:26:58 +00004562 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004563 len = (2*strlen(keyhex))+1;
4564 new->digest = opt_malloc(len);
4565 unhide_metas(keyhex);
4566 /* 4034: "Whitespace is allowed within digits" */
4567 for (cp = keyhex; *cp; )
4568 if (isspace(*cp))
4569 for (cp1 = cp; *cp1; cp1++)
4570 *cp1 = *(cp1+1);
4571 else
4572 cp++;
4573 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004574 {
4575 free(new->name);
4576 ret_err_free(_("bad HEX in trust anchor"), new);
4577 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004578
Simon Kelleyee415862014-02-11 11:07:22 +00004579 new->next = daemon->ds;
4580 daemon->ds = new;
4581
Simon Kelley0fc2f312014-01-08 10:26:58 +00004582 break;
4583 }
4584#endif
4585
Simon Kelley7622fc02009-06-04 20:32:05 +01004586 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004587 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004588
Simon Kelley849a8352006-06-09 21:02:31 +01004589 }
Simon Kelley824af852008-02-12 20:43:05 +00004590
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004591 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004592}
4593
Simon Kelley28866e92011-02-14 20:19:14 +00004594static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004595{
Simon Kelley824af852008-02-12 20:43:05 +00004596 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004597 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004598
4599 while (fgets(buff, MAXDNAME, f))
4600 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004601 int white, i;
4602 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004603 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004604 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004605
Simon Kelley824af852008-02-12 20:43:05 +00004606 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004607 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004608 {
4609 if (setjmp(mem_jmp))
4610 continue;
4611 mem_recover = 1;
4612 }
4613
Simon Kelley13dee6f2017-02-28 16:51:58 +00004614 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004615 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004616 errmess = NULL;
4617
Simon Kelley849a8352006-06-09 21:02:31 +01004618 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4619 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004620 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004621 {
4622 if (*p == '"')
4623 {
4624 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004625
Simon Kelley849a8352006-06-09 21:02:31 +01004626 for(; *p && *p != '"'; p++)
4627 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004628 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004629 {
4630 if (p[1] == 't')
4631 p[1] = '\t';
4632 else if (p[1] == 'n')
4633 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004634 else if (p[1] == 'b')
4635 p[1] = '\b';
4636 else if (p[1] == 'r')
4637 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004638 else if (p[1] == 'e') /* escape */
4639 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004640 memmove(p, p+1, strlen(p+1)+1);
4641 }
4642 *p = hide_meta(*p);
4643 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004644
4645 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004646 {
4647 errmess = _("missing \"");
4648 goto oops;
4649 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004650
4651 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004652 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004653
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004654 if (isspace(*p))
4655 {
4656 *p = ' ';
4657 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004658 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004659 else
4660 {
4661 if (white && *p == '#')
4662 {
4663 *p = 0;
4664 break;
4665 }
4666 white = 0;
4667 }
Simon Kelley849a8352006-06-09 21:02:31 +01004668 }
4669
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004670
4671 /* strip leading spaces */
4672 for (start = buff; *start && *start == ' '; start++);
4673
4674 /* strip trailing spaces */
4675 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4676
4677 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004678 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004679 else
4680 start[len] = 0;
4681
Simon Kelley611ebc52012-07-16 16:23:46 +01004682 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004683 arg = start;
4684 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004685 {
4686 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004687 for (arg = p+1; *arg == ' '; arg++);
4688 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004689 *p = 0;
4690 }
4691 else
4692 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004693
Simon Kelley611ebc52012-07-16 16:23:46 +01004694 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004695 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004696 for (option = 0, i = 0; opts[i].name; i++)
4697 if (strcmp(opts[i].name, start) == 0)
4698 {
4699 option = opts[i].val;
4700 break;
4701 }
4702
4703 if (!option)
4704 errmess = _("bad option");
4705 else if (opts[i].has_arg == 0 && arg)
4706 errmess = _("extraneous parameter");
4707 else if (opts[i].has_arg == 1 && !arg)
4708 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004709 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4710 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004711 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004712
4713 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004714 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004715 strcpy(daemon->namebuff, errmess);
4716
Simon Kelley9bafdc62018-08-21 22:53:38 +01004717 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004718 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004719 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004720 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004721 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004722 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004723 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004724 }
Simon Kelley849a8352006-06-09 21:02:31 +01004725 }
4726
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004727 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004728 fclose(f);
4729}
4730
Simon Kelley4f7bb572018-03-08 18:47:08 +00004731#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004732int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004733{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004734 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4735
Simon Kelley70d18732015-01-31 19:59:29 +00004736 if (flags & AH_DHCP_HST)
4737 return one_file(file, LOPT_BANK);
4738 else if (flags & AH_DHCP_OPT)
4739 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004740
Simon Kelley70d18732015-01-31 19:59:29 +00004741 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004742}
4743#endif
4744
Simon Kelley395eb712012-07-06 22:07:05 +01004745static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004746{
4747 FILE *f;
4748 int nofile_ok = 0;
4749 static int read_stdin = 0;
4750 static struct fileread {
4751 dev_t dev;
4752 ino_t ino;
4753 struct fileread *next;
4754 } *filesread = NULL;
4755
4756 if (hard_opt == '7')
4757 {
4758 /* default conf-file reading */
4759 hard_opt = 0;
4760 nofile_ok = 1;
4761 }
4762
4763 if (hard_opt == 0 && strcmp(file, "-") == 0)
4764 {
4765 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004766 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004767 read_stdin = 1;
4768 file = "stdin";
4769 f = stdin;
4770 }
4771 else
4772 {
4773 /* ignore repeated files. */
4774 struct stat statbuf;
4775
4776 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4777 {
4778 struct fileread *r;
4779
4780 for (r = filesread; r; r = r->next)
4781 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004782 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004783
4784 r = safe_malloc(sizeof(struct fileread));
4785 r->next = filesread;
4786 filesread = r;
4787 r->dev = statbuf.st_dev;
4788 r->ino = statbuf.st_ino;
4789 }
4790
4791 if (!(f = fopen(file, "r")))
4792 {
4793 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004794 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004795 else
4796 {
4797 char *str = _("cannot read %s: %s");
4798 if (hard_opt != 0)
4799 {
4800 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004801 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004802 }
4803 else
4804 die(str, file, EC_FILE);
4805 }
4806 }
4807 }
4808
4809 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004810 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004811}
4812
4813/* expand any name which is a directory */
4814struct hostsfile *expand_filelist(struct hostsfile *list)
4815{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004816 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004817 struct hostsfile *ah;
4818
Simon Kelley19c51cf2014-03-18 22:38:30 +00004819 /* find largest used index */
4820 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004821 {
4822 if (i <= ah->index)
4823 i = ah->index + 1;
4824
4825 if (ah->flags & AH_DIR)
4826 ah->flags |= AH_INACTIVE;
4827 else
4828 ah->flags &= ~AH_INACTIVE;
4829 }
4830
4831 for (ah = list; ah; ah = ah->next)
4832 if (!(ah->flags & AH_INACTIVE))
4833 {
4834 struct stat buf;
4835 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4836 {
4837 DIR *dir_stream;
4838 struct dirent *ent;
4839
4840 /* don't read this as a file */
4841 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004842
Simon Kelley28866e92011-02-14 20:19:14 +00004843 if (!(dir_stream = opendir(ah->fname)))
4844 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4845 ah->fname, strerror(errno));
4846 else
4847 {
4848 while ((ent = readdir(dir_stream)))
4849 {
4850 size_t lendir = strlen(ah->fname);
4851 size_t lenfile = strlen(ent->d_name);
4852 struct hostsfile *ah1;
4853 char *path;
4854
4855 /* ignore emacs backups and dotfiles */
4856 if (lenfile == 0 ||
4857 ent->d_name[lenfile - 1] == '~' ||
4858 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4859 ent->d_name[0] == '.')
4860 continue;
4861
4862 /* see if we have an existing record.
4863 dir is ah->fname
4864 file is ent->d_name
4865 path to match is ah1->fname */
4866
4867 for (ah1 = list; ah1; ah1 = ah1->next)
4868 {
4869 if (lendir < strlen(ah1->fname) &&
4870 strstr(ah1->fname, ah->fname) == ah1->fname &&
4871 ah1->fname[lendir] == '/' &&
4872 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4873 {
4874 ah1->flags &= ~AH_INACTIVE;
4875 break;
4876 }
4877 }
4878
4879 /* make new record */
4880 if (!ah1)
4881 {
4882 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4883 continue;
4884
4885 if (!(path = whine_malloc(lendir + lenfile + 2)))
4886 {
4887 free(ah1);
4888 continue;
4889 }
4890
4891 strcpy(path, ah->fname);
4892 strcat(path, "/");
4893 strcat(path, ent->d_name);
4894 ah1->fname = path;
4895 ah1->index = i++;
4896 ah1->flags = AH_DIR;
4897 ah1->next = list;
4898 list = ah1;
4899 }
4900
4901 /* inactivate record if not regular file */
4902 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4903 ah1->flags |= AH_INACTIVE;
4904
4905 }
4906 closedir(dir_stream);
4907 }
4908 }
4909 }
4910
4911 return list;
4912}
4913
Simon Kelley7b1eae42014-02-20 13:43:28 +00004914void read_servers_file(void)
4915{
4916 FILE *f;
4917
4918 if (!(f = fopen(daemon->servers_file, "r")))
4919 {
4920 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4921 return;
4922 }
4923
4924 mark_servers(SERV_FROM_FILE);
4925 cleanup_servers();
4926
4927 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4928}
4929
Simon Kelley28866e92011-02-14 20:19:14 +00004930
Simon Kelley7622fc02009-06-04 20:32:05 +01004931#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004932static void clear_dynamic_conf(void)
4933{
4934 struct dhcp_config *configs, *cp, **up;
4935
4936 /* remove existing... */
4937 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4938 {
4939 cp = configs->next;
4940
4941 if (configs->flags & CONFIG_BANK)
4942 {
4943 struct hwaddr_config *mac, *tmp;
4944 struct dhcp_netid_list *list, *tmplist;
4945
4946 for (mac = configs->hwaddr; mac; mac = tmp)
4947 {
4948 tmp = mac->next;
4949 free(mac);
4950 }
4951
4952 if (configs->flags & CONFIG_CLID)
4953 free(configs->clid);
4954
4955 for (list = configs->netid; list; list = tmplist)
4956 {
4957 free(list->list);
4958 tmplist = list->next;
4959 free(list);
4960 }
4961
4962 if (configs->flags & CONFIG_NAME)
4963 free(configs->hostname);
4964
4965 *up = configs->next;
4966 free(configs);
4967 }
4968 else
4969 up = &configs->next;
4970 }
4971}
4972
4973static void clear_dynamic_opt(void)
4974{
4975 struct dhcp_opt *opts, *cp, **up;
4976 struct dhcp_netid *id, *next;
4977
4978 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4979 {
4980 cp = opts->next;
4981
4982 if (opts->flags & DHOPT_BANK)
4983 {
4984 if ((opts->flags & DHOPT_VENDOR))
4985 free(opts->u.vendor_class);
4986 free(opts->val);
4987 for (id = opts->netid; id; id = next)
4988 {
4989 next = id->next;
4990 free(id->net);
4991 free(id);
4992 }
4993 *up = opts->next;
4994 free(opts);
4995 }
4996 else
4997 up = &opts->next;
4998 }
4999}
5000
Simon Kelley824af852008-02-12 20:43:05 +00005001void reread_dhcp(void)
5002{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005003 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005004
Simon Kelley4f7bb572018-03-08 18:47:08 +00005005 /* Do these even if there is no daemon->dhcp_hosts_file or
5006 daemon->dhcp_opts_file since entries may have been created by the
5007 inotify dynamic file reading system. */
5008
5009 clear_dynamic_conf();
5010 clear_dynamic_opt();
5011
5012 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005013 {
Simon Kelley28866e92011-02-14 20:19:14 +00005014 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5015 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005016 if (!(hf->flags & AH_INACTIVE))
5017 {
5018 if (one_file(hf->fname, LOPT_BANK))
5019 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5020 }
Simon Kelley824af852008-02-12 20:43:05 +00005021 }
5022
5023 if (daemon->dhcp_opts_file)
5024 {
Simon Kelley28866e92011-02-14 20:19:14 +00005025 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5026 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5027 if (!(hf->flags & AH_INACTIVE))
5028 {
Simon Kelley395eb712012-07-06 22:07:05 +01005029 if (one_file(hf->fname, LOPT_OPTS))
5030 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005031 }
Simon Kelley824af852008-02-12 20:43:05 +00005032 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005033
5034# ifdef HAVE_INOTIFY
5035 /* Setup notify and read pre-existing files. */
5036 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5037# endif
Simon Kelley824af852008-02-12 20:43:05 +00005038}
Simon Kelley7622fc02009-06-04 20:32:05 +01005039#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005040
Simon Kelley5aabfc72007-08-29 11:24:47 +01005041void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005042{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005043 size_t argbuf_size = MAXDNAME;
5044 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005045 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005046 int option, testmode = 0;
5047 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005048
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005049 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005050
Simon Kelley824af852008-02-12 20:43:05 +00005051 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005052 memset(daemon, 0, sizeof(struct daemon));
5053 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005054
Simon Kelley3be34542004-09-11 19:12:13 +01005055 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005056 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005057 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005058 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005059 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5060 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005061 daemon->default_resolv.is_default = 1;
5062 daemon->default_resolv.name = RESOLVFILE;
5063 daemon->resolv_files = &daemon->default_resolv;
5064 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005065 daemon->runfile = RUNFILE;
5066 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005067 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005068 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005069 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005070 daemon->auth_ttl = AUTH_TTL;
5071 daemon->soa_refresh = SOA_REFRESH;
5072 daemon->soa_retry = SOA_RETRY;
5073 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00005074 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00005075 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01005076
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005077#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005078 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5079 add_txt("authors.bind", "Simon Kelley", 0);
5080 add_txt("copyright.bind", COPYRIGHT, 0);
5081 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5082 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5083 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5084 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5085 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5086#ifdef HAVE_AUTH
5087 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5088#endif
5089 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005090#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005091
Simon Kelley849a8352006-06-09 21:02:31 +01005092 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005093 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005094#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005095 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005096#else
Simon Kelley849a8352006-06-09 21:02:31 +01005097 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005098#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005099
5100 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005101 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005102 for (; optind < argc; optind++)
5103 {
5104 unsigned char *c = (unsigned char *)argv[optind];
5105 for (; *c != 0; c++)
5106 if (!isspace(*c))
5107 die(_("junk found in command line"), NULL, EC_BADCONF);
5108 }
Simon Kelley28866e92011-02-14 20:19:14 +00005109 break;
5110 }
5111
Simon Kelley849a8352006-06-09 21:02:31 +01005112 /* Copy optarg so that argv doesn't get changed */
5113 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005114 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005115 if (strlen(optarg) >= argbuf_size)
5116 {
5117 free(argbuf);
5118 argbuf_size = strlen(optarg) + 1;
5119 argbuf = opt_malloc(argbuf_size);
5120 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005121 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005122 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005123 }
5124 else
5125 arg = NULL;
5126
5127 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005128 if (option == LOPT_TEST)
5129 testmode = 1;
5130 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005131 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005132#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005133 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005134 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005135#ifdef HAVE_DHCP6
5136 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5137 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005138#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005139 else
5140#endif
5141 do_usage();
5142
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005143 exit(0);
5144 }
Simon Kelley849a8352006-06-09 21:02:31 +01005145 else if (option == 'v')
5146 {
5147 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005148 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005149 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5150 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005151 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005152 exit(0);
5153 }
Simon Kelley849a8352006-06-09 21:02:31 +01005154 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005155 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005156 if (!conffile)
5157 conffile = opt_string_alloc(arg);
5158 else
5159 {
5160 char *extra = opt_string_alloc(arg);
5161 one_file(extra, 0);
5162 free(extra);
5163 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005164 }
Simon Kelley849a8352006-06-09 21:02:31 +01005165 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005166 {
Simon Kelley26128d22004-11-14 16:43:54 +00005167#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005168 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005169#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005170 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005171#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005172 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005173 }
5174 }
Simon Kelley849a8352006-06-09 21:02:31 +01005175
Neil Jerram3bd4c472018-01-18 22:49:38 +00005176 free(argbuf);
5177
Simon Kelley849a8352006-06-09 21:02:31 +01005178 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005179 {
Petr Menšík59e47032018-11-02 22:39:39 +00005180 one_file(conffile, 0);
5181 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005182 }
Petr Menšík59e47032018-11-02 22:39:39 +00005183 else
5184 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005185
Simon Kelley1a6bca82008-07-11 11:11:42 +01005186 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005187 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005188 {
5189 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005190 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005191 if (!(tmp->flags & SERV_HAS_SOURCE))
5192 {
5193 if (tmp->source_addr.sa.sa_family == AF_INET)
5194 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005195 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5196 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005197 }
5198 }
5199
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005200 if (daemon->host_records)
5201 {
5202 struct host_record *hr;
5203
5204 for (hr = daemon->host_records; hr; hr = hr->next)
5205 if (hr->ttl == -1)
5206 hr->ttl = daemon->local_ttl;
5207 }
5208
5209 if (daemon->cnames)
5210 {
Simon Kelley903df072017-01-19 17:22:00 +00005211 struct cname *cn, *cn2, *cn3;
5212
5213#define NOLOOP 1
5214#define TESTLOOP 2
5215
Simon Kelley157d8cf2019-10-25 17:46:49 +01005216 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005217 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005218 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005219 {
5220 if (cn->ttl == -1)
5221 cn->ttl = daemon->local_ttl;
5222 cn->flag = 0;
5223 cn->targetp = NULL;
5224 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5225 if (hostname_isequal(cn->target, cn2->alias))
5226 {
5227 cn->targetp = cn2;
5228 break;
5229 }
5230 }
5231
5232 /* Find any CNAME loops.*/
5233 for (cn = daemon->cnames; cn; cn = cn->next)
5234 {
5235 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5236 {
5237 if (cn2->flag == NOLOOP)
5238 break;
5239
5240 if (cn2->flag == TESTLOOP)
5241 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5242
5243 cn2->flag = TESTLOOP;
5244 }
5245
5246 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5247 cn3->flag = NOLOOP;
5248 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005249 }
5250
Simon Kelley3be34542004-09-11 19:12:13 +01005251 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005252 {
5253 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005254 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005255 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005256 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005257 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005258 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005259 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005260
5261 /* create default, if not specified */
5262 if (daemon->authserver && !daemon->hostmaster)
5263 {
5264 strcpy(buff, "hostmaster.");
5265 strcat(buff, daemon->authserver);
5266 daemon->hostmaster = opt_string_alloc(buff);
5267 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005268
5269 if (!daemon->dhcp_pxe_vendors)
5270 {
5271 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5272 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5273 daemon->dhcp_pxe_vendors->next = NULL;
5274 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005275
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005276 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005277 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005278 {
Simon Kelley0a852542005-03-23 20:28:59 +00005279 struct mx_srv_record *mx;
5280
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005281 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005282 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005283
Simon Kelley0a852542005-03-23 20:28:59 +00005284 for (mx = daemon->mxnames; mx; mx = mx->next)
5285 if (!mx->issrv && hostname_isequal(mx->name, buff))
5286 break;
5287
Simon Kelley28866e92011-02-14 20:19:14 +00005288 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005289 {
Simon Kelley824af852008-02-12 20:43:05 +00005290 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005291 mx->next = daemon->mxnames;
5292 mx->issrv = 0;
5293 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005294 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005295 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005296 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005297
Simon Kelley3be34542004-09-11 19:12:13 +01005298 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005299 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005300
5301 for (mx = daemon->mxnames; mx; mx = mx->next)
5302 if (!mx->issrv && !mx->target)
5303 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005304 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005305
Simon Kelley28866e92011-02-14 20:19:14 +00005306 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005307 daemon->resolv_files &&
5308 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005309 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005310 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005311
Simon Kelley28866e92011-02-14 20:19:14 +00005312 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005313 {
5314 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005315 FILE *f;
5316
Simon Kelley28866e92011-02-14 20:19:14 +00005317 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005318 !daemon->resolv_files ||
5319 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005320 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005321
Simon Kelley3be34542004-09-11 19:12:13 +01005322 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005323 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005324
5325 while ((line = fgets(buff, MAXDNAME, f)))
5326 {
5327 char *token = strtok(line, " \t\n\r");
5328
5329 if (!token || strcmp(token, "search") != 0)
5330 continue;
5331
5332 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005333 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005334 break;
5335 }
Simon Kelley3be34542004-09-11 19:12:13 +01005336
Simon Kelleyde379512004-06-22 20:23:33 +01005337 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005338
Simon Kelley3be34542004-09-11 19:12:13 +01005339 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005340 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005341 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005342
5343 if (daemon->domain_suffix)
5344 {
5345 /* add domain for any srv record without one. */
5346 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005347
Simon Kelley3d8df262005-08-29 12:19:27 +01005348 for (srv = daemon->mxnames; srv; srv = srv->next)
5349 if (srv->issrv &&
5350 strchr(srv->name, '.') &&
5351 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5352 {
5353 strcpy(buff, srv->name);
5354 strcat(buff, ".");
5355 strcat(buff, daemon->domain_suffix);
5356 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005357 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005358 }
5359 }
Simon Kelley28866e92011-02-14 20:19:14 +00005360 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005361 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005362
Simon Kelleyc8a80482014-03-05 14:29:54 +00005363 /* If there's access-control config, then ignore --local-service, it's intended
5364 as a system default to keep otherwise unconfigured installations safe. */
5365 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5366 reset_option_bool(OPT_LOCAL_SERVICE);
5367
Simon Kelley7622fc02009-06-04 20:32:05 +01005368 if (testmode)
5369 {
5370 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5371 exit(0);
5372 }
Simon Kelley849a8352006-06-09 21:02:31 +01005373}