blob: 03b0d647a6ae5ab88d2a0d25762e3d8e32c5c133 [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>
Kyle Swenson545712c2021-11-17 12:25:04 -070021/* CRADLEPOINT */
22#include "unescape_c_string.h"
23#include "opendns.h"
24/* CRADLEPOINT */
Simon Kelley824af852008-02-12 20:43:05 +000025
Simon Kelley7622fc02009-06-04 20:32:05 +010026static volatile int mem_recover = 0;
27static jmp_buf mem_jmp;
Simon Kelley395eb712012-07-06 22:07:05 +010028static int one_file(char *file, int hard_opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010029
Simon Kelley824af852008-02-12 20:43:05 +000030/* Solaris headers don't have facility names. */
31#ifdef HAVE_SOLARIS_NETWORK
32static const struct {
33 char *c_name;
34 unsigned int c_val;
35} facilitynames[] = {
36 { "kern", LOG_KERN },
37 { "user", LOG_USER },
38 { "mail", LOG_MAIL },
39 { "daemon", LOG_DAEMON },
40 { "auth", LOG_AUTH },
41 { "syslog", LOG_SYSLOG },
42 { "lpr", LOG_LPR },
43 { "news", LOG_NEWS },
44 { "uucp", LOG_UUCP },
Simon Kelley824af852008-02-12 20:43:05 +000045 { "audit", LOG_AUDIT },
Simon Kelley824af852008-02-12 20:43:05 +000046 { "cron", LOG_CRON },
47 { "local0", LOG_LOCAL0 },
48 { "local1", LOG_LOCAL1 },
49 { "local2", LOG_LOCAL2 },
50 { "local3", LOG_LOCAL3 },
51 { "local4", LOG_LOCAL4 },
52 { "local5", LOG_LOCAL5 },
53 { "local6", LOG_LOCAL6 },
54 { "local7", LOG_LOCAL7 },
55 { NULL, 0 }
56};
57#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000058
Simon Kelley849a8352006-06-09 21:02:31 +010059#ifndef HAVE_GETOPT_LONG
Simon Kelley9e4abcb2004-01-22 19:47:41 +000060struct myoption {
61 const char *name;
62 int has_arg;
63 int *flag;
64 int val;
65};
Simon Kelley849a8352006-06-09 21:02:31 +010066#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000067
Simon Kelley9009d742008-11-14 20:04:27 +000068#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 +000069
Simon Kelley16972692006-10-16 20:04:18 +010070/* options which don't have a one-char version */
Simon Kelleye98bd522014-03-28 20:41:23 +000071#define LOPT_RELOAD 256
72#define LOPT_NO_NAMES 257
73#define LOPT_TFTP 258
74#define LOPT_SECURE 259
75#define LOPT_PREFIX 260
76#define LOPT_PTR 261
77#define LOPT_BRIDGE 262
78#define LOPT_TFTP_MAX 263
79#define LOPT_FORCE 264
80#define LOPT_NOBLOCK 265
81#define LOPT_LOG_OPTS 266
82#define LOPT_MAX_LOGS 267
83#define LOPT_CIRCUIT 268
84#define LOPT_REMOTE 269
85#define LOPT_SUBSCR 270
86#define LOPT_INTNAME 271
87#define LOPT_BANK 272
88#define LOPT_DHCP_HOST 273
89#define LOPT_APREF 274
90#define LOPT_OVERRIDE 275
91#define LOPT_TFTPPORTS 276
92#define LOPT_REBIND 277
93#define LOPT_NOLAST 278
94#define LOPT_OPTS 279
95#define LOPT_DHCP_OPTS 280
96#define LOPT_MATCH 281
97#define LOPT_BROADCAST 282
98#define LOPT_NEGTTL 283
99#define LOPT_ALTPORT 284
100#define LOPT_SCRIPTUSR 285
101#define LOPT_LOCAL 286
102#define LOPT_NAPTR 287
103#define LOPT_MINPORT 288
104#define LOPT_DHCP_FQDN 289
105#define LOPT_CNAME 290
106#define LOPT_PXE_PROMT 291
107#define LOPT_PXE_SERV 292
108#define LOPT_TEST 293
109#define LOPT_TAG_IF 294
110#define LOPT_PROXY 295
111#define LOPT_GEN_NAMES 296
112#define LOPT_MAXTTL 297
113#define LOPT_NO_REBIND 298
114#define LOPT_LOC_REBND 299
115#define LOPT_ADD_MAC 300
116#define LOPT_DNSSEC 301
117#define LOPT_INCR_ADDR 302
118#define LOPT_CONNTRACK 303
119#define LOPT_FQDN 304
120#define LOPT_LUASCRIPT 305
121#define LOPT_RA 306
122#define LOPT_DUID 307
123#define LOPT_HOST_REC 308
124#define LOPT_TFTP_LC 309
125#define LOPT_RR 310
126#define LOPT_CLVERBIND 311
127#define LOPT_MAXCTTL 312
128#define LOPT_AUTHZONE 313
129#define LOPT_AUTHSERV 314
130#define LOPT_AUTHTTL 315
131#define LOPT_AUTHSOA 316
132#define LOPT_AUTHSFS 317
133#define LOPT_AUTHPEER 318
134#define LOPT_IPSET 319
135#define LOPT_SYNTH 320
Simon Kelleye98bd522014-03-28 20:41:23 +0000136#define LOPT_RELAY 323
137#define LOPT_RA_PARAM 324
138#define LOPT_ADD_SBNET 325
139#define LOPT_QUIET_DHCP 326
140#define LOPT_QUIET_DHCP6 327
141#define LOPT_QUIET_RA 328
142#define LOPT_SEC_VALID 329
143#define LOPT_TRUST_ANCHOR 330
144#define LOPT_DNSSEC_DEBUG 331
145#define LOPT_REV_SERV 332
146#define LOPT_SERVERS_FILE 333
147#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000148#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000149#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100150#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000151#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000152#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000153#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000154#define LOPT_DHOPT_INOTIFY 341
155#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000156#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100157#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000158#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000159#define LOPT_CPE_ID 346
160#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000161#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000162#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100163#define LOPT_REPLY_DELAY 350
Simon Kelley734d5312018-03-23 23:09:53 +0000164#define LOPT_RAPID_COMMIT 351
Simon Kelley6b173352018-05-08 18:32:14 +0100165#define LOPT_DUMPFILE 352
166#define LOPT_DUMPMASK 353
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100167#define LOPT_UBUS 354
Simon Kelleyc8226202018-08-08 23:46:03 +0100168#define LOPT_NAME_MATCH 355
Simon Kelley974a6d02018-08-23 23:01:16 +0100169#define LOPT_CAA 356
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000170#define LOPT_SHARED_NET 357
Florent Fourcot13a58f92019-06-20 10:26:40 +0200171#define LOPT_IGNORE_CLID 358
Simon Kelley66f62652020-01-05 16:21:24 +0000172#define LOPT_SINGLE_PORT 359
Simon Kelleyee645822020-02-27 16:34:14 +0000173#define LOPT_SCRIPT_TIME 360
Wang Shanker4ded9622020-12-04 10:17:35 +0800174#define LOPT_PXE_VENDOR 361
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000175#define LOPT_DYNHOST 362
Simon Kelleyb260d222021-03-12 21:57:57 +0000176#define LOPT_LOG_DEBUG 363
Kyle Swenson545712c2021-11-17 12:25:04 -0700177/* CRADLEPOINT */
178#define LOPT_EDNS_OPTION 364
179#define LOPT_SSID_MAP 365
180/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +0100181#ifdef HAVE_GETOPT_LONG
182static const struct option opts[] =
183#else
184static const struct myoption opts[] =
185#endif
186 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100187 { "version", 0, 0, 'v' },
188 { "no-hosts", 0, 0, 'h' },
189 { "no-poll", 0, 0, 'n' },
190 { "help", 0, 0, 'w' },
191 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000192 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100193 { "user", 2, 0, 'u' },
194 { "group", 2, 0, 'g' },
195 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000196 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100197 { "mx-host", 1, 0, 'm' },
198 { "mx-target", 1, 0, 't' },
199 { "cache-size", 2, 0, 'c' },
200 { "port", 1, 0, 'p' },
201 { "dhcp-leasefile", 2, 0, 'l' },
202 { "dhcp-lease", 1, 0, 'l' },
203 { "dhcp-host", 1, 0, 'G' },
204 { "dhcp-range", 1, 0, 'F' },
205 { "dhcp-option", 1, 0, 'O' },
206 { "dhcp-boot", 1, 0, 'M' },
207 { "domain", 1, 0, 's' },
208 { "domain-suffix", 1, 0, 's' },
209 { "interface", 1, 0, 'i' },
210 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000211 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100212 { "bogus-priv", 0, 0, 'b' },
213 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000214 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100215 { "selfmx", 0, 0, 'e' },
216 { "filterwin2k", 0, 0, 'f' },
217 { "pid-file", 2, 0, 'x' },
218 { "strict-order", 0, 0, 'o' },
219 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000220 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100221 { "local", 1, 0, LOPT_LOCAL },
222 { "address", 1, 0, 'A' },
223 { "conf-file", 2, 0, 'C' },
224 { "no-resolv", 0, 0, 'R' },
225 { "expand-hosts", 0, 0, 'E' },
226 { "localmx", 0, 0, 'L' },
227 { "local-ttl", 1, 0, 'T' },
228 { "no-negcache", 0, 0, 'N' },
229 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000230 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100231 { "query-port", 1, 0, 'Q' },
232 { "except-interface", 1, 0, 'I' },
233 { "no-dhcp-interface", 1, 0, '2' },
234 { "domain-needed", 0, 0, 'D' },
235 { "dhcp-lease-max", 1, 0, 'X' },
236 { "bind-interfaces", 0, 0, 'z' },
237 { "read-ethers", 0, 0, 'Z' },
238 { "alias", 1, 0, 'V' },
239 { "dhcp-vendorclass", 1, 0, 'U' },
240 { "dhcp-userclass", 1, 0, 'j' },
241 { "dhcp-ignore", 1, 0, 'J' },
242 { "edns-packet-max", 1, 0, 'P' },
243 { "keep-in-foreground", 0, 0, 'k' },
244 { "dhcp-authoritative", 0, 0, 'K' },
245 { "srv-host", 1, 0, 'W' },
246 { "localise-queries", 0, 0, 'y' },
247 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100248 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100249 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100250 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100251 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100252 { "bootp-dynamic", 2, 0, '3' },
253 { "dhcp-mac", 1, 0, '4' },
254 { "no-ping", 0, 0, '5' },
255 { "dhcp-script", 1, 0, '6' },
256 { "conf-dir", 1, 0, '7' },
257 { "log-facility", 1, 0 ,'8' },
258 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000259 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100260 { "dns-forward-max", 1, 0, '0' },
261 { "clear-on-reload", 0, 0, LOPT_RELOAD },
262 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100263 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100264 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100265 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100266 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100267 { "tftp-root", 1, 0, LOPT_PREFIX },
268 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000269 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100270 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000271 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100272 { "ptr-record", 1, 0, LOPT_PTR },
273 { "naptr-record", 1, 0, LOPT_NAPTR },
274 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000275 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100276 { "dhcp-option-force", 1, 0, LOPT_FORCE },
277 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
278 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
279 { "log-async", 2, 0, LOPT_MAX_LOGS },
280 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
281 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
282 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800283 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100284 { "interface-name", 1, 0, LOPT_INTNAME },
285 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
286 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000287 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000288 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100289 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
290 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
291 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100292 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100293 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100294 { "dhcp-match", 1, 0, LOPT_MATCH },
295 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100296 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100297 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100298 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000299 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100300 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100301 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
302 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
303 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000304 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100305 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
306 { "cname", 1, 0, LOPT_CNAME },
307 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
308 { "pxe-service", 1, 0, LOPT_PXE_SERV },
309 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100310 { "tag-if", 1, 0, LOPT_TAG_IF },
311 { "dhcp-proxy", 2, 0, LOPT_PROXY },
312 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
313 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000314 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100315 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000316 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000317 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100318 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
319 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000320 { "dhcp-client-update", 0, 0, LOPT_FQDN },
321 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000322 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000323 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000324 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100325 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000326 { "auth-zone", 1, 0, LOPT_AUTHZONE },
327 { "auth-server", 1, 0, LOPT_AUTHSERV },
328 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
329 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000330 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000331 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000332 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100333 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200334 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000335 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000336 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100337 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000338 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000339 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100340 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100341 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100342 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
343 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
344 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100345 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000346 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000347 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100348 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000349 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100350 { "dumpfile", 1, 0, LOPT_DUMPFILE },
351 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200352 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000353 { "dynamic-host", 1, 0, LOPT_DYNHOST },
Simon Kelleyb260d222021-03-12 21:57:57 +0000354 { "log-debug", 0, 0, LOPT_LOG_DEBUG },
Kyle Swenson545712c2021-11-17 12:25:04 -0700355/* CRADLEPOINT */
356 { "edns-option", 1, 0, LOPT_EDNS_OPTION },
357 { "ssid-map", 1, 0, LOPT_SSID_MAP },
358/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +0100359 { NULL, 0, 0, 0 }
360 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000361
Simon Kelley28866e92011-02-14 20:19:14 +0000362
363#define ARG_DUP OPT_LAST
364#define ARG_ONE OPT_LAST + 1
365#define ARG_USED_CL OPT_LAST + 2
366#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000367
Simon Kelley1a6bca82008-07-11 11:11:42 +0100368static struct {
369 int opt;
370 unsigned int rept;
371 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000372 char * const desc;
373 char * const arg;
374} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000375 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
376 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
379 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
380 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100381 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
382 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
383 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
384 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
385 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000386 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
387 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100388 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000389 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
390 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000391 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000392 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100393 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100394 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000395 { '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 +0000396 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000397 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
398 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100399 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
400 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
401 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
402 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800403 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100404 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
405 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100406 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
407 { '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 +0000408 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100409 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000410 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100411 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
412 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
413 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
414 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
415 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
416 { 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 +0000417 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
418 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000419 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000420 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100421 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000422 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000423 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000424 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000425 { 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 +0000426 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000427 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000428 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
429 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
430 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
431 { 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 +0000432 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
433 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000434 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100435 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100436 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000437 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
438 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100439 { '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 +0000440 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
441 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100442 { '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 +0000443 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
444 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
445 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100446 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
447 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100448 { '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 +0100449 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000450 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100451 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
452 { '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 +0000453 { 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 +0000454 { 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 +0100455 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000456 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
457 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
458 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000459 { 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 +0000460 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000461 { '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 +0100462 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000463 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100464 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100465 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100466 { 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 +0100467 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100468 { 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 +0100469 { 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 +0100470 { 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 +0100471 { 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 +0000472 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000473 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100474 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100475 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100476 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000477 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100478 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000479 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100480 { 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 +0100481 { 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 +0000482 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100483 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100484 { 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 +0100485 { 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 +0100486 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100487 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
488 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000489 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000490 { 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 +0000491 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
492 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100493 { 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 +0000494 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100495 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
496 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
497 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000498 { 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 +0100499 { 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 +0000500 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100501 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100502 { 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 +0200503 { 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 +0100504 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000505 { 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 +0000506 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000507 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000508 { 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 +0000509 { 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 +0100510 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100511 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000512 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000513 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000514 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000515 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000516 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000517 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
518 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100519 { 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 +0100520 { 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 +0000521 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000522 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000523 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100524 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000525 { 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 +0000526 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100527 { 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 +0100528 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
529 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
530 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelleyb260d222021-03-12 21:57:57 +0000531 { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000532 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
533 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000534 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000535 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100536 { 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 +0000537 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100538 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
539 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000540 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100541 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000542};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000543
Josh Soref730c6742017-02-06 16:14:04 +0000544/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100545 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 +0100546 following sequence so that they map to themselves: it is therefore possible to call
547 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000548 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100549 couple of other places.
550 Note that space is included here so that
551 --dhcp-option=3, string
552 has five characters, whilst
553 --dhcp-option=3," string"
554 has six.
555*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100556
Simon Kelleyf2621c72007-04-29 19:47:21 +0100557static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100558
559static char hide_meta(char c)
560{
561 unsigned int i;
562
563 for (i = 0; i < (sizeof(meta) - 1); i++)
564 if (c == meta[i])
565 return (char)i;
566
567 return c;
568}
569
570static char unhide_meta(char cr)
571{
572 unsigned int c = cr;
573
574 if (c < (sizeof(meta) - 1))
575 cr = meta[c];
576
577 return cr;
578}
579
580static void unhide_metas(char *cp)
581{
582 if (cp)
583 for(; *cp; cp++)
584 *cp = unhide_meta(*cp);
585}
586
Simon Kelley824af852008-02-12 20:43:05 +0000587static void *opt_malloc(size_t size)
588{
589 void *ret;
590
591 if (mem_recover)
592 {
593 ret = whine_malloc(size);
594 if (!ret)
595 longjmp(mem_jmp, 1);
596 }
597 else
598 ret = safe_malloc(size);
599
600 return ret;
601}
602
Petr Menšík59e47032018-11-02 22:39:39 +0000603static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100604{
605 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000606 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100607
Petr Menšík59e47032018-11-02 22:39:39 +0000608 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100609 {
Petr Menšík59e47032018-11-02 22:39:39 +0000610 ret = opt_malloc(len+1);
611 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100612
613 /* restore hidden metachars */
614 unhide_metas(ret);
615 }
616
617 return ret;
618}
619
Simon Kelley3d8df262005-08-29 12:19:27 +0100620
Simon Kelleyf2621c72007-04-29 19:47:21 +0100621/* find next comma, split string with zero and eliminate spaces.
622 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000623
624static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100625{
626 char *comma, *p;
627
Simon Kelley73a08a22009-02-05 20:28:08 +0000628 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100629 return NULL;
630
631 p = comma;
632 *comma = ' ';
633
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100634 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100635
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100636 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100637 *p = 0;
638
639 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100640}
641
Simon Kelley73a08a22009-02-05 20:28:08 +0000642static char *split(char *s)
643{
644 return split_chr(s, ',');
645}
646
Simon Kelley1f15b812009-10-13 17:49:32 +0100647static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100648{
Simon Kelley1f15b812009-10-13 17:49:32 +0100649 char *ret;
650 int nomem;
651
Simon Kelley3d8df262005-08-29 12:19:27 +0100652 if (!s)
653 return 0;
654
655 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100656 if (!(ret = canonicalise(s, &nomem)) && nomem)
657 {
658 if (mem_recover)
659 longjmp(mem_jmp, 1);
660 else
661 die(_("could not get memory"), NULL, EC_NOMEM);
662 }
663
664 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100665}
666
667static int atoi_check(char *a, int *res)
668{
669 char *p;
670
671 if (!a)
672 return 0;
673
674 unhide_metas(a);
675
676 for (p = a; *p; p++)
677 if (*p < '0' || *p > '9')
678 return 0;
679
680 *res = atoi(a);
681 return 1;
682}
683
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100684static int atoi_check16(char *a, int *res)
685{
686 if (!(atoi_check(a, res)) ||
687 *res < 0 ||
688 *res > 0xffff)
689 return 0;
690
691 return 1;
692}
Simon Kelleyee415862014-02-11 11:07:22 +0000693
Simon Kelleyde73a492014-02-17 21:43:27 +0000694#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000695static int atoi_check8(char *a, int *res)
696{
697 if (!(atoi_check(a, res)) ||
698 *res < 0 ||
699 *res > 0xff)
700 return 0;
701
702 return 1;
703}
Simon Kelleyde73a492014-02-17 21:43:27 +0000704#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100705
706#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000707static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000708{
Simon Kelley824af852008-02-12 20:43:05 +0000709 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000710
711 if (txt)
712 {
713 size_t len = strlen(txt);
714 r->txt = opt_malloc(len+1);
715 r->len = len+1;
716 *(r->txt) = len;
717 memcpy((r->txt)+1, txt, len);
718 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100719
Simon Kelleyfec216d2014-03-27 20:54:34 +0000720 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000721 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000722 r->next = daemon->txt;
723 daemon->txt = r;
724 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000725}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100726#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000727
Simon Kelley849a8352006-06-09 21:02:31 +0100728static void do_usage(void)
729{
730 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000731 int i, j;
732
733 struct {
734 char handle;
735 int val;
736 } tab[] = {
737 { '$', CACHESIZ },
738 { '*', EDNS_PKTSZ },
739 { '&', MAXLEASES },
740 { '!', FTABSIZ },
741 { '#', TFTP_MAX_CONNECTIONS },
742 { '\0', 0 }
743 };
Simon Kelley849a8352006-06-09 21:02:31 +0100744
745 printf(_("Usage: dnsmasq [options]\n\n"));
746#ifndef HAVE_GETOPT_LONG
747 printf(_("Use short options only on the command line.\n"));
748#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100749 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100750
Simon Kelley1a6bca82008-07-11 11:11:42 +0100751 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100752 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100753 char *desc = usage[i].flagdesc;
754 char *eq = "=";
755
756 if (!desc || *desc == '[')
757 eq = "";
758
759 if (!desc)
760 desc = "";
761
762 for ( j = 0; opts[j].name; j++)
763 if (opts[j].val == usage[i].opt)
764 break;
765 if (usage[i].opt < 256)
766 sprintf(buff, "-%c, ", usage[i].opt);
767 else
768 sprintf(buff, " ");
769
770 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100771 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100772
Simon Kelley849a8352006-06-09 21:02:31 +0100773 if (usage[i].arg)
774 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000775 strcpy(buff, usage[i].arg);
776 for (j = 0; tab[j].handle; j++)
777 if (tab[j].handle == *(usage[i].arg))
778 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100779 }
Simon Kelley849a8352006-06-09 21:02:31 +0100780 printf(_(usage[i].desc), buff);
781 printf("\n");
782 }
783}
784
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100785#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000786#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
787#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100788
Ed Bardsleya7369be2015-08-05 21:17:18 +0100789static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
790{
791 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
792 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100793 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
794 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100795 else
796 return _("bad address");
797
798 return NULL;
799}
800
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100801char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
802{
803 int source_port = 0, serv_port = NAMESERVER_PORT;
804 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000805 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100806 int scope_index = 0;
807 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100808
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000809 if (!arg || strlen(arg) == 0)
810 {
811 *flags |= SERV_NO_ADDR;
812 *interface = 0;
813 return NULL;
814 }
815
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100816 if ((source = split_chr(arg, '@')) && /* is there a source. */
817 (portno = split_chr(source, '#')) &&
818 !atoi_check16(portno, &source_port))
819 return _("bad port");
820
821 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
822 !atoi_check16(portno, &serv_port))
823 return _("bad port");
824
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100825 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100826
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000827 if (source) {
828 interface_opt = split_chr(source, '@');
829
830 if (interface_opt)
831 {
832#if defined(SO_BINDTODEVICE)
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000833 safe_strncpy(interface, source, IF_NAMESIZE);
834 source = interface_opt;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000835#else
836 return _("interface binding not supported");
837#endif
838 }
839 }
840
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100841 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100842 {
843 addr->in.sin_port = htons(serv_port);
844 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
845#ifdef HAVE_SOCKADDR_SA_LEN
846 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
847#endif
848 source_addr->in.sin_addr.s_addr = INADDR_ANY;
849 source_addr->in.sin_port = htons(daemon->query_port);
850
851 if (source)
852 {
853 if (flags)
854 *flags |= SERV_HAS_SOURCE;
855 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100856 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100857 {
858#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000859 if (interface_opt)
860 return _("interface can only be specified once");
861
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100862 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200863 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100864#else
865 return _("interface binding not supported");
866#endif
867 }
868 }
869 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100870 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
871 {
872 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
873 return _("bad interface name");
874
875 addr->in6.sin6_port = htons(serv_port);
876 addr->in6.sin6_scope_id = scope_index;
877 source_addr->in6.sin6_addr = in6addr_any;
878 source_addr->in6.sin6_port = htons(daemon->query_port);
879 source_addr->in6.sin6_scope_id = 0;
880 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
881 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
882#ifdef HAVE_SOCKADDR_SA_LEN
883 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
884#endif
885 if (source)
886 {
887 if (flags)
888 *flags |= SERV_HAS_SOURCE;
889 source_addr->in6.sin6_port = htons(source_port);
890 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
891 {
892#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000893 if (interface_opt)
894 return _("interface can only be specified once");
895
896 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200897 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100898#else
899 return _("interface binding not supported");
900#endif
901 }
902 }
903 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100904 else
905 return _("bad address");
906
907 return NULL;
908}
909
Simon Kelleyde73a492014-02-17 21:43:27 +0000910static struct server *add_rev4(struct in_addr addr, int msize)
911{
912 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000913 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000914 char *p;
915
916 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000917 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
918
919 switch (msize)
920 {
921 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100922 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000923 /* fall through */
924 case 24:
925 p += sprintf(p, "%d.", (a >> 8) & 0xff);
926 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000927 case 16:
928 p += sprintf(p, "%d.", (a >> 16) & 0xff);
929 /* fall through */
930 case 8:
931 p += sprintf(p, "%d.", (a >> 24) & 0xff);
932 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000933 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000934 free(serv->domain);
935 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000936 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000937 }
938
939 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000940
941 serv->flags = SERV_HAS_DOMAIN;
942 serv->next = daemon->servers;
943 daemon->servers = serv;
944
945 return serv;
946
947}
948
949static struct server *add_rev6(struct in6_addr *addr, int msize)
950{
951 struct server *serv = opt_malloc(sizeof(struct server));
952 char *p;
953 int i;
954
955 memset(serv, 0, sizeof(struct server));
956 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
957
958 for (i = msize-1; i >= 0; i -= 4)
959 {
960 int dig = ((unsigned char *)addr)[i>>3];
961 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
962 }
963 p += sprintf(p, "ip6.arpa");
964
965 serv->flags = SERV_HAS_DOMAIN;
966 serv->next = daemon->servers;
967 daemon->servers = serv;
968
969 return serv;
970}
971
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000972#ifdef HAVE_DHCP
973
974static int is_tag_prefix(char *arg)
975{
976 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
977 return 1;
978
979 return 0;
980}
981
982static char *set_prefix(char *arg)
983{
984 if (strstr(arg, "set:") == arg)
985 return arg+4;
986
987 return arg;
988}
989
Simon Kelley52ec7832020-02-07 21:05:54 +0000990static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000991{
992 struct dhcp_netid *tt;
993 tt = opt_malloc(sizeof (struct dhcp_netid));
994 tt->net = opt_string_alloc(net);
995 tt->next = next;
996 return tt;
997}
998
999static void dhcp_netid_free(struct dhcp_netid *nid)
1000{
1001 while (nid)
1002 {
1003 struct dhcp_netid *tmp = nid;
1004 nid = nid->next;
1005 free(tmp->net);
1006 free(tmp);
1007 }
1008}
1009
1010/* Parse one or more tag:s before parameters.
1011 * Moves arg to the end of tags. */
1012static struct dhcp_netid * dhcp_tags(char **arg)
1013{
1014 struct dhcp_netid *id = NULL;
1015
1016 while (is_tag_prefix(*arg))
1017 {
1018 char *comma = split(*arg);
1019 id = dhcp_netid_create((*arg)+4, id);
1020 *arg = comma;
1021 };
1022 if (!*arg)
1023 {
1024 dhcp_netid_free(id);
1025 id = NULL;
1026 }
1027 return id;
1028}
1029
1030static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1031{
1032 while (netid)
1033 {
1034 struct dhcp_netid_list *tmplist = netid;
1035 netid = netid->next;
1036 dhcp_netid_free(tmplist->list);
1037 free(tmplist);
1038 }
1039}
1040
1041static void dhcp_config_free(struct dhcp_config *config)
1042{
1043 if (config)
1044 {
1045 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001046
Petr Menšík59e47032018-11-02 22:39:39 +00001047 while (hwaddr)
1048 {
1049 struct hwaddr_config *tmp = hwaddr;
1050 hwaddr = hwaddr->next;
1051 free(tmp);
1052 }
Simon Kelley137286e2020-02-06 22:09:30 +00001053
Petr Menšík59e47032018-11-02 22:39:39 +00001054 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001055 dhcp_netid_free(config->filter);
1056
Petr Menšík59e47032018-11-02 22:39:39 +00001057 if (config->flags & CONFIG_CLID)
1058 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001059
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001060#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001061 if (config->flags & CONFIG_ADDR6)
1062 {
1063 struct addrlist *addr, *tmp;
1064
1065 for (addr = config->addr6; addr; addr = tmp)
1066 {
1067 tmp = addr->next;
1068 free(addr);
1069 }
1070 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001071#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001072
Petr Menšík59e47032018-11-02 22:39:39 +00001073 free(config);
1074 }
1075}
1076
1077static void dhcp_context_free(struct dhcp_context *ctx)
1078{
1079 if (ctx)
1080 {
1081 dhcp_netid_free(ctx->filter);
1082 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001083#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001084 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001085#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001086 free(ctx);
1087 }
1088}
1089
1090static void dhcp_opt_free(struct dhcp_opt *opt)
1091{
1092 if (opt->flags & DHOPT_VENDOR)
1093 free(opt->u.vendor_class);
1094 dhcp_netid_free(opt->netid);
1095 free(opt->val);
1096 free(opt);
1097}
1098
1099
Simon Kelley832af0b2007-01-21 20:01:28 +00001100/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001101static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001102{
Simon Kelley824af852008-02-12 20:43:05 +00001103 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001104 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001105 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001106 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001107 u16 opt_len = 0;
1108 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001109 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001110
1111 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001112 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001113 new->netid = NULL;
1114 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001115 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001116
Simon Kelleyf2621c72007-04-29 19:47:21 +01001117 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001118 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001119 comma = split(arg);
1120
1121 for (cp = arg; *cp; cp++)
1122 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001123 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001124
1125 if (!*cp)
1126 {
1127 new->opt = atoi(arg);
1128 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001129 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001130 break;
1131 }
1132
1133 if (strstr(arg, "option:") == arg)
1134 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001135 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1136 {
1137 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1138 /* option:<optname> must follow tag and vendor string. */
1139 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1140 option_ok = 1;
1141 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001142 break;
1143 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001144#ifdef HAVE_DHCP6
1145 else if (strstr(arg, "option6:") == arg)
1146 {
1147 for (cp = arg+8; *cp; cp++)
1148 if (*cp < '0' || *cp > '9')
1149 break;
1150
1151 if (!*cp)
1152 {
1153 new->opt = atoi(arg+8);
1154 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001155 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001156 }
1157 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001158 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001159 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1160 {
1161 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1162 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1163 option_ok = 1;
1164 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001165 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001166 /* option6:<opt>|<optname> must follow tag and vendor string. */
1167 is6 = 1;
1168 break;
1169 }
1170#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001171 else if (strstr(arg, "vendor:") == arg)
1172 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001173 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1174 new->flags |= DHOPT_VENDOR;
1175 }
1176 else if (strstr(arg, "encap:") == arg)
1177 {
1178 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001179 new->flags |= DHOPT_ENCAPSULATE;
1180 }
Simon Kelley316e2732010-01-22 20:16:09 +00001181 else if (strstr(arg, "vi-encap:") == arg)
1182 {
1183 new->u.encap = atoi(arg+9);
1184 new->flags |= DHOPT_RFC3925;
1185 if (flags == DHOPT_MATCH)
1186 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001187 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001188 break;
1189 }
1190 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001191 else
1192 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001193 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001194 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1195 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001196 }
1197
1198 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001199 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001200
1201#ifdef HAVE_DHCP6
1202 if (is6)
1203 {
1204 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001205 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001206
1207 if (opt_len == 0 &&
1208 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001209 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001210 }
1211 else
1212#endif
1213 if (opt_len == 0 &&
1214 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001215 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001216
Simon Kelley316e2732010-01-22 20:16:09 +00001217 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001218 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001219 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001220
1221 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001222 {
1223 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001224 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001225 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001226 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001227 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001228 dots = 0;
1229 for (cp = comma; (c = *cp); cp++)
1230 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001231 {
1232 addrs++;
1233 is_dec = is_hex = 0;
1234 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001235 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001236 {
1237 digs++;
1238 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001239 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001240 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001241 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001242 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001243 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001244 if (cp == comma) /* leading / means a pathname */
1245 is_addr = 0;
1246 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001247 else if (c == '.')
1248 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001249 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001250 dots++;
1251 }
1252 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001253 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001254 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001255 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001256 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001257 {
1258 is_addr = 0;
1259 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001260 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001261 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001262 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001263 *cp = 0;
1264 }
1265 else
1266 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001267 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001268 (c >='a' && c <= 'f') ||
1269 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001270 {
1271 is_hex = 0;
1272 if (c != '[' && c != ']')
1273 is_addr6 = 0;
1274 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001275 }
Simon Kelley28866e92011-02-14 20:19:14 +00001276 else
1277 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001278
Simon Kelley28866e92011-02-14 20:19:14 +00001279 if (!found_dig)
1280 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001281
1282 if (!found_colon)
1283 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001284
1285#ifdef HAVE_DHCP6
1286 /* NTP server option takes hex, addresses or FQDN */
1287 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1288 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1289#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001290
Simon Kelleyf2621c72007-04-29 19:47:21 +01001291 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001292 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001293 {
1294 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001295
1296 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001297 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001298
1299 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001300 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001301 }
Simon Kelley28866e92011-02-14 20:19:14 +00001302 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001303 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1304 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001305
1306 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1307 {
1308 int val, fac = 1;
1309
1310 switch (comma[strlen(comma) - 1])
1311 {
Simon Kelley42243212012-07-20 15:19:18 +01001312 case 'w':
1313 case 'W':
1314 fac *= 7;
1315 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001316 case 'd':
1317 case 'D':
1318 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001319 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001320 case 'h':
1321 case 'H':
1322 fac *= 60;
1323 /* fall through */
1324 case 'm':
1325 case 'M':
1326 fac *= 60;
1327 /* fall through */
1328 case 's':
1329 case 'S':
1330 comma[strlen(comma) - 1] = 0;
1331 }
1332
1333 new->len = 4;
1334 new->val = opt_malloc(4);
1335 val = atoi(comma);
1336 *((int *)new->val) = htonl(val * fac);
1337 }
1338 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001339 {
1340 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001341 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001342 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1343 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001344 }
1345 else if (is_dec)
1346 {
1347 int i, val = atoi(comma);
1348 /* assume numeric arg is 1 byte except for
1349 options where it is known otherwise.
1350 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001351 if (opt_len != 0)
1352 new->len = opt_len;
1353 else if (val & 0xffff0000)
1354 new->len = 4;
1355 else if (val & 0xff00)
1356 new->len = 2;
1357 else
1358 new->len = 1;
1359
Simon Kelley832af0b2007-01-21 20:01:28 +00001360 if (lenchar == 'b')
1361 new->len = 1;
1362 else if (lenchar == 's')
1363 new->len = 2;
1364 else if (lenchar == 'i')
1365 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001366
Simon Kelley824af852008-02-12 20:43:05 +00001367 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001368 for (i=0; i<new->len; i++)
1369 new->val[i] = val>>((new->len - i - 1)*8);
1370 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001371 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001372 {
1373 struct in_addr in;
1374 unsigned char *op;
1375 char *slash;
1376 /* max length of address/subnet descriptor is five bytes,
1377 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001378 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001379 new->flags |= DHOPT_ADDR;
1380
Simon Kelley572b41e2011-02-18 18:11:18 +00001381 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1382 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001383 {
Simon Kelley6b010842007-02-12 20:32:07 +00001384 *(op++) = 1; /* RFC 3361 "enc byte" */
1385 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001386 }
1387 while (addrs--)
1388 {
1389 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001390 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001391 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001392 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001393 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001394 if (!slash)
1395 {
1396 memcpy(op, &in, INADDRSZ);
1397 op += INADDRSZ;
1398 }
1399 else
1400 {
1401 unsigned char *p = (unsigned char *)&in;
1402 int netsize = atoi(slash);
1403 *op++ = netsize;
1404 if (netsize > 0)
1405 *op++ = *p++;
1406 if (netsize > 8)
1407 *op++ = *p++;
1408 if (netsize > 16)
1409 *op++ = *p++;
1410 if (netsize > 24)
1411 *op++ = *p++;
1412 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1413 }
1414 }
1415 new->len = op - new->val;
1416 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001417 else if (is_addr6 && is6)
1418 {
1419 unsigned char *op;
1420 new->val = op = opt_malloc(16 * addrs);
1421 new->flags |= DHOPT_ADDR6;
1422 while (addrs--)
1423 {
1424 cp = comma;
1425 comma = split(cp);
1426
1427 /* check for [1234::7] */
1428 if (*cp == '[')
1429 cp++;
1430 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1431 cp[strlen(cp)-1] = 0;
1432
1433 if (inet_pton(AF_INET6, cp, op))
1434 {
1435 op += IN6ADDRSZ;
1436 continue;
1437 }
Petr Menšík59e47032018-11-02 22:39:39 +00001438
1439 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001440 }
1441 new->len = op - new->val;
1442 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001443 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001444 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001445 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001446 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001447 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001448 {
1449 /* dns search, RFC 3397, or SIP, RFC 3361 */
1450 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001451 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001452 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001453 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001454
1455 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001456 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001457
1458 while (arg && *arg)
1459 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001460 char *in, *dom = NULL;
1461 size_t domlen = 1;
1462 /* Allow "." as an empty domain */
1463 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001464 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001465 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001466 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001467
Simon Kelleyc52e1892010-06-07 22:01:39 +01001468 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001469 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001470
1471 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001472 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001473 {
1474 memcpy(newp, m, header_size + len);
1475 free(m);
1476 }
Simon Kelley824af852008-02-12 20:43:05 +00001477 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001478 p = m + header_size;
1479 q = p + len;
1480
1481 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001482 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001483 {
1484 unsigned char *cp = q++;
1485 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001486 for (j = 0; *in && (*in != '.'); in++, j++)
1487 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001488 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001489 if (*in)
1490 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001491 }
1492 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001493 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001494
Simon Kelley832af0b2007-01-21 20:01:28 +00001495 /* Now tail-compress using earlier names. */
1496 newlen = q - p;
1497 for (tail = p + len; *tail; tail += (*tail) + 1)
1498 for (r = p; r - p < (int)len; r += (*r) + 1)
1499 if (strcmp((char *)r, (char *)tail) == 0)
1500 {
1501 PUTSHORT((r - p) | 0xc000, tail);
1502 newlen = tail - p;
1503 goto end;
1504 }
1505 end:
1506 len = newlen;
1507
1508 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001509 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001510 }
1511
1512 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001513 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001514 m[0] = 0;
1515 new->len = (int) len + header_size;
1516 new->val = m;
1517 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001518#ifdef HAVE_DHCP6
1519 else if (comma && (opt_len & OT_CSTRING))
1520 {
1521 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001522 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001523 unsigned char *p, *newp;
1524
Simon Kelley40ef23b2012-03-13 21:59:28 +00001525 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001526 if (comma[i] == ',')
1527 commas++;
1528
1529 newp = opt_malloc(strlen(comma)+(2*commas));
1530 p = newp;
1531 arg = comma;
1532 comma = split(arg);
1533
1534 while (arg && *arg)
1535 {
1536 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001537 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001538 PUTSHORT(len, p);
1539 memcpy(p, arg, len);
1540 p += len;
1541
1542 arg = comma;
1543 comma = split(arg);
1544 }
1545
1546 new->val = newp;
1547 new->len = p - newp;
1548 }
1549 else if (comma && (opt_len & OT_RFC1035_NAME))
1550 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001551 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001552 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001553 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001554 arg = comma;
1555 comma = split(arg);
1556
1557 while (arg && *arg)
1558 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001559 char *dom = canonicalise_opt(arg);
1560 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001561 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001562
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001563 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001564
1565 if (p)
1566 {
1567 memcpy(newp, p, len);
1568 free(p);
1569 }
1570
1571 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001572 q = p + len;
1573 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001574 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001575 if (is6 && new->opt == OPTION6_NTP_SERVER)
1576 {
1577 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1578 PUTSHORT(end - q - 2, q);
1579 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001580 len = end - p;
1581 free(dom);
1582
Simon Kelley4cb1b322012-02-06 14:30:41 +00001583 arg = comma;
1584 comma = split(arg);
1585 }
1586
Simon Kelley18f0fb02012-03-31 21:18:55 +01001587 new->val = p;
1588 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001589 }
1590#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001591 else
1592 {
1593 new->len = strlen(comma);
1594 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001595 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001596 new->flags |= DHOPT_STRING;
1597 }
1598 }
1599 }
1600
Simon Kelley4cb1b322012-02-06 14:30:41 +00001601 if (!is6 &&
1602 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001603 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001604 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001605 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001606
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001607 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001608 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001609 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1610 !new->netid ||
1611 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001612 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001613
1614 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001615 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001616 new->next = daemon->dhcp_match6;
1617 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001618 }
1619 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001620 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001621 new->next = daemon->dhcp_match;
1622 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001623 }
Simon Kelley824af852008-02-12 20:43:05 +00001624 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001625 else if (is6)
1626 {
1627 new->next = daemon->dhcp_opts6;
1628 daemon->dhcp_opts6 = new;
1629 }
1630 else
1631 {
1632 new->next = daemon->dhcp_opts;
1633 daemon->dhcp_opts = new;
1634 }
1635
1636 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001637on_error:
1638 dhcp_opt_free(new);
1639 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001640}
1641
Simon Kelley7622fc02009-06-04 20:32:05 +01001642#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001643
Simon Kelley28866e92011-02-14 20:19:14 +00001644void set_option_bool(unsigned int opt)
1645{
Petr Menšík24b87602018-10-24 22:30:18 +01001646 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001647}
1648
Simon Kelley2b5bae92012-06-26 16:55:23 +01001649void reset_option_bool(unsigned int opt)
1650{
Petr Menšík24b87602018-10-24 22:30:18 +01001651 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001652}
1653
Petr Menšík59e47032018-11-02 22:39:39 +00001654static void server_list_free(struct server *list)
1655{
1656 while (list)
1657 {
1658 struct server *tmp = list;
1659 list = list->next;
1660 free(tmp);
1661 }
1662}
1663
Simon Kelley7b1eae42014-02-20 13:43:28 +00001664static 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 +01001665{
1666 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001667 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001668
Simon Kelley832af0b2007-01-21 20:01:28 +00001669 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001670 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001671
Simon Kelley1a6bca82008-07-11 11:11:42 +01001672 for (i=0; usage[i].opt != 0; i++)
1673 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001674 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001675 int rept = usage[i].rept;
1676
Simon Kelley28866e92011-02-14 20:19:14 +00001677 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001678 {
1679 /* command line */
1680 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001681 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001682 if (rept == ARG_ONE)
1683 usage[i].rept = ARG_USED_CL;
1684 }
1685 else
1686 {
1687 /* allow file to override command line */
1688 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001689 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001690 if (rept == ARG_USED_CL || rept == ARG_ONE)
1691 usage[i].rept = ARG_USED_FILE;
1692 }
1693
1694 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1695 {
Simon Kelley28866e92011-02-14 20:19:14 +00001696 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001697 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001698 }
1699
1700 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001701 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001702
Simon Kelley849a8352006-06-09 21:02:31 +01001703 switch (option)
1704 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001705 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001706 {
Simon Kelley824af852008-02-12 20:43:05 +00001707 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001708 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001709 {
Simon Kelley28866e92011-02-14 20:19:14 +00001710 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001711 free(file);
1712 }
Simon Kelley849a8352006-06-09 21:02:31 +01001713 break;
1714 }
1715
Simon Kelleyf2621c72007-04-29 19:47:21 +01001716 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001717 {
1718 DIR *dir_stream;
1719 struct dirent *ent;
1720 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001721 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001722 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001723 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001724 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001725
Simon Kelley1f15b812009-10-13 17:49:32 +01001726 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001727 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001728 break;
1729
Simon Kelley1f15b812009-10-13 17:49:32 +01001730 for (arg = comma; arg; arg = comma)
1731 {
1732 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001733 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001734 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001735 li = opt_malloc(sizeof(struct list));
1736 if (*arg == '*')
1737 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001738 /* "*" with no suffix is a no-op */
1739 if (arg[1] == 0)
1740 free(li);
1741 else
1742 {
1743 li->next = match_suffix;
1744 match_suffix = li;
1745 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001746 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001747 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001748 }
1749 else
1750 {
1751 li->next = ignore_suffix;
1752 ignore_suffix = li;
1753 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001754 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001755 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001756 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001757 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001758
Simon Kelley849a8352006-06-09 21:02:31 +01001759 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001760 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001761
Simon Kelley849a8352006-06-09 21:02:31 +01001762 while ((ent = readdir(dir_stream)))
1763 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001764 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001765 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001766
1767 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001768 if (len == 0 ||
1769 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001770 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1771 ent->d_name[0] == '.')
1772 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001773
Simon Kelley3e1551a2014-09-09 21:46:07 +01001774 if (match_suffix)
1775 {
1776 for (li = match_suffix; li; li = li->next)
1777 {
1778 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001779 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001780 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001781 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001782 break;
1783 }
1784 if (!li)
1785 continue;
1786 }
1787
Simon Kelley1f15b812009-10-13 17:49:32 +01001788 for (li = ignore_suffix; li; li = li->next)
1789 {
1790 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001791 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001792 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001793 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001794 break;
1795 }
1796 if (li)
1797 continue;
1798
Simon Kelley824af852008-02-12 20:43:05 +00001799 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001800 strcpy(path, directory);
1801 strcat(path, "/");
1802 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001803
Simon Kelley39595cf2013-02-04 21:40:07 +00001804 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001805 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001806 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001807
Simon Kelley39595cf2013-02-04 21:40:07 +00001808 /* only reg files allowed. */
1809 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001810 {
1811 /* sort files into order. */
1812 struct list **up, *new = opt_malloc(sizeof(struct list));
1813 new->name = path;
1814
1815 for (up = &files, li = files; li; up = &li->next, li = li->next)
1816 if (strcmp(li->name, path) >=0)
1817 break;
1818
1819 new->next = li;
1820 *up = new;
1821 }
1822
Simon Kelley849a8352006-06-09 21:02:31 +01001823 }
Simon Kelleyab538832020-01-10 20:44:48 +00001824
1825 for (li = files; li; li = li->next)
1826 one_file(li->name, 0);
1827
Simon Kelley849a8352006-06-09 21:02:31 +01001828 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001829 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001830 for(; ignore_suffix; ignore_suffix = li)
1831 {
1832 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001833 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001834 free(ignore_suffix);
1835 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001836 for(; match_suffix; match_suffix = li)
1837 {
1838 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001839 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001840 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001841 }
Simon Kelleyab538832020-01-10 20:44:48 +00001842 for(; files; files = li)
1843 {
1844 li = files->next;
1845 free(files->name);
1846 free(files);
1847 }
Simon Kelley849a8352006-06-09 21:02:31 +01001848 break;
1849 }
1850
Simon Kelleyed4c0762013-10-08 20:46:34 +01001851 case LOPT_ADD_SBNET: /* --add-subnet */
1852 set_option_bool(OPT_CLIENT_SUBNET);
1853 if (arg)
1854 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001855 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001856 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001857
1858 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1859 if ((end = split_chr(arg, '/')))
1860 {
1861 /* has subnet+len */
1862 err = parse_mysockaddr(arg, &new->addr);
1863 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001864 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001865 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001866 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001867 new->addr_used = 1;
1868 }
1869 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001870 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001871
1872 daemon->add_subnet4 = new;
1873
Ed Bardsleya7369be2015-08-05 21:17:18 +01001874 if (comma)
1875 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001876 new = opt_malloc(sizeof(struct mysubnet));
1877 if ((end = split_chr(comma, '/')))
1878 {
1879 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001880 err = parse_mysockaddr(comma, &new->addr);
1881 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001882 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001883 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001884 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001885 new->addr_used = 1;
1886 }
1887 else
1888 {
1889 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001890 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001891 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001892
1893 daemon->add_subnet6 = new;
1894 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001895 }
1896 break;
1897
Simon Kelleyad094272012-08-10 17:10:54 +01001898 case '1': /* --enable-dbus */
1899 set_option_bool(OPT_DBUS);
1900 if (arg)
1901 daemon->dbus_name = opt_string_alloc(arg);
1902 else
1903 daemon->dbus_name = DNSMASQ_SERVICE;
1904 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001905
1906 case LOPT_UBUS: /* --enable-ubus */
1907 set_option_bool(OPT_UBUS);
1908 if (arg)
1909 daemon->ubus_name = opt_string_alloc(arg);
1910 else
1911 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1912 break;
1913
Simon Kelleyf2621c72007-04-29 19:47:21 +01001914 case '8': /* --log-facility */
1915 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001916 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001917 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001918 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001919 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001920#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001921 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001922#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001923 for (i = 0; facilitynames[i].c_name; i++)
1924 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1925 break;
1926
1927 if (facilitynames[i].c_name)
1928 daemon->log_fac = facilitynames[i].c_val;
1929 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001930 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001931#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001932 }
1933 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001934
Simon Kelleyf2621c72007-04-29 19:47:21 +01001935 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001936 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001937 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001938
Simon Kelleyf2621c72007-04-29 19:47:21 +01001939 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001940 {
Simon Kelley824af852008-02-12 20:43:05 +00001941 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001942 struct resolvc *new, *list = daemon->resolv_files;
1943
1944 if (list && list->is_default)
1945 {
1946 /* replace default resolv file - possibly with nothing */
1947 if (name)
1948 {
1949 list->is_default = 0;
1950 list->name = name;
1951 }
1952 else
1953 list = NULL;
1954 }
1955 else if (name)
1956 {
Simon Kelley824af852008-02-12 20:43:05 +00001957 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001958 new->next = list;
1959 new->name = name;
1960 new->is_default = 0;
1961 new->mtime = 0;
1962 new->logged = 0;
1963 list = new;
1964 }
1965 daemon->resolv_files = list;
1966 break;
1967 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001968
1969 case LOPT_SERVERS_FILE:
1970 daemon->servers_file = opt_string_alloc(arg);
1971 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001972
Simon Kelleyf2621c72007-04-29 19:47:21 +01001973 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001974 {
1975 int pref = 1;
1976 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001977 char *name, *target = NULL;
1978
Simon Kelleyf2621c72007-04-29 19:47:21 +01001979 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001980 {
1981 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001982 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001983 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001984 }
1985
Simon Kelley1f15b812009-10-13 17:49:32 +01001986 if (!(name = canonicalise_opt(arg)) ||
1987 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001988 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001989
Simon Kelley824af852008-02-12 20:43:05 +00001990 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001991 new->next = daemon->mxnames;
1992 daemon->mxnames = new;
1993 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001994 new->name = name;
1995 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001996 new->weight = pref;
1997 break;
1998 }
1999
Simon Kelleyf2621c72007-04-29 19:47:21 +01002000 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01002001 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002002 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01002003 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002004
Simon Kelley6b173352018-05-08 18:32:14 +01002005 case LOPT_DUMPFILE: /* --dumpfile */
2006 daemon->dump_file = opt_string_alloc(arg);
2007 break;
2008
2009 case LOPT_DUMPMASK: /* --dumpmask */
2010 daemon->dump_mask = strtol(arg, NULL, 0);
2011 break;
2012
Simon Kelley7622fc02009-06-04 20:32:05 +01002013#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002014 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002015 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002016 break;
2017
Simon Kelleyc72daea2012-01-05 21:33:27 +00002018 /* Sorry about the gross pre-processor abuse */
2019 case '6': /* --dhcp-script */
2020 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002021# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002022 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002023# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002024 if (option == LOPT_LUASCRIPT)
2025# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002026 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002027# else
2028 daemon->luascript = opt_string_alloc(arg);
2029# endif
2030 else
2031 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002032# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002033 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002034#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002035
Simon Kelley70d18732015-01-31 19:59:29 +00002036 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2037 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2038 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2039 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2040 case LOPT_HOST_INOTIFY: /* --hostsdir */
2041 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002042 {
Simon Kelley824af852008-02-12 20:43:05 +00002043 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002044 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002045 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002046 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002047 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002048 if (option == 'H')
2049 {
2050 new->next = daemon->addn_hosts;
2051 daemon->addn_hosts = new;
2052 }
2053 else if (option == LOPT_DHCP_HOST)
2054 {
2055 new->next = daemon->dhcp_hosts_file;
2056 daemon->dhcp_hosts_file = new;
2057 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002058 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002059 {
2060 new->next = daemon->dhcp_opts_file;
2061 daemon->dhcp_opts_file = new;
2062 }
Simon Kelley70d18732015-01-31 19:59:29 +00002063 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002064 {
Simon Kelley70d18732015-01-31 19:59:29 +00002065 new->next = daemon->dynamic_dirs;
2066 daemon->dynamic_dirs = new;
2067 if (option == LOPT_DHCP_INOTIFY)
2068 new->flags |= AH_DHCP_HST;
2069 else if (option == LOPT_DHOPT_INOTIFY)
2070 new->flags |= AH_DHCP_OPT;
2071 else if (option == LOPT_HOST_INOTIFY)
2072 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002073 }
2074
Simon Kelley849a8352006-06-09 21:02:31 +01002075 break;
2076 }
2077
Simon Kelley4f7b3042012-11-28 21:27:02 +00002078 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002079 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002080
Simon Kelley4f7b3042012-11-28 21:27:02 +00002081 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002082
2083 while ((arg = comma))
2084 {
2085 struct iname *new = opt_malloc(sizeof(struct iname));
2086 comma = split(arg);
2087 new->name = NULL;
2088 unhide_metas(arg);
2089 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2090 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002091 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2092 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002093 else
2094 {
2095 char *fam = split_chr(arg, '/');
2096 new->name = opt_string_alloc(arg);
2097 new->addr.sa.sa_family = 0;
2098 if (fam)
2099 {
2100 if (strcmp(fam, "4") == 0)
2101 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002102 else if (strcmp(fam, "6") == 0)
2103 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002104 else
Petr Menšík59e47032018-11-02 22:39:39 +00002105 {
2106 free(new->name);
2107 ret_err_free(gen_err, new);
2108 }
Simon Kelley08933472018-10-05 16:34:35 +01002109 }
2110 }
2111 new->next = daemon->authinterface;
2112 daemon->authinterface = new;
2113 };
Simon Kelley429798f2012-12-10 20:45:53 +00002114
Simon Kelley4f7b3042012-11-28 21:27:02 +00002115 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002116
2117 case LOPT_AUTHSFS: /* --auth-sec-servers */
2118 {
2119 struct name_list *new;
2120
2121 do {
2122 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002123 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002124 new->name = opt_string_alloc(arg);
2125 new->next = daemon->secondary_forward_server;
2126 daemon->secondary_forward_server = new;
2127 arg = comma;
2128 } while (arg);
2129 break;
2130 }
2131
Simon Kelley4f7b3042012-11-28 21:27:02 +00002132 case LOPT_AUTHZONE: /* --auth-zone */
2133 {
2134 struct auth_zone *new;
2135
2136 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002137
Simon Kelley429798f2012-12-10 20:45:53 +00002138 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002139 new->domain = opt_string_alloc(arg);
2140 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002141 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002142 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002143 new->next = daemon->auth_zones;
2144 daemon->auth_zones = new;
2145
2146 while ((arg = comma))
2147 {
2148 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002149 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002150 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002151 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002152 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002153
2154 comma = split(arg);
2155 prefix = split_chr(arg, '/');
2156
2157 if (prefix && !atoi_check(prefix, &prefixlen))
2158 ret_err(gen_err);
2159
Mathias Kresin094bfae2016-07-24 14:15:22 +01002160 if (strstr(arg, "exclude:") == arg)
2161 {
2162 is_exclude = 1;
2163 arg = arg+8;
2164 }
2165
Simon Kelleycc921df2019-01-02 22:48:59 +00002166 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002167 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002168 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002169 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002170 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002171 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002172 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002173 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002174 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002175 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002176 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002177 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002178 else
2179 {
2180 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2181 name->name = opt_string_alloc(arg);
2182 name->flags = AUTH4 | AUTH6;
2183 name->next = new->interface_names;
2184 new->interface_names = name;
2185 if (prefix)
2186 {
2187 if (prefixlen == 4)
2188 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002189 else if (prefixlen == 6)
2190 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002191 else
2192 ret_err(gen_err);
2193 }
2194 }
2195
2196 if (subnet)
2197 {
2198 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002199
2200 if (is_exclude)
2201 {
2202 subnet->next = new->exclude;
2203 new->exclude = subnet;
2204 }
2205 else
2206 {
2207 subnet->next = new->subnet;
2208 new->subnet = subnet;
2209 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002210 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002211 }
2212 break;
2213 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002214
Simon Kelley4f7b3042012-11-28 21:27:02 +00002215 case LOPT_AUTHSOA: /* --auth-soa */
2216 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002217 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002218 if (comma)
2219 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002220 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002221 arg = comma;
2222 comma = split(arg);
2223 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002224 for (cp = daemon->hostmaster; *cp; cp++)
2225 if (*cp == '@')
2226 *cp = '.';
2227
Simon Kelley4f7b3042012-11-28 21:27:02 +00002228 if (comma)
2229 {
2230 arg = comma;
2231 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002232 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002233 if (comma)
2234 {
2235 arg = comma;
2236 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002237 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002238 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002239 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002240 }
2241 }
2242 }
2243
2244 break;
2245
Simon Kelley2bb73af2013-04-24 17:38:19 +01002246 case 's': /* --domain */
2247 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002248 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002249 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002250 else
Simon Kelley9009d742008-11-14 20:04:27 +00002251 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002252 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002253 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002254 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002255 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002256 else
2257 {
Simon Kelley9009d742008-11-14 20:04:27 +00002258 if (comma)
2259 {
Simon Kelley429798f2012-12-10 20:45:53 +00002260 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002261 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002262
Simon Kelley48fd1c42013-04-25 09:49:38 +01002263 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002264 new->indexed = 0;
2265
Simon Kelley9009d742008-11-14 20:04:27 +00002266 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002267 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002268 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002269 int msize;
2270
Simon Kelley28866e92011-02-14 20:19:14 +00002271 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002272 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002273 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002274 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002275 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002276 int mask = (1 << (32 - msize)) - 1;
2277 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002278 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2279 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002280 if (arg)
2281 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002282 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002283 {
2284 if (!(new->prefix = canonicalise_opt(arg)) ||
2285 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002286 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002287 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002288 else if (strcmp(arg, "local") != 0 ||
2289 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002290 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002291 else
2292 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002293 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002294 local=/xxx.yyy.zzz.in-addr.arpa/ */
2295 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002296 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002297 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002298
Simon Kelleyde73a492014-02-17 21:43:27 +00002299 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002300
2301 /* local=/<domain>/ */
2302 serv = opt_malloc(sizeof(struct server));
2303 memset(serv, 0, sizeof(struct server));
2304 serv->domain = d;
2305 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2306 serv->next = daemon->servers;
2307 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002308 }
2309 }
Simon Kelley9009d742008-11-14 20:04:27 +00002310 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002311 else if (inet_pton(AF_INET6, comma, &new->start6))
2312 {
2313 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2314 u64 addrpart = addr6part(&new->start6);
2315 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002316
Simon Kelleyd74942a2012-02-07 20:51:56 +00002317 /* prefix==64 overflows the mask calculation above */
2318 if (msize == 64)
2319 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002320
Simon Kelleyd74942a2012-02-07 20:51:56 +00002321 new->end6 = new->start6;
2322 setaddr6part(&new->start6, addrpart & ~mask);
2323 setaddr6part(&new->end6, addrpart | mask);
2324
2325 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002326 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002327 else if (arg)
2328 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002329 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002330 {
2331 if (!(new->prefix = canonicalise_opt(arg)) ||
2332 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002333 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002334 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002335 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002336 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002337 else
2338 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002339 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002340 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002341 struct server *serv = add_rev6(&new->start6, msize);
2342 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002343
2344 /* local=/<domain>/ */
2345 serv = opt_malloc(sizeof(struct server));
2346 memset(serv, 0, sizeof(struct server));
2347 serv->domain = d;
2348 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2349 serv->next = daemon->servers;
2350 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002351 }
2352 }
2353 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002354 else
Petr Menšík59e47032018-11-02 22:39:39 +00002355 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002356 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002357 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002358 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002359 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002360 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002361 prefstr = split(arg);
2362
Simon Kelleyd74942a2012-02-07 20:51:56 +00002363 if (inet_pton(AF_INET, comma, &new->start))
2364 {
2365 new->is6 = 0;
2366 if (!arg)
2367 new->end.s_addr = new->start.s_addr;
2368 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002369 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002370 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002371 else if (inet_pton(AF_INET6, comma, &new->start6))
2372 {
2373 new->is6 = 1;
2374 if (!arg)
2375 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2376 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002377 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002378 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002379 else
Petr Menšík59e47032018-11-02 22:39:39 +00002380 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002381
2382 if (option != 's' && prefstr)
2383 {
2384 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2385 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002386 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002387 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002388 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002389
2390 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002391 if (option == 's')
2392 {
2393 new->next = daemon->cond_domain;
2394 daemon->cond_domain = new;
2395 }
2396 else
2397 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002398 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002399 new->next = daemon->synth_domains;
2400 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002401 if (new->prefix &&
2402 (star = strrchr(new->prefix, '*'))
2403 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002404 {
2405 *star = 0;
2406 new->indexed = 1;
2407 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002408 }
Simon Kelley9009d742008-11-14 20:04:27 +00002409 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002410 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002411 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002412 else
2413 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002414 }
2415 }
Simon Kelley849a8352006-06-09 21:02:31 +01002416 break;
2417
Simon Kelley1e505122016-01-25 21:29:23 +00002418 case LOPT_CPE_ID: /* --add-dns-client */
2419 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002420 daemon->dns_client_id = opt_string_alloc(arg);
2421 break;
2422
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002423 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002424 if (!arg)
2425 set_option_bool(OPT_ADD_MAC);
2426 else
2427 {
2428 unhide_metas(arg);
2429 if (strcmp(arg, "base64") == 0)
2430 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002431 else if (strcmp(arg, "text") == 0)
2432 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002433 else
2434 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002435 }
2436 break;
2437
Simon Kelleyf2621c72007-04-29 19:47:21 +01002438 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002439 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002440 break;
2441
Simon Kelleyf2621c72007-04-29 19:47:21 +01002442 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002443 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002444 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002445 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002446
Simon Kelley7622fc02009-06-04 20:32:05 +01002447#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002448 case LOPT_SCRIPTUSR: /* --scriptuser */
2449 daemon->scriptuser = opt_string_alloc(arg);
2450 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002451#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002452
Simon Kelleyf2621c72007-04-29 19:47:21 +01002453 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002454 do {
Simon Kelley824af852008-02-12 20:43:05 +00002455 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002456 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002457 new->next = daemon->if_names;
2458 daemon->if_names = new;
2459 /* new->name may be NULL if someone does
2460 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002461 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002462 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002463 arg = comma;
2464 } while (arg);
2465 break;
2466
Simon Kelley2937f8a2013-07-29 19:49:07 +01002467 case LOPT_TFTP: /* --enable-tftp */
2468 set_option_bool(OPT_TFTP);
2469 if (!arg)
2470 break;
2471 /* fall through */
2472
Simon Kelleyf2621c72007-04-29 19:47:21 +01002473 case 'I': /* --except-interface */
2474 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002475 do {
Simon Kelley824af852008-02-12 20:43:05 +00002476 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002477 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002478 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002479 if (option == 'I')
2480 {
2481 new->next = daemon->if_except;
2482 daemon->if_except = new;
2483 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002484 else if (option == LOPT_TFTP)
2485 {
2486 new->next = daemon->tftp_interfaces;
2487 daemon->tftp_interfaces = new;
2488 }
Simon Kelley849a8352006-06-09 21:02:31 +01002489 else
2490 {
2491 new->next = daemon->dhcp_except;
2492 daemon->dhcp_except = new;
2493 }
2494 arg = comma;
2495 } while (arg);
2496 break;
2497
Simon Kelleyf2621c72007-04-29 19:47:21 +01002498 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002499 case LOPT_IGNORE_ADDR: /* --ignore-address */
2500 {
Simon Kelley849a8352006-06-09 21:02:31 +01002501 struct in_addr addr;
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002502 int prefix = 32;
Simon Kelley849a8352006-06-09 21:02:31 +01002503 unhide_metas(arg);
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002504
2505 if (!arg ||
2506 ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
2507 (inet_pton(AF_INET, arg, &addr) != 1))
2508 ret_err(gen_err); /* error */
2509 else
Simon Kelley849a8352006-06-09 21:02:31 +01002510 {
Simon Kelley824af852008-02-12 20:43:05 +00002511 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002512 if (option == 'B')
2513 {
2514 baddr->next = daemon->bogus_addr;
2515 daemon->bogus_addr = baddr;
2516 }
2517 else
2518 {
2519 baddr->next = daemon->ignore_addr;
2520 daemon->ignore_addr = baddr;
2521 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002522 baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
2523 baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
Simon Kelley849a8352006-06-09 21:02:31 +01002524 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002525 break;
2526 }
Simon Kelley849a8352006-06-09 21:02:31 +01002527
Simon Kelleyf2621c72007-04-29 19:47:21 +01002528 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002529 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002530 do {
Simon Kelley824af852008-02-12 20:43:05 +00002531 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002532 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002533 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002534 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002535 {
2536 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002537 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002538#ifdef HAVE_SOCKADDR_SA_LEN
2539 new->addr.in.sin_len = sizeof(new->addr.in);
2540#endif
2541 }
Simon Kelley849a8352006-06-09 21:02:31 +01002542 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2543 {
2544 new->addr.sa.sa_family = AF_INET6;
2545 new->addr.in6.sin6_flowinfo = 0;
2546 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002547 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002548#ifdef HAVE_SOCKADDR_SA_LEN
2549 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2550#endif
2551 }
Simon Kelley849a8352006-06-09 21:02:31 +01002552 else
Petr Menšík59e47032018-11-02 22:39:39 +00002553 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002554
2555 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002556 if (option == 'a')
2557 {
2558 new->next = daemon->if_addrs;
2559 daemon->if_addrs = new;
2560 }
2561 else
2562 {
2563 new->next = daemon->auth_peers;
2564 daemon->auth_peers = new;
2565 }
Simon Kelley849a8352006-06-09 21:02:31 +01002566 arg = comma;
2567 } while (arg);
2568 break;
2569
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002570 case 'S': /* --server */
2571 case LOPT_LOCAL: /* --local */
2572 case 'A': /* --address */
2573 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002574 {
2575 struct server *serv, *newlist = NULL;
2576
2577 unhide_metas(arg);
2578
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002579 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002580 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002581 int rebind = !(*arg == '/');
2582 char *end = NULL;
2583 if (!rebind)
2584 arg++;
2585 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002586 {
2587 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002588 /* elide leading dots - they are implied in the search algorithm */
2589 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002590 /* # matches everything and becomes a zero length domain string */
2591 if (strcmp(arg, "#") == 0)
2592 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002593 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002594 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002595 serv = opt_malloc(sizeof(struct server));
2596 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002597 serv->next = newlist;
2598 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002599 serv->domain = domain;
2600 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002601 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002602 if (rebind)
2603 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002604 }
2605 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002606 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002607 }
2608 else
2609 {
Simon Kelley824af852008-02-12 20:43:05 +00002610 newlist = opt_malloc(sizeof(struct server));
2611 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002612#ifdef HAVE_LOOP
2613 newlist->uid = rand32();
2614#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002615 }
2616
Simon Kelley7b1eae42014-02-20 13:43:28 +00002617 if (servers_only && option == 'S')
2618 newlist->flags |= SERV_FROM_FILE;
2619
Simon Kelley849a8352006-06-09 21:02:31 +01002620 if (option == 'A')
2621 {
2622 newlist->flags |= SERV_LITERAL_ADDRESS;
2623 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002624 {
2625 server_list_free(newlist);
2626 ret_err(gen_err);
2627 }
Simon Kelley849a8352006-06-09 21:02:31 +01002628 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002629 else if (option == LOPT_NO_REBIND)
2630 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002631
2632 if (!arg || !*arg)
2633 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002634 if (!(newlist->flags & SERV_NO_REBIND))
2635 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002636 }
2637
2638 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002639 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002640 else
2641 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002642 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2643 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002644 {
2645 server_list_free(newlist);
2646 ret_err(err);
2647 }
Simon Kelley849a8352006-06-09 21:02:31 +01002648 }
2649
Simon Kelleyf2621c72007-04-29 19:47:21 +01002650 serv = newlist;
2651 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002652 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002653 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002654 serv->next->addr = serv->addr;
2655 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002656 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002657 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002658 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002659 serv->next = daemon->servers;
2660 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002661 break;
2662 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002663
Simon Kelleyde73a492014-02-17 21:43:27 +00002664 case LOPT_REV_SERV: /* --rev-server */
2665 {
2666 char *string;
2667 int size;
2668 struct server *serv;
2669 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002670 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002671
2672 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002673 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002674 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002675
2676 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002677
Simon Kelleya9b022a2020-02-11 21:58:59 +00002678 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2679 ret_err(gen_err);
2680
Simon Kelleyde73a492014-02-17 21:43:27 +00002681 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002682 {
2683 serv = add_rev4(addr4, size);
2684 if (!serv)
2685 ret_err(_("bad prefix"));
2686 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002687 else if (inet_pton(AF_INET6, arg, &addr6))
2688 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002689 else
2690 ret_err(gen_err);
2691
2692 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2693
2694 if (string)
2695 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002696
2697 if (servers_only)
2698 serv->flags |= SERV_FROM_FILE;
2699
Simon Kelleyde73a492014-02-17 21:43:27 +00002700 break;
2701 }
2702
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002703 case LOPT_IPSET: /* --ipset */
2704#ifndef HAVE_IPSET
2705 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2706 break;
2707#else
2708 {
2709 struct ipsets ipsets_head;
2710 struct ipsets *ipsets = &ipsets_head;
2711 int size;
2712 char *end;
2713 char **sets, **sets_pos;
2714 memset(ipsets, 0, sizeof(struct ipsets));
2715 unhide_metas(arg);
2716 if (arg && *arg == '/')
2717 {
2718 arg++;
2719 while ((end = split_chr(arg, '/')))
2720 {
2721 char *domain = NULL;
2722 /* elide leading dots - they are implied in the search algorithm */
2723 while (*arg == '.')
2724 arg++;
2725 /* # matches everything and becomes a zero length domain string */
2726 if (strcmp(arg, "#") == 0 || !*arg)
2727 domain = "";
2728 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002729 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002730 ipsets->next = opt_malloc(sizeof(struct ipsets));
2731 ipsets = ipsets->next;
2732 memset(ipsets, 0, sizeof(struct ipsets));
2733 ipsets->domain = domain;
2734 arg = end;
2735 }
2736 }
2737 else
2738 {
2739 ipsets->next = opt_malloc(sizeof(struct ipsets));
2740 ipsets = ipsets->next;
2741 memset(ipsets, 0, sizeof(struct ipsets));
2742 ipsets->domain = "";
2743 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002744
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002745 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002746 ret_err(gen_err);
2747
2748 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002749 if (*end == ',')
2750 ++size;
2751
2752 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2753
2754 do {
2755 end = split(arg);
2756 *sets_pos++ = opt_string_alloc(arg);
2757 arg = end;
2758 } while (end);
2759 *sets_pos = 0;
2760 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2761 ipsets->next->sets = sets;
2762 ipsets->next = daemon->ipsets;
2763 daemon->ipsets = ipsets_head.next;
2764
2765 break;
2766 }
2767#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002768
Simon Kelleyf2621c72007-04-29 19:47:21 +01002769 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002770 {
2771 int size;
2772
2773 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002774 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002775 else
2776 {
2777 /* zero is OK, and means no caching. */
2778
2779 if (size < 0)
2780 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002781
2782 /* Note that for very large cache sizes, the malloc()
2783 will overflow. For the size of the cache record
2784 at the time this was noted, the value of "very large"
2785 was 46684428. Limit to an order of magnitude less than
2786 that to be safe from changes to the cache record. */
2787 if (size > 5000000)
2788 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002789
2790 daemon->cachesize = size;
2791 }
2792 break;
2793 }
2794
Simon Kelleyf2621c72007-04-29 19:47:21 +01002795 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002796 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002797 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002798 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002799
Simon Kelley1a6bca82008-07-11 11:11:42 +01002800 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002801 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002802 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002803 break;
2804
Hans Dedecker926332a2016-01-23 10:48:12 +00002805 case LOPT_MAXPORT: /* --max-port */
2806 if (!atoi_check16(arg, &daemon->max_port))
2807 ret_err(gen_err);
2808 break;
2809
Simon Kelleyf2621c72007-04-29 19:47:21 +01002810 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002811 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002812 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002813 break;
2814
Simon Kelley25cf5e32015-01-09 15:53:03 +00002815 case 'q': /* --log-queries */
2816 set_option_bool(OPT_LOG);
2817 if (arg && strcmp(arg, "extra") == 0)
2818 set_option_bool(OPT_EXTRALOG);
2819 break;
2820
Simon Kelleyf2621c72007-04-29 19:47:21 +01002821 case LOPT_MAX_LOGS: /* --log-async */
2822 daemon->max_logs = LOG_MAX; /* default */
2823 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002824 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002825 else if (daemon->max_logs > 100)
2826 daemon->max_logs = 100;
2827 break;
2828
2829 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002830 {
2831 int i;
2832 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002833 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002834 daemon->edns_pktsz = (unsigned short)i;
2835 break;
2836 }
2837
Simon Kelleyf2621c72007-04-29 19:47:21 +01002838 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002839 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002840 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002841 /* if explicitly set to zero, use single OS ephemeral port
2842 and disable random ports */
2843 if (daemon->query_port == 0)
2844 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002845 break;
2846
Simon Kelley824af852008-02-12 20:43:05 +00002847 case 'T': /* --local-ttl */
2848 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002849 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002850 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002851 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002852 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002853 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002854 {
2855 int ttl;
2856 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002857 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002858 else if (option == LOPT_NEGTTL)
2859 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002860 else if (option == LOPT_MAXTTL)
2861 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002862 else if (option == LOPT_MINCTTL)
2863 {
2864 if (ttl > TTL_FLOOR_LIMIT)
2865 ttl = TTL_FLOOR_LIMIT;
2866 daemon->min_cache_ttl = (unsigned long)ttl;
2867 }
Simon Kelley1d860412012-09-20 20:48:04 +01002868 else if (option == LOPT_MAXCTTL)
2869 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002870 else if (option == LOPT_AUTHTTL)
2871 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002872 else if (option == LOPT_DHCPTTL)
2873 {
2874 daemon->dhcp_ttl = (unsigned long)ttl;
2875 daemon->use_dhcp_ttl = 1;
2876 }
Simon Kelley849a8352006-06-09 21:02:31 +01002877 else
2878 daemon->local_ttl = (unsigned long)ttl;
2879 break;
2880 }
2881
Simon Kelley7622fc02009-06-04 20:32:05 +01002882#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002883 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002884 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002885 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002886 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002887#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002888
Simon Kelley7622fc02009-06-04 20:32:05 +01002889#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002890 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002891 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002892 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002893 break;
2894
Simon Kelleybec366b2016-02-24 22:03:26 +00002895 case LOPT_TFTP_MTU: /* --tftp-mtu */
2896 if (!atoi_check(arg, &daemon->tftp_mtu))
2897 ret_err(gen_err);
2898 break;
2899
Simon Kelley824af852008-02-12 20:43:05 +00002900 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002901 comma = split(arg);
2902 if (comma)
2903 {
2904 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2905 new->interface = opt_string_alloc(comma);
2906 new->prefix = opt_string_alloc(arg);
2907 new->next = daemon->if_prefix;
2908 daemon->if_prefix = new;
2909 }
2910 else
2911 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002912 break;
2913
Simon Kelley824af852008-02-12 20:43:05 +00002914 case LOPT_TFTPPORTS: /* --tftp-port-range */
2915 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002916 !atoi_check16(arg, &daemon->start_tftp_port) ||
2917 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002918 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002919
2920 if (daemon->start_tftp_port > daemon->end_tftp_port)
2921 {
2922 int tmp = daemon->start_tftp_port;
2923 daemon->start_tftp_port = daemon->end_tftp_port;
2924 daemon->end_tftp_port = tmp;
2925 }
2926
2927 break;
Floris Bos60704f52017-04-09 22:22:49 +01002928
2929 case LOPT_APREF: /* --tftp-unique-root */
2930 if (!arg || strcasecmp(arg, "ip") == 0)
2931 set_option_bool(OPT_TFTP_APREF_IP);
2932 else if (strcasecmp(arg, "mac") == 0)
2933 set_option_bool(OPT_TFTP_APREF_MAC);
2934 else
2935 ret_err(gen_err);
2936 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002937#endif
Simon Kelley824af852008-02-12 20:43:05 +00002938
Simon Kelleyf2621c72007-04-29 19:47:21 +01002939 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002940 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002941 struct dhcp_bridge *new;
2942
Simon Kelley316e2732010-01-22 20:16:09 +00002943 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002944 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002945
Simon Kelley22cd8602018-01-14 22:57:14 +00002946 for (new = daemon->bridges; new; new = new->next)
2947 if (strcmp(new->iface, arg) == 0)
2948 break;
2949
2950 if (!new)
2951 {
2952 new = opt_malloc(sizeof(struct dhcp_bridge));
2953 strcpy(new->iface, arg);
2954 new->alias = NULL;
2955 new->next = daemon->bridges;
2956 daemon->bridges = new;
2957 }
2958
Simon Kelley832af0b2007-01-21 20:01:28 +00002959 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002960 arg = comma;
2961 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002962 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002963 {
Simon Kelley824af852008-02-12 20:43:05 +00002964 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002965 b->next = new->alias;
2966 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002967 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002968 }
2969 } while (comma);
2970
2971 break;
2972 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002973
Simon Kelley7622fc02009-06-04 20:32:05 +01002974#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002975 case LOPT_SHARED_NET: /* --shared-network */
2976 {
2977 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2978
2979#ifdef HAVE_DHCP6
2980 new->shared_addr.s_addr = 0;
2981#endif
2982 new->if_index = 0;
2983
2984 if (!(comma = split(arg)))
2985 {
2986 snerr:
2987 free(new);
2988 ret_err(_("bad shared-network"));
2989 }
2990
2991 if (inet_pton(AF_INET, comma, &new->shared_addr))
2992 {
2993 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2994 !(new->if_index = if_nametoindex(arg)))
2995 goto snerr;
2996 }
2997#ifdef HAVE_DHCP6
2998 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2999 {
3000 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
3001 !(new->if_index = if_nametoindex(arg)))
3002 goto snerr;
3003 }
3004#endif
3005 else
3006 goto snerr;
3007
3008 new->next = daemon->shared_networks;
3009 daemon->shared_networks = new;
3010 break;
3011 }
3012
Simon Kelleyf2621c72007-04-29 19:47:21 +01003013 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01003014 {
3015 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00003016 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003017 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003018
Simon Kelley52b92f42012-01-22 16:05:15 +00003019 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003020
Simon Kelley849a8352006-06-09 21:02:31 +01003021 while(1)
3022 {
3023 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003024 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3025 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3026 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003027 break;
3028
Simon Kelleyf2621c72007-04-29 19:47:21 +01003029 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003030 {
Kyle Swenson545712c2021-11-17 12:25:04 -07003031/* CRADLEPOINT */
3032 if (strstr(arg, "forcedinterface:") == arg)
3033 new->forcedinterface = opt_string_alloc(arg+16);
3034 else if (strstr(arg, "forcedaddress:") == arg)
3035 new->forcedaddress.s_addr = inet_addr(arg+14);
3036 else if (is_tag_prefix(arg))
3037/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +01003038 {
Simon Kelley0c387192013-09-05 10:21:12 +01003039 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003040 if (arg[4])
3041 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003042 }
3043 else
3044 {
3045 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003046 {
3047 dhcp_context_free(new);
3048 ret_err(_("only one tag allowed"));
3049 }
Simon Kelley849a8352006-06-09 21:02:31 +01003050 else
Petr Menšík59e47032018-11-02 22:39:39 +00003051 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003052 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003053 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003054 }
3055 else
3056 {
3057 a[0] = arg;
3058 break;
3059 }
3060 }
3061
Simon Kelley1f776932012-12-16 19:46:08 +00003062 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003063 if (!(a[k] = split(a[k-1])))
3064 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003065
Simon Kelley52b92f42012-01-22 16:05:15 +00003066 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003067 {
3068 dhcp_context_free(new);
3069 ret_err(_("bad dhcp-range"));
3070 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003071
3072 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003073 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003074 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003075 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003076 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003077 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003078 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003079 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003080 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003081 new->flags |= CONTEXT_PROXY;
3082 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003083 {
3084 dhcp_context_free(new);
3085 ret_err(_("bad dhcp-range"));
3086 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003087
3088 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3089 {
3090 struct in_addr tmp = new->start;
3091 new->start = new->end;
3092 new->end = tmp;
3093 }
3094
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003095 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003096 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003097 {
3098 new->flags |= CONTEXT_NETMASK;
3099 leasepos = 3;
3100 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003101 {
3102 dhcp_context_free(new);
3103 ret_err(_("inconsistent DHCP range"));
3104 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003105
Simon Kelley52b92f42012-01-22 16:05:15 +00003106
Simon Kelleyfa794662016-03-03 20:33:54 +00003107 if (k >= 4 && strchr(a[3], '.') &&
3108 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3109 {
3110 new->flags |= CONTEXT_BRDCAST;
3111 leasepos = 4;
3112 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003113 }
Simon Kelley849a8352006-06-09 21:02:31 +01003114 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003115#ifdef HAVE_DHCP6
3116 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003117 {
Petr Menšík59e47032018-11-02 22:39:39 +00003118 const char *err = NULL;
3119
Simon Kelley89500e32013-09-20 16:29:20 +01003120 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003121 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003122 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003123 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003124 new->next = daemon->dhcp6;
3125 daemon->dhcp6 = new;
3126
Simon Kelley30cd9662012-03-25 20:44:38 +01003127 for (leasepos = 1; leasepos < k; leasepos++)
3128 {
3129 if (strcmp(a[leasepos], "static") == 0)
3130 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3131 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003132 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003133 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003134 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003135 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3136 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003137 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003138 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003139 else if (strcmp(a[leasepos], "off-link") == 0)
3140 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003141 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3142 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003143 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3144 {
3145 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3146 new->flags |= CONTEXT_TEMPLATE;
3147 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003148 else
3149 break;
3150 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003151
Simon Kelley52b92f42012-01-22 16:05:15 +00003152 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003153 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003154 {
3155 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003156 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003157 if (!(*cp >= '0' && *cp <= '9'))
3158 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003159 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003160 {
3161 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003162 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003163 }
3164 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003165
Petr Menšík59e47032018-11-02 22:39:39 +00003166 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003167 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003168 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003169 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003170 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003171 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003172 }
Petr Menšík59e47032018-11-02 22:39:39 +00003173 else if (new->prefix < 64)
3174 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003175
Petr Menšík59e47032018-11-02 22:39:39 +00003176 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3177 err=(_("inconsistent DHCPv6 range"));
3178
3179 if (err)
3180 {
3181 dhcp_context_free(new);
3182 ret_err(err);
3183 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003184
3185 /* dhcp-range=:: enables DHCP stateless on any interface */
3186 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3187 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003188
3189 if (new->flags & CONTEXT_TEMPLATE)
3190 {
3191 struct in6_addr zero;
3192 memset(&zero, 0, sizeof(zero));
3193 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003194 {
3195 dhcp_context_free(new);
3196 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3197 }
Simon Kelley66409192013-08-01 20:19:32 +01003198 }
3199
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003200 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003201 {
3202 struct in6_addr tmp = new->start6;
3203 new->start6 = new->end6;
3204 new->end6 = tmp;
3205 }
Simon Kelley849a8352006-06-09 21:02:31 +01003206 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003207#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003208 else
Petr Menšík59e47032018-11-02 22:39:39 +00003209 {
3210 dhcp_context_free(new);
3211 ret_err(_("bad dhcp-range"));
3212 }
Simon Kelley849a8352006-06-09 21:02:31 +01003213
Simon Kelley30cd9662012-03-25 20:44:38 +01003214 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003215 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003216 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003217 {
3218 dhcp_context_free(new);
3219 ret_err(_("bad dhcp-range"));
3220 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003221
Simon Kelley849a8352006-06-09 21:02:31 +01003222 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003223 {
3224 new->lease_time = 0xffffffff;
3225 new->flags |= CONTEXT_SETLEASE;
3226 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003227 else if (strcmp(a[leasepos], "deprecated") == 0)
3228 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003229 else
3230 {
3231 int fac = 1;
3232 if (strlen(a[leasepos]) > 0)
3233 {
3234 switch (a[leasepos][strlen(a[leasepos]) - 1])
3235 {
Simon Kelley42243212012-07-20 15:19:18 +01003236 case 'w':
3237 case 'W':
3238 fac *= 7;
3239 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003240 case 'd':
3241 case 'D':
3242 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003243 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003244 case 'h':
3245 case 'H':
3246 fac *= 60;
3247 /* fall through */
3248 case 'm':
3249 case 'M':
3250 fac *= 60;
3251 /* fall through */
3252 case 's':
3253 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003254 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003255 }
3256
Simon Kelleybe379862012-12-23 12:01:39 +00003257 for (cp = a[leasepos]; *cp; cp++)
3258 if (!(*cp >= '0' && *cp <= '9'))
3259 break;
3260
Simon Kelley54dae552013-02-05 17:55:10 +00003261 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003262 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003263
Simon Kelley849a8352006-06-09 21:02:31 +01003264 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003265 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003266 /* Leases of a minute or less confuse
3267 some clients, notably Apple's */
3268 if (new->lease_time < 120)
3269 new->lease_time = 120;
3270 }
3271 }
3272 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003273
Simon Kelley849a8352006-06-09 21:02:31 +01003274 break;
3275 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003276
Simon Kelley5aabfc72007-08-29 11:24:47 +01003277 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003278 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003279 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003280 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003281 struct in_addr in;
3282
Simon Kelley824af852008-02-12 20:43:05 +00003283 new = opt_malloc(sizeof(struct dhcp_config));
3284
Simon Kelley849a8352006-06-09 21:02:31 +01003285 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003286 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3287 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003288 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003289 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003290 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003291#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003292 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003293#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003294
Simon Kelley137286e2020-02-06 22:09:30 +00003295 while (arg)
3296 {
3297 comma = split(arg);
3298 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3299 {
3300 if ((arg[0] == 'i' || arg[0] == 'I') &&
3301 (arg[1] == 'd' || arg[1] == 'D') &&
3302 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003303 {
Simon Kelley137286e2020-02-06 22:09:30 +00003304 if (arg[3] == '*')
3305 new->flags |= CONFIG_NOCLID;
3306 else
3307 {
3308 int len;
3309 arg += 3; /* dump id: */
3310 if (strchr(arg, ':'))
3311 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3312 else
3313 {
3314 unhide_metas(arg);
3315 len = (int) strlen(arg);
3316 }
3317
3318 if (len == -1)
3319 {
3320 dhcp_config_free(new);
3321 ret_err(_("bad hex constant"));
3322 }
3323 else if ((new->clid = opt_malloc(len)))
3324 {
3325 new->flags |= CONFIG_CLID;
3326 new->clid_len = len;
3327 memcpy(new->clid, arg, len);
3328 }
3329 }
3330 }
3331 /* dhcp-host has strange backwards-compat needs. */
3332 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3333 {
3334 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3335 newlist->next = new->netid;
3336 new->netid = newlist;
3337 newlist->list = dhcp_netid_create(arg+4, NULL);
3338 }
3339 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003340 new->filter = dhcp_netid_create(arg+4, new->filter);
3341
Simon Kelley137286e2020-02-06 22:09:30 +00003342#ifdef HAVE_DHCP6
3343 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3344 {
3345 char *pref;
3346 struct in6_addr in6;
3347 struct addrlist *new_addr;
3348
3349 arg[strlen(arg)-1] = 0;
3350 arg++;
3351 pref = split_chr(arg, '/');
3352
3353 if (!inet_pton(AF_INET6, arg, &in6))
3354 {
3355 dhcp_config_free(new);
3356 ret_err(_("bad IPv6 address"));
3357 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003358
Simon Kelley137286e2020-02-06 22:09:30 +00003359 new_addr = opt_malloc(sizeof(struct addrlist));
3360 new_addr->next = new->addr6;
3361 new_addr->flags = 0;
3362 new_addr->addr.addr6 = in6;
3363 new->addr6 = new_addr;
3364
3365 if (pref)
3366 {
3367 u64 addrpart = addr6part(&in6);
3368
3369 if (!atoi_check(pref, &new_addr->prefixlen) ||
3370 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003371 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003372 {
3373 dhcp_config_free(new);
3374 ret_err(_("bad IPv6 prefix"));
3375 }
3376
3377 new_addr->flags |= ADDRLIST_PREFIX;
3378 }
3379
3380 for (i= 0; i < 8; i++)
3381 if (in6.s6_addr[i] != 0)
3382 break;
3383
3384 /* set WILDCARD if network part all zeros */
3385 if (i == 8)
3386 new_addr->flags |= ADDRLIST_WILDCARD;
3387
3388 new->flags |= CONFIG_ADDR6;
3389 }
3390#endif
3391 else
3392 {
3393 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3394 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3395 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3396 {
3397 free(newhw);
3398 dhcp_config_free(new);
3399 ret_err(_("bad hex constant"));
3400 }
3401 else
3402 {
3403 newhw->next = new->hwaddr;
3404 new->hwaddr = newhw;
3405 }
3406 }
3407 }
3408 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3409 {
3410 struct dhcp_config *configs;
3411
3412 new->addr = in;
3413 new->flags |= CONFIG_ADDR;
3414
3415 /* If the same IP appears in more than one host config, then DISCOVER
3416 for one of the hosts will get the address, but REQUEST will be NAKed,
3417 since the address is reserved by the other one -> protocol loop. */
3418 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3419 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003420 {
Simon Kelley137286e2020-02-06 22:09:30 +00003421 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3422 return 0;
3423 }
3424 }
3425 else
3426 {
3427 char *cp, *lastp = NULL, last = 0;
3428 int fac = 1, isdig = 0;
3429
3430 if (strlen(arg) > 1)
3431 {
3432 lastp = arg + strlen(arg) - 1;
3433 last = *lastp;
3434 switch (last)
3435 {
3436 case 'w':
3437 case 'W':
3438 fac *= 7;
3439 /* fall through */
3440 case 'd':
3441 case 'D':
3442 fac *= 24;
3443 /* fall through */
3444 case 'h':
3445 case 'H':
3446 fac *= 60;
3447 /* fall through */
3448 case 'm':
3449 case 'M':
3450 fac *= 60;
3451 /* fall through */
3452 case 's':
3453 case 'S':
3454 *lastp = 0;
3455 }
3456 }
3457
3458 for (cp = arg; *cp; cp++)
3459 if (isdigit((unsigned char)*cp))
3460 isdig = 1;
3461 else if (*cp != ' ')
3462 break;
3463
3464 if (*cp)
3465 {
3466 if (lastp)
3467 *lastp = last;
3468 if (strcmp(arg, "infinite") == 0)
3469 {
3470 new->lease_time = 0xffffffff;
3471 new->flags |= CONFIG_TIME;
3472 }
3473 else if (strcmp(arg, "ignore") == 0)
3474 new->flags |= CONFIG_DISABLE;
3475 else
3476 {
3477 if (!(new->hostname = canonicalise_opt(arg)) ||
3478 !legal_hostname(new->hostname))
3479 {
3480 dhcp_config_free(new);
3481 ret_err(_("bad DHCP host name"));
3482 }
3483
3484 new->flags |= CONFIG_NAME;
3485 new->domain = strip_hostname(new->hostname);
3486 }
3487 }
3488 else if (isdig)
3489 {
3490 new->lease_time = atoi(arg) * fac;
3491 /* Leases of a minute or less confuse
3492 some clients, notably Apple's */
3493 if (new->lease_time < 120)
3494 new->lease_time = 120;
3495 new->flags |= CONFIG_TIME;
3496 }
3497 }
3498
3499 arg = comma;
3500 }
3501
Simon Kelley5aabfc72007-08-29 11:24:47 +01003502 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003503 break;
3504 }
Simon Kelley137286e2020-02-06 22:09:30 +00003505
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003506 case LOPT_TAG_IF: /* --tag-if */
3507 {
3508 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3509
3510 new->tag = NULL;
3511 new->set = NULL;
3512 new->next = NULL;
3513
3514 /* preserve order */
3515 if (!daemon->tag_if)
3516 daemon->tag_if = new;
3517 else
3518 {
3519 struct tag_if *tmp;
3520 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3521 tmp->next = new;
3522 }
3523
3524 while (arg)
3525 {
3526 size_t len;
3527
3528 comma = split(arg);
3529 len = strlen(arg);
3530
3531 if (len < 5)
3532 {
3533 new->set = NULL;
3534 break;
3535 }
3536 else
3537 {
Petr Menšík59e47032018-11-02 22:39:39 +00003538 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003539
3540 if (strstr(arg, "set:") == arg)
3541 {
3542 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3543 newlist->next = new->set;
3544 new->set = newlist;
3545 newlist->list = newtag;
3546 }
3547 else if (strstr(arg, "tag:") == arg)
3548 {
3549 newtag->next = new->tag;
3550 new->tag = newtag;
3551 }
3552 else
3553 {
3554 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003555 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003556 break;
3557 }
3558 }
3559
3560 arg = comma;
3561 }
3562
3563 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003564 {
3565 dhcp_netid_free(new->tag);
3566 dhcp_netid_list_free(new->set);
3567 ret_err_free(_("bad tag-if"), new);
3568 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003569
3570 break;
3571 }
3572
Simon Kelley849a8352006-06-09 21:02:31 +01003573
Simon Kelley73a08a22009-02-05 20:28:08 +00003574 case 'O': /* --dhcp-option */
3575 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003576 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003577 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003578 return parse_dhcp_opt(errstr, arg,
3579 option == LOPT_FORCE ? DHOPT_FORCE :
3580 (option == LOPT_MATCH ? DHOPT_MATCH :
3581 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003582
3583 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3584 {
3585 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3586 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3587 ssize_t len;
3588
3589 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3590 ret_err(gen_err);
3591
3592 new->wildcard = 0;
3593 new->netid = id;
3594 id->net = opt_string_alloc(set_prefix(arg));
3595
3596 if (comma[len-1] == '*')
3597 {
3598 comma[len-1] = 0;
3599 new->wildcard = 1;
3600 }
3601 new->name = opt_string_alloc(comma);
3602
3603 new->next = daemon->dhcp_name_match;
3604 daemon->dhcp_name_match = new;
3605
3606 break;
3607 }
3608
Simon Kelleyf2621c72007-04-29 19:47:21 +01003609 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003610 {
Petr Menšík59e47032018-11-02 22:39:39 +00003611 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003612
Petr Menšík137e9f82018-12-16 21:25:29 +00003613 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003614 {
3615 ret_err(gen_err);
3616 }
Simon Kelley849a8352006-06-09 21:02:31 +01003617 else
3618 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003619 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003620 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003621 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003622 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003623 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003624 dhcp_next_server.s_addr = 0;
3625 if (comma)
3626 {
3627 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003628 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003629 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003630 if (comma)
3631 {
3632 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003633 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3634 {
3635 /*
3636 * The user may have specified the tftp hostname here.
3637 * save it so that it can be resolved/looked up during
3638 * actual dhcp_reply().
3639 */
3640
3641 tftp_sname = opt_string_alloc(comma);
3642 dhcp_next_server.s_addr = 0;
3643 }
Simon Kelley849a8352006-06-09 21:02:31 +01003644 }
3645 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003646
3647 new = opt_malloc(sizeof(struct dhcp_boot));
3648 new->file = dhcp_file;
3649 new->sname = dhcp_sname;
3650 new->tftp_sname = tftp_sname;
3651 new->next_server = dhcp_next_server;
3652 new->netid = id;
3653 new->next = daemon->boot_config;
3654 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003655 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003656
Simon Kelley849a8352006-06-09 21:02:31 +01003657 break;
3658 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003659
Floris Bos503c6092017-04-09 23:07:13 +01003660 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3661 {
Petr Menšík59e47032018-11-02 22:39:39 +00003662 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003663
Petr Menšík137e9f82018-12-16 21:25:29 +00003664 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003665 {
3666 ret_err(gen_err);
3667 }
Floris Bos503c6092017-04-09 23:07:13 +01003668 else
3669 {
3670 struct delay_config *new;
3671 int delay;
3672 if (!atoi_check(arg, &delay))
3673 ret_err(gen_err);
3674
3675 new = opt_malloc(sizeof(struct delay_config));
3676 new->delay = delay;
3677 new->netid = id;
3678 new->next = daemon->delay_conf;
3679 daemon->delay_conf = new;
3680 }
3681
3682 break;
3683 }
3684
Simon Kelley7622fc02009-06-04 20:32:05 +01003685 case LOPT_PXE_PROMT: /* --pxe-prompt */
3686 {
3687 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3688 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003689
Simon Kelley7622fc02009-06-04 20:32:05 +01003690 new->netid = NULL;
3691 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003692 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003693
Petr Menšík137e9f82018-12-16 21:25:29 +00003694 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003695 {
3696 dhcp_opt_free(new);
3697 ret_err(gen_err);
3698 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003699 else
3700 {
3701 comma = split(arg);
3702 unhide_metas(arg);
3703 new->len = strlen(arg) + 1;
3704 new->val = opt_malloc(new->len);
3705 memcpy(new->val + 1, arg, new->len - 1);
3706
Wang Shanker4ded9622020-12-04 10:17:35 +08003707 new->u.vendor_class = NULL;
3708 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003709
3710 if (comma && atoi_check(comma, &timeout))
3711 *(new->val) = timeout;
3712 else
3713 *(new->val) = 255;
3714
3715 new->next = daemon->dhcp_opts;
3716 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003717 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003718 }
3719
3720 break;
3721 }
3722
3723 case LOPT_PXE_SERV: /* --pxe-service */
3724 {
3725 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3726 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003727 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3728 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003729 static int boottype = 32768;
3730
3731 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003732 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003733 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003734 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003735
Simon Kelley7622fc02009-06-04 20:32:05 +01003736 if (arg && (comma = split(arg)))
3737 {
3738 for (i = 0; CSA[i]; i++)
3739 if (strcasecmp(CSA[i], arg) == 0)
3740 break;
3741
3742 if (CSA[i] || atoi_check(arg, &i))
3743 {
3744 arg = comma;
3745 comma = split(arg);
3746
3747 new->CSA = i;
3748 new->menu = opt_string_alloc(arg);
3749
Simon Kelley316e2732010-01-22 20:16:09 +00003750 if (!comma)
3751 {
3752 new->type = 0; /* local boot */
3753 new->basename = NULL;
3754 }
3755 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003756 {
3757 arg = comma;
3758 comma = split(arg);
3759 if (atoi_check(arg, &i))
3760 {
3761 new->type = i;
3762 new->basename = NULL;
3763 }
3764 else
3765 {
3766 new->type = boottype++;
3767 new->basename = opt_string_alloc(arg);
3768 }
3769
Simon Kelley751d6f42012-02-10 15:24:51 +00003770 if (comma)
3771 {
3772 if (!inet_pton(AF_INET, comma, &new->server))
3773 {
3774 new->server.s_addr = 0;
3775 new->sname = opt_string_alloc(comma);
3776 }
3777
3778 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003779 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003780
Simon Kelley316e2732010-01-22 20:16:09 +00003781 /* Order matters */
3782 new->next = NULL;
3783 if (!daemon->pxe_services)
3784 daemon->pxe_services = new;
3785 else
3786 {
3787 struct pxe_service *s;
3788 for (s = daemon->pxe_services; s->next; s = s->next);
3789 s->next = new;
3790 }
3791
3792 daemon->enable_pxe = 1;
3793 break;
3794
Simon Kelley7622fc02009-06-04 20:32:05 +01003795 }
3796 }
3797
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003798 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003799 }
3800
Simon Kelleyf2621c72007-04-29 19:47:21 +01003801 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003802 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003803 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003804 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003805 else
3806 {
Simon Kelley824af852008-02-12 20:43:05 +00003807 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003808 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003809 unhide_metas(comma);
3810 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003811 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003812 {
3813 free(new->netid.net);
3814 ret_err_free(gen_err, new);
3815 }
Simon Kelley28866e92011-02-14 20:19:14 +00003816 else
3817 {
3818 new->next = daemon->dhcp_macs;
3819 daemon->dhcp_macs = new;
3820 }
Simon Kelley849a8352006-06-09 21:02:31 +01003821 }
3822 }
3823 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003824
Simon Kelleyf2621c72007-04-29 19:47:21 +01003825 case 'U': /* --dhcp-vendorclass */
3826 case 'j': /* --dhcp-userclass */
3827 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3828 case LOPT_REMOTE: /* --dhcp-remoteid */
3829 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003830 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003831 unsigned char *p;
3832 int dig = 0;
3833 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3834
3835 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003836 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003837
3838 new->netid.net = opt_string_alloc(set_prefix(arg));
3839 /* check for hex string - must digits may include : must not have nothing else,
3840 only allowed for agent-options. */
3841
3842 arg = comma;
3843 if ((comma = split(arg)))
3844 {
3845 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003846 {
3847 free(new->netid.net);
3848 ret_err_free(gen_err, new);
3849 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003850 else
3851 new->enterprise = atoi(arg+11);
3852 }
3853 else
3854 comma = arg;
3855
3856 for (p = (unsigned char *)comma; *p; p++)
3857 if (isxdigit(*p))
3858 dig = 1;
3859 else if (*p != ':')
3860 break;
3861 unhide_metas(comma);
3862 if (option == 'U' || option == 'j' || *p || !dig)
3863 {
3864 new->len = strlen(comma);
3865 new->data = opt_malloc(new->len);
3866 memcpy(new->data, comma, new->len);
3867 }
3868 else
3869 {
3870 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3871 new->data = opt_malloc(new->len);
3872 memcpy(new->data, comma, new->len);
3873 }
3874
3875 switch (option)
3876 {
3877 case 'j':
3878 new->match_type = MATCH_USER;
3879 break;
3880 case 'U':
3881 new->match_type = MATCH_VENDOR;
3882 break;
3883 case LOPT_CIRCUIT:
3884 new->match_type = MATCH_CIRCUIT;
3885 break;
3886 case LOPT_REMOTE:
3887 new->match_type = MATCH_REMOTE;
3888 break;
3889 case LOPT_SUBSCR:
3890 new->match_type = MATCH_SUBSCRIBER;
3891 break;
3892 }
3893 new->next = daemon->dhcp_vendors;
3894 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003895
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003896 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003897 }
3898
Simon Kelley9e038942008-05-30 20:06:34 +01003899 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3900 if (!arg)
3901 {
3902 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3903 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3904 }
3905 else
3906 {
3907 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003908 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3909 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003910 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003911 if (!comma)
3912 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3913 }
3914 break;
3915
Simon Kelley824af852008-02-12 20:43:05 +00003916 case 'J': /* --dhcp-ignore */
3917 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3918 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003919 case '3': /* --bootp-dynamic */
3920 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003921 {
Simon Kelley824af852008-02-12 20:43:05 +00003922 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003923 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003924 if (option == 'J')
3925 {
3926 new->next = daemon->dhcp_ignore;
3927 daemon->dhcp_ignore = new;
3928 }
Simon Kelley824af852008-02-12 20:43:05 +00003929 else if (option == LOPT_BROADCAST)
3930 {
3931 new->next = daemon->force_broadcast;
3932 daemon->force_broadcast = new;
3933 }
Simon Kelley9009d742008-11-14 20:04:27 +00003934 else if (option == '3')
3935 {
3936 new->next = daemon->bootp_dynamic;
3937 daemon->bootp_dynamic = new;
3938 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003939 else if (option == LOPT_GEN_NAMES)
3940 {
3941 new->next = daemon->dhcp_gen_names;
3942 daemon->dhcp_gen_names = new;
3943 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003944 else
3945 {
3946 new->next = daemon->dhcp_ignore_names;
3947 daemon->dhcp_ignore_names = new;
3948 }
3949
3950 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003951 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003952 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003953 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003954 }
Simon Kelley849a8352006-06-09 21:02:31 +01003955
3956 new->list = list;
3957 break;
3958 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003959
3960 case LOPT_PROXY: /* --dhcp-proxy */
3961 daemon->override = 1;
3962 while (arg) {
3963 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3964 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003965 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003966 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003967 new->next = daemon->override_relays;
3968 daemon->override_relays = new;
3969 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003970 }
3971 break;
3972
3973 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3974 {
3975 while (arg) {
3976 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3977 comma = split(arg);
3978 new->data = opt_string_alloc(arg);
3979 new->next = daemon->dhcp_pxe_vendors;
3980 daemon->dhcp_pxe_vendors = new;
3981 arg = comma;
3982 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003983 }
3984 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003985
3986 case LOPT_RELAY: /* --dhcp-relay */
3987 {
3988 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3989 comma = split(arg);
3990 new->interface = opt_string_alloc(split(comma));
3991 new->iface_index = 0;
3992 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3993 {
3994 new->next = daemon->relay4;
3995 daemon->relay4 = new;
3996 }
3997#ifdef HAVE_DHCP6
3998 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3999 {
4000 new->next = daemon->relay6;
4001 daemon->relay6 = new;
4002 }
4003#endif
4004 else
Petr Menšík59e47032018-11-02 22:39:39 +00004005 {
4006 free(new->interface);
4007 ret_err_free(_("Bad dhcp-relay"), new);
4008 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01004009
4010 break;
4011 }
4012
Simon Kelley7622fc02009-06-04 20:32:05 +01004013#endif
Simon Kelley849a8352006-06-09 21:02:31 +01004014
Simon Kelley8b372702012-03-09 17:45:10 +00004015#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004016 case LOPT_RA_PARAM: /* --ra-param */
4017 if ((comma = split(arg)))
4018 {
4019 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
4020 new->lifetime = -1;
4021 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01004022 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004023 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004024 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004025 if (strcasestr(comma, "mtu:") == comma)
4026 {
4027 arg = comma + 4;
4028 if (!(comma = split(comma)))
4029 goto err;
4030 if (!strcasecmp(arg, "off"))
4031 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004032 else if (!atoi_check(arg, &new->mtu))
4033 new->mtu_name = opt_string_alloc(arg);
4034 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004035 goto err;
4036 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004037 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4038 {
4039 if (*comma == 'l' || *comma == 'L')
4040 new->prio = 0x18;
4041 else
4042 new->prio = 0x08;
4043 comma = split(comma);
4044 }
4045 arg = split(comma);
4046 if (!atoi_check(comma, &new->interval) ||
4047 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004048 {
David Flamand005c46d2017-04-11 11:49:54 +01004049err:
Petr Menšík59e47032018-11-02 22:39:39 +00004050 free(new->name);
4051 ret_err_free(_("bad RA-params"), new);
4052 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004053
4054 new->next = daemon->ra_interfaces;
4055 daemon->ra_interfaces = new;
4056 }
4057 break;
4058
Simon Kelley8b372702012-03-09 17:45:10 +00004059 case LOPT_DUID: /* --dhcp-duid */
4060 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004061 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004062 else
4063 {
4064 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4065 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4066 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4067 }
4068 break;
4069#endif
4070
Simon Kelleyf2621c72007-04-29 19:47:21 +01004071 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004072 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004073 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004074 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004075 struct doctor *new = opt_malloc(sizeof(struct doctor));
4076 new->next = daemon->doctors;
4077 daemon->doctors = new;
4078 new->mask.s_addr = 0xffffffff;
4079 new->end.s_addr = 0;
4080
Simon Kelley849a8352006-06-09 21:02:31 +01004081 if ((a[0] = arg))
4082 for (k = 1; k < 3; k++)
4083 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004084 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004085 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004086 unhide_metas(a[k]);
4087 }
Simon Kelley849a8352006-06-09 21:02:31 +01004088
Simon Kelley73a08a22009-02-05 20:28:08 +00004089 dash = split_chr(a[0], '-');
4090
Simon Kelley849a8352006-06-09 21:02:31 +01004091 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004092 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004093 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4094 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4095 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004096
Simon Kelley73a08a22009-02-05 20:28:08 +00004097 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004098 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004099 !is_same_net(new->in, new->end, new->mask) ||
4100 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004101 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004102
4103 break;
4104 }
4105
Simon Kelleyf2621c72007-04-29 19:47:21 +01004106 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004107 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004108 {
4109 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004110 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004111
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004112 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004113
Simon Kelley824af852008-02-12 20:43:05 +00004114 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004115 memset(new, 0, sizeof(struct interface_name));
4116 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004117
Simon Kelleyf2621c72007-04-29 19:47:21 +01004118 /* Add to the end of the list, so that first name
4119 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004120 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004121 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004122
4123 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004124 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004125 if (inet_pton(AF_INET, arg, &new->proto4))
4126 new->flags |= INP4;
4127 else if (inet_pton(AF_INET6, arg, &new->proto6))
4128 new->flags |= INP6;
4129 else
4130 break;
4131
4132 arg = comma;
4133 }
4134
4135 if ((comma = split_chr(arg, '/')))
4136 {
4137 if (strcmp(comma, "4") == 0)
4138 new->flags &= ~IN6;
4139 else if (strcmp(comma, "6") == 0)
4140 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004141 else
Petr Menšík59e47032018-11-02 22:39:39 +00004142 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004143 }
4144
4145 new->intr = opt_string_alloc(arg);
4146
4147 if (option == LOPT_DYNHOST)
4148 {
4149 if (!(new->flags & (INP4 | INP6)))
4150 ret_err(_("missing address in dynamic host"));
4151
4152 if (!(new->flags & IN4) || !(new->flags & IN6))
4153 arg = NULL; /* provoke error below */
4154
4155 new->flags &= ~(IN4 | IN6);
4156 }
4157 else
4158 {
4159 if (new->flags & (INP4 | INP6))
4160 arg = NULL; /* provoke error below */
4161 }
4162
4163 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4164 ret_err(option == LOPT_DYNHOST ?
4165 _("bad dynamic host") : _("bad interface name"));
4166
Simon Kelleyf2621c72007-04-29 19:47:21 +01004167 break;
4168 }
Simon Kelley9009d742008-11-14 20:04:27 +00004169
4170 case LOPT_CNAME: /* --cname */
4171 {
4172 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004173 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004174 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004175
Simon Kelleya1d973f2016-12-22 22:09:50 +00004176 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004177 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004178 pen = last;
4179 last = comma;
4180 }
4181
4182 if (!pen)
4183 ret_err(_("bad CNAME"));
4184
4185 if (pen != arg && atoi_check(last, &ttl))
4186 last = pen;
4187
4188 target = canonicalise_opt(last);
4189
4190 while (arg != last)
4191 {
Petr Menšík56f06232018-03-06 23:13:32 +00004192 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004193 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004194
4195 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004196 {
4197 free(target);
4198 free(alias);
4199 ret_err(_("bad CNAME"));
4200 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004201
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004202 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004203 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004204 {
4205 free(target);
4206 free(alias);
4207 ret_err(_("duplicate CNAME"));
4208 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004209 new = opt_malloc(sizeof(struct cname));
4210 new->next = daemon->cnames;
4211 daemon->cnames = new;
4212 new->alias = alias;
4213 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004214 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004215
Petr Menšík56f06232018-03-06 23:13:32 +00004216 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004217 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004218
Simon Kelley9009d742008-11-14 20:04:27 +00004219 break;
4220 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004221
4222 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004223 {
4224 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004225 char *dom, *target = NULL;
4226
Simon Kelleyf2621c72007-04-29 19:47:21 +01004227 comma = split(arg);
4228
Simon Kelley1f15b812009-10-13 17:49:32 +01004229 if (!(dom = canonicalise_opt(arg)) ||
4230 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004231 {
4232 free(dom);
4233 free(target);
4234 ret_err(_("bad PTR record"));
4235 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004236 else
4237 {
4238 new = opt_malloc(sizeof(struct ptr_record));
4239 new->next = daemon->ptr;
4240 daemon->ptr = new;
4241 new->name = dom;
4242 new->ptr = target;
4243 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004244 break;
4245 }
4246
Simon Kelley1a6bca82008-07-11 11:11:42 +01004247 case LOPT_NAPTR: /* --naptr-record */
4248 {
4249 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4250 int k = 0;
4251 struct naptr *new;
4252 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004253 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004254
4255 if ((a[0] = arg))
4256 for (k = 1; k < 7; k++)
4257 if (!(a[k] = split(a[k-1])))
4258 break;
4259
4260
4261 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004262 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004263 !atoi_check16(a[1], &order) ||
4264 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004265 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004266 {
4267 free(name);
4268 free(replace);
4269 ret_err(_("bad NAPTR record"));
4270 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004271 else
4272 {
4273 new = opt_malloc(sizeof(struct naptr));
4274 new->next = daemon->naptr;
4275 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004276 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004277 new->flags = opt_string_alloc(a[3]);
4278 new->services = opt_string_alloc(a[4]);
4279 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004280 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004281 new->order = order;
4282 new->pref = pref;
4283 }
4284 break;
4285 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004286
4287 case LOPT_RR: /* dns-rr */
4288 {
4289 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004290 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004291 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004292 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004293
4294 comma = split(arg);
4295 data = split(comma);
4296
4297 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004298 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004299
Petr Menšík59e47032018-11-02 22:39:39 +00004300 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004301 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004302 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004303 {
4304 free(new->name);
4305 ret_err_free(_("bad RR record"), new);
4306 }
4307
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004308 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004309 new->class = class;
4310 new->next = daemon->rr;
4311 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004312
4313 if (data)
4314 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004315 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004316 new->len = len;
4317 memcpy(new->txt, data, len);
4318 }
4319
4320 break;
4321 }
4322
Simon Kelley974a6d02018-08-23 23:01:16 +01004323 case LOPT_CAA: /* --caa-record */
4324 {
4325 struct txt_record *new;
4326 char *tag, *value;
4327 int flags;
4328
4329 comma = split(arg);
4330 tag = split(comma);
4331 value = split(tag);
4332
4333 new = opt_malloc(sizeof(struct txt_record));
4334 new->next = daemon->rr;
4335 daemon->rr = new;
4336
4337 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4338 ret_err(_("bad CAA record"));
4339
4340 unhide_metas(tag);
4341 unhide_metas(value);
4342
4343 new->len = strlen(tag) + strlen(value) + 2;
4344 new->txt = opt_malloc(new->len);
4345 new->txt[0] = flags;
4346 new->txt[1] = strlen(tag);
4347 memcpy(&new->txt[2], tag, strlen(tag));
4348 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4349 new->class = T_CAA;
4350
4351 break;
4352 }
4353
Simon Kelleyf2621c72007-04-29 19:47:21 +01004354 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004355 {
4356 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004357 unsigned char *p, *cnt;
4358 size_t len;
4359
4360 comma = split(arg);
4361
Simon Kelley824af852008-02-12 20:43:05 +00004362 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004363 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004364 new->stat = 0;
4365
Simon Kelley1f15b812009-10-13 17:49:32 +01004366 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004367 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004368
Petr Menšík59e47032018-11-02 22:39:39 +00004369 new->next = daemon->txt;
4370 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004371 len = comma ? strlen(comma) : 0;
4372 len += (len/255) + 1; /* room for extra counts */
4373 new->txt = p = opt_malloc(len);
4374
4375 cnt = p++;
4376 *cnt = 0;
4377
4378 while (comma && *comma)
4379 {
4380 unsigned char c = (unsigned char)*comma++;
4381
4382 if (c == ',' || *cnt == 255)
4383 {
4384 if (c != ',')
4385 comma--;
4386 cnt = p++;
4387 *cnt = 0;
4388 }
4389 else
4390 {
4391 *p++ = unhide_meta(c);
4392 (*cnt)++;
4393 }
4394 }
4395
4396 new->len = p - new->txt;
4397
Simon Kelley849a8352006-06-09 21:02:31 +01004398 break;
4399 }
4400
Simon Kelleyf2621c72007-04-29 19:47:21 +01004401 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004402 {
4403 int port = 1, priority = 0, weight = 0;
4404 char *name, *target = NULL;
4405 struct mx_srv_record *new;
4406
Simon Kelleyf2621c72007-04-29 19:47:21 +01004407 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004408
Simon Kelley1f15b812009-10-13 17:49:32 +01004409 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004410 ret_err(_("bad SRV record"));
4411
Simon Kelley849a8352006-06-09 21:02:31 +01004412 if (comma)
4413 {
4414 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004415 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004416 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004417 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004418
Simon Kelley849a8352006-06-09 21:02:31 +01004419 if (comma)
4420 {
4421 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004422 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004423 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004424 {
4425 free(name);
4426 ret_err_free(_("invalid port number"), target);
4427 }
Simon Kelley824af852008-02-12 20:43:05 +00004428
Simon Kelley849a8352006-06-09 21:02:31 +01004429 if (comma)
4430 {
4431 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004432 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004433 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004434 {
4435 free(name);
4436 ret_err_free(_("invalid priority"), target);
4437 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004438 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004439 {
4440 free(name);
4441 ret_err_free(_("invalid weight"), target);
4442 }
Simon Kelley849a8352006-06-09 21:02:31 +01004443 }
4444 }
4445 }
4446
Simon Kelley824af852008-02-12 20:43:05 +00004447 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004448 new->next = daemon->mxnames;
4449 daemon->mxnames = new;
4450 new->issrv = 1;
4451 new->name = name;
4452 new->target = target;
4453 new->srvport = port;
4454 new->priority = priority;
4455 new->weight = weight;
4456 break;
4457 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004458
Simon Kelleye759d422012-03-16 13:18:57 +00004459 case LOPT_HOST_REC: /* --host-record */
4460 {
Petr Menšík59e47032018-11-02 22:39:39 +00004461 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004462
Simon Kelleye759d422012-03-16 13:18:57 +00004463 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004464 ret_err(_("Bad host-record"));
4465
Petr Menšík59e47032018-11-02 22:39:39 +00004466 new = opt_malloc(sizeof(struct host_record));
4467 memset(new, 0, sizeof(struct host_record));
4468 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004469 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004470
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004471 while (arg)
4472 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004473 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004474 char *dig;
4475
4476 for (dig = arg; *dig != 0; dig++)
4477 if (*dig < '0' || *dig > '9')
4478 break;
4479 if (*dig == 0)
4480 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004481 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004482 {
4483 new->addr = addr.addr4;
4484 new->flags |= HR_4;
4485 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004486 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004487 {
4488 new->addr6 = addr.addr6;
4489 new->flags |= HR_6;
4490 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004491 else
4492 {
4493 int nomem;
4494 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004495 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004496 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004497 {
4498 struct name_list *tmp = new->names, *next;
4499 for (tmp = new->names; tmp; tmp = next)
4500 {
4501 next = tmp->next;
4502 free(tmp);
4503 }
4504 ret_err_free(_("Bad name in host-record"), new);
4505 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004506
Petr Menšík59e47032018-11-02 22:39:39 +00004507 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004508 nl->name = canon;
4509 /* keep order, so that PTR record goes to first name */
4510 nl->next = NULL;
4511 if (!new->names)
4512 new->names = nl;
4513 else
4514 {
4515 struct name_list *tmp;
4516 for (tmp = new->names; tmp->next; tmp = tmp->next);
4517 tmp->next = nl;
4518 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004519 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004520
4521 arg = comma;
4522 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004523 }
Simon Kelleye759d422012-03-16 13:18:57 +00004524
4525 /* Keep list order */
4526 if (!daemon->host_records_tail)
4527 daemon->host_records = new;
4528 else
4529 daemon->host_records_tail->next = new;
4530 new->next = NULL;
4531 daemon->host_records_tail = new;
4532 break;
4533 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004534
4535#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004536 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004537 daemon->timestamp_file = opt_string_alloc(arg);
4538 break;
4539
Simon Kelleyf3e57872018-07-20 21:10:48 +01004540 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004541 if (arg)
4542 {
4543 if (strcmp(arg, "no") == 0)
4544 set_option_bool(OPT_DNSSEC_IGN_NS);
4545 else
4546 ret_err(_("bad value for dnssec-check-unsigned"));
4547 }
4548 break;
4549
Simon Kelleyf3e57872018-07-20 21:10:48 +01004550 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004551 {
Simon Kelleyee415862014-02-11 11:07:22 +00004552 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4553 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4554 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004555
4556 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004557 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004558
Simon Kelleycbf13a22014-01-25 17:59:14 +00004559 if ((comma = split(arg)) && (algo = split(comma)))
4560 {
4561 int class = 0;
4562 if (strcmp(comma, "IN") == 0)
4563 class = C_IN;
4564 else if (strcmp(comma, "CH") == 0)
4565 class = C_CHAOS;
4566 else if (strcmp(comma, "HS") == 0)
4567 class = C_HESIOD;
4568
4569 if (class != 0)
4570 {
4571 new->class = class;
4572 comma = algo;
4573 algo = split(comma);
4574 }
4575 }
4576
Simon Kelleyee415862014-02-11 11:07:22 +00004577 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4578 !atoi_check16(comma, &new->keytag) ||
4579 !atoi_check8(algo, &new->algo) ||
4580 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004581 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004582 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004583
Simon Kelley0fc2f312014-01-08 10:26:58 +00004584 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004585 len = (2*strlen(keyhex))+1;
4586 new->digest = opt_malloc(len);
4587 unhide_metas(keyhex);
4588 /* 4034: "Whitespace is allowed within digits" */
4589 for (cp = keyhex; *cp; )
4590 if (isspace(*cp))
4591 for (cp1 = cp; *cp1; cp1++)
4592 *cp1 = *(cp1+1);
4593 else
4594 cp++;
4595 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004596 {
4597 free(new->name);
4598 ret_err_free(_("bad HEX in trust anchor"), new);
4599 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004600
Simon Kelleyee415862014-02-11 11:07:22 +00004601 new->next = daemon->ds;
4602 daemon->ds = new;
4603
Simon Kelley0fc2f312014-01-08 10:26:58 +00004604 break;
4605 }
4606#endif
Kyle Swenson545712c2021-11-17 12:25:04 -07004607/* CRADLEPOINT */
4608/* This option (OPT_EDNS_OPTION) is required to tack a(n) owner record onto a DNS request:
4609 * Option Code: Owner (reserved) 4 (0x00 0x04)
4610 * Option Length: 19 ("Cradlepoint" + device_id (obtained from OpenDNS)) (0x00 0x13)
4611 * Option Data: "Cradlepoint" + device_id (an 4 byte identifier)
4612 *
4613 * The EDNS option is passed in like this:
4614 * --edns-option=4,CradlepointAABBCCDD
4615 */
4616 case LOPT_EDNS_OPTION:
4617 {
4618 unsigned char *tmp;
4619 int option_code, option_len = 0;
4620
4621 comma = split(arg);
4622
4623 if (*arg == 0 || !atoi_check16(arg, &option_code))
4624 {
4625 option = '?';
4626 ret_err(_("invalid EDNS option code"));
4627 break;
4628 }
4629
4630 if (comma &&
4631 (option_len = str_unescape_c_string(comma, comma)) <= 0)
4632 {
4633 option = '?';
4634 ret_err(_("invalid EDNS option data"));
4635 break;
4636 }
4637 tmp = opt_malloc(daemon->edns_options_len + 4 + option_len);
4638
4639 if (daemon->edns_options_len > 0)
4640 {
4641 memcpy(tmp, daemon->edns_options, daemon->edns_options_len);
4642 free(daemon->edns_options);
4643 }
4644 daemon->edns_options = tmp;
4645 tmp += daemon->edns_options_len;
4646 PUTSHORT(option_code, tmp);
4647 PUTSHORT(option_len, tmp);
4648 memcpy(tmp, comma, option_len);
4649 daemon->edns_options_len += 4 + option_len;
4650 my_syslog(LOG_DEBUG, _("EDNS Option %d added length %d"), option_code,
4651 daemon->edns_options_len);
4652
4653 break;
4654 }
4655
4656 case LOPT_SSID_MAP:
4657 {
4658 char *err;
4659
4660 unhide_metas(arg);
4661
4662 if ((err = opendns_parse_device_id_opt(arg)) != NULL) {
4663 /* int vs pointer warning return err; */
4664 ret_err(_("opendns parse device id failed"));
4665 }
4666
4667 break;
4668 }
4669/* CRADLEPOINT */
4670
Simon Kelley7622fc02009-06-04 20:32:05 +01004671 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004672 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004673
Simon Kelley849a8352006-06-09 21:02:31 +01004674 }
Simon Kelley824af852008-02-12 20:43:05 +00004675
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004676 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004677}
4678
Simon Kelley28866e92011-02-14 20:19:14 +00004679static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004680{
Simon Kelley824af852008-02-12 20:43:05 +00004681 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004682 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004683
4684 while (fgets(buff, MAXDNAME, f))
4685 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004686 int white, i;
4687 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004688 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004689 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004690
Simon Kelley824af852008-02-12 20:43:05 +00004691 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004692 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004693 {
4694 if (setjmp(mem_jmp))
4695 continue;
4696 mem_recover = 1;
4697 }
4698
Simon Kelley13dee6f2017-02-28 16:51:58 +00004699 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004700 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004701 errmess = NULL;
4702
Simon Kelley849a8352006-06-09 21:02:31 +01004703 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4704 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004705 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004706 {
4707 if (*p == '"')
4708 {
4709 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004710
Simon Kelley849a8352006-06-09 21:02:31 +01004711 for(; *p && *p != '"'; p++)
4712 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004713 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004714 {
4715 if (p[1] == 't')
4716 p[1] = '\t';
4717 else if (p[1] == 'n')
4718 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004719 else if (p[1] == 'b')
4720 p[1] = '\b';
4721 else if (p[1] == 'r')
4722 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004723 else if (p[1] == 'e') /* escape */
4724 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004725 memmove(p, p+1, strlen(p+1)+1);
4726 }
4727 *p = hide_meta(*p);
4728 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004729
4730 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004731 {
4732 errmess = _("missing \"");
4733 goto oops;
4734 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004735
4736 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004737 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004738
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004739 if (isspace(*p))
4740 {
4741 *p = ' ';
4742 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004743 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004744 else
4745 {
4746 if (white && *p == '#')
4747 {
4748 *p = 0;
4749 break;
4750 }
4751 white = 0;
4752 }
Simon Kelley849a8352006-06-09 21:02:31 +01004753 }
4754
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004755
4756 /* strip leading spaces */
4757 for (start = buff; *start && *start == ' '; start++);
4758
4759 /* strip trailing spaces */
4760 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4761
4762 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004763 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004764 else
4765 start[len] = 0;
4766
Simon Kelley611ebc52012-07-16 16:23:46 +01004767 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004768 arg = start;
4769 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004770 {
4771 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004772 for (arg = p+1; *arg == ' '; arg++);
4773 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004774 *p = 0;
4775 }
4776 else
4777 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004778
Simon Kelley611ebc52012-07-16 16:23:46 +01004779 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004780 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004781 for (option = 0, i = 0; opts[i].name; i++)
4782 if (strcmp(opts[i].name, start) == 0)
4783 {
4784 option = opts[i].val;
4785 break;
4786 }
4787
4788 if (!option)
4789 errmess = _("bad option");
4790 else if (opts[i].has_arg == 0 && arg)
4791 errmess = _("extraneous parameter");
4792 else if (opts[i].has_arg == 1 && !arg)
4793 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004794 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4795 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004796 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004797
4798 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004799 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004800 strcpy(daemon->namebuff, errmess);
4801
Simon Kelley9bafdc62018-08-21 22:53:38 +01004802 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004803 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004804 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004805 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004806 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004807 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004808 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004809 }
Simon Kelley849a8352006-06-09 21:02:31 +01004810 }
4811
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004812 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004813 fclose(f);
4814}
4815
Simon Kelley4f7bb572018-03-08 18:47:08 +00004816#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004817int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004818{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004819 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4820
Simon Kelley70d18732015-01-31 19:59:29 +00004821 if (flags & AH_DHCP_HST)
4822 return one_file(file, LOPT_BANK);
4823 else if (flags & AH_DHCP_OPT)
4824 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004825
Simon Kelley70d18732015-01-31 19:59:29 +00004826 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004827}
4828#endif
4829
Simon Kelley395eb712012-07-06 22:07:05 +01004830static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004831{
4832 FILE *f;
4833 int nofile_ok = 0;
4834 static int read_stdin = 0;
4835 static struct fileread {
4836 dev_t dev;
4837 ino_t ino;
4838 struct fileread *next;
4839 } *filesread = NULL;
4840
4841 if (hard_opt == '7')
4842 {
4843 /* default conf-file reading */
4844 hard_opt = 0;
4845 nofile_ok = 1;
4846 }
4847
4848 if (hard_opt == 0 && strcmp(file, "-") == 0)
4849 {
4850 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004851 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004852 read_stdin = 1;
4853 file = "stdin";
4854 f = stdin;
4855 }
4856 else
4857 {
4858 /* ignore repeated files. */
4859 struct stat statbuf;
4860
4861 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4862 {
4863 struct fileread *r;
4864
4865 for (r = filesread; r; r = r->next)
4866 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004867 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004868
4869 r = safe_malloc(sizeof(struct fileread));
4870 r->next = filesread;
4871 filesread = r;
4872 r->dev = statbuf.st_dev;
4873 r->ino = statbuf.st_ino;
4874 }
4875
4876 if (!(f = fopen(file, "r")))
4877 {
4878 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004879 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004880 else
4881 {
4882 char *str = _("cannot read %s: %s");
4883 if (hard_opt != 0)
4884 {
4885 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004886 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004887 }
4888 else
4889 die(str, file, EC_FILE);
4890 }
4891 }
4892 }
4893
4894 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004895 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004896}
4897
4898/* expand any name which is a directory */
4899struct hostsfile *expand_filelist(struct hostsfile *list)
4900{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004901 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004902 struct hostsfile *ah;
4903
Simon Kelley19c51cf2014-03-18 22:38:30 +00004904 /* find largest used index */
4905 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004906 {
4907 if (i <= ah->index)
4908 i = ah->index + 1;
4909
4910 if (ah->flags & AH_DIR)
4911 ah->flags |= AH_INACTIVE;
4912 else
4913 ah->flags &= ~AH_INACTIVE;
4914 }
4915
4916 for (ah = list; ah; ah = ah->next)
4917 if (!(ah->flags & AH_INACTIVE))
4918 {
4919 struct stat buf;
4920 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4921 {
4922 DIR *dir_stream;
4923 struct dirent *ent;
4924
4925 /* don't read this as a file */
4926 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004927
Simon Kelley28866e92011-02-14 20:19:14 +00004928 if (!(dir_stream = opendir(ah->fname)))
4929 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4930 ah->fname, strerror(errno));
4931 else
4932 {
4933 while ((ent = readdir(dir_stream)))
4934 {
4935 size_t lendir = strlen(ah->fname);
4936 size_t lenfile = strlen(ent->d_name);
4937 struct hostsfile *ah1;
4938 char *path;
4939
4940 /* ignore emacs backups and dotfiles */
4941 if (lenfile == 0 ||
4942 ent->d_name[lenfile - 1] == '~' ||
4943 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4944 ent->d_name[0] == '.')
4945 continue;
4946
4947 /* see if we have an existing record.
4948 dir is ah->fname
4949 file is ent->d_name
4950 path to match is ah1->fname */
4951
4952 for (ah1 = list; ah1; ah1 = ah1->next)
4953 {
4954 if (lendir < strlen(ah1->fname) &&
4955 strstr(ah1->fname, ah->fname) == ah1->fname &&
4956 ah1->fname[lendir] == '/' &&
4957 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4958 {
4959 ah1->flags &= ~AH_INACTIVE;
4960 break;
4961 }
4962 }
4963
4964 /* make new record */
4965 if (!ah1)
4966 {
4967 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4968 continue;
4969
4970 if (!(path = whine_malloc(lendir + lenfile + 2)))
4971 {
4972 free(ah1);
4973 continue;
4974 }
4975
4976 strcpy(path, ah->fname);
4977 strcat(path, "/");
4978 strcat(path, ent->d_name);
4979 ah1->fname = path;
4980 ah1->index = i++;
4981 ah1->flags = AH_DIR;
4982 ah1->next = list;
4983 list = ah1;
4984 }
4985
4986 /* inactivate record if not regular file */
4987 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4988 ah1->flags |= AH_INACTIVE;
4989
4990 }
4991 closedir(dir_stream);
4992 }
4993 }
4994 }
4995
4996 return list;
4997}
4998
Simon Kelley7b1eae42014-02-20 13:43:28 +00004999void read_servers_file(void)
5000{
5001 FILE *f;
5002
5003 if (!(f = fopen(daemon->servers_file, "r")))
5004 {
5005 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
5006 return;
5007 }
5008
5009 mark_servers(SERV_FROM_FILE);
5010 cleanup_servers();
5011
5012 read_file(daemon->servers_file, f, LOPT_REV_SERV);
5013}
5014
Simon Kelley28866e92011-02-14 20:19:14 +00005015
Simon Kelley7622fc02009-06-04 20:32:05 +01005016#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00005017static void clear_dynamic_conf(void)
5018{
5019 struct dhcp_config *configs, *cp, **up;
5020
5021 /* remove existing... */
5022 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
5023 {
5024 cp = configs->next;
5025
5026 if (configs->flags & CONFIG_BANK)
5027 {
5028 struct hwaddr_config *mac, *tmp;
5029 struct dhcp_netid_list *list, *tmplist;
5030
5031 for (mac = configs->hwaddr; mac; mac = tmp)
5032 {
5033 tmp = mac->next;
5034 free(mac);
5035 }
5036
5037 if (configs->flags & CONFIG_CLID)
5038 free(configs->clid);
5039
5040 for (list = configs->netid; list; list = tmplist)
5041 {
5042 free(list->list);
5043 tmplist = list->next;
5044 free(list);
5045 }
5046
5047 if (configs->flags & CONFIG_NAME)
5048 free(configs->hostname);
5049
5050 *up = configs->next;
5051 free(configs);
5052 }
5053 else
5054 up = &configs->next;
5055 }
5056}
5057
5058static void clear_dynamic_opt(void)
5059{
5060 struct dhcp_opt *opts, *cp, **up;
5061 struct dhcp_netid *id, *next;
5062
5063 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
5064 {
5065 cp = opts->next;
5066
5067 if (opts->flags & DHOPT_BANK)
5068 {
5069 if ((opts->flags & DHOPT_VENDOR))
5070 free(opts->u.vendor_class);
5071 free(opts->val);
5072 for (id = opts->netid; id; id = next)
5073 {
5074 next = id->next;
5075 free(id->net);
5076 free(id);
5077 }
5078 *up = opts->next;
5079 free(opts);
5080 }
5081 else
5082 up = &opts->next;
5083 }
5084}
5085
Simon Kelley824af852008-02-12 20:43:05 +00005086void reread_dhcp(void)
5087{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005088 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005089
Simon Kelley4f7bb572018-03-08 18:47:08 +00005090 /* Do these even if there is no daemon->dhcp_hosts_file or
5091 daemon->dhcp_opts_file since entries may have been created by the
5092 inotify dynamic file reading system. */
5093
5094 clear_dynamic_conf();
5095 clear_dynamic_opt();
5096
5097 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005098 {
Simon Kelley28866e92011-02-14 20:19:14 +00005099 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5100 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005101 if (!(hf->flags & AH_INACTIVE))
5102 {
5103 if (one_file(hf->fname, LOPT_BANK))
5104 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5105 }
Simon Kelley824af852008-02-12 20:43:05 +00005106 }
5107
5108 if (daemon->dhcp_opts_file)
5109 {
Simon Kelley28866e92011-02-14 20:19:14 +00005110 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5111 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5112 if (!(hf->flags & AH_INACTIVE))
5113 {
Simon Kelley395eb712012-07-06 22:07:05 +01005114 if (one_file(hf->fname, LOPT_OPTS))
5115 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005116 }
Simon Kelley824af852008-02-12 20:43:05 +00005117 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005118
5119# ifdef HAVE_INOTIFY
5120 /* Setup notify and read pre-existing files. */
5121 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5122# endif
Simon Kelley824af852008-02-12 20:43:05 +00005123}
Simon Kelley7622fc02009-06-04 20:32:05 +01005124#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005125
Simon Kelley5aabfc72007-08-29 11:24:47 +01005126void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005127{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005128 size_t argbuf_size = MAXDNAME;
5129 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005130 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005131 int option, testmode = 0;
5132 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005133
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005134 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005135
Simon Kelley824af852008-02-12 20:43:05 +00005136 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005137 memset(daemon, 0, sizeof(struct daemon));
5138 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005139
Simon Kelley3be34542004-09-11 19:12:13 +01005140 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005141 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005142 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005143 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005144 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5145 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005146 daemon->default_resolv.is_default = 1;
5147 daemon->default_resolv.name = RESOLVFILE;
5148 daemon->resolv_files = &daemon->default_resolv;
5149 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005150 daemon->runfile = RUNFILE;
5151 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005152 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005153 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005154 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005155 daemon->auth_ttl = AUTH_TTL;
5156 daemon->soa_refresh = SOA_REFRESH;
5157 daemon->soa_retry = SOA_RETRY;
5158 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley4a8c0982021-03-26 21:19:39 +00005159
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005160#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005161 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5162 add_txt("authors.bind", "Simon Kelley", 0);
5163 add_txt("copyright.bind", COPYRIGHT, 0);
5164 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5165 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5166 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5167 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5168 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5169#ifdef HAVE_AUTH
5170 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5171#endif
5172 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005173#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005174
Simon Kelley849a8352006-06-09 21:02:31 +01005175 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005176 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005177#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005178 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005179#else
Simon Kelley849a8352006-06-09 21:02:31 +01005180 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005181#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005182
5183 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005184 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005185 for (; optind < argc; optind++)
5186 {
5187 unsigned char *c = (unsigned char *)argv[optind];
5188 for (; *c != 0; c++)
5189 if (!isspace(*c))
5190 die(_("junk found in command line"), NULL, EC_BADCONF);
5191 }
Simon Kelley28866e92011-02-14 20:19:14 +00005192 break;
5193 }
5194
Simon Kelley849a8352006-06-09 21:02:31 +01005195 /* Copy optarg so that argv doesn't get changed */
5196 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005197 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005198 if (strlen(optarg) >= argbuf_size)
5199 {
5200 free(argbuf);
5201 argbuf_size = strlen(optarg) + 1;
5202 argbuf = opt_malloc(argbuf_size);
5203 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005204 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005205 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005206 }
5207 else
5208 arg = NULL;
5209
5210 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005211 if (option == LOPT_TEST)
5212 testmode = 1;
5213 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005214 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005215#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005216 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005217 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005218#ifdef HAVE_DHCP6
5219 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5220 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005221#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005222 else
5223#endif
5224 do_usage();
5225
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005226 exit(0);
5227 }
Simon Kelley849a8352006-06-09 21:02:31 +01005228 else if (option == 'v')
5229 {
5230 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005231 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005232 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5233 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005234 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005235 exit(0);
5236 }
Simon Kelley849a8352006-06-09 21:02:31 +01005237 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005238 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005239 if (!conffile)
5240 conffile = opt_string_alloc(arg);
5241 else
5242 {
5243 char *extra = opt_string_alloc(arg);
5244 one_file(extra, 0);
5245 free(extra);
5246 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005247 }
Simon Kelley849a8352006-06-09 21:02:31 +01005248 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005249 {
Simon Kelley26128d22004-11-14 16:43:54 +00005250#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005251 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005252#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005253 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005254#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005255 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005256 }
5257 }
Simon Kelley849a8352006-06-09 21:02:31 +01005258
Neil Jerram3bd4c472018-01-18 22:49:38 +00005259 free(argbuf);
5260
Simon Kelley849a8352006-06-09 21:02:31 +01005261 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005262 {
Petr Menšík59e47032018-11-02 22:39:39 +00005263 one_file(conffile, 0);
5264 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005265 }
Petr Menšík59e47032018-11-02 22:39:39 +00005266 else
5267 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005268
Simon Kelley1a6bca82008-07-11 11:11:42 +01005269 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005270 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005271 {
5272 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005273 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005274 if (!(tmp->flags & SERV_HAS_SOURCE))
5275 {
5276 if (tmp->source_addr.sa.sa_family == AF_INET)
5277 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005278 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5279 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005280 }
5281 }
5282
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005283 if (daemon->host_records)
5284 {
5285 struct host_record *hr;
5286
5287 for (hr = daemon->host_records; hr; hr = hr->next)
5288 if (hr->ttl == -1)
5289 hr->ttl = daemon->local_ttl;
5290 }
5291
5292 if (daemon->cnames)
5293 {
Simon Kelley903df072017-01-19 17:22:00 +00005294 struct cname *cn, *cn2, *cn3;
5295
5296#define NOLOOP 1
5297#define TESTLOOP 2
5298
Simon Kelley157d8cf2019-10-25 17:46:49 +01005299 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005300 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005301 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005302 {
5303 if (cn->ttl == -1)
5304 cn->ttl = daemon->local_ttl;
5305 cn->flag = 0;
5306 cn->targetp = NULL;
5307 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5308 if (hostname_isequal(cn->target, cn2->alias))
5309 {
5310 cn->targetp = cn2;
5311 break;
5312 }
5313 }
5314
5315 /* Find any CNAME loops.*/
5316 for (cn = daemon->cnames; cn; cn = cn->next)
5317 {
5318 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5319 {
5320 if (cn2->flag == NOLOOP)
5321 break;
5322
5323 if (cn2->flag == TESTLOOP)
5324 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5325
5326 cn2->flag = TESTLOOP;
5327 }
5328
5329 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5330 cn3->flag = NOLOOP;
5331 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005332 }
5333
Simon Kelley3be34542004-09-11 19:12:13 +01005334 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005335 {
5336 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005337 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005338 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005339 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005340 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005341 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005342 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005343
5344 /* create default, if not specified */
5345 if (daemon->authserver && !daemon->hostmaster)
5346 {
5347 strcpy(buff, "hostmaster.");
5348 strcat(buff, daemon->authserver);
5349 daemon->hostmaster = opt_string_alloc(buff);
5350 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005351
5352 if (!daemon->dhcp_pxe_vendors)
5353 {
5354 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5355 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5356 daemon->dhcp_pxe_vendors->next = NULL;
5357 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005358
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005359 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005360 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005361 {
Simon Kelley0a852542005-03-23 20:28:59 +00005362 struct mx_srv_record *mx;
5363
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005364 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005365 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005366
Simon Kelley0a852542005-03-23 20:28:59 +00005367 for (mx = daemon->mxnames; mx; mx = mx->next)
5368 if (!mx->issrv && hostname_isequal(mx->name, buff))
5369 break;
5370
Simon Kelley28866e92011-02-14 20:19:14 +00005371 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005372 {
Simon Kelley824af852008-02-12 20:43:05 +00005373 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005374 mx->next = daemon->mxnames;
5375 mx->issrv = 0;
5376 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005377 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005378 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005379 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005380
Simon Kelley3be34542004-09-11 19:12:13 +01005381 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005382 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005383
5384 for (mx = daemon->mxnames; mx; mx = mx->next)
5385 if (!mx->issrv && !mx->target)
5386 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005387 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005388
Simon Kelley28866e92011-02-14 20:19:14 +00005389 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005390 daemon->resolv_files &&
5391 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005392 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005393 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005394
Simon Kelley28866e92011-02-14 20:19:14 +00005395 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005396 {
5397 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005398 FILE *f;
5399
Simon Kelley28866e92011-02-14 20:19:14 +00005400 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005401 !daemon->resolv_files ||
5402 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005403 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005404
Simon Kelley3be34542004-09-11 19:12:13 +01005405 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005406 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005407
5408 while ((line = fgets(buff, MAXDNAME, f)))
5409 {
5410 char *token = strtok(line, " \t\n\r");
5411
5412 if (!token || strcmp(token, "search") != 0)
5413 continue;
5414
5415 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005416 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005417 break;
5418 }
Simon Kelley3be34542004-09-11 19:12:13 +01005419
Simon Kelleyde379512004-06-22 20:23:33 +01005420 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005421
Simon Kelley3be34542004-09-11 19:12:13 +01005422 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005423 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005424 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005425
5426 if (daemon->domain_suffix)
5427 {
5428 /* add domain for any srv record without one. */
5429 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005430
Simon Kelley3d8df262005-08-29 12:19:27 +01005431 for (srv = daemon->mxnames; srv; srv = srv->next)
5432 if (srv->issrv &&
5433 strchr(srv->name, '.') &&
5434 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5435 {
5436 strcpy(buff, srv->name);
5437 strcat(buff, ".");
5438 strcat(buff, daemon->domain_suffix);
5439 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005440 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005441 }
5442 }
Simon Kelley28866e92011-02-14 20:19:14 +00005443 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005444 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005445
Simon Kelleyc8a80482014-03-05 14:29:54 +00005446 /* If there's access-control config, then ignore --local-service, it's intended
5447 as a system default to keep otherwise unconfigured installations safe. */
5448 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5449 reset_option_bool(OPT_LOCAL_SERVICE);
5450
Simon Kelley7622fc02009-06-04 20:32:05 +01005451 if (testmode)
5452 {
5453 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5454 exit(0);
5455 }
Simon Kelley849a8352006-06-09 21:02:31 +01005456}