blob: b6ab7ce4b14e7df512d842b6790caf6acd3f4adc [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley849a8352006-06-09 21:02:31 +010017/* define this to get facilitynames */
18#define SYSLOG_NAMES
Simon Kelley9e4abcb2004-01-22 19:47:41 +000019#include "dnsmasq.h"
Simon Kelley824af852008-02-12 20:43:05 +000020#include <setjmp.h>
21
Simon Kelley7622fc02009-06-04 20:32:05 +010022static volatile int mem_recover = 0;
23static jmp_buf mem_jmp;
Simon Kelley395eb712012-07-06 22:07:05 +010024static int one_file(char *file, int hard_opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010025
Simon Kelley824af852008-02-12 20:43:05 +000026/* Solaris headers don't have facility names. */
27#ifdef HAVE_SOLARIS_NETWORK
28static const struct {
29 char *c_name;
30 unsigned int c_val;
31} facilitynames[] = {
32 { "kern", LOG_KERN },
33 { "user", LOG_USER },
34 { "mail", LOG_MAIL },
35 { "daemon", LOG_DAEMON },
36 { "auth", LOG_AUTH },
37 { "syslog", LOG_SYSLOG },
38 { "lpr", LOG_LPR },
39 { "news", LOG_NEWS },
40 { "uucp", LOG_UUCP },
Simon Kelley824af852008-02-12 20:43:05 +000041 { "audit", LOG_AUDIT },
Simon Kelley824af852008-02-12 20:43:05 +000042 { "cron", LOG_CRON },
43 { "local0", LOG_LOCAL0 },
44 { "local1", LOG_LOCAL1 },
45 { "local2", LOG_LOCAL2 },
46 { "local3", LOG_LOCAL3 },
47 { "local4", LOG_LOCAL4 },
48 { "local5", LOG_LOCAL5 },
49 { "local6", LOG_LOCAL6 },
50 { "local7", LOG_LOCAL7 },
51 { NULL, 0 }
52};
53#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000054
Simon Kelley849a8352006-06-09 21:02:31 +010055#ifndef HAVE_GETOPT_LONG
Simon Kelley9e4abcb2004-01-22 19:47:41 +000056struct myoption {
57 const char *name;
58 int has_arg;
59 int *flag;
60 int val;
61};
Simon Kelley849a8352006-06-09 21:02:31 +010062#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000063
Simon Kelley9009d742008-11-14 20:04:27 +000064#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
Simon Kelley9e4abcb2004-01-22 19:47:41 +000065
Simon Kelley16972692006-10-16 20:04:18 +010066/* options which don't have a one-char version */
Simon Kelleye98bd522014-03-28 20:41:23 +000067#define LOPT_RELOAD 256
68#define LOPT_NO_NAMES 257
69#define LOPT_TFTP 258
70#define LOPT_SECURE 259
71#define LOPT_PREFIX 260
72#define LOPT_PTR 261
73#define LOPT_BRIDGE 262
74#define LOPT_TFTP_MAX 263
75#define LOPT_FORCE 264
76#define LOPT_NOBLOCK 265
77#define LOPT_LOG_OPTS 266
78#define LOPT_MAX_LOGS 267
79#define LOPT_CIRCUIT 268
80#define LOPT_REMOTE 269
81#define LOPT_SUBSCR 270
82#define LOPT_INTNAME 271
83#define LOPT_BANK 272
84#define LOPT_DHCP_HOST 273
85#define LOPT_APREF 274
86#define LOPT_OVERRIDE 275
87#define LOPT_TFTPPORTS 276
88#define LOPT_REBIND 277
89#define LOPT_NOLAST 278
90#define LOPT_OPTS 279
91#define LOPT_DHCP_OPTS 280
92#define LOPT_MATCH 281
93#define LOPT_BROADCAST 282
94#define LOPT_NEGTTL 283
95#define LOPT_ALTPORT 284
96#define LOPT_SCRIPTUSR 285
97#define LOPT_LOCAL 286
98#define LOPT_NAPTR 287
99#define LOPT_MINPORT 288
100#define LOPT_DHCP_FQDN 289
101#define LOPT_CNAME 290
102#define LOPT_PXE_PROMT 291
103#define LOPT_PXE_SERV 292
104#define LOPT_TEST 293
105#define LOPT_TAG_IF 294
106#define LOPT_PROXY 295
107#define LOPT_GEN_NAMES 296
108#define LOPT_MAXTTL 297
109#define LOPT_NO_REBIND 298
110#define LOPT_LOC_REBND 299
111#define LOPT_ADD_MAC 300
112#define LOPT_DNSSEC 301
113#define LOPT_INCR_ADDR 302
114#define LOPT_CONNTRACK 303
115#define LOPT_FQDN 304
116#define LOPT_LUASCRIPT 305
117#define LOPT_RA 306
118#define LOPT_DUID 307
119#define LOPT_HOST_REC 308
120#define LOPT_TFTP_LC 309
121#define LOPT_RR 310
122#define LOPT_CLVERBIND 311
123#define LOPT_MAXCTTL 312
124#define LOPT_AUTHZONE 313
125#define LOPT_AUTHSERV 314
126#define LOPT_AUTHTTL 315
127#define LOPT_AUTHSOA 316
128#define LOPT_AUTHSFS 317
129#define LOPT_AUTHPEER 318
130#define LOPT_IPSET 319
131#define LOPT_SYNTH 320
Simon Kelleye98bd522014-03-28 20:41:23 +0000132#define LOPT_RELAY 323
133#define LOPT_RA_PARAM 324
134#define LOPT_ADD_SBNET 325
135#define LOPT_QUIET_DHCP 326
136#define LOPT_QUIET_DHCP6 327
137#define LOPT_QUIET_RA 328
138#define LOPT_SEC_VALID 329
139#define LOPT_TRUST_ANCHOR 330
140#define LOPT_DNSSEC_DEBUG 331
141#define LOPT_REV_SERV 332
142#define LOPT_SERVERS_FILE 333
143#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000144#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000145#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100146#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000147#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000148#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000149#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000150#define LOPT_DHOPT_INOTIFY 341
151#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000152#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100153#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000154#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000155#define LOPT_CPE_ID 346
156#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000157#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000158#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100159#define LOPT_REPLY_DELAY 350
Simon Kelley734d5312018-03-23 23:09:53 +0000160#define LOPT_RAPID_COMMIT 351
Simon Kelley6b173352018-05-08 18:32:14 +0100161#define LOPT_DUMPFILE 352
162#define LOPT_DUMPMASK 353
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100163#define LOPT_UBUS 354
Simon Kelleyc8226202018-08-08 23:46:03 +0100164#define LOPT_NAME_MATCH 355
Simon Kelley974a6d02018-08-23 23:01:16 +0100165#define LOPT_CAA 356
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000166#define LOPT_SHARED_NET 357
Florent Fourcot13a58f92019-06-20 10:26:40 +0200167#define LOPT_IGNORE_CLID 358
Simon Kelley66f62652020-01-05 16:21:24 +0000168#define LOPT_SINGLE_PORT 359
Simon Kelleyee645822020-02-27 16:34:14 +0000169#define LOPT_SCRIPT_TIME 360
Wang Shanker4ded9622020-12-04 10:17:35 +0800170#define LOPT_PXE_VENDOR 361
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000171#define LOPT_DYNHOST 362
Simon Kelleyb260d222021-03-12 21:57:57 +0000172#define LOPT_LOG_DEBUG 363
Simon Kelleybec366b2016-02-24 22:03:26 +0000173
Simon Kelley849a8352006-06-09 21:02:31 +0100174#ifdef HAVE_GETOPT_LONG
175static const struct option opts[] =
176#else
177static const struct myoption opts[] =
178#endif
179 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100180 { "version", 0, 0, 'v' },
181 { "no-hosts", 0, 0, 'h' },
182 { "no-poll", 0, 0, 'n' },
183 { "help", 0, 0, 'w' },
184 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000185 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100186 { "user", 2, 0, 'u' },
187 { "group", 2, 0, 'g' },
188 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000189 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100190 { "mx-host", 1, 0, 'm' },
191 { "mx-target", 1, 0, 't' },
192 { "cache-size", 2, 0, 'c' },
193 { "port", 1, 0, 'p' },
194 { "dhcp-leasefile", 2, 0, 'l' },
195 { "dhcp-lease", 1, 0, 'l' },
196 { "dhcp-host", 1, 0, 'G' },
197 { "dhcp-range", 1, 0, 'F' },
198 { "dhcp-option", 1, 0, 'O' },
199 { "dhcp-boot", 1, 0, 'M' },
200 { "domain", 1, 0, 's' },
201 { "domain-suffix", 1, 0, 's' },
202 { "interface", 1, 0, 'i' },
203 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000204 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100205 { "bogus-priv", 0, 0, 'b' },
206 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000207 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100208 { "selfmx", 0, 0, 'e' },
209 { "filterwin2k", 0, 0, 'f' },
210 { "pid-file", 2, 0, 'x' },
211 { "strict-order", 0, 0, 'o' },
212 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000213 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 { "local", 1, 0, LOPT_LOCAL },
215 { "address", 1, 0, 'A' },
216 { "conf-file", 2, 0, 'C' },
217 { "no-resolv", 0, 0, 'R' },
218 { "expand-hosts", 0, 0, 'E' },
219 { "localmx", 0, 0, 'L' },
220 { "local-ttl", 1, 0, 'T' },
221 { "no-negcache", 0, 0, 'N' },
222 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000223 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100224 { "query-port", 1, 0, 'Q' },
225 { "except-interface", 1, 0, 'I' },
226 { "no-dhcp-interface", 1, 0, '2' },
227 { "domain-needed", 0, 0, 'D' },
228 { "dhcp-lease-max", 1, 0, 'X' },
229 { "bind-interfaces", 0, 0, 'z' },
230 { "read-ethers", 0, 0, 'Z' },
231 { "alias", 1, 0, 'V' },
232 { "dhcp-vendorclass", 1, 0, 'U' },
233 { "dhcp-userclass", 1, 0, 'j' },
234 { "dhcp-ignore", 1, 0, 'J' },
235 { "edns-packet-max", 1, 0, 'P' },
236 { "keep-in-foreground", 0, 0, 'k' },
237 { "dhcp-authoritative", 0, 0, 'K' },
238 { "srv-host", 1, 0, 'W' },
239 { "localise-queries", 0, 0, 'y' },
240 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100241 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100242 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100243 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100244 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100245 { "bootp-dynamic", 2, 0, '3' },
246 { "dhcp-mac", 1, 0, '4' },
247 { "no-ping", 0, 0, '5' },
248 { "dhcp-script", 1, 0, '6' },
249 { "conf-dir", 1, 0, '7' },
250 { "log-facility", 1, 0 ,'8' },
251 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000252 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100253 { "dns-forward-max", 1, 0, '0' },
254 { "clear-on-reload", 0, 0, LOPT_RELOAD },
255 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100256 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100257 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100258 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100259 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100260 { "tftp-root", 1, 0, LOPT_PREFIX },
261 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000262 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100263 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000264 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100265 { "ptr-record", 1, 0, LOPT_PTR },
266 { "naptr-record", 1, 0, LOPT_NAPTR },
267 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000268 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100269 { "dhcp-option-force", 1, 0, LOPT_FORCE },
270 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
271 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
272 { "log-async", 2, 0, LOPT_MAX_LOGS },
273 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
274 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
275 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800276 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100277 { "interface-name", 1, 0, LOPT_INTNAME },
278 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
279 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000280 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000281 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100282 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
283 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
284 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100285 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100286 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100287 { "dhcp-match", 1, 0, LOPT_MATCH },
288 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100289 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100290 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100291 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000292 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100293 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100294 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
295 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
296 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000297 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100298 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
299 { "cname", 1, 0, LOPT_CNAME },
300 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
301 { "pxe-service", 1, 0, LOPT_PXE_SERV },
302 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100303 { "tag-if", 1, 0, LOPT_TAG_IF },
304 { "dhcp-proxy", 2, 0, LOPT_PROXY },
305 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
306 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000307 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100308 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000309 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000310 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100311 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
312 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000313 { "dhcp-client-update", 0, 0, LOPT_FQDN },
314 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000315 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000316 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000317 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100318 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000319 { "auth-zone", 1, 0, LOPT_AUTHZONE },
320 { "auth-server", 1, 0, LOPT_AUTHSERV },
321 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
322 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000323 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000324 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000325 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100326 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200327 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000328 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000329 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100330 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000331 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000332 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100333 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100334 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100335 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
336 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
337 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100338 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000339 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000340 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100341 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000342 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100343 { "dumpfile", 1, 0, LOPT_DUMPFILE },
344 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200345 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000346 { "dynamic-host", 1, 0, LOPT_DYNHOST },
Simon Kelleyb260d222021-03-12 21:57:57 +0000347 { "log-debug", 0, 0, LOPT_LOG_DEBUG },
Simon Kelley849a8352006-06-09 21:02:31 +0100348 { NULL, 0, 0, 0 }
349 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000350
Simon Kelley28866e92011-02-14 20:19:14 +0000351
352#define ARG_DUP OPT_LAST
353#define ARG_ONE OPT_LAST + 1
354#define ARG_USED_CL OPT_LAST + 2
355#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356
Simon Kelley1a6bca82008-07-11 11:11:42 +0100357static struct {
358 int opt;
359 unsigned int rept;
360 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000361 char * const desc;
362 char * const arg;
363} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000364 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
365 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100366 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000367 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
368 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
369 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100370 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
371 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
372 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
373 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
374 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000375 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
376 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
379 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000380 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000381 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100382 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100383 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000384 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
Simon Kelley70d18732015-01-31 19:59:29 +0000385 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000386 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
387 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100388 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
389 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
390 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
391 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800392 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100393 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
394 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100395 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
396 { 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000397 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000399 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100400 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
401 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
402 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
403 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
404 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
405 { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000406 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
407 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000408 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000409 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000411 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000412 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000413 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000414 { LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000415 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000416 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000417 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
418 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
419 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
420 { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
RinSatsuki28de3872015-01-10 15:22:21 +0000421 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
422 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000423 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100424 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100425 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000426 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
427 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100428 { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp or --help dhcp6 for known DHCP options."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000429 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
430 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100431 { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000432 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
433 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
434 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100435 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
436 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100437 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100438 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000439 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100440 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
441 { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000442 { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000443 { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
Simon Kelley1a6bca82008-07-11 11:11:42 +0100444 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000445 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
446 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
447 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000448 { LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000449 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000450 { '8', ARG_ONE, "<facility>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000452 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100453 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100454 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100455 { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100456 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100457 { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
Floris Bos60704f52017-04-09 22:22:49 +0100458 { LOPT_APREF, ARG_DUP, "[=ip|mac]", gettext_noop("Add client IP or hardware address to tftp-root."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100459 { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100460 { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000461 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000462 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100463 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100464 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100465 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000466 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100467 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000468 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100469 { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100470 { LOPT_LOC_REBND, OPT_LOCAL_REBIND, NULL, gettext_noop("Allow rebinding of 127.0.0.0/8, for RBL servers."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000471 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100472 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100473 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelleyc8226202018-08-08 23:46:03 +0100474 { LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100475 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100476 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
477 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000478 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000479 { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000480 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
481 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100482 { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000483 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100484 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
485 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
486 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000487 { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
Ed Bardsleya7369be2015-08-05 21:17:18 +0100488 { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000489 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100490 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100491 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200492 { LOPT_IGNORE_CLID, OPT_IGNORE_CLID, NULL, gettext_noop("Ignore client identifier option sent by DHCP clients."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100493 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000494 { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000495 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000496 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000497 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000498 { LOPT_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), NULL },
Simon Kelley974a6d02018-08-23 23:01:16 +0100499 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100500 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000501 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000502 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000503 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000504 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000505 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000506 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
507 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100508 { LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
Gildasa9bf81a2013-10-24 13:31:40 +0100509 { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
Simon Kelley3a237152013-12-12 12:15:50 +0000510 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000511 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000512 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100513 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000514 { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000515 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100516 { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100517 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
518 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
519 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelleyb260d222021-03-12 21:57:57 +0000520 { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000521 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
522 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000523 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000524 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100525 { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
Simon Kelley734d5312018-03-23 23:09:53 +0000526 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100527 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
528 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000529 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100530 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000531};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000532
Josh Soref730c6742017-02-06 16:14:04 +0000533/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100534 character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
Simon Kelley3d8df262005-08-29 12:19:27 +0100535 following sequence so that they map to themselves: it is therefore possible to call
536 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000537 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100538 couple of other places.
539 Note that space is included here so that
540 --dhcp-option=3, string
541 has five characters, whilst
542 --dhcp-option=3," string"
543 has six.
544*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100545
Simon Kelleyf2621c72007-04-29 19:47:21 +0100546static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100547
548static char hide_meta(char c)
549{
550 unsigned int i;
551
552 for (i = 0; i < (sizeof(meta) - 1); i++)
553 if (c == meta[i])
554 return (char)i;
555
556 return c;
557}
558
559static char unhide_meta(char cr)
560{
561 unsigned int c = cr;
562
563 if (c < (sizeof(meta) - 1))
564 cr = meta[c];
565
566 return cr;
567}
568
569static void unhide_metas(char *cp)
570{
571 if (cp)
572 for(; *cp; cp++)
573 *cp = unhide_meta(*cp);
574}
575
Simon Kelley824af852008-02-12 20:43:05 +0000576static void *opt_malloc(size_t size)
577{
578 void *ret;
579
580 if (mem_recover)
581 {
582 ret = whine_malloc(size);
583 if (!ret)
584 longjmp(mem_jmp, 1);
585 }
586 else
587 ret = safe_malloc(size);
588
589 return ret;
590}
591
Petr Menšík59e47032018-11-02 22:39:39 +0000592static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100593{
594 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000595 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100596
Petr Menšík59e47032018-11-02 22:39:39 +0000597 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100598 {
Petr Menšík59e47032018-11-02 22:39:39 +0000599 ret = opt_malloc(len+1);
600 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100601
602 /* restore hidden metachars */
603 unhide_metas(ret);
604 }
605
606 return ret;
607}
608
Simon Kelley3d8df262005-08-29 12:19:27 +0100609
Simon Kelleyf2621c72007-04-29 19:47:21 +0100610/* find next comma, split string with zero and eliminate spaces.
611 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000612
613static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100614{
615 char *comma, *p;
616
Simon Kelley73a08a22009-02-05 20:28:08 +0000617 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100618 return NULL;
619
620 p = comma;
621 *comma = ' ';
622
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100623 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100624
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100625 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100626 *p = 0;
627
628 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100629}
630
Simon Kelley73a08a22009-02-05 20:28:08 +0000631static char *split(char *s)
632{
633 return split_chr(s, ',');
634}
635
Simon Kelley1f15b812009-10-13 17:49:32 +0100636static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100637{
Simon Kelley1f15b812009-10-13 17:49:32 +0100638 char *ret;
639 int nomem;
640
Simon Kelley3d8df262005-08-29 12:19:27 +0100641 if (!s)
642 return 0;
643
644 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100645 if (!(ret = canonicalise(s, &nomem)) && nomem)
646 {
647 if (mem_recover)
648 longjmp(mem_jmp, 1);
649 else
650 die(_("could not get memory"), NULL, EC_NOMEM);
651 }
652
653 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100654}
655
656static int atoi_check(char *a, int *res)
657{
658 char *p;
659
660 if (!a)
661 return 0;
662
663 unhide_metas(a);
664
665 for (p = a; *p; p++)
666 if (*p < '0' || *p > '9')
667 return 0;
668
669 *res = atoi(a);
670 return 1;
671}
672
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100673static int atoi_check16(char *a, int *res)
674{
675 if (!(atoi_check(a, res)) ||
676 *res < 0 ||
677 *res > 0xffff)
678 return 0;
679
680 return 1;
681}
Simon Kelleyee415862014-02-11 11:07:22 +0000682
Simon Kelleyde73a492014-02-17 21:43:27 +0000683#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000684static int atoi_check8(char *a, int *res)
685{
686 if (!(atoi_check(a, res)) ||
687 *res < 0 ||
688 *res > 0xff)
689 return 0;
690
691 return 1;
692}
Simon Kelleyde73a492014-02-17 21:43:27 +0000693#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100694
695#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000696static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000697{
Simon Kelley824af852008-02-12 20:43:05 +0000698 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000699
700 if (txt)
701 {
702 size_t len = strlen(txt);
703 r->txt = opt_malloc(len+1);
704 r->len = len+1;
705 *(r->txt) = len;
706 memcpy((r->txt)+1, txt, len);
707 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100708
Simon Kelleyfec216d2014-03-27 20:54:34 +0000709 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000710 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000711 r->next = daemon->txt;
712 daemon->txt = r;
713 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000714}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100715#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000716
Simon Kelley849a8352006-06-09 21:02:31 +0100717static void do_usage(void)
718{
719 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000720 int i, j;
721
722 struct {
723 char handle;
724 int val;
725 } tab[] = {
726 { '$', CACHESIZ },
727 { '*', EDNS_PKTSZ },
728 { '&', MAXLEASES },
729 { '!', FTABSIZ },
730 { '#', TFTP_MAX_CONNECTIONS },
731 { '\0', 0 }
732 };
Simon Kelley849a8352006-06-09 21:02:31 +0100733
734 printf(_("Usage: dnsmasq [options]\n\n"));
735#ifndef HAVE_GETOPT_LONG
736 printf(_("Use short options only on the command line.\n"));
737#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100738 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100739
Simon Kelley1a6bca82008-07-11 11:11:42 +0100740 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100741 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100742 char *desc = usage[i].flagdesc;
743 char *eq = "=";
744
745 if (!desc || *desc == '[')
746 eq = "";
747
748 if (!desc)
749 desc = "";
750
751 for ( j = 0; opts[j].name; j++)
752 if (opts[j].val == usage[i].opt)
753 break;
754 if (usage[i].opt < 256)
755 sprintf(buff, "-%c, ", usage[i].opt);
756 else
757 sprintf(buff, " ");
758
759 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100760 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100761
Simon Kelley849a8352006-06-09 21:02:31 +0100762 if (usage[i].arg)
763 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000764 strcpy(buff, usage[i].arg);
765 for (j = 0; tab[j].handle; j++)
766 if (tab[j].handle == *(usage[i].arg))
767 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100768 }
Simon Kelley849a8352006-06-09 21:02:31 +0100769 printf(_(usage[i].desc), buff);
770 printf("\n");
771 }
772}
773
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100774#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000775#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
776#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100777
Ed Bardsleya7369be2015-08-05 21:17:18 +0100778static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
779{
780 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
781 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100782 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
783 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100784 else
785 return _("bad address");
786
787 return NULL;
788}
789
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100790char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
791{
792 int source_port = 0, serv_port = NAMESERVER_PORT;
793 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000794 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100795 int scope_index = 0;
796 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100797
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000798 if (!arg || strlen(arg) == 0)
799 {
800 *flags |= SERV_NO_ADDR;
801 *interface = 0;
802 return NULL;
803 }
804
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100805 if ((source = split_chr(arg, '@')) && /* is there a source. */
806 (portno = split_chr(source, '#')) &&
807 !atoi_check16(portno, &source_port))
808 return _("bad port");
809
810 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
811 !atoi_check16(portno, &serv_port))
812 return _("bad port");
813
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100814 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100815
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000816 if (source) {
817 interface_opt = split_chr(source, '@');
818
819 if (interface_opt)
820 {
821#if defined(SO_BINDTODEVICE)
Petr Menšík47b45b22018-08-15 18:17:00 +0200822 safe_strncpy(interface, interface_opt, IF_NAMESIZE);
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000823#else
824 return _("interface binding not supported");
825#endif
826 }
827 }
828
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100829 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100830 {
831 addr->in.sin_port = htons(serv_port);
832 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
833#ifdef HAVE_SOCKADDR_SA_LEN
834 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
835#endif
836 source_addr->in.sin_addr.s_addr = INADDR_ANY;
837 source_addr->in.sin_port = htons(daemon->query_port);
838
839 if (source)
840 {
841 if (flags)
842 *flags |= SERV_HAS_SOURCE;
843 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100844 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100845 {
846#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000847 if (interface_opt)
848 return _("interface can only be specified once");
849
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100850 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200851 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100852#else
853 return _("interface binding not supported");
854#endif
855 }
856 }
857 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100858 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
859 {
860 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
861 return _("bad interface name");
862
863 addr->in6.sin6_port = htons(serv_port);
864 addr->in6.sin6_scope_id = scope_index;
865 source_addr->in6.sin6_addr = in6addr_any;
866 source_addr->in6.sin6_port = htons(daemon->query_port);
867 source_addr->in6.sin6_scope_id = 0;
868 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
869 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
870#ifdef HAVE_SOCKADDR_SA_LEN
871 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
872#endif
873 if (source)
874 {
875 if (flags)
876 *flags |= SERV_HAS_SOURCE;
877 source_addr->in6.sin6_port = htons(source_port);
878 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
879 {
880#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000881 if (interface_opt)
882 return _("interface can only be specified once");
883
884 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200885 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100886#else
887 return _("interface binding not supported");
888#endif
889 }
890 }
891 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100892 else
893 return _("bad address");
894
895 return NULL;
896}
897
Simon Kelleyde73a492014-02-17 21:43:27 +0000898static struct server *add_rev4(struct in_addr addr, int msize)
899{
900 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000901 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000902 char *p;
903
904 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000905 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
906
907 switch (msize)
908 {
909 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100910 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000911 /* fall through */
912 case 24:
913 p += sprintf(p, "%d.", (a >> 8) & 0xff);
914 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000915 case 16:
916 p += sprintf(p, "%d.", (a >> 16) & 0xff);
917 /* fall through */
918 case 8:
919 p += sprintf(p, "%d.", (a >> 24) & 0xff);
920 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000921 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000922 free(serv->domain);
923 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000924 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000925 }
926
927 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000928
929 serv->flags = SERV_HAS_DOMAIN;
930 serv->next = daemon->servers;
931 daemon->servers = serv;
932
933 return serv;
934
935}
936
937static struct server *add_rev6(struct in6_addr *addr, int msize)
938{
939 struct server *serv = opt_malloc(sizeof(struct server));
940 char *p;
941 int i;
942
943 memset(serv, 0, sizeof(struct server));
944 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
945
946 for (i = msize-1; i >= 0; i -= 4)
947 {
948 int dig = ((unsigned char *)addr)[i>>3];
949 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
950 }
951 p += sprintf(p, "ip6.arpa");
952
953 serv->flags = SERV_HAS_DOMAIN;
954 serv->next = daemon->servers;
955 daemon->servers = serv;
956
957 return serv;
958}
959
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000960#ifdef HAVE_DHCP
961
962static int is_tag_prefix(char *arg)
963{
964 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
965 return 1;
966
967 return 0;
968}
969
970static char *set_prefix(char *arg)
971{
972 if (strstr(arg, "set:") == arg)
973 return arg+4;
974
975 return arg;
976}
977
Simon Kelley52ec7832020-02-07 21:05:54 +0000978static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000979{
980 struct dhcp_netid *tt;
981 tt = opt_malloc(sizeof (struct dhcp_netid));
982 tt->net = opt_string_alloc(net);
983 tt->next = next;
984 return tt;
985}
986
987static void dhcp_netid_free(struct dhcp_netid *nid)
988{
989 while (nid)
990 {
991 struct dhcp_netid *tmp = nid;
992 nid = nid->next;
993 free(tmp->net);
994 free(tmp);
995 }
996}
997
998/* Parse one or more tag:s before parameters.
999 * Moves arg to the end of tags. */
1000static struct dhcp_netid * dhcp_tags(char **arg)
1001{
1002 struct dhcp_netid *id = NULL;
1003
1004 while (is_tag_prefix(*arg))
1005 {
1006 char *comma = split(*arg);
1007 id = dhcp_netid_create((*arg)+4, id);
1008 *arg = comma;
1009 };
1010 if (!*arg)
1011 {
1012 dhcp_netid_free(id);
1013 id = NULL;
1014 }
1015 return id;
1016}
1017
1018static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1019{
1020 while (netid)
1021 {
1022 struct dhcp_netid_list *tmplist = netid;
1023 netid = netid->next;
1024 dhcp_netid_free(tmplist->list);
1025 free(tmplist);
1026 }
1027}
1028
1029static void dhcp_config_free(struct dhcp_config *config)
1030{
1031 if (config)
1032 {
1033 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001034
Petr Menšík59e47032018-11-02 22:39:39 +00001035 while (hwaddr)
1036 {
1037 struct hwaddr_config *tmp = hwaddr;
1038 hwaddr = hwaddr->next;
1039 free(tmp);
1040 }
Simon Kelley137286e2020-02-06 22:09:30 +00001041
Petr Menšík59e47032018-11-02 22:39:39 +00001042 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001043 dhcp_netid_free(config->filter);
1044
Petr Menšík59e47032018-11-02 22:39:39 +00001045 if (config->flags & CONFIG_CLID)
1046 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001047
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001048#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001049 if (config->flags & CONFIG_ADDR6)
1050 {
1051 struct addrlist *addr, *tmp;
1052
1053 for (addr = config->addr6; addr; addr = tmp)
1054 {
1055 tmp = addr->next;
1056 free(addr);
1057 }
1058 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001059#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001060
Petr Menšík59e47032018-11-02 22:39:39 +00001061 free(config);
1062 }
1063}
1064
1065static void dhcp_context_free(struct dhcp_context *ctx)
1066{
1067 if (ctx)
1068 {
1069 dhcp_netid_free(ctx->filter);
1070 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001071#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001072 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001073#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001074 free(ctx);
1075 }
1076}
1077
1078static void dhcp_opt_free(struct dhcp_opt *opt)
1079{
1080 if (opt->flags & DHOPT_VENDOR)
1081 free(opt->u.vendor_class);
1082 dhcp_netid_free(opt->netid);
1083 free(opt->val);
1084 free(opt);
1085}
1086
1087
Simon Kelley832af0b2007-01-21 20:01:28 +00001088/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001089static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001090{
Simon Kelley824af852008-02-12 20:43:05 +00001091 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001092 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001093 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001094 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001095 u16 opt_len = 0;
1096 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001097 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001098
1099 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001100 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001101 new->netid = NULL;
1102 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001103 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001104
Simon Kelleyf2621c72007-04-29 19:47:21 +01001105 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001106 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001107 comma = split(arg);
1108
1109 for (cp = arg; *cp; cp++)
1110 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001111 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112
1113 if (!*cp)
1114 {
1115 new->opt = atoi(arg);
1116 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001117 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001118 break;
1119 }
1120
1121 if (strstr(arg, "option:") == arg)
1122 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001123 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1124 {
1125 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1126 /* option:<optname> must follow tag and vendor string. */
1127 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1128 option_ok = 1;
1129 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001130 break;
1131 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001132#ifdef HAVE_DHCP6
1133 else if (strstr(arg, "option6:") == arg)
1134 {
1135 for (cp = arg+8; *cp; cp++)
1136 if (*cp < '0' || *cp > '9')
1137 break;
1138
1139 if (!*cp)
1140 {
1141 new->opt = atoi(arg+8);
1142 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001143 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001144 }
1145 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001146 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001147 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1148 {
1149 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1150 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1151 option_ok = 1;
1152 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001153 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001154 /* option6:<opt>|<optname> must follow tag and vendor string. */
1155 is6 = 1;
1156 break;
1157 }
1158#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001159 else if (strstr(arg, "vendor:") == arg)
1160 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001161 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1162 new->flags |= DHOPT_VENDOR;
1163 }
1164 else if (strstr(arg, "encap:") == arg)
1165 {
1166 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001167 new->flags |= DHOPT_ENCAPSULATE;
1168 }
Simon Kelley316e2732010-01-22 20:16:09 +00001169 else if (strstr(arg, "vi-encap:") == arg)
1170 {
1171 new->u.encap = atoi(arg+9);
1172 new->flags |= DHOPT_RFC3925;
1173 if (flags == DHOPT_MATCH)
1174 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001175 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001176 break;
1177 }
1178 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001179 else
1180 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001181 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001182 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1183 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001184 }
1185
1186 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001187 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001188
1189#ifdef HAVE_DHCP6
1190 if (is6)
1191 {
1192 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001193 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001194
1195 if (opt_len == 0 &&
1196 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001197 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001198 }
1199 else
1200#endif
1201 if (opt_len == 0 &&
1202 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001203 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001204
Simon Kelley316e2732010-01-22 20:16:09 +00001205 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001206 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001207 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001208
1209 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001210 {
1211 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001212 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001213 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001214 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001215 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001216 dots = 0;
1217 for (cp = comma; (c = *cp); cp++)
1218 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001219 {
1220 addrs++;
1221 is_dec = is_hex = 0;
1222 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001223 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001224 {
1225 digs++;
1226 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001227 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001228 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001229 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001230 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001231 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001232 if (cp == comma) /* leading / means a pathname */
1233 is_addr = 0;
1234 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001235 else if (c == '.')
1236 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001237 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001238 dots++;
1239 }
1240 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001241 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001242 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001243 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001244 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001245 {
1246 is_addr = 0;
1247 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001248 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001249 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001250 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001251 *cp = 0;
1252 }
1253 else
1254 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001255 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001256 (c >='a' && c <= 'f') ||
1257 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001258 {
1259 is_hex = 0;
1260 if (c != '[' && c != ']')
1261 is_addr6 = 0;
1262 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001263 }
Simon Kelley28866e92011-02-14 20:19:14 +00001264 else
1265 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001266
Simon Kelley28866e92011-02-14 20:19:14 +00001267 if (!found_dig)
1268 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001269
1270 if (!found_colon)
1271 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001272
1273#ifdef HAVE_DHCP6
1274 /* NTP server option takes hex, addresses or FQDN */
1275 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1276 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1277#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001278
Simon Kelleyf2621c72007-04-29 19:47:21 +01001279 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001280 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001281 {
1282 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001283
1284 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001285 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001286
1287 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001288 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001289 }
Simon Kelley28866e92011-02-14 20:19:14 +00001290 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001291 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1292 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001293
1294 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1295 {
1296 int val, fac = 1;
1297
1298 switch (comma[strlen(comma) - 1])
1299 {
Simon Kelley42243212012-07-20 15:19:18 +01001300 case 'w':
1301 case 'W':
1302 fac *= 7;
1303 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001304 case 'd':
1305 case 'D':
1306 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001307 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001308 case 'h':
1309 case 'H':
1310 fac *= 60;
1311 /* fall through */
1312 case 'm':
1313 case 'M':
1314 fac *= 60;
1315 /* fall through */
1316 case 's':
1317 case 'S':
1318 comma[strlen(comma) - 1] = 0;
1319 }
1320
1321 new->len = 4;
1322 new->val = opt_malloc(4);
1323 val = atoi(comma);
1324 *((int *)new->val) = htonl(val * fac);
1325 }
1326 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001327 {
1328 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001329 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001330 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1331 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001332 }
1333 else if (is_dec)
1334 {
1335 int i, val = atoi(comma);
1336 /* assume numeric arg is 1 byte except for
1337 options where it is known otherwise.
1338 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001339 if (opt_len != 0)
1340 new->len = opt_len;
1341 else if (val & 0xffff0000)
1342 new->len = 4;
1343 else if (val & 0xff00)
1344 new->len = 2;
1345 else
1346 new->len = 1;
1347
Simon Kelley832af0b2007-01-21 20:01:28 +00001348 if (lenchar == 'b')
1349 new->len = 1;
1350 else if (lenchar == 's')
1351 new->len = 2;
1352 else if (lenchar == 'i')
1353 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001354
Simon Kelley824af852008-02-12 20:43:05 +00001355 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001356 for (i=0; i<new->len; i++)
1357 new->val[i] = val>>((new->len - i - 1)*8);
1358 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001359 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001360 {
1361 struct in_addr in;
1362 unsigned char *op;
1363 char *slash;
1364 /* max length of address/subnet descriptor is five bytes,
1365 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001366 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001367 new->flags |= DHOPT_ADDR;
1368
Simon Kelley572b41e2011-02-18 18:11:18 +00001369 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1370 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001371 {
Simon Kelley6b010842007-02-12 20:32:07 +00001372 *(op++) = 1; /* RFC 3361 "enc byte" */
1373 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001374 }
1375 while (addrs--)
1376 {
1377 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001378 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001379 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001380 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001381 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001382 if (!slash)
1383 {
1384 memcpy(op, &in, INADDRSZ);
1385 op += INADDRSZ;
1386 }
1387 else
1388 {
1389 unsigned char *p = (unsigned char *)&in;
1390 int netsize = atoi(slash);
1391 *op++ = netsize;
1392 if (netsize > 0)
1393 *op++ = *p++;
1394 if (netsize > 8)
1395 *op++ = *p++;
1396 if (netsize > 16)
1397 *op++ = *p++;
1398 if (netsize > 24)
1399 *op++ = *p++;
1400 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1401 }
1402 }
1403 new->len = op - new->val;
1404 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001405 else if (is_addr6 && is6)
1406 {
1407 unsigned char *op;
1408 new->val = op = opt_malloc(16 * addrs);
1409 new->flags |= DHOPT_ADDR6;
1410 while (addrs--)
1411 {
1412 cp = comma;
1413 comma = split(cp);
1414
1415 /* check for [1234::7] */
1416 if (*cp == '[')
1417 cp++;
1418 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1419 cp[strlen(cp)-1] = 0;
1420
1421 if (inet_pton(AF_INET6, cp, op))
1422 {
1423 op += IN6ADDRSZ;
1424 continue;
1425 }
Petr Menšík59e47032018-11-02 22:39:39 +00001426
1427 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001428 }
1429 new->len = op - new->val;
1430 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001431 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001432 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001433 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001434 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001435 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001436 {
1437 /* dns search, RFC 3397, or SIP, RFC 3361 */
1438 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001439 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001440 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001441 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001442
1443 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001444 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001445
1446 while (arg && *arg)
1447 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001448 char *in, *dom = NULL;
1449 size_t domlen = 1;
1450 /* Allow "." as an empty domain */
1451 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001452 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001453 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001454 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001455
Simon Kelleyc52e1892010-06-07 22:01:39 +01001456 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001457 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001458
1459 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001460 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001461 {
1462 memcpy(newp, m, header_size + len);
1463 free(m);
1464 }
Simon Kelley824af852008-02-12 20:43:05 +00001465 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001466 p = m + header_size;
1467 q = p + len;
1468
1469 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001470 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001471 {
1472 unsigned char *cp = q++;
1473 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001474 for (j = 0; *in && (*in != '.'); in++, j++)
1475 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001476 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001477 if (*in)
1478 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001479 }
1480 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001481 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001482
Simon Kelley832af0b2007-01-21 20:01:28 +00001483 /* Now tail-compress using earlier names. */
1484 newlen = q - p;
1485 for (tail = p + len; *tail; tail += (*tail) + 1)
1486 for (r = p; r - p < (int)len; r += (*r) + 1)
1487 if (strcmp((char *)r, (char *)tail) == 0)
1488 {
1489 PUTSHORT((r - p) | 0xc000, tail);
1490 newlen = tail - p;
1491 goto end;
1492 }
1493 end:
1494 len = newlen;
1495
1496 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001497 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001498 }
1499
1500 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001501 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001502 m[0] = 0;
1503 new->len = (int) len + header_size;
1504 new->val = m;
1505 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001506#ifdef HAVE_DHCP6
1507 else if (comma && (opt_len & OT_CSTRING))
1508 {
1509 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001510 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001511 unsigned char *p, *newp;
1512
Simon Kelley40ef23b2012-03-13 21:59:28 +00001513 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001514 if (comma[i] == ',')
1515 commas++;
1516
1517 newp = opt_malloc(strlen(comma)+(2*commas));
1518 p = newp;
1519 arg = comma;
1520 comma = split(arg);
1521
1522 while (arg && *arg)
1523 {
1524 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001525 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001526 PUTSHORT(len, p);
1527 memcpy(p, arg, len);
1528 p += len;
1529
1530 arg = comma;
1531 comma = split(arg);
1532 }
1533
1534 new->val = newp;
1535 new->len = p - newp;
1536 }
1537 else if (comma && (opt_len & OT_RFC1035_NAME))
1538 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001539 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001540 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001541 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001542 arg = comma;
1543 comma = split(arg);
1544
1545 while (arg && *arg)
1546 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001547 char *dom = canonicalise_opt(arg);
1548 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001549 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001550
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001551 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001552
1553 if (p)
1554 {
1555 memcpy(newp, p, len);
1556 free(p);
1557 }
1558
1559 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001560 q = p + len;
1561 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001562 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001563 if (is6 && new->opt == OPTION6_NTP_SERVER)
1564 {
1565 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1566 PUTSHORT(end - q - 2, q);
1567 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001568 len = end - p;
1569 free(dom);
1570
Simon Kelley4cb1b322012-02-06 14:30:41 +00001571 arg = comma;
1572 comma = split(arg);
1573 }
1574
Simon Kelley18f0fb02012-03-31 21:18:55 +01001575 new->val = p;
1576 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001577 }
1578#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001579 else
1580 {
1581 new->len = strlen(comma);
1582 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001583 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001584 new->flags |= DHOPT_STRING;
1585 }
1586 }
1587 }
1588
Simon Kelley4cb1b322012-02-06 14:30:41 +00001589 if (!is6 &&
1590 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001591 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001592 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001593 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001594
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001595 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001596 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001597 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1598 !new->netid ||
1599 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001600 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001601
1602 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001603 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001604 new->next = daemon->dhcp_match6;
1605 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001606 }
1607 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001608 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001609 new->next = daemon->dhcp_match;
1610 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001611 }
Simon Kelley824af852008-02-12 20:43:05 +00001612 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001613 else if (is6)
1614 {
1615 new->next = daemon->dhcp_opts6;
1616 daemon->dhcp_opts6 = new;
1617 }
1618 else
1619 {
1620 new->next = daemon->dhcp_opts;
1621 daemon->dhcp_opts = new;
1622 }
1623
1624 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001625on_error:
1626 dhcp_opt_free(new);
1627 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001628}
1629
Simon Kelley7622fc02009-06-04 20:32:05 +01001630#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001631
Simon Kelley28866e92011-02-14 20:19:14 +00001632void set_option_bool(unsigned int opt)
1633{
Petr Menšík24b87602018-10-24 22:30:18 +01001634 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001635}
1636
Simon Kelley2b5bae92012-06-26 16:55:23 +01001637void reset_option_bool(unsigned int opt)
1638{
Petr Menšík24b87602018-10-24 22:30:18 +01001639 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001640}
1641
Petr Menšík59e47032018-11-02 22:39:39 +00001642static void server_list_free(struct server *list)
1643{
1644 while (list)
1645 {
1646 struct server *tmp = list;
1647 list = list->next;
1648 free(tmp);
1649 }
1650}
1651
Simon Kelley7b1eae42014-02-20 13:43:28 +00001652static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
Simon Kelley849a8352006-06-09 21:02:31 +01001653{
1654 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001655 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001656
Simon Kelley832af0b2007-01-21 20:01:28 +00001657 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001658 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001659
Simon Kelley1a6bca82008-07-11 11:11:42 +01001660 for (i=0; usage[i].opt != 0; i++)
1661 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001662 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001663 int rept = usage[i].rept;
1664
Simon Kelley28866e92011-02-14 20:19:14 +00001665 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001666 {
1667 /* command line */
1668 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001669 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001670 if (rept == ARG_ONE)
1671 usage[i].rept = ARG_USED_CL;
1672 }
1673 else
1674 {
1675 /* allow file to override command line */
1676 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001677 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001678 if (rept == ARG_USED_CL || rept == ARG_ONE)
1679 usage[i].rept = ARG_USED_FILE;
1680 }
1681
1682 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1683 {
Simon Kelley28866e92011-02-14 20:19:14 +00001684 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001685 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001686 }
1687
1688 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001689 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001690
Simon Kelley849a8352006-06-09 21:02:31 +01001691 switch (option)
1692 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001693 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001694 {
Simon Kelley824af852008-02-12 20:43:05 +00001695 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001696 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001697 {
Simon Kelley28866e92011-02-14 20:19:14 +00001698 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001699 free(file);
1700 }
Simon Kelley849a8352006-06-09 21:02:31 +01001701 break;
1702 }
1703
Simon Kelleyf2621c72007-04-29 19:47:21 +01001704 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001705 {
1706 DIR *dir_stream;
1707 struct dirent *ent;
1708 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001709 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001710 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001711 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001712 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001713
Simon Kelley1f15b812009-10-13 17:49:32 +01001714 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001715 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001716 break;
1717
Simon Kelley1f15b812009-10-13 17:49:32 +01001718 for (arg = comma; arg; arg = comma)
1719 {
1720 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001721 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001722 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001723 li = opt_malloc(sizeof(struct list));
1724 if (*arg == '*')
1725 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001726 /* "*" with no suffix is a no-op */
1727 if (arg[1] == 0)
1728 free(li);
1729 else
1730 {
1731 li->next = match_suffix;
1732 match_suffix = li;
1733 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001734 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001735 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001736 }
1737 else
1738 {
1739 li->next = ignore_suffix;
1740 ignore_suffix = li;
1741 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001742 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001743 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001744 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001745 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001746
Simon Kelley849a8352006-06-09 21:02:31 +01001747 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001748 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001749
Simon Kelley849a8352006-06-09 21:02:31 +01001750 while ((ent = readdir(dir_stream)))
1751 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001752 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001753 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001754
1755 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001756 if (len == 0 ||
1757 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001758 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1759 ent->d_name[0] == '.')
1760 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001761
Simon Kelley3e1551a2014-09-09 21:46:07 +01001762 if (match_suffix)
1763 {
1764 for (li = match_suffix; li; li = li->next)
1765 {
1766 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001767 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001768 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001769 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001770 break;
1771 }
1772 if (!li)
1773 continue;
1774 }
1775
Simon Kelley1f15b812009-10-13 17:49:32 +01001776 for (li = ignore_suffix; li; li = li->next)
1777 {
1778 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001779 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001780 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001781 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001782 break;
1783 }
1784 if (li)
1785 continue;
1786
Simon Kelley824af852008-02-12 20:43:05 +00001787 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001788 strcpy(path, directory);
1789 strcat(path, "/");
1790 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001791
Simon Kelley39595cf2013-02-04 21:40:07 +00001792 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001793 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001794 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001795
Simon Kelley39595cf2013-02-04 21:40:07 +00001796 /* only reg files allowed. */
1797 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001798 {
1799 /* sort files into order. */
1800 struct list **up, *new = opt_malloc(sizeof(struct list));
1801 new->name = path;
1802
1803 for (up = &files, li = files; li; up = &li->next, li = li->next)
1804 if (strcmp(li->name, path) >=0)
1805 break;
1806
1807 new->next = li;
1808 *up = new;
1809 }
1810
Simon Kelley849a8352006-06-09 21:02:31 +01001811 }
Simon Kelleyab538832020-01-10 20:44:48 +00001812
1813 for (li = files; li; li = li->next)
1814 one_file(li->name, 0);
1815
Simon Kelley849a8352006-06-09 21:02:31 +01001816 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001817 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001818 for(; ignore_suffix; ignore_suffix = li)
1819 {
1820 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001821 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001822 free(ignore_suffix);
1823 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001824 for(; match_suffix; match_suffix = li)
1825 {
1826 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001827 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001828 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001829 }
Simon Kelleyab538832020-01-10 20:44:48 +00001830 for(; files; files = li)
1831 {
1832 li = files->next;
1833 free(files->name);
1834 free(files);
1835 }
Simon Kelley849a8352006-06-09 21:02:31 +01001836 break;
1837 }
1838
Simon Kelleyed4c0762013-10-08 20:46:34 +01001839 case LOPT_ADD_SBNET: /* --add-subnet */
1840 set_option_bool(OPT_CLIENT_SUBNET);
1841 if (arg)
1842 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001843 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001844 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001845
1846 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1847 if ((end = split_chr(arg, '/')))
1848 {
1849 /* has subnet+len */
1850 err = parse_mysockaddr(arg, &new->addr);
1851 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001852 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001853 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001854 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001855 new->addr_used = 1;
1856 }
1857 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001858 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001859
1860 daemon->add_subnet4 = new;
1861
Ed Bardsleya7369be2015-08-05 21:17:18 +01001862 if (comma)
1863 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001864 new = opt_malloc(sizeof(struct mysubnet));
1865 if ((end = split_chr(comma, '/')))
1866 {
1867 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001868 err = parse_mysockaddr(comma, &new->addr);
1869 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001870 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001871 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001872 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001873 new->addr_used = 1;
1874 }
1875 else
1876 {
1877 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001878 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001879 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001880
1881 daemon->add_subnet6 = new;
1882 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001883 }
1884 break;
1885
Simon Kelleyad094272012-08-10 17:10:54 +01001886 case '1': /* --enable-dbus */
1887 set_option_bool(OPT_DBUS);
1888 if (arg)
1889 daemon->dbus_name = opt_string_alloc(arg);
1890 else
1891 daemon->dbus_name = DNSMASQ_SERVICE;
1892 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001893
1894 case LOPT_UBUS: /* --enable-ubus */
1895 set_option_bool(OPT_UBUS);
1896 if (arg)
1897 daemon->ubus_name = opt_string_alloc(arg);
1898 else
1899 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1900 break;
1901
Simon Kelleyf2621c72007-04-29 19:47:21 +01001902 case '8': /* --log-facility */
1903 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001904 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001905 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001906 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001907 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001908#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001909 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001910#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001911 for (i = 0; facilitynames[i].c_name; i++)
1912 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1913 break;
1914
1915 if (facilitynames[i].c_name)
1916 daemon->log_fac = facilitynames[i].c_val;
1917 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001918 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001919#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001920 }
1921 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001922
Simon Kelleyf2621c72007-04-29 19:47:21 +01001923 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001924 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001925 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001926
Simon Kelleyf2621c72007-04-29 19:47:21 +01001927 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001928 {
Simon Kelley824af852008-02-12 20:43:05 +00001929 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001930 struct resolvc *new, *list = daemon->resolv_files;
1931
1932 if (list && list->is_default)
1933 {
1934 /* replace default resolv file - possibly with nothing */
1935 if (name)
1936 {
1937 list->is_default = 0;
1938 list->name = name;
1939 }
1940 else
1941 list = NULL;
1942 }
1943 else if (name)
1944 {
Simon Kelley824af852008-02-12 20:43:05 +00001945 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001946 new->next = list;
1947 new->name = name;
1948 new->is_default = 0;
1949 new->mtime = 0;
1950 new->logged = 0;
1951 list = new;
1952 }
1953 daemon->resolv_files = list;
1954 break;
1955 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001956
1957 case LOPT_SERVERS_FILE:
1958 daemon->servers_file = opt_string_alloc(arg);
1959 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001960
Simon Kelleyf2621c72007-04-29 19:47:21 +01001961 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001962 {
1963 int pref = 1;
1964 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001965 char *name, *target = NULL;
1966
Simon Kelleyf2621c72007-04-29 19:47:21 +01001967 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001968 {
1969 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001970 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001971 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001972 }
1973
Simon Kelley1f15b812009-10-13 17:49:32 +01001974 if (!(name = canonicalise_opt(arg)) ||
1975 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001976 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001977
Simon Kelley824af852008-02-12 20:43:05 +00001978 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001979 new->next = daemon->mxnames;
1980 daemon->mxnames = new;
1981 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001982 new->name = name;
1983 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001984 new->weight = pref;
1985 break;
1986 }
1987
Simon Kelleyf2621c72007-04-29 19:47:21 +01001988 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001989 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001990 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001991 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001992
Simon Kelley6b173352018-05-08 18:32:14 +01001993 case LOPT_DUMPFILE: /* --dumpfile */
1994 daemon->dump_file = opt_string_alloc(arg);
1995 break;
1996
1997 case LOPT_DUMPMASK: /* --dumpmask */
1998 daemon->dump_mask = strtol(arg, NULL, 0);
1999 break;
2000
Simon Kelley7622fc02009-06-04 20:32:05 +01002001#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002002 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002003 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002004 break;
2005
Simon Kelleyc72daea2012-01-05 21:33:27 +00002006 /* Sorry about the gross pre-processor abuse */
2007 case '6': /* --dhcp-script */
2008 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002009# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002010 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002011# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002012 if (option == LOPT_LUASCRIPT)
2013# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002014 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002015# else
2016 daemon->luascript = opt_string_alloc(arg);
2017# endif
2018 else
2019 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002020# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002021 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002022#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002023
Simon Kelley70d18732015-01-31 19:59:29 +00002024 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2025 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2026 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2027 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2028 case LOPT_HOST_INOTIFY: /* --hostsdir */
2029 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002030 {
Simon Kelley824af852008-02-12 20:43:05 +00002031 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002032 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002033 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002034 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002035 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002036 if (option == 'H')
2037 {
2038 new->next = daemon->addn_hosts;
2039 daemon->addn_hosts = new;
2040 }
2041 else if (option == LOPT_DHCP_HOST)
2042 {
2043 new->next = daemon->dhcp_hosts_file;
2044 daemon->dhcp_hosts_file = new;
2045 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002046 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002047 {
2048 new->next = daemon->dhcp_opts_file;
2049 daemon->dhcp_opts_file = new;
2050 }
Simon Kelley70d18732015-01-31 19:59:29 +00002051 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002052 {
Simon Kelley70d18732015-01-31 19:59:29 +00002053 new->next = daemon->dynamic_dirs;
2054 daemon->dynamic_dirs = new;
2055 if (option == LOPT_DHCP_INOTIFY)
2056 new->flags |= AH_DHCP_HST;
2057 else if (option == LOPT_DHOPT_INOTIFY)
2058 new->flags |= AH_DHCP_OPT;
2059 else if (option == LOPT_HOST_INOTIFY)
2060 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002061 }
2062
Simon Kelley849a8352006-06-09 21:02:31 +01002063 break;
2064 }
2065
Simon Kelley4f7b3042012-11-28 21:27:02 +00002066 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002067 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002068
Simon Kelley4f7b3042012-11-28 21:27:02 +00002069 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002070
2071 while ((arg = comma))
2072 {
2073 struct iname *new = opt_malloc(sizeof(struct iname));
2074 comma = split(arg);
2075 new->name = NULL;
2076 unhide_metas(arg);
2077 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2078 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002079 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2080 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002081 else
2082 {
2083 char *fam = split_chr(arg, '/');
2084 new->name = opt_string_alloc(arg);
2085 new->addr.sa.sa_family = 0;
2086 if (fam)
2087 {
2088 if (strcmp(fam, "4") == 0)
2089 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002090 else if (strcmp(fam, "6") == 0)
2091 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002092 else
Petr Menšík59e47032018-11-02 22:39:39 +00002093 {
2094 free(new->name);
2095 ret_err_free(gen_err, new);
2096 }
Simon Kelley08933472018-10-05 16:34:35 +01002097 }
2098 }
2099 new->next = daemon->authinterface;
2100 daemon->authinterface = new;
2101 };
Simon Kelley429798f2012-12-10 20:45:53 +00002102
Simon Kelley4f7b3042012-11-28 21:27:02 +00002103 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002104
2105 case LOPT_AUTHSFS: /* --auth-sec-servers */
2106 {
2107 struct name_list *new;
2108
2109 do {
2110 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002111 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002112 new->name = opt_string_alloc(arg);
2113 new->next = daemon->secondary_forward_server;
2114 daemon->secondary_forward_server = new;
2115 arg = comma;
2116 } while (arg);
2117 break;
2118 }
2119
Simon Kelley4f7b3042012-11-28 21:27:02 +00002120 case LOPT_AUTHZONE: /* --auth-zone */
2121 {
2122 struct auth_zone *new;
2123
2124 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002125
Simon Kelley429798f2012-12-10 20:45:53 +00002126 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002127 new->domain = opt_string_alloc(arg);
2128 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002129 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002130 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002131 new->next = daemon->auth_zones;
2132 daemon->auth_zones = new;
2133
2134 while ((arg = comma))
2135 {
2136 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002137 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002138 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002139 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002140 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002141
2142 comma = split(arg);
2143 prefix = split_chr(arg, '/');
2144
2145 if (prefix && !atoi_check(prefix, &prefixlen))
2146 ret_err(gen_err);
2147
Mathias Kresin094bfae2016-07-24 14:15:22 +01002148 if (strstr(arg, "exclude:") == arg)
2149 {
2150 is_exclude = 1;
2151 arg = arg+8;
2152 }
2153
Simon Kelleycc921df2019-01-02 22:48:59 +00002154 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002155 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002156 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002157 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002158 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002159 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002160 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002161 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002162 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002163 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002164 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002165 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002166 else
2167 {
2168 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2169 name->name = opt_string_alloc(arg);
2170 name->flags = AUTH4 | AUTH6;
2171 name->next = new->interface_names;
2172 new->interface_names = name;
2173 if (prefix)
2174 {
2175 if (prefixlen == 4)
2176 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002177 else if (prefixlen == 6)
2178 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002179 else
2180 ret_err(gen_err);
2181 }
2182 }
2183
2184 if (subnet)
2185 {
2186 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002187
2188 if (is_exclude)
2189 {
2190 subnet->next = new->exclude;
2191 new->exclude = subnet;
2192 }
2193 else
2194 {
2195 subnet->next = new->subnet;
2196 new->subnet = subnet;
2197 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002198 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002199 }
2200 break;
2201 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002202
Simon Kelley4f7b3042012-11-28 21:27:02 +00002203 case LOPT_AUTHSOA: /* --auth-soa */
2204 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002205 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002206 if (comma)
2207 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002208 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002209 arg = comma;
2210 comma = split(arg);
2211 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002212 for (cp = daemon->hostmaster; *cp; cp++)
2213 if (*cp == '@')
2214 *cp = '.';
2215
Simon Kelley4f7b3042012-11-28 21:27:02 +00002216 if (comma)
2217 {
2218 arg = comma;
2219 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002220 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002221 if (comma)
2222 {
2223 arg = comma;
2224 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002225 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002226 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002227 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002228 }
2229 }
2230 }
2231
2232 break;
2233
Simon Kelley2bb73af2013-04-24 17:38:19 +01002234 case 's': /* --domain */
2235 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002236 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002237 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002238 else
Simon Kelley9009d742008-11-14 20:04:27 +00002239 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002240 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002241 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002242 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002243 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002244 else
2245 {
Simon Kelley9009d742008-11-14 20:04:27 +00002246 if (comma)
2247 {
Simon Kelley429798f2012-12-10 20:45:53 +00002248 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002249 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002250
Simon Kelley48fd1c42013-04-25 09:49:38 +01002251 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002252 new->indexed = 0;
2253
Simon Kelley9009d742008-11-14 20:04:27 +00002254 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002255 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002256 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002257 int msize;
2258
Simon Kelley28866e92011-02-14 20:19:14 +00002259 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002260 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002261 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002262 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002263 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002264 int mask = (1 << (32 - msize)) - 1;
2265 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002266 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2267 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002268 if (arg)
2269 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002270 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002271 {
2272 if (!(new->prefix = canonicalise_opt(arg)) ||
2273 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002274 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002275 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002276 else if (strcmp(arg, "local") != 0 ||
2277 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002278 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002279 else
2280 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002281 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002282 local=/xxx.yyy.zzz.in-addr.arpa/ */
2283 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002284 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002285 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002286
Simon Kelleyde73a492014-02-17 21:43:27 +00002287 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002288
2289 /* local=/<domain>/ */
2290 serv = opt_malloc(sizeof(struct server));
2291 memset(serv, 0, sizeof(struct server));
2292 serv->domain = d;
2293 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2294 serv->next = daemon->servers;
2295 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002296 }
2297 }
Simon Kelley9009d742008-11-14 20:04:27 +00002298 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002299 else if (inet_pton(AF_INET6, comma, &new->start6))
2300 {
2301 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2302 u64 addrpart = addr6part(&new->start6);
2303 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002304
Simon Kelleyd74942a2012-02-07 20:51:56 +00002305 /* prefix==64 overflows the mask calculation above */
2306 if (msize == 64)
2307 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002308
Simon Kelleyd74942a2012-02-07 20:51:56 +00002309 new->end6 = new->start6;
2310 setaddr6part(&new->start6, addrpart & ~mask);
2311 setaddr6part(&new->end6, addrpart | mask);
2312
2313 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002314 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002315 else if (arg)
2316 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002317 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002318 {
2319 if (!(new->prefix = canonicalise_opt(arg)) ||
2320 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002321 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002322 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002323 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002324 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002325 else
2326 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002327 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002328 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002329 struct server *serv = add_rev6(&new->start6, msize);
2330 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002331
2332 /* local=/<domain>/ */
2333 serv = opt_malloc(sizeof(struct server));
2334 memset(serv, 0, sizeof(struct server));
2335 serv->domain = d;
2336 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2337 serv->next = daemon->servers;
2338 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002339 }
2340 }
2341 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002342 else
Petr Menšík59e47032018-11-02 22:39:39 +00002343 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002344 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002345 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002346 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002347 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002348 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002349 prefstr = split(arg);
2350
Simon Kelleyd74942a2012-02-07 20:51:56 +00002351 if (inet_pton(AF_INET, comma, &new->start))
2352 {
2353 new->is6 = 0;
2354 if (!arg)
2355 new->end.s_addr = new->start.s_addr;
2356 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002357 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002358 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002359 else if (inet_pton(AF_INET6, comma, &new->start6))
2360 {
2361 new->is6 = 1;
2362 if (!arg)
2363 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2364 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002365 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002366 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002367 else
Petr Menšík59e47032018-11-02 22:39:39 +00002368 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002369
2370 if (option != 's' && prefstr)
2371 {
2372 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2373 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002374 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002375 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002376 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002377
2378 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002379 if (option == 's')
2380 {
2381 new->next = daemon->cond_domain;
2382 daemon->cond_domain = new;
2383 }
2384 else
2385 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002386 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002387 new->next = daemon->synth_domains;
2388 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002389 if (new->prefix &&
2390 (star = strrchr(new->prefix, '*'))
2391 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002392 {
2393 *star = 0;
2394 new->indexed = 1;
2395 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002396 }
Simon Kelley9009d742008-11-14 20:04:27 +00002397 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002398 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002399 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002400 else
2401 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002402 }
2403 }
Simon Kelley849a8352006-06-09 21:02:31 +01002404 break;
2405
Simon Kelley1e505122016-01-25 21:29:23 +00002406 case LOPT_CPE_ID: /* --add-dns-client */
2407 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002408 daemon->dns_client_id = opt_string_alloc(arg);
2409 break;
2410
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002411 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002412 if (!arg)
2413 set_option_bool(OPT_ADD_MAC);
2414 else
2415 {
2416 unhide_metas(arg);
2417 if (strcmp(arg, "base64") == 0)
2418 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002419 else if (strcmp(arg, "text") == 0)
2420 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002421 else
2422 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002423 }
2424 break;
2425
Simon Kelleyf2621c72007-04-29 19:47:21 +01002426 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002427 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002428 break;
2429
Simon Kelleyf2621c72007-04-29 19:47:21 +01002430 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002431 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002432 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002433 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002434
Simon Kelley7622fc02009-06-04 20:32:05 +01002435#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002436 case LOPT_SCRIPTUSR: /* --scriptuser */
2437 daemon->scriptuser = opt_string_alloc(arg);
2438 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002439#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002440
Simon Kelleyf2621c72007-04-29 19:47:21 +01002441 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002442 do {
Simon Kelley824af852008-02-12 20:43:05 +00002443 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002444 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002445 new->next = daemon->if_names;
2446 daemon->if_names = new;
2447 /* new->name may be NULL if someone does
2448 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002449 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002450 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002451 arg = comma;
2452 } while (arg);
2453 break;
2454
Simon Kelley2937f8a2013-07-29 19:49:07 +01002455 case LOPT_TFTP: /* --enable-tftp */
2456 set_option_bool(OPT_TFTP);
2457 if (!arg)
2458 break;
2459 /* fall through */
2460
Simon Kelleyf2621c72007-04-29 19:47:21 +01002461 case 'I': /* --except-interface */
2462 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002463 do {
Simon Kelley824af852008-02-12 20:43:05 +00002464 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002465 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002466 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002467 if (option == 'I')
2468 {
2469 new->next = daemon->if_except;
2470 daemon->if_except = new;
2471 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002472 else if (option == LOPT_TFTP)
2473 {
2474 new->next = daemon->tftp_interfaces;
2475 daemon->tftp_interfaces = new;
2476 }
Simon Kelley849a8352006-06-09 21:02:31 +01002477 else
2478 {
2479 new->next = daemon->dhcp_except;
2480 daemon->dhcp_except = new;
2481 }
2482 arg = comma;
2483 } while (arg);
2484 break;
2485
Simon Kelleyf2621c72007-04-29 19:47:21 +01002486 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002487 case LOPT_IGNORE_ADDR: /* --ignore-address */
2488 {
Simon Kelley849a8352006-06-09 21:02:31 +01002489 struct in_addr addr;
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002490 int prefix = 32;
Simon Kelley849a8352006-06-09 21:02:31 +01002491 unhide_metas(arg);
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002492
2493 if (!arg ||
2494 ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
2495 (inet_pton(AF_INET, arg, &addr) != 1))
2496 ret_err(gen_err); /* error */
2497 else
Simon Kelley849a8352006-06-09 21:02:31 +01002498 {
Simon Kelley824af852008-02-12 20:43:05 +00002499 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002500 if (option == 'B')
2501 {
2502 baddr->next = daemon->bogus_addr;
2503 daemon->bogus_addr = baddr;
2504 }
2505 else
2506 {
2507 baddr->next = daemon->ignore_addr;
2508 daemon->ignore_addr = baddr;
2509 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002510 baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
2511 baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
Simon Kelley849a8352006-06-09 21:02:31 +01002512 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002513 break;
2514 }
Simon Kelley849a8352006-06-09 21:02:31 +01002515
Simon Kelleyf2621c72007-04-29 19:47:21 +01002516 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002517 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002518 do {
Simon Kelley824af852008-02-12 20:43:05 +00002519 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002520 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002521 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002522 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002523 {
2524 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002525 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002526#ifdef HAVE_SOCKADDR_SA_LEN
2527 new->addr.in.sin_len = sizeof(new->addr.in);
2528#endif
2529 }
Simon Kelley849a8352006-06-09 21:02:31 +01002530 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2531 {
2532 new->addr.sa.sa_family = AF_INET6;
2533 new->addr.in6.sin6_flowinfo = 0;
2534 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002535 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002536#ifdef HAVE_SOCKADDR_SA_LEN
2537 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2538#endif
2539 }
Simon Kelley849a8352006-06-09 21:02:31 +01002540 else
Petr Menšík59e47032018-11-02 22:39:39 +00002541 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002542
2543 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002544 if (option == 'a')
2545 {
2546 new->next = daemon->if_addrs;
2547 daemon->if_addrs = new;
2548 }
2549 else
2550 {
2551 new->next = daemon->auth_peers;
2552 daemon->auth_peers = new;
2553 }
Simon Kelley849a8352006-06-09 21:02:31 +01002554 arg = comma;
2555 } while (arg);
2556 break;
2557
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002558 case 'S': /* --server */
2559 case LOPT_LOCAL: /* --local */
2560 case 'A': /* --address */
2561 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002562 {
2563 struct server *serv, *newlist = NULL;
2564
2565 unhide_metas(arg);
2566
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002567 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002568 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002569 int rebind = !(*arg == '/');
2570 char *end = NULL;
2571 if (!rebind)
2572 arg++;
2573 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002574 {
2575 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002576 /* elide leading dots - they are implied in the search algorithm */
2577 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002578 /* # matches everything and becomes a zero length domain string */
2579 if (strcmp(arg, "#") == 0)
2580 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002581 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002582 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002583 serv = opt_malloc(sizeof(struct server));
2584 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002585 serv->next = newlist;
2586 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002587 serv->domain = domain;
2588 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002589 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002590 if (rebind)
2591 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002592 }
2593 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002594 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002595 }
2596 else
2597 {
Simon Kelley824af852008-02-12 20:43:05 +00002598 newlist = opt_malloc(sizeof(struct server));
2599 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002600#ifdef HAVE_LOOP
2601 newlist->uid = rand32();
2602#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002603 }
2604
Simon Kelley7b1eae42014-02-20 13:43:28 +00002605 if (servers_only && option == 'S')
2606 newlist->flags |= SERV_FROM_FILE;
2607
Simon Kelley849a8352006-06-09 21:02:31 +01002608 if (option == 'A')
2609 {
2610 newlist->flags |= SERV_LITERAL_ADDRESS;
2611 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002612 {
2613 server_list_free(newlist);
2614 ret_err(gen_err);
2615 }
Simon Kelley849a8352006-06-09 21:02:31 +01002616 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002617 else if (option == LOPT_NO_REBIND)
2618 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002619
2620 if (!arg || !*arg)
2621 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002622 if (!(newlist->flags & SERV_NO_REBIND))
2623 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002624 }
2625
2626 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002627 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002628 else
2629 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002630 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2631 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002632 {
2633 server_list_free(newlist);
2634 ret_err(err);
2635 }
Simon Kelley849a8352006-06-09 21:02:31 +01002636 }
2637
Simon Kelleyf2621c72007-04-29 19:47:21 +01002638 serv = newlist;
2639 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002640 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002641 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002642 serv->next->addr = serv->addr;
2643 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002644 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002645 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002646 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002647 serv->next = daemon->servers;
2648 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002649 break;
2650 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002651
Simon Kelleyde73a492014-02-17 21:43:27 +00002652 case LOPT_REV_SERV: /* --rev-server */
2653 {
2654 char *string;
2655 int size;
2656 struct server *serv;
2657 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002658 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002659
2660 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002661 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002662 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002663
2664 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002665
Simon Kelleya9b022a2020-02-11 21:58:59 +00002666 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2667 ret_err(gen_err);
2668
Simon Kelleyde73a492014-02-17 21:43:27 +00002669 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002670 {
2671 serv = add_rev4(addr4, size);
2672 if (!serv)
2673 ret_err(_("bad prefix"));
2674 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002675 else if (inet_pton(AF_INET6, arg, &addr6))
2676 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002677 else
2678 ret_err(gen_err);
2679
2680 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2681
2682 if (string)
2683 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002684
2685 if (servers_only)
2686 serv->flags |= SERV_FROM_FILE;
2687
Simon Kelleyde73a492014-02-17 21:43:27 +00002688 break;
2689 }
2690
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002691 case LOPT_IPSET: /* --ipset */
2692#ifndef HAVE_IPSET
2693 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2694 break;
2695#else
2696 {
2697 struct ipsets ipsets_head;
2698 struct ipsets *ipsets = &ipsets_head;
2699 int size;
2700 char *end;
2701 char **sets, **sets_pos;
2702 memset(ipsets, 0, sizeof(struct ipsets));
2703 unhide_metas(arg);
2704 if (arg && *arg == '/')
2705 {
2706 arg++;
2707 while ((end = split_chr(arg, '/')))
2708 {
2709 char *domain = NULL;
2710 /* elide leading dots - they are implied in the search algorithm */
2711 while (*arg == '.')
2712 arg++;
2713 /* # matches everything and becomes a zero length domain string */
2714 if (strcmp(arg, "#") == 0 || !*arg)
2715 domain = "";
2716 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002717 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002718 ipsets->next = opt_malloc(sizeof(struct ipsets));
2719 ipsets = ipsets->next;
2720 memset(ipsets, 0, sizeof(struct ipsets));
2721 ipsets->domain = domain;
2722 arg = end;
2723 }
2724 }
2725 else
2726 {
2727 ipsets->next = opt_malloc(sizeof(struct ipsets));
2728 ipsets = ipsets->next;
2729 memset(ipsets, 0, sizeof(struct ipsets));
2730 ipsets->domain = "";
2731 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002732
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002733 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002734 ret_err(gen_err);
2735
2736 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002737 if (*end == ',')
2738 ++size;
2739
2740 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2741
2742 do {
2743 end = split(arg);
2744 *sets_pos++ = opt_string_alloc(arg);
2745 arg = end;
2746 } while (end);
2747 *sets_pos = 0;
2748 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2749 ipsets->next->sets = sets;
2750 ipsets->next = daemon->ipsets;
2751 daemon->ipsets = ipsets_head.next;
2752
2753 break;
2754 }
2755#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002756
Simon Kelleyf2621c72007-04-29 19:47:21 +01002757 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002758 {
2759 int size;
2760
2761 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002762 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002763 else
2764 {
2765 /* zero is OK, and means no caching. */
2766
2767 if (size < 0)
2768 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002769
2770 /* Note that for very large cache sizes, the malloc()
2771 will overflow. For the size of the cache record
2772 at the time this was noted, the value of "very large"
2773 was 46684428. Limit to an order of magnitude less than
2774 that to be safe from changes to the cache record. */
2775 if (size > 5000000)
2776 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002777
2778 daemon->cachesize = size;
2779 }
2780 break;
2781 }
2782
Simon Kelleyf2621c72007-04-29 19:47:21 +01002783 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002784 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002785 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002786 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002787
Simon Kelley1a6bca82008-07-11 11:11:42 +01002788 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002789 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002790 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002791 break;
2792
Hans Dedecker926332a2016-01-23 10:48:12 +00002793 case LOPT_MAXPORT: /* --max-port */
2794 if (!atoi_check16(arg, &daemon->max_port))
2795 ret_err(gen_err);
2796 break;
2797
Simon Kelleyf2621c72007-04-29 19:47:21 +01002798 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002799 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002800 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002801 break;
2802
Simon Kelley25cf5e32015-01-09 15:53:03 +00002803 case 'q': /* --log-queries */
2804 set_option_bool(OPT_LOG);
2805 if (arg && strcmp(arg, "extra") == 0)
2806 set_option_bool(OPT_EXTRALOG);
2807 break;
2808
Simon Kelleyf2621c72007-04-29 19:47:21 +01002809 case LOPT_MAX_LOGS: /* --log-async */
2810 daemon->max_logs = LOG_MAX; /* default */
2811 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002812 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002813 else if (daemon->max_logs > 100)
2814 daemon->max_logs = 100;
2815 break;
2816
2817 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002818 {
2819 int i;
2820 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002821 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002822 daemon->edns_pktsz = (unsigned short)i;
2823 break;
2824 }
2825
Simon Kelleyf2621c72007-04-29 19:47:21 +01002826 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002827 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002828 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002829 /* if explicitly set to zero, use single OS ephemeral port
2830 and disable random ports */
2831 if (daemon->query_port == 0)
2832 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002833 break;
2834
Simon Kelley824af852008-02-12 20:43:05 +00002835 case 'T': /* --local-ttl */
2836 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002837 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002838 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002839 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002840 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002841 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002842 {
2843 int ttl;
2844 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002845 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002846 else if (option == LOPT_NEGTTL)
2847 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002848 else if (option == LOPT_MAXTTL)
2849 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002850 else if (option == LOPT_MINCTTL)
2851 {
2852 if (ttl > TTL_FLOOR_LIMIT)
2853 ttl = TTL_FLOOR_LIMIT;
2854 daemon->min_cache_ttl = (unsigned long)ttl;
2855 }
Simon Kelley1d860412012-09-20 20:48:04 +01002856 else if (option == LOPT_MAXCTTL)
2857 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002858 else if (option == LOPT_AUTHTTL)
2859 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002860 else if (option == LOPT_DHCPTTL)
2861 {
2862 daemon->dhcp_ttl = (unsigned long)ttl;
2863 daemon->use_dhcp_ttl = 1;
2864 }
Simon Kelley849a8352006-06-09 21:02:31 +01002865 else
2866 daemon->local_ttl = (unsigned long)ttl;
2867 break;
2868 }
2869
Simon Kelley7622fc02009-06-04 20:32:05 +01002870#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002871 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002872 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002873 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002874 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002875#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002876
Simon Kelley7622fc02009-06-04 20:32:05 +01002877#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002878 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002879 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002880 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002881 break;
2882
Simon Kelleybec366b2016-02-24 22:03:26 +00002883 case LOPT_TFTP_MTU: /* --tftp-mtu */
2884 if (!atoi_check(arg, &daemon->tftp_mtu))
2885 ret_err(gen_err);
2886 break;
2887
Simon Kelley824af852008-02-12 20:43:05 +00002888 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002889 comma = split(arg);
2890 if (comma)
2891 {
2892 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2893 new->interface = opt_string_alloc(comma);
2894 new->prefix = opt_string_alloc(arg);
2895 new->next = daemon->if_prefix;
2896 daemon->if_prefix = new;
2897 }
2898 else
2899 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002900 break;
2901
Simon Kelley824af852008-02-12 20:43:05 +00002902 case LOPT_TFTPPORTS: /* --tftp-port-range */
2903 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002904 !atoi_check16(arg, &daemon->start_tftp_port) ||
2905 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002906 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002907
2908 if (daemon->start_tftp_port > daemon->end_tftp_port)
2909 {
2910 int tmp = daemon->start_tftp_port;
2911 daemon->start_tftp_port = daemon->end_tftp_port;
2912 daemon->end_tftp_port = tmp;
2913 }
2914
2915 break;
Floris Bos60704f52017-04-09 22:22:49 +01002916
2917 case LOPT_APREF: /* --tftp-unique-root */
2918 if (!arg || strcasecmp(arg, "ip") == 0)
2919 set_option_bool(OPT_TFTP_APREF_IP);
2920 else if (strcasecmp(arg, "mac") == 0)
2921 set_option_bool(OPT_TFTP_APREF_MAC);
2922 else
2923 ret_err(gen_err);
2924 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002925#endif
Simon Kelley824af852008-02-12 20:43:05 +00002926
Simon Kelleyf2621c72007-04-29 19:47:21 +01002927 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002928 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002929 struct dhcp_bridge *new;
2930
Simon Kelley316e2732010-01-22 20:16:09 +00002931 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002932 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002933
Simon Kelley22cd8602018-01-14 22:57:14 +00002934 for (new = daemon->bridges; new; new = new->next)
2935 if (strcmp(new->iface, arg) == 0)
2936 break;
2937
2938 if (!new)
2939 {
2940 new = opt_malloc(sizeof(struct dhcp_bridge));
2941 strcpy(new->iface, arg);
2942 new->alias = NULL;
2943 new->next = daemon->bridges;
2944 daemon->bridges = new;
2945 }
2946
Simon Kelley832af0b2007-01-21 20:01:28 +00002947 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002948 arg = comma;
2949 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002950 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002951 {
Simon Kelley824af852008-02-12 20:43:05 +00002952 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002953 b->next = new->alias;
2954 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002955 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002956 }
2957 } while (comma);
2958
2959 break;
2960 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002961
Simon Kelley7622fc02009-06-04 20:32:05 +01002962#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002963 case LOPT_SHARED_NET: /* --shared-network */
2964 {
2965 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2966
2967#ifdef HAVE_DHCP6
2968 new->shared_addr.s_addr = 0;
2969#endif
2970 new->if_index = 0;
2971
2972 if (!(comma = split(arg)))
2973 {
2974 snerr:
2975 free(new);
2976 ret_err(_("bad shared-network"));
2977 }
2978
2979 if (inet_pton(AF_INET, comma, &new->shared_addr))
2980 {
2981 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2982 !(new->if_index = if_nametoindex(arg)))
2983 goto snerr;
2984 }
2985#ifdef HAVE_DHCP6
2986 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2987 {
2988 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
2989 !(new->if_index = if_nametoindex(arg)))
2990 goto snerr;
2991 }
2992#endif
2993 else
2994 goto snerr;
2995
2996 new->next = daemon->shared_networks;
2997 daemon->shared_networks = new;
2998 break;
2999 }
3000
Simon Kelleyf2621c72007-04-29 19:47:21 +01003001 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01003002 {
3003 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00003004 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003005 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003006
Simon Kelley52b92f42012-01-22 16:05:15 +00003007 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003008
Simon Kelley849a8352006-06-09 21:02:31 +01003009 while(1)
3010 {
3011 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003012 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3013 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3014 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003015 break;
3016
Simon Kelleyf2621c72007-04-29 19:47:21 +01003017 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003018 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003019 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003020 {
Simon Kelley0c387192013-09-05 10:21:12 +01003021 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003022 if (arg[4])
3023 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003024 }
3025 else
3026 {
3027 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003028 {
3029 dhcp_context_free(new);
3030 ret_err(_("only one tag allowed"));
3031 }
Simon Kelley849a8352006-06-09 21:02:31 +01003032 else
Petr Menšík59e47032018-11-02 22:39:39 +00003033 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003034 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003035 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003036 }
3037 else
3038 {
3039 a[0] = arg;
3040 break;
3041 }
3042 }
3043
Simon Kelley1f776932012-12-16 19:46:08 +00003044 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003045 if (!(a[k] = split(a[k-1])))
3046 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003047
Simon Kelley52b92f42012-01-22 16:05:15 +00003048 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003049 {
3050 dhcp_context_free(new);
3051 ret_err(_("bad dhcp-range"));
3052 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003053
3054 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003055 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003056 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003057 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003058 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003059 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003060 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003061 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003062 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003063 new->flags |= CONTEXT_PROXY;
3064 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003065 {
3066 dhcp_context_free(new);
3067 ret_err(_("bad dhcp-range"));
3068 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003069
3070 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3071 {
3072 struct in_addr tmp = new->start;
3073 new->start = new->end;
3074 new->end = tmp;
3075 }
3076
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003077 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003078 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003079 {
3080 new->flags |= CONTEXT_NETMASK;
3081 leasepos = 3;
3082 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003083 {
3084 dhcp_context_free(new);
3085 ret_err(_("inconsistent DHCP range"));
3086 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003087
Simon Kelley52b92f42012-01-22 16:05:15 +00003088
Simon Kelleyfa794662016-03-03 20:33:54 +00003089 if (k >= 4 && strchr(a[3], '.') &&
3090 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3091 {
3092 new->flags |= CONTEXT_BRDCAST;
3093 leasepos = 4;
3094 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003095 }
Simon Kelley849a8352006-06-09 21:02:31 +01003096 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003097#ifdef HAVE_DHCP6
3098 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003099 {
Petr Menšík59e47032018-11-02 22:39:39 +00003100 const char *err = NULL;
3101
Simon Kelley89500e32013-09-20 16:29:20 +01003102 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003103 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003104 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003105 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003106 new->next = daemon->dhcp6;
3107 daemon->dhcp6 = new;
3108
Simon Kelley30cd9662012-03-25 20:44:38 +01003109 for (leasepos = 1; leasepos < k; leasepos++)
3110 {
3111 if (strcmp(a[leasepos], "static") == 0)
3112 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3113 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003114 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003115 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003116 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003117 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3118 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003119 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003120 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003121 else if (strcmp(a[leasepos], "off-link") == 0)
3122 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003123 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3124 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003125 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3126 {
3127 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3128 new->flags |= CONTEXT_TEMPLATE;
3129 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003130 else
3131 break;
3132 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003133
Simon Kelley52b92f42012-01-22 16:05:15 +00003134 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003135 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003136 {
3137 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003138 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003139 if (!(*cp >= '0' && *cp <= '9'))
3140 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003141 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003142 {
3143 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003144 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003145 }
3146 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003147
Petr Menšík59e47032018-11-02 22:39:39 +00003148 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003149 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003150 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003151 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003152 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003153 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003154 }
Petr Menšík59e47032018-11-02 22:39:39 +00003155 else if (new->prefix < 64)
3156 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003157
Petr Menšík59e47032018-11-02 22:39:39 +00003158 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3159 err=(_("inconsistent DHCPv6 range"));
3160
3161 if (err)
3162 {
3163 dhcp_context_free(new);
3164 ret_err(err);
3165 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003166
3167 /* dhcp-range=:: enables DHCP stateless on any interface */
3168 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3169 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003170
3171 if (new->flags & CONTEXT_TEMPLATE)
3172 {
3173 struct in6_addr zero;
3174 memset(&zero, 0, sizeof(zero));
3175 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003176 {
3177 dhcp_context_free(new);
3178 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3179 }
Simon Kelley66409192013-08-01 20:19:32 +01003180 }
3181
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003182 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003183 {
3184 struct in6_addr tmp = new->start6;
3185 new->start6 = new->end6;
3186 new->end6 = tmp;
3187 }
Simon Kelley849a8352006-06-09 21:02:31 +01003188 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003189#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003190 else
Petr Menšík59e47032018-11-02 22:39:39 +00003191 {
3192 dhcp_context_free(new);
3193 ret_err(_("bad dhcp-range"));
3194 }
Simon Kelley849a8352006-06-09 21:02:31 +01003195
Simon Kelley30cd9662012-03-25 20:44:38 +01003196 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003197 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003198 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003199 {
3200 dhcp_context_free(new);
3201 ret_err(_("bad dhcp-range"));
3202 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003203
Simon Kelley849a8352006-06-09 21:02:31 +01003204 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003205 {
3206 new->lease_time = 0xffffffff;
3207 new->flags |= CONTEXT_SETLEASE;
3208 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003209 else if (strcmp(a[leasepos], "deprecated") == 0)
3210 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003211 else
3212 {
3213 int fac = 1;
3214 if (strlen(a[leasepos]) > 0)
3215 {
3216 switch (a[leasepos][strlen(a[leasepos]) - 1])
3217 {
Simon Kelley42243212012-07-20 15:19:18 +01003218 case 'w':
3219 case 'W':
3220 fac *= 7;
3221 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003222 case 'd':
3223 case 'D':
3224 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003225 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003226 case 'h':
3227 case 'H':
3228 fac *= 60;
3229 /* fall through */
3230 case 'm':
3231 case 'M':
3232 fac *= 60;
3233 /* fall through */
3234 case 's':
3235 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003236 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003237 }
3238
Simon Kelleybe379862012-12-23 12:01:39 +00003239 for (cp = a[leasepos]; *cp; cp++)
3240 if (!(*cp >= '0' && *cp <= '9'))
3241 break;
3242
Simon Kelley54dae552013-02-05 17:55:10 +00003243 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003244 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003245
Simon Kelley849a8352006-06-09 21:02:31 +01003246 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003247 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003248 /* Leases of a minute or less confuse
3249 some clients, notably Apple's */
3250 if (new->lease_time < 120)
3251 new->lease_time = 120;
3252 }
3253 }
3254 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003255
Simon Kelley849a8352006-06-09 21:02:31 +01003256 break;
3257 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003258
Simon Kelley5aabfc72007-08-29 11:24:47 +01003259 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003260 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003261 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003262 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003263 struct in_addr in;
3264
Simon Kelley824af852008-02-12 20:43:05 +00003265 new = opt_malloc(sizeof(struct dhcp_config));
3266
Simon Kelley849a8352006-06-09 21:02:31 +01003267 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003268 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3269 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003270 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003271 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003272 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003273#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003274 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003275#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003276
Simon Kelley137286e2020-02-06 22:09:30 +00003277 while (arg)
3278 {
3279 comma = split(arg);
3280 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3281 {
3282 if ((arg[0] == 'i' || arg[0] == 'I') &&
3283 (arg[1] == 'd' || arg[1] == 'D') &&
3284 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003285 {
Simon Kelley137286e2020-02-06 22:09:30 +00003286 if (arg[3] == '*')
3287 new->flags |= CONFIG_NOCLID;
3288 else
3289 {
3290 int len;
3291 arg += 3; /* dump id: */
3292 if (strchr(arg, ':'))
3293 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3294 else
3295 {
3296 unhide_metas(arg);
3297 len = (int) strlen(arg);
3298 }
3299
3300 if (len == -1)
3301 {
3302 dhcp_config_free(new);
3303 ret_err(_("bad hex constant"));
3304 }
3305 else if ((new->clid = opt_malloc(len)))
3306 {
3307 new->flags |= CONFIG_CLID;
3308 new->clid_len = len;
3309 memcpy(new->clid, arg, len);
3310 }
3311 }
3312 }
3313 /* dhcp-host has strange backwards-compat needs. */
3314 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3315 {
3316 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3317 newlist->next = new->netid;
3318 new->netid = newlist;
3319 newlist->list = dhcp_netid_create(arg+4, NULL);
3320 }
3321 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003322 new->filter = dhcp_netid_create(arg+4, new->filter);
3323
Simon Kelley137286e2020-02-06 22:09:30 +00003324#ifdef HAVE_DHCP6
3325 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3326 {
3327 char *pref;
3328 struct in6_addr in6;
3329 struct addrlist *new_addr;
3330
3331 arg[strlen(arg)-1] = 0;
3332 arg++;
3333 pref = split_chr(arg, '/');
3334
3335 if (!inet_pton(AF_INET6, arg, &in6))
3336 {
3337 dhcp_config_free(new);
3338 ret_err(_("bad IPv6 address"));
3339 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003340
Simon Kelley137286e2020-02-06 22:09:30 +00003341 new_addr = opt_malloc(sizeof(struct addrlist));
3342 new_addr->next = new->addr6;
3343 new_addr->flags = 0;
3344 new_addr->addr.addr6 = in6;
3345 new->addr6 = new_addr;
3346
3347 if (pref)
3348 {
3349 u64 addrpart = addr6part(&in6);
3350
3351 if (!atoi_check(pref, &new_addr->prefixlen) ||
3352 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003353 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003354 {
3355 dhcp_config_free(new);
3356 ret_err(_("bad IPv6 prefix"));
3357 }
3358
3359 new_addr->flags |= ADDRLIST_PREFIX;
3360 }
3361
3362 for (i= 0; i < 8; i++)
3363 if (in6.s6_addr[i] != 0)
3364 break;
3365
3366 /* set WILDCARD if network part all zeros */
3367 if (i == 8)
3368 new_addr->flags |= ADDRLIST_WILDCARD;
3369
3370 new->flags |= CONFIG_ADDR6;
3371 }
3372#endif
3373 else
3374 {
3375 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3376 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3377 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3378 {
3379 free(newhw);
3380 dhcp_config_free(new);
3381 ret_err(_("bad hex constant"));
3382 }
3383 else
3384 {
3385 newhw->next = new->hwaddr;
3386 new->hwaddr = newhw;
3387 }
3388 }
3389 }
3390 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3391 {
3392 struct dhcp_config *configs;
3393
3394 new->addr = in;
3395 new->flags |= CONFIG_ADDR;
3396
3397 /* If the same IP appears in more than one host config, then DISCOVER
3398 for one of the hosts will get the address, but REQUEST will be NAKed,
3399 since the address is reserved by the other one -> protocol loop. */
3400 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3401 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003402 {
Simon Kelley137286e2020-02-06 22:09:30 +00003403 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3404 return 0;
3405 }
3406 }
3407 else
3408 {
3409 char *cp, *lastp = NULL, last = 0;
3410 int fac = 1, isdig = 0;
3411
3412 if (strlen(arg) > 1)
3413 {
3414 lastp = arg + strlen(arg) - 1;
3415 last = *lastp;
3416 switch (last)
3417 {
3418 case 'w':
3419 case 'W':
3420 fac *= 7;
3421 /* fall through */
3422 case 'd':
3423 case 'D':
3424 fac *= 24;
3425 /* fall through */
3426 case 'h':
3427 case 'H':
3428 fac *= 60;
3429 /* fall through */
3430 case 'm':
3431 case 'M':
3432 fac *= 60;
3433 /* fall through */
3434 case 's':
3435 case 'S':
3436 *lastp = 0;
3437 }
3438 }
3439
3440 for (cp = arg; *cp; cp++)
3441 if (isdigit((unsigned char)*cp))
3442 isdig = 1;
3443 else if (*cp != ' ')
3444 break;
3445
3446 if (*cp)
3447 {
3448 if (lastp)
3449 *lastp = last;
3450 if (strcmp(arg, "infinite") == 0)
3451 {
3452 new->lease_time = 0xffffffff;
3453 new->flags |= CONFIG_TIME;
3454 }
3455 else if (strcmp(arg, "ignore") == 0)
3456 new->flags |= CONFIG_DISABLE;
3457 else
3458 {
3459 if (!(new->hostname = canonicalise_opt(arg)) ||
3460 !legal_hostname(new->hostname))
3461 {
3462 dhcp_config_free(new);
3463 ret_err(_("bad DHCP host name"));
3464 }
3465
3466 new->flags |= CONFIG_NAME;
3467 new->domain = strip_hostname(new->hostname);
3468 }
3469 }
3470 else if (isdig)
3471 {
3472 new->lease_time = atoi(arg) * fac;
3473 /* Leases of a minute or less confuse
3474 some clients, notably Apple's */
3475 if (new->lease_time < 120)
3476 new->lease_time = 120;
3477 new->flags |= CONFIG_TIME;
3478 }
3479 }
3480
3481 arg = comma;
3482 }
3483
Simon Kelley5aabfc72007-08-29 11:24:47 +01003484 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003485 break;
3486 }
Simon Kelley137286e2020-02-06 22:09:30 +00003487
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003488 case LOPT_TAG_IF: /* --tag-if */
3489 {
3490 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3491
3492 new->tag = NULL;
3493 new->set = NULL;
3494 new->next = NULL;
3495
3496 /* preserve order */
3497 if (!daemon->tag_if)
3498 daemon->tag_if = new;
3499 else
3500 {
3501 struct tag_if *tmp;
3502 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3503 tmp->next = new;
3504 }
3505
3506 while (arg)
3507 {
3508 size_t len;
3509
3510 comma = split(arg);
3511 len = strlen(arg);
3512
3513 if (len < 5)
3514 {
3515 new->set = NULL;
3516 break;
3517 }
3518 else
3519 {
Petr Menšík59e47032018-11-02 22:39:39 +00003520 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003521
3522 if (strstr(arg, "set:") == arg)
3523 {
3524 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3525 newlist->next = new->set;
3526 new->set = newlist;
3527 newlist->list = newtag;
3528 }
3529 else if (strstr(arg, "tag:") == arg)
3530 {
3531 newtag->next = new->tag;
3532 new->tag = newtag;
3533 }
3534 else
3535 {
3536 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003537 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003538 break;
3539 }
3540 }
3541
3542 arg = comma;
3543 }
3544
3545 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003546 {
3547 dhcp_netid_free(new->tag);
3548 dhcp_netid_list_free(new->set);
3549 ret_err_free(_("bad tag-if"), new);
3550 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003551
3552 break;
3553 }
3554
Simon Kelley849a8352006-06-09 21:02:31 +01003555
Simon Kelley73a08a22009-02-05 20:28:08 +00003556 case 'O': /* --dhcp-option */
3557 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003558 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003559 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003560 return parse_dhcp_opt(errstr, arg,
3561 option == LOPT_FORCE ? DHOPT_FORCE :
3562 (option == LOPT_MATCH ? DHOPT_MATCH :
3563 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003564
3565 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3566 {
3567 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3568 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3569 ssize_t len;
3570
3571 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3572 ret_err(gen_err);
3573
3574 new->wildcard = 0;
3575 new->netid = id;
3576 id->net = opt_string_alloc(set_prefix(arg));
3577
3578 if (comma[len-1] == '*')
3579 {
3580 comma[len-1] = 0;
3581 new->wildcard = 1;
3582 }
3583 new->name = opt_string_alloc(comma);
3584
3585 new->next = daemon->dhcp_name_match;
3586 daemon->dhcp_name_match = new;
3587
3588 break;
3589 }
3590
Simon Kelleyf2621c72007-04-29 19:47:21 +01003591 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003592 {
Petr Menšík59e47032018-11-02 22:39:39 +00003593 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003594
Petr Menšík137e9f82018-12-16 21:25:29 +00003595 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003596 {
3597 ret_err(gen_err);
3598 }
Simon Kelley849a8352006-06-09 21:02:31 +01003599 else
3600 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003601 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003602 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003603 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003604 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003605 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003606 dhcp_next_server.s_addr = 0;
3607 if (comma)
3608 {
3609 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003610 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003611 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003612 if (comma)
3613 {
3614 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003615 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3616 {
3617 /*
3618 * The user may have specified the tftp hostname here.
3619 * save it so that it can be resolved/looked up during
3620 * actual dhcp_reply().
3621 */
3622
3623 tftp_sname = opt_string_alloc(comma);
3624 dhcp_next_server.s_addr = 0;
3625 }
Simon Kelley849a8352006-06-09 21:02:31 +01003626 }
3627 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003628
3629 new = opt_malloc(sizeof(struct dhcp_boot));
3630 new->file = dhcp_file;
3631 new->sname = dhcp_sname;
3632 new->tftp_sname = tftp_sname;
3633 new->next_server = dhcp_next_server;
3634 new->netid = id;
3635 new->next = daemon->boot_config;
3636 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003637 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003638
Simon Kelley849a8352006-06-09 21:02:31 +01003639 break;
3640 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003641
Floris Bos503c6092017-04-09 23:07:13 +01003642 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3643 {
Petr Menšík59e47032018-11-02 22:39:39 +00003644 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003645
Petr Menšík137e9f82018-12-16 21:25:29 +00003646 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003647 {
3648 ret_err(gen_err);
3649 }
Floris Bos503c6092017-04-09 23:07:13 +01003650 else
3651 {
3652 struct delay_config *new;
3653 int delay;
3654 if (!atoi_check(arg, &delay))
3655 ret_err(gen_err);
3656
3657 new = opt_malloc(sizeof(struct delay_config));
3658 new->delay = delay;
3659 new->netid = id;
3660 new->next = daemon->delay_conf;
3661 daemon->delay_conf = new;
3662 }
3663
3664 break;
3665 }
3666
Simon Kelley7622fc02009-06-04 20:32:05 +01003667 case LOPT_PXE_PROMT: /* --pxe-prompt */
3668 {
3669 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3670 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003671
Simon Kelley7622fc02009-06-04 20:32:05 +01003672 new->netid = NULL;
3673 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003674 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003675
Petr Menšík137e9f82018-12-16 21:25:29 +00003676 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003677 {
3678 dhcp_opt_free(new);
3679 ret_err(gen_err);
3680 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003681 else
3682 {
3683 comma = split(arg);
3684 unhide_metas(arg);
3685 new->len = strlen(arg) + 1;
3686 new->val = opt_malloc(new->len);
3687 memcpy(new->val + 1, arg, new->len - 1);
3688
Wang Shanker4ded9622020-12-04 10:17:35 +08003689 new->u.vendor_class = NULL;
3690 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003691
3692 if (comma && atoi_check(comma, &timeout))
3693 *(new->val) = timeout;
3694 else
3695 *(new->val) = 255;
3696
3697 new->next = daemon->dhcp_opts;
3698 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003699 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003700 }
3701
3702 break;
3703 }
3704
3705 case LOPT_PXE_SERV: /* --pxe-service */
3706 {
3707 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3708 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003709 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3710 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003711 static int boottype = 32768;
3712
3713 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003714 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003715 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003716 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003717
Simon Kelley7622fc02009-06-04 20:32:05 +01003718 if (arg && (comma = split(arg)))
3719 {
3720 for (i = 0; CSA[i]; i++)
3721 if (strcasecmp(CSA[i], arg) == 0)
3722 break;
3723
3724 if (CSA[i] || atoi_check(arg, &i))
3725 {
3726 arg = comma;
3727 comma = split(arg);
3728
3729 new->CSA = i;
3730 new->menu = opt_string_alloc(arg);
3731
Simon Kelley316e2732010-01-22 20:16:09 +00003732 if (!comma)
3733 {
3734 new->type = 0; /* local boot */
3735 new->basename = NULL;
3736 }
3737 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003738 {
3739 arg = comma;
3740 comma = split(arg);
3741 if (atoi_check(arg, &i))
3742 {
3743 new->type = i;
3744 new->basename = NULL;
3745 }
3746 else
3747 {
3748 new->type = boottype++;
3749 new->basename = opt_string_alloc(arg);
3750 }
3751
Simon Kelley751d6f42012-02-10 15:24:51 +00003752 if (comma)
3753 {
3754 if (!inet_pton(AF_INET, comma, &new->server))
3755 {
3756 new->server.s_addr = 0;
3757 new->sname = opt_string_alloc(comma);
3758 }
3759
3760 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003761 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003762
Simon Kelley316e2732010-01-22 20:16:09 +00003763 /* Order matters */
3764 new->next = NULL;
3765 if (!daemon->pxe_services)
3766 daemon->pxe_services = new;
3767 else
3768 {
3769 struct pxe_service *s;
3770 for (s = daemon->pxe_services; s->next; s = s->next);
3771 s->next = new;
3772 }
3773
3774 daemon->enable_pxe = 1;
3775 break;
3776
Simon Kelley7622fc02009-06-04 20:32:05 +01003777 }
3778 }
3779
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003780 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003781 }
3782
Simon Kelleyf2621c72007-04-29 19:47:21 +01003783 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003784 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003785 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003786 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003787 else
3788 {
Simon Kelley824af852008-02-12 20:43:05 +00003789 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003790 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003791 unhide_metas(comma);
3792 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003793 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003794 {
3795 free(new->netid.net);
3796 ret_err_free(gen_err, new);
3797 }
Simon Kelley28866e92011-02-14 20:19:14 +00003798 else
3799 {
3800 new->next = daemon->dhcp_macs;
3801 daemon->dhcp_macs = new;
3802 }
Simon Kelley849a8352006-06-09 21:02:31 +01003803 }
3804 }
3805 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003806
Simon Kelleyf2621c72007-04-29 19:47:21 +01003807 case 'U': /* --dhcp-vendorclass */
3808 case 'j': /* --dhcp-userclass */
3809 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3810 case LOPT_REMOTE: /* --dhcp-remoteid */
3811 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003812 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003813 unsigned char *p;
3814 int dig = 0;
3815 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3816
3817 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003818 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003819
3820 new->netid.net = opt_string_alloc(set_prefix(arg));
3821 /* check for hex string - must digits may include : must not have nothing else,
3822 only allowed for agent-options. */
3823
3824 arg = comma;
3825 if ((comma = split(arg)))
3826 {
3827 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003828 {
3829 free(new->netid.net);
3830 ret_err_free(gen_err, new);
3831 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003832 else
3833 new->enterprise = atoi(arg+11);
3834 }
3835 else
3836 comma = arg;
3837
3838 for (p = (unsigned char *)comma; *p; p++)
3839 if (isxdigit(*p))
3840 dig = 1;
3841 else if (*p != ':')
3842 break;
3843 unhide_metas(comma);
3844 if (option == 'U' || option == 'j' || *p || !dig)
3845 {
3846 new->len = strlen(comma);
3847 new->data = opt_malloc(new->len);
3848 memcpy(new->data, comma, new->len);
3849 }
3850 else
3851 {
3852 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3853 new->data = opt_malloc(new->len);
3854 memcpy(new->data, comma, new->len);
3855 }
3856
3857 switch (option)
3858 {
3859 case 'j':
3860 new->match_type = MATCH_USER;
3861 break;
3862 case 'U':
3863 new->match_type = MATCH_VENDOR;
3864 break;
3865 case LOPT_CIRCUIT:
3866 new->match_type = MATCH_CIRCUIT;
3867 break;
3868 case LOPT_REMOTE:
3869 new->match_type = MATCH_REMOTE;
3870 break;
3871 case LOPT_SUBSCR:
3872 new->match_type = MATCH_SUBSCRIBER;
3873 break;
3874 }
3875 new->next = daemon->dhcp_vendors;
3876 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003877
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003878 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003879 }
3880
Simon Kelley9e038942008-05-30 20:06:34 +01003881 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3882 if (!arg)
3883 {
3884 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3885 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3886 }
3887 else
3888 {
3889 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003890 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3891 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003892 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003893 if (!comma)
3894 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3895 }
3896 break;
3897
Simon Kelley824af852008-02-12 20:43:05 +00003898 case 'J': /* --dhcp-ignore */
3899 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3900 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003901 case '3': /* --bootp-dynamic */
3902 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003903 {
Simon Kelley824af852008-02-12 20:43:05 +00003904 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003905 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003906 if (option == 'J')
3907 {
3908 new->next = daemon->dhcp_ignore;
3909 daemon->dhcp_ignore = new;
3910 }
Simon Kelley824af852008-02-12 20:43:05 +00003911 else if (option == LOPT_BROADCAST)
3912 {
3913 new->next = daemon->force_broadcast;
3914 daemon->force_broadcast = new;
3915 }
Simon Kelley9009d742008-11-14 20:04:27 +00003916 else if (option == '3')
3917 {
3918 new->next = daemon->bootp_dynamic;
3919 daemon->bootp_dynamic = new;
3920 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003921 else if (option == LOPT_GEN_NAMES)
3922 {
3923 new->next = daemon->dhcp_gen_names;
3924 daemon->dhcp_gen_names = new;
3925 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003926 else
3927 {
3928 new->next = daemon->dhcp_ignore_names;
3929 daemon->dhcp_ignore_names = new;
3930 }
3931
3932 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003933 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003934 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003935 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003936 }
Simon Kelley849a8352006-06-09 21:02:31 +01003937
3938 new->list = list;
3939 break;
3940 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003941
3942 case LOPT_PROXY: /* --dhcp-proxy */
3943 daemon->override = 1;
3944 while (arg) {
3945 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3946 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003947 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003948 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003949 new->next = daemon->override_relays;
3950 daemon->override_relays = new;
3951 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003952 }
3953 break;
3954
3955 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3956 {
3957 while (arg) {
3958 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3959 comma = split(arg);
3960 new->data = opt_string_alloc(arg);
3961 new->next = daemon->dhcp_pxe_vendors;
3962 daemon->dhcp_pxe_vendors = new;
3963 arg = comma;
3964 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003965 }
3966 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003967
3968 case LOPT_RELAY: /* --dhcp-relay */
3969 {
3970 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3971 comma = split(arg);
3972 new->interface = opt_string_alloc(split(comma));
3973 new->iface_index = 0;
3974 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3975 {
3976 new->next = daemon->relay4;
3977 daemon->relay4 = new;
3978 }
3979#ifdef HAVE_DHCP6
3980 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3981 {
3982 new->next = daemon->relay6;
3983 daemon->relay6 = new;
3984 }
3985#endif
3986 else
Petr Menšík59e47032018-11-02 22:39:39 +00003987 {
3988 free(new->interface);
3989 ret_err_free(_("Bad dhcp-relay"), new);
3990 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003991
3992 break;
3993 }
3994
Simon Kelley7622fc02009-06-04 20:32:05 +01003995#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003996
Simon Kelley8b372702012-03-09 17:45:10 +00003997#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003998 case LOPT_RA_PARAM: /* --ra-param */
3999 if ((comma = split(arg)))
4000 {
4001 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
4002 new->lifetime = -1;
4003 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01004004 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004005 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004006 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004007 if (strcasestr(comma, "mtu:") == comma)
4008 {
4009 arg = comma + 4;
4010 if (!(comma = split(comma)))
4011 goto err;
4012 if (!strcasecmp(arg, "off"))
4013 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004014 else if (!atoi_check(arg, &new->mtu))
4015 new->mtu_name = opt_string_alloc(arg);
4016 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004017 goto err;
4018 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004019 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4020 {
4021 if (*comma == 'l' || *comma == 'L')
4022 new->prio = 0x18;
4023 else
4024 new->prio = 0x08;
4025 comma = split(comma);
4026 }
4027 arg = split(comma);
4028 if (!atoi_check(comma, &new->interval) ||
4029 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004030 {
David Flamand005c46d2017-04-11 11:49:54 +01004031err:
Petr Menšík59e47032018-11-02 22:39:39 +00004032 free(new->name);
4033 ret_err_free(_("bad RA-params"), new);
4034 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004035
4036 new->next = daemon->ra_interfaces;
4037 daemon->ra_interfaces = new;
4038 }
4039 break;
4040
Simon Kelley8b372702012-03-09 17:45:10 +00004041 case LOPT_DUID: /* --dhcp-duid */
4042 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004043 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004044 else
4045 {
4046 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4047 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4048 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4049 }
4050 break;
4051#endif
4052
Simon Kelleyf2621c72007-04-29 19:47:21 +01004053 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004054 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004055 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004056 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004057 struct doctor *new = opt_malloc(sizeof(struct doctor));
4058 new->next = daemon->doctors;
4059 daemon->doctors = new;
4060 new->mask.s_addr = 0xffffffff;
4061 new->end.s_addr = 0;
4062
Simon Kelley849a8352006-06-09 21:02:31 +01004063 if ((a[0] = arg))
4064 for (k = 1; k < 3; k++)
4065 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004066 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004067 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004068 unhide_metas(a[k]);
4069 }
Simon Kelley849a8352006-06-09 21:02:31 +01004070
Simon Kelley73a08a22009-02-05 20:28:08 +00004071 dash = split_chr(a[0], '-');
4072
Simon Kelley849a8352006-06-09 21:02:31 +01004073 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004074 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004075 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4076 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4077 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004078
Simon Kelley73a08a22009-02-05 20:28:08 +00004079 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004080 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004081 !is_same_net(new->in, new->end, new->mask) ||
4082 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004083 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004084
4085 break;
4086 }
4087
Simon Kelleyf2621c72007-04-29 19:47:21 +01004088 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004089 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004090 {
4091 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004092 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004093
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004094 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004095
Simon Kelley824af852008-02-12 20:43:05 +00004096 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004097 memset(new, 0, sizeof(struct interface_name));
4098 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004099
Simon Kelleyf2621c72007-04-29 19:47:21 +01004100 /* Add to the end of the list, so that first name
4101 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004102 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004103 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004104
4105 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004106 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004107 if (inet_pton(AF_INET, arg, &new->proto4))
4108 new->flags |= INP4;
4109 else if (inet_pton(AF_INET6, arg, &new->proto6))
4110 new->flags |= INP6;
4111 else
4112 break;
4113
4114 arg = comma;
4115 }
4116
4117 if ((comma = split_chr(arg, '/')))
4118 {
4119 if (strcmp(comma, "4") == 0)
4120 new->flags &= ~IN6;
4121 else if (strcmp(comma, "6") == 0)
4122 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004123 else
Petr Menšík59e47032018-11-02 22:39:39 +00004124 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004125 }
4126
4127 new->intr = opt_string_alloc(arg);
4128
4129 if (option == LOPT_DYNHOST)
4130 {
4131 if (!(new->flags & (INP4 | INP6)))
4132 ret_err(_("missing address in dynamic host"));
4133
4134 if (!(new->flags & IN4) || !(new->flags & IN6))
4135 arg = NULL; /* provoke error below */
4136
4137 new->flags &= ~(IN4 | IN6);
4138 }
4139 else
4140 {
4141 if (new->flags & (INP4 | INP6))
4142 arg = NULL; /* provoke error below */
4143 }
4144
4145 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4146 ret_err(option == LOPT_DYNHOST ?
4147 _("bad dynamic host") : _("bad interface name"));
4148
Simon Kelleyf2621c72007-04-29 19:47:21 +01004149 break;
4150 }
Simon Kelley9009d742008-11-14 20:04:27 +00004151
4152 case LOPT_CNAME: /* --cname */
4153 {
4154 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004155 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004156 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004157
Simon Kelleya1d973f2016-12-22 22:09:50 +00004158 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004159 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004160 pen = last;
4161 last = comma;
4162 }
4163
4164 if (!pen)
4165 ret_err(_("bad CNAME"));
4166
4167 if (pen != arg && atoi_check(last, &ttl))
4168 last = pen;
4169
4170 target = canonicalise_opt(last);
4171
4172 while (arg != last)
4173 {
Petr Menšík56f06232018-03-06 23:13:32 +00004174 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004175 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004176
4177 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004178 {
4179 free(target);
4180 free(alias);
4181 ret_err(_("bad CNAME"));
4182 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004183
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004184 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004185 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004186 {
4187 free(target);
4188 free(alias);
4189 ret_err(_("duplicate CNAME"));
4190 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004191 new = opt_malloc(sizeof(struct cname));
4192 new->next = daemon->cnames;
4193 daemon->cnames = new;
4194 new->alias = alias;
4195 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004196 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004197
Petr Menšík56f06232018-03-06 23:13:32 +00004198 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004199 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004200
Simon Kelley9009d742008-11-14 20:04:27 +00004201 break;
4202 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004203
4204 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004205 {
4206 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004207 char *dom, *target = NULL;
4208
Simon Kelleyf2621c72007-04-29 19:47:21 +01004209 comma = split(arg);
4210
Simon Kelley1f15b812009-10-13 17:49:32 +01004211 if (!(dom = canonicalise_opt(arg)) ||
4212 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004213 {
4214 free(dom);
4215 free(target);
4216 ret_err(_("bad PTR record"));
4217 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004218 else
4219 {
4220 new = opt_malloc(sizeof(struct ptr_record));
4221 new->next = daemon->ptr;
4222 daemon->ptr = new;
4223 new->name = dom;
4224 new->ptr = target;
4225 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004226 break;
4227 }
4228
Simon Kelley1a6bca82008-07-11 11:11:42 +01004229 case LOPT_NAPTR: /* --naptr-record */
4230 {
4231 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4232 int k = 0;
4233 struct naptr *new;
4234 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004235 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004236
4237 if ((a[0] = arg))
4238 for (k = 1; k < 7; k++)
4239 if (!(a[k] = split(a[k-1])))
4240 break;
4241
4242
4243 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004244 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004245 !atoi_check16(a[1], &order) ||
4246 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004247 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004248 {
4249 free(name);
4250 free(replace);
4251 ret_err(_("bad NAPTR record"));
4252 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004253 else
4254 {
4255 new = opt_malloc(sizeof(struct naptr));
4256 new->next = daemon->naptr;
4257 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004258 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004259 new->flags = opt_string_alloc(a[3]);
4260 new->services = opt_string_alloc(a[4]);
4261 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004262 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004263 new->order = order;
4264 new->pref = pref;
4265 }
4266 break;
4267 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004268
4269 case LOPT_RR: /* dns-rr */
4270 {
4271 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004272 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004273 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004274 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004275
4276 comma = split(arg);
4277 data = split(comma);
4278
4279 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004280 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004281
Petr Menšík59e47032018-11-02 22:39:39 +00004282 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004283 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004284 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004285 {
4286 free(new->name);
4287 ret_err_free(_("bad RR record"), new);
4288 }
4289
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004290 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004291 new->class = class;
4292 new->next = daemon->rr;
4293 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004294
4295 if (data)
4296 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004297 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004298 new->len = len;
4299 memcpy(new->txt, data, len);
4300 }
4301
4302 break;
4303 }
4304
Simon Kelley974a6d02018-08-23 23:01:16 +01004305 case LOPT_CAA: /* --caa-record */
4306 {
4307 struct txt_record *new;
4308 char *tag, *value;
4309 int flags;
4310
4311 comma = split(arg);
4312 tag = split(comma);
4313 value = split(tag);
4314
4315 new = opt_malloc(sizeof(struct txt_record));
4316 new->next = daemon->rr;
4317 daemon->rr = new;
4318
4319 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4320 ret_err(_("bad CAA record"));
4321
4322 unhide_metas(tag);
4323 unhide_metas(value);
4324
4325 new->len = strlen(tag) + strlen(value) + 2;
4326 new->txt = opt_malloc(new->len);
4327 new->txt[0] = flags;
4328 new->txt[1] = strlen(tag);
4329 memcpy(&new->txt[2], tag, strlen(tag));
4330 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4331 new->class = T_CAA;
4332
4333 break;
4334 }
4335
Simon Kelleyf2621c72007-04-29 19:47:21 +01004336 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004337 {
4338 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004339 unsigned char *p, *cnt;
4340 size_t len;
4341
4342 comma = split(arg);
4343
Simon Kelley824af852008-02-12 20:43:05 +00004344 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004345 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004346 new->stat = 0;
4347
Simon Kelley1f15b812009-10-13 17:49:32 +01004348 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004349 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004350
Petr Menšík59e47032018-11-02 22:39:39 +00004351 new->next = daemon->txt;
4352 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004353 len = comma ? strlen(comma) : 0;
4354 len += (len/255) + 1; /* room for extra counts */
4355 new->txt = p = opt_malloc(len);
4356
4357 cnt = p++;
4358 *cnt = 0;
4359
4360 while (comma && *comma)
4361 {
4362 unsigned char c = (unsigned char)*comma++;
4363
4364 if (c == ',' || *cnt == 255)
4365 {
4366 if (c != ',')
4367 comma--;
4368 cnt = p++;
4369 *cnt = 0;
4370 }
4371 else
4372 {
4373 *p++ = unhide_meta(c);
4374 (*cnt)++;
4375 }
4376 }
4377
4378 new->len = p - new->txt;
4379
Simon Kelley849a8352006-06-09 21:02:31 +01004380 break;
4381 }
4382
Simon Kelleyf2621c72007-04-29 19:47:21 +01004383 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004384 {
4385 int port = 1, priority = 0, weight = 0;
4386 char *name, *target = NULL;
4387 struct mx_srv_record *new;
4388
Simon Kelleyf2621c72007-04-29 19:47:21 +01004389 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004390
Simon Kelley1f15b812009-10-13 17:49:32 +01004391 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004392 ret_err(_("bad SRV record"));
4393
Simon Kelley849a8352006-06-09 21:02:31 +01004394 if (comma)
4395 {
4396 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004397 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004398 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004399 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004400
Simon Kelley849a8352006-06-09 21:02:31 +01004401 if (comma)
4402 {
4403 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004404 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004405 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004406 {
4407 free(name);
4408 ret_err_free(_("invalid port number"), target);
4409 }
Simon Kelley824af852008-02-12 20:43:05 +00004410
Simon Kelley849a8352006-06-09 21:02:31 +01004411 if (comma)
4412 {
4413 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004414 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004415 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004416 {
4417 free(name);
4418 ret_err_free(_("invalid priority"), target);
4419 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004420 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004421 {
4422 free(name);
4423 ret_err_free(_("invalid weight"), target);
4424 }
Simon Kelley849a8352006-06-09 21:02:31 +01004425 }
4426 }
4427 }
4428
Simon Kelley824af852008-02-12 20:43:05 +00004429 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004430 new->next = daemon->mxnames;
4431 daemon->mxnames = new;
4432 new->issrv = 1;
4433 new->name = name;
4434 new->target = target;
4435 new->srvport = port;
4436 new->priority = priority;
4437 new->weight = weight;
4438 break;
4439 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004440
Simon Kelleye759d422012-03-16 13:18:57 +00004441 case LOPT_HOST_REC: /* --host-record */
4442 {
Petr Menšík59e47032018-11-02 22:39:39 +00004443 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004444
Simon Kelleye759d422012-03-16 13:18:57 +00004445 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004446 ret_err(_("Bad host-record"));
4447
Petr Menšík59e47032018-11-02 22:39:39 +00004448 new = opt_malloc(sizeof(struct host_record));
4449 memset(new, 0, sizeof(struct host_record));
4450 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004451 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004452
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004453 while (arg)
4454 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004455 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004456 char *dig;
4457
4458 for (dig = arg; *dig != 0; dig++)
4459 if (*dig < '0' || *dig > '9')
4460 break;
4461 if (*dig == 0)
4462 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004463 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004464 {
4465 new->addr = addr.addr4;
4466 new->flags |= HR_4;
4467 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004468 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004469 {
4470 new->addr6 = addr.addr6;
4471 new->flags |= HR_6;
4472 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004473 else
4474 {
4475 int nomem;
4476 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004477 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004478 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004479 {
4480 struct name_list *tmp = new->names, *next;
4481 for (tmp = new->names; tmp; tmp = next)
4482 {
4483 next = tmp->next;
4484 free(tmp);
4485 }
4486 ret_err_free(_("Bad name in host-record"), new);
4487 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004488
Petr Menšík59e47032018-11-02 22:39:39 +00004489 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004490 nl->name = canon;
4491 /* keep order, so that PTR record goes to first name */
4492 nl->next = NULL;
4493 if (!new->names)
4494 new->names = nl;
4495 else
4496 {
4497 struct name_list *tmp;
4498 for (tmp = new->names; tmp->next; tmp = tmp->next);
4499 tmp->next = nl;
4500 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004501 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004502
4503 arg = comma;
4504 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004505 }
Simon Kelleye759d422012-03-16 13:18:57 +00004506
4507 /* Keep list order */
4508 if (!daemon->host_records_tail)
4509 daemon->host_records = new;
4510 else
4511 daemon->host_records_tail->next = new;
4512 new->next = NULL;
4513 daemon->host_records_tail = new;
4514 break;
4515 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004516
4517#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004518 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004519 daemon->timestamp_file = opt_string_alloc(arg);
4520 break;
4521
Simon Kelleyf3e57872018-07-20 21:10:48 +01004522 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004523 if (arg)
4524 {
4525 if (strcmp(arg, "no") == 0)
4526 set_option_bool(OPT_DNSSEC_IGN_NS);
4527 else
4528 ret_err(_("bad value for dnssec-check-unsigned"));
4529 }
4530 break;
4531
Simon Kelleyf3e57872018-07-20 21:10:48 +01004532 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004533 {
Simon Kelleyee415862014-02-11 11:07:22 +00004534 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4535 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4536 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004537
4538 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004539 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004540
Simon Kelleycbf13a22014-01-25 17:59:14 +00004541 if ((comma = split(arg)) && (algo = split(comma)))
4542 {
4543 int class = 0;
4544 if (strcmp(comma, "IN") == 0)
4545 class = C_IN;
4546 else if (strcmp(comma, "CH") == 0)
4547 class = C_CHAOS;
4548 else if (strcmp(comma, "HS") == 0)
4549 class = C_HESIOD;
4550
4551 if (class != 0)
4552 {
4553 new->class = class;
4554 comma = algo;
4555 algo = split(comma);
4556 }
4557 }
4558
Simon Kelleyee415862014-02-11 11:07:22 +00004559 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4560 !atoi_check16(comma, &new->keytag) ||
4561 !atoi_check8(algo, &new->algo) ||
4562 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004563 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004564 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004565
Simon Kelley0fc2f312014-01-08 10:26:58 +00004566 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004567 len = (2*strlen(keyhex))+1;
4568 new->digest = opt_malloc(len);
4569 unhide_metas(keyhex);
4570 /* 4034: "Whitespace is allowed within digits" */
4571 for (cp = keyhex; *cp; )
4572 if (isspace(*cp))
4573 for (cp1 = cp; *cp1; cp1++)
4574 *cp1 = *(cp1+1);
4575 else
4576 cp++;
4577 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004578 {
4579 free(new->name);
4580 ret_err_free(_("bad HEX in trust anchor"), new);
4581 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004582
Simon Kelleyee415862014-02-11 11:07:22 +00004583 new->next = daemon->ds;
4584 daemon->ds = new;
4585
Simon Kelley0fc2f312014-01-08 10:26:58 +00004586 break;
4587 }
4588#endif
4589
Simon Kelley7622fc02009-06-04 20:32:05 +01004590 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004591 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004592
Simon Kelley849a8352006-06-09 21:02:31 +01004593 }
Simon Kelley824af852008-02-12 20:43:05 +00004594
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004595 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004596}
4597
Simon Kelley28866e92011-02-14 20:19:14 +00004598static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004599{
Simon Kelley824af852008-02-12 20:43:05 +00004600 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004601 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004602
4603 while (fgets(buff, MAXDNAME, f))
4604 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004605 int white, i;
4606 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004607 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004608 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004609
Simon Kelley824af852008-02-12 20:43:05 +00004610 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004611 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004612 {
4613 if (setjmp(mem_jmp))
4614 continue;
4615 mem_recover = 1;
4616 }
4617
Simon Kelley13dee6f2017-02-28 16:51:58 +00004618 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004619 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004620 errmess = NULL;
4621
Simon Kelley849a8352006-06-09 21:02:31 +01004622 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4623 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004624 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004625 {
4626 if (*p == '"')
4627 {
4628 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004629
Simon Kelley849a8352006-06-09 21:02:31 +01004630 for(; *p && *p != '"'; p++)
4631 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004632 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004633 {
4634 if (p[1] == 't')
4635 p[1] = '\t';
4636 else if (p[1] == 'n')
4637 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004638 else if (p[1] == 'b')
4639 p[1] = '\b';
4640 else if (p[1] == 'r')
4641 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004642 else if (p[1] == 'e') /* escape */
4643 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004644 memmove(p, p+1, strlen(p+1)+1);
4645 }
4646 *p = hide_meta(*p);
4647 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004648
4649 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004650 {
4651 errmess = _("missing \"");
4652 goto oops;
4653 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004654
4655 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004656 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004657
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004658 if (isspace(*p))
4659 {
4660 *p = ' ';
4661 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004662 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004663 else
4664 {
4665 if (white && *p == '#')
4666 {
4667 *p = 0;
4668 break;
4669 }
4670 white = 0;
4671 }
Simon Kelley849a8352006-06-09 21:02:31 +01004672 }
4673
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004674
4675 /* strip leading spaces */
4676 for (start = buff; *start && *start == ' '; start++);
4677
4678 /* strip trailing spaces */
4679 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4680
4681 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004682 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004683 else
4684 start[len] = 0;
4685
Simon Kelley611ebc52012-07-16 16:23:46 +01004686 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004687 arg = start;
4688 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004689 {
4690 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004691 for (arg = p+1; *arg == ' '; arg++);
4692 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004693 *p = 0;
4694 }
4695 else
4696 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004697
Simon Kelley611ebc52012-07-16 16:23:46 +01004698 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004699 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004700 for (option = 0, i = 0; opts[i].name; i++)
4701 if (strcmp(opts[i].name, start) == 0)
4702 {
4703 option = opts[i].val;
4704 break;
4705 }
4706
4707 if (!option)
4708 errmess = _("bad option");
4709 else if (opts[i].has_arg == 0 && arg)
4710 errmess = _("extraneous parameter");
4711 else if (opts[i].has_arg == 1 && !arg)
4712 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004713 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4714 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004715 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004716
4717 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004718 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004719 strcpy(daemon->namebuff, errmess);
4720
Simon Kelley9bafdc62018-08-21 22:53:38 +01004721 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004722 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004723 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004724 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004725 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004726 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004727 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004728 }
Simon Kelley849a8352006-06-09 21:02:31 +01004729 }
4730
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004731 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004732 fclose(f);
4733}
4734
Simon Kelley4f7bb572018-03-08 18:47:08 +00004735#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004736int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004737{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004738 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4739
Simon Kelley70d18732015-01-31 19:59:29 +00004740 if (flags & AH_DHCP_HST)
4741 return one_file(file, LOPT_BANK);
4742 else if (flags & AH_DHCP_OPT)
4743 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004744
Simon Kelley70d18732015-01-31 19:59:29 +00004745 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004746}
4747#endif
4748
Simon Kelley395eb712012-07-06 22:07:05 +01004749static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004750{
4751 FILE *f;
4752 int nofile_ok = 0;
4753 static int read_stdin = 0;
4754 static struct fileread {
4755 dev_t dev;
4756 ino_t ino;
4757 struct fileread *next;
4758 } *filesread = NULL;
4759
4760 if (hard_opt == '7')
4761 {
4762 /* default conf-file reading */
4763 hard_opt = 0;
4764 nofile_ok = 1;
4765 }
4766
4767 if (hard_opt == 0 && strcmp(file, "-") == 0)
4768 {
4769 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004770 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004771 read_stdin = 1;
4772 file = "stdin";
4773 f = stdin;
4774 }
4775 else
4776 {
4777 /* ignore repeated files. */
4778 struct stat statbuf;
4779
4780 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4781 {
4782 struct fileread *r;
4783
4784 for (r = filesread; r; r = r->next)
4785 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004786 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004787
4788 r = safe_malloc(sizeof(struct fileread));
4789 r->next = filesread;
4790 filesread = r;
4791 r->dev = statbuf.st_dev;
4792 r->ino = statbuf.st_ino;
4793 }
4794
4795 if (!(f = fopen(file, "r")))
4796 {
4797 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004798 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004799 else
4800 {
4801 char *str = _("cannot read %s: %s");
4802 if (hard_opt != 0)
4803 {
4804 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004805 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004806 }
4807 else
4808 die(str, file, EC_FILE);
4809 }
4810 }
4811 }
4812
4813 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004814 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004815}
4816
4817/* expand any name which is a directory */
4818struct hostsfile *expand_filelist(struct hostsfile *list)
4819{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004820 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004821 struct hostsfile *ah;
4822
Simon Kelley19c51cf2014-03-18 22:38:30 +00004823 /* find largest used index */
4824 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004825 {
4826 if (i <= ah->index)
4827 i = ah->index + 1;
4828
4829 if (ah->flags & AH_DIR)
4830 ah->flags |= AH_INACTIVE;
4831 else
4832 ah->flags &= ~AH_INACTIVE;
4833 }
4834
4835 for (ah = list; ah; ah = ah->next)
4836 if (!(ah->flags & AH_INACTIVE))
4837 {
4838 struct stat buf;
4839 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4840 {
4841 DIR *dir_stream;
4842 struct dirent *ent;
4843
4844 /* don't read this as a file */
4845 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004846
Simon Kelley28866e92011-02-14 20:19:14 +00004847 if (!(dir_stream = opendir(ah->fname)))
4848 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4849 ah->fname, strerror(errno));
4850 else
4851 {
4852 while ((ent = readdir(dir_stream)))
4853 {
4854 size_t lendir = strlen(ah->fname);
4855 size_t lenfile = strlen(ent->d_name);
4856 struct hostsfile *ah1;
4857 char *path;
4858
4859 /* ignore emacs backups and dotfiles */
4860 if (lenfile == 0 ||
4861 ent->d_name[lenfile - 1] == '~' ||
4862 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4863 ent->d_name[0] == '.')
4864 continue;
4865
4866 /* see if we have an existing record.
4867 dir is ah->fname
4868 file is ent->d_name
4869 path to match is ah1->fname */
4870
4871 for (ah1 = list; ah1; ah1 = ah1->next)
4872 {
4873 if (lendir < strlen(ah1->fname) &&
4874 strstr(ah1->fname, ah->fname) == ah1->fname &&
4875 ah1->fname[lendir] == '/' &&
4876 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4877 {
4878 ah1->flags &= ~AH_INACTIVE;
4879 break;
4880 }
4881 }
4882
4883 /* make new record */
4884 if (!ah1)
4885 {
4886 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4887 continue;
4888
4889 if (!(path = whine_malloc(lendir + lenfile + 2)))
4890 {
4891 free(ah1);
4892 continue;
4893 }
4894
4895 strcpy(path, ah->fname);
4896 strcat(path, "/");
4897 strcat(path, ent->d_name);
4898 ah1->fname = path;
4899 ah1->index = i++;
4900 ah1->flags = AH_DIR;
4901 ah1->next = list;
4902 list = ah1;
4903 }
4904
4905 /* inactivate record if not regular file */
4906 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4907 ah1->flags |= AH_INACTIVE;
4908
4909 }
4910 closedir(dir_stream);
4911 }
4912 }
4913 }
4914
4915 return list;
4916}
4917
Simon Kelley7b1eae42014-02-20 13:43:28 +00004918void read_servers_file(void)
4919{
4920 FILE *f;
4921
4922 if (!(f = fopen(daemon->servers_file, "r")))
4923 {
4924 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4925 return;
4926 }
4927
4928 mark_servers(SERV_FROM_FILE);
4929 cleanup_servers();
4930
4931 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4932}
4933
Simon Kelley28866e92011-02-14 20:19:14 +00004934
Simon Kelley7622fc02009-06-04 20:32:05 +01004935#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004936static void clear_dynamic_conf(void)
4937{
4938 struct dhcp_config *configs, *cp, **up;
4939
4940 /* remove existing... */
4941 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4942 {
4943 cp = configs->next;
4944
4945 if (configs->flags & CONFIG_BANK)
4946 {
4947 struct hwaddr_config *mac, *tmp;
4948 struct dhcp_netid_list *list, *tmplist;
4949
4950 for (mac = configs->hwaddr; mac; mac = tmp)
4951 {
4952 tmp = mac->next;
4953 free(mac);
4954 }
4955
4956 if (configs->flags & CONFIG_CLID)
4957 free(configs->clid);
4958
4959 for (list = configs->netid; list; list = tmplist)
4960 {
4961 free(list->list);
4962 tmplist = list->next;
4963 free(list);
4964 }
4965
4966 if (configs->flags & CONFIG_NAME)
4967 free(configs->hostname);
4968
4969 *up = configs->next;
4970 free(configs);
4971 }
4972 else
4973 up = &configs->next;
4974 }
4975}
4976
4977static void clear_dynamic_opt(void)
4978{
4979 struct dhcp_opt *opts, *cp, **up;
4980 struct dhcp_netid *id, *next;
4981
4982 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4983 {
4984 cp = opts->next;
4985
4986 if (opts->flags & DHOPT_BANK)
4987 {
4988 if ((opts->flags & DHOPT_VENDOR))
4989 free(opts->u.vendor_class);
4990 free(opts->val);
4991 for (id = opts->netid; id; id = next)
4992 {
4993 next = id->next;
4994 free(id->net);
4995 free(id);
4996 }
4997 *up = opts->next;
4998 free(opts);
4999 }
5000 else
5001 up = &opts->next;
5002 }
5003}
5004
Simon Kelley824af852008-02-12 20:43:05 +00005005void reread_dhcp(void)
5006{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005007 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005008
Simon Kelley4f7bb572018-03-08 18:47:08 +00005009 /* Do these even if there is no daemon->dhcp_hosts_file or
5010 daemon->dhcp_opts_file since entries may have been created by the
5011 inotify dynamic file reading system. */
5012
5013 clear_dynamic_conf();
5014 clear_dynamic_opt();
5015
5016 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005017 {
Simon Kelley28866e92011-02-14 20:19:14 +00005018 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5019 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005020 if (!(hf->flags & AH_INACTIVE))
5021 {
5022 if (one_file(hf->fname, LOPT_BANK))
5023 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5024 }
Simon Kelley824af852008-02-12 20:43:05 +00005025 }
5026
5027 if (daemon->dhcp_opts_file)
5028 {
Simon Kelley28866e92011-02-14 20:19:14 +00005029 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5030 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5031 if (!(hf->flags & AH_INACTIVE))
5032 {
Simon Kelley395eb712012-07-06 22:07:05 +01005033 if (one_file(hf->fname, LOPT_OPTS))
5034 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005035 }
Simon Kelley824af852008-02-12 20:43:05 +00005036 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005037
5038# ifdef HAVE_INOTIFY
5039 /* Setup notify and read pre-existing files. */
5040 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5041# endif
Simon Kelley824af852008-02-12 20:43:05 +00005042}
Simon Kelley7622fc02009-06-04 20:32:05 +01005043#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005044
Simon Kelley5aabfc72007-08-29 11:24:47 +01005045void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005046{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005047 size_t argbuf_size = MAXDNAME;
5048 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005049 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005050 int option, testmode = 0;
5051 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005052
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005053 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005054
Simon Kelley824af852008-02-12 20:43:05 +00005055 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005056 memset(daemon, 0, sizeof(struct daemon));
5057 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005058
Simon Kelley3be34542004-09-11 19:12:13 +01005059 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005060 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005061 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005062 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005063 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5064 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005065 daemon->default_resolv.is_default = 1;
5066 daemon->default_resolv.name = RESOLVFILE;
5067 daemon->resolv_files = &daemon->default_resolv;
5068 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005069 daemon->runfile = RUNFILE;
5070 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005071 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005072 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005073 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005074 daemon->auth_ttl = AUTH_TTL;
5075 daemon->soa_refresh = SOA_REFRESH;
5076 daemon->soa_retry = SOA_RETRY;
5077 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00005078 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00005079 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01005080
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005081#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005082 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5083 add_txt("authors.bind", "Simon Kelley", 0);
5084 add_txt("copyright.bind", COPYRIGHT, 0);
5085 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5086 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5087 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5088 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5089 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5090#ifdef HAVE_AUTH
5091 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5092#endif
5093 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005094#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005095
Simon Kelley849a8352006-06-09 21:02:31 +01005096 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005097 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005098#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005099 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005100#else
Simon Kelley849a8352006-06-09 21:02:31 +01005101 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005102#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005103
5104 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005105 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005106 for (; optind < argc; optind++)
5107 {
5108 unsigned char *c = (unsigned char *)argv[optind];
5109 for (; *c != 0; c++)
5110 if (!isspace(*c))
5111 die(_("junk found in command line"), NULL, EC_BADCONF);
5112 }
Simon Kelley28866e92011-02-14 20:19:14 +00005113 break;
5114 }
5115
Simon Kelley849a8352006-06-09 21:02:31 +01005116 /* Copy optarg so that argv doesn't get changed */
5117 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005118 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005119 if (strlen(optarg) >= argbuf_size)
5120 {
5121 free(argbuf);
5122 argbuf_size = strlen(optarg) + 1;
5123 argbuf = opt_malloc(argbuf_size);
5124 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005125 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005126 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005127 }
5128 else
5129 arg = NULL;
5130
5131 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005132 if (option == LOPT_TEST)
5133 testmode = 1;
5134 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005135 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005136#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005137 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005138 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005139#ifdef HAVE_DHCP6
5140 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5141 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005142#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005143 else
5144#endif
5145 do_usage();
5146
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005147 exit(0);
5148 }
Simon Kelley849a8352006-06-09 21:02:31 +01005149 else if (option == 'v')
5150 {
5151 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005152 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005153 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5154 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005155 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005156 exit(0);
5157 }
Simon Kelley849a8352006-06-09 21:02:31 +01005158 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005159 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005160 if (!conffile)
5161 conffile = opt_string_alloc(arg);
5162 else
5163 {
5164 char *extra = opt_string_alloc(arg);
5165 one_file(extra, 0);
5166 free(extra);
5167 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005168 }
Simon Kelley849a8352006-06-09 21:02:31 +01005169 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005170 {
Simon Kelley26128d22004-11-14 16:43:54 +00005171#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005172 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005173#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005174 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005175#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005176 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005177 }
5178 }
Simon Kelley849a8352006-06-09 21:02:31 +01005179
Neil Jerram3bd4c472018-01-18 22:49:38 +00005180 free(argbuf);
5181
Simon Kelley849a8352006-06-09 21:02:31 +01005182 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005183 {
Petr Menšík59e47032018-11-02 22:39:39 +00005184 one_file(conffile, 0);
5185 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005186 }
Petr Menšík59e47032018-11-02 22:39:39 +00005187 else
5188 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005189
Simon Kelley1a6bca82008-07-11 11:11:42 +01005190 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005191 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005192 {
5193 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005194 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005195 if (!(tmp->flags & SERV_HAS_SOURCE))
5196 {
5197 if (tmp->source_addr.sa.sa_family == AF_INET)
5198 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005199 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5200 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005201 }
5202 }
5203
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005204 if (daemon->host_records)
5205 {
5206 struct host_record *hr;
5207
5208 for (hr = daemon->host_records; hr; hr = hr->next)
5209 if (hr->ttl == -1)
5210 hr->ttl = daemon->local_ttl;
5211 }
5212
5213 if (daemon->cnames)
5214 {
Simon Kelley903df072017-01-19 17:22:00 +00005215 struct cname *cn, *cn2, *cn3;
5216
5217#define NOLOOP 1
5218#define TESTLOOP 2
5219
Simon Kelley157d8cf2019-10-25 17:46:49 +01005220 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005221 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005222 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005223 {
5224 if (cn->ttl == -1)
5225 cn->ttl = daemon->local_ttl;
5226 cn->flag = 0;
5227 cn->targetp = NULL;
5228 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5229 if (hostname_isequal(cn->target, cn2->alias))
5230 {
5231 cn->targetp = cn2;
5232 break;
5233 }
5234 }
5235
5236 /* Find any CNAME loops.*/
5237 for (cn = daemon->cnames; cn; cn = cn->next)
5238 {
5239 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5240 {
5241 if (cn2->flag == NOLOOP)
5242 break;
5243
5244 if (cn2->flag == TESTLOOP)
5245 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5246
5247 cn2->flag = TESTLOOP;
5248 }
5249
5250 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5251 cn3->flag = NOLOOP;
5252 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005253 }
5254
Simon Kelley3be34542004-09-11 19:12:13 +01005255 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005256 {
5257 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005258 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005259 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005260 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005261 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005262 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005263 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005264
5265 /* create default, if not specified */
5266 if (daemon->authserver && !daemon->hostmaster)
5267 {
5268 strcpy(buff, "hostmaster.");
5269 strcat(buff, daemon->authserver);
5270 daemon->hostmaster = opt_string_alloc(buff);
5271 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005272
5273 if (!daemon->dhcp_pxe_vendors)
5274 {
5275 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5276 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5277 daemon->dhcp_pxe_vendors->next = NULL;
5278 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005279
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005280 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005281 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005282 {
Simon Kelley0a852542005-03-23 20:28:59 +00005283 struct mx_srv_record *mx;
5284
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005285 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005286 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005287
Simon Kelley0a852542005-03-23 20:28:59 +00005288 for (mx = daemon->mxnames; mx; mx = mx->next)
5289 if (!mx->issrv && hostname_isequal(mx->name, buff))
5290 break;
5291
Simon Kelley28866e92011-02-14 20:19:14 +00005292 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005293 {
Simon Kelley824af852008-02-12 20:43:05 +00005294 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005295 mx->next = daemon->mxnames;
5296 mx->issrv = 0;
5297 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005298 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005299 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005300 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005301
Simon Kelley3be34542004-09-11 19:12:13 +01005302 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005303 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005304
5305 for (mx = daemon->mxnames; mx; mx = mx->next)
5306 if (!mx->issrv && !mx->target)
5307 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005308 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005309
Simon Kelley28866e92011-02-14 20:19:14 +00005310 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005311 daemon->resolv_files &&
5312 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005313 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005314 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005315
Simon Kelley28866e92011-02-14 20:19:14 +00005316 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005317 {
5318 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005319 FILE *f;
5320
Simon Kelley28866e92011-02-14 20:19:14 +00005321 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005322 !daemon->resolv_files ||
5323 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005324 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005325
Simon Kelley3be34542004-09-11 19:12:13 +01005326 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005327 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005328
5329 while ((line = fgets(buff, MAXDNAME, f)))
5330 {
5331 char *token = strtok(line, " \t\n\r");
5332
5333 if (!token || strcmp(token, "search") != 0)
5334 continue;
5335
5336 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005337 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005338 break;
5339 }
Simon Kelley3be34542004-09-11 19:12:13 +01005340
Simon Kelleyde379512004-06-22 20:23:33 +01005341 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005342
Simon Kelley3be34542004-09-11 19:12:13 +01005343 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005344 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005345 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005346
5347 if (daemon->domain_suffix)
5348 {
5349 /* add domain for any srv record without one. */
5350 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005351
Simon Kelley3d8df262005-08-29 12:19:27 +01005352 for (srv = daemon->mxnames; srv; srv = srv->next)
5353 if (srv->issrv &&
5354 strchr(srv->name, '.') &&
5355 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5356 {
5357 strcpy(buff, srv->name);
5358 strcat(buff, ".");
5359 strcat(buff, daemon->domain_suffix);
5360 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005361 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005362 }
5363 }
Simon Kelley28866e92011-02-14 20:19:14 +00005364 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005365 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005366
Simon Kelleyc8a80482014-03-05 14:29:54 +00005367 /* If there's access-control config, then ignore --local-service, it's intended
5368 as a system default to keep otherwise unconfigured installations safe. */
5369 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5370 reset_option_bool(OPT_LOCAL_SERVICE);
5371
Simon Kelley7622fc02009-06-04 20:32:05 +01005372 if (testmode)
5373 {
5374 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5375 exit(0);
5376 }
Simon Kelley849a8352006-06-09 21:02:31 +01005377}