blob: da94de8f08351d9e3cea2dea1f00431916968b3d [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
Kyle Swensonba77fe82021-11-19 09:33:46 -0700180#define LOPT_EDNS_RESTRICT 366
Kyle Swenson545712c2021-11-17 12:25:04 -0700181/* CRADLEPOINT */
Tarun Kundu024baf12022-06-28 10:56:47 -0700182#define LOPT_RA_ADV_DISABLE 367
Simon Kelley849a8352006-06-09 21:02:31 +0100183#ifdef HAVE_GETOPT_LONG
184static const struct option opts[] =
185#else
186static const struct myoption opts[] =
187#endif
188 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100189 { "version", 0, 0, 'v' },
190 { "no-hosts", 0, 0, 'h' },
191 { "no-poll", 0, 0, 'n' },
192 { "help", 0, 0, 'w' },
193 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000194 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100195 { "user", 2, 0, 'u' },
196 { "group", 2, 0, 'g' },
197 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000198 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100199 { "mx-host", 1, 0, 'm' },
200 { "mx-target", 1, 0, 't' },
201 { "cache-size", 2, 0, 'c' },
202 { "port", 1, 0, 'p' },
203 { "dhcp-leasefile", 2, 0, 'l' },
204 { "dhcp-lease", 1, 0, 'l' },
205 { "dhcp-host", 1, 0, 'G' },
206 { "dhcp-range", 1, 0, 'F' },
207 { "dhcp-option", 1, 0, 'O' },
208 { "dhcp-boot", 1, 0, 'M' },
209 { "domain", 1, 0, 's' },
210 { "domain-suffix", 1, 0, 's' },
211 { "interface", 1, 0, 'i' },
212 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000213 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 { "bogus-priv", 0, 0, 'b' },
215 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000216 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100217 { "selfmx", 0, 0, 'e' },
218 { "filterwin2k", 0, 0, 'f' },
219 { "pid-file", 2, 0, 'x' },
220 { "strict-order", 0, 0, 'o' },
221 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000222 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100223 { "local", 1, 0, LOPT_LOCAL },
224 { "address", 1, 0, 'A' },
225 { "conf-file", 2, 0, 'C' },
226 { "no-resolv", 0, 0, 'R' },
227 { "expand-hosts", 0, 0, 'E' },
228 { "localmx", 0, 0, 'L' },
229 { "local-ttl", 1, 0, 'T' },
230 { "no-negcache", 0, 0, 'N' },
231 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000232 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100233 { "query-port", 1, 0, 'Q' },
234 { "except-interface", 1, 0, 'I' },
235 { "no-dhcp-interface", 1, 0, '2' },
236 { "domain-needed", 0, 0, 'D' },
237 { "dhcp-lease-max", 1, 0, 'X' },
238 { "bind-interfaces", 0, 0, 'z' },
239 { "read-ethers", 0, 0, 'Z' },
240 { "alias", 1, 0, 'V' },
241 { "dhcp-vendorclass", 1, 0, 'U' },
242 { "dhcp-userclass", 1, 0, 'j' },
243 { "dhcp-ignore", 1, 0, 'J' },
244 { "edns-packet-max", 1, 0, 'P' },
245 { "keep-in-foreground", 0, 0, 'k' },
246 { "dhcp-authoritative", 0, 0, 'K' },
247 { "srv-host", 1, 0, 'W' },
248 { "localise-queries", 0, 0, 'y' },
249 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100250 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100251 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100252 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100253 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100254 { "bootp-dynamic", 2, 0, '3' },
255 { "dhcp-mac", 1, 0, '4' },
256 { "no-ping", 0, 0, '5' },
257 { "dhcp-script", 1, 0, '6' },
258 { "conf-dir", 1, 0, '7' },
259 { "log-facility", 1, 0 ,'8' },
260 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000261 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100262 { "dns-forward-max", 1, 0, '0' },
263 { "clear-on-reload", 0, 0, LOPT_RELOAD },
264 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100265 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100266 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100267 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100268 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100269 { "tftp-root", 1, 0, LOPT_PREFIX },
270 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000271 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100272 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000273 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100274 { "ptr-record", 1, 0, LOPT_PTR },
275 { "naptr-record", 1, 0, LOPT_NAPTR },
276 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000277 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100278 { "dhcp-option-force", 1, 0, LOPT_FORCE },
279 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
280 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
281 { "log-async", 2, 0, LOPT_MAX_LOGS },
282 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
283 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
284 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800285 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100286 { "interface-name", 1, 0, LOPT_INTNAME },
287 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
288 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000289 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000290 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100291 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
292 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
293 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100294 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100295 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100296 { "dhcp-match", 1, 0, LOPT_MATCH },
297 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100298 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100299 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100300 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000301 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100302 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100303 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
304 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
305 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000306 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100307 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
308 { "cname", 1, 0, LOPT_CNAME },
309 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
310 { "pxe-service", 1, 0, LOPT_PXE_SERV },
311 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100312 { "tag-if", 1, 0, LOPT_TAG_IF },
313 { "dhcp-proxy", 2, 0, LOPT_PROXY },
314 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
315 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000316 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100317 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000318 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000319 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100320 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
321 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000322 { "dhcp-client-update", 0, 0, LOPT_FQDN },
323 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000324 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000325 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000326 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100327 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000328 { "auth-zone", 1, 0, LOPT_AUTHZONE },
329 { "auth-server", 1, 0, LOPT_AUTHSERV },
330 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
331 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000332 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000333 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000334 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100335 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200336 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000337 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000338 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100339 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000340 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000341 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100342 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100343 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100344 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
345 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
346 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100347 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000348 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000349 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100350 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000351 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100352 { "dumpfile", 1, 0, LOPT_DUMPFILE },
353 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200354 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000355 { "dynamic-host", 1, 0, LOPT_DYNHOST },
Simon Kelleyb260d222021-03-12 21:57:57 +0000356 { "log-debug", 0, 0, LOPT_LOG_DEBUG },
Kyle Swenson545712c2021-11-17 12:25:04 -0700357/* CRADLEPOINT */
358 { "edns-option", 1, 0, LOPT_EDNS_OPTION },
359 { "ssid-map", 1, 0, LOPT_SSID_MAP },
Kyle Swensonba77fe82021-11-19 09:33:46 -0700360 { "edns-restrict", 0, 0, LOPT_EDNS_RESTRICT },
Tarun Kundu024baf12022-06-28 10:56:47 -0700361 { "ra-adv-disable", 1, 0, LOPT_RA_ADV_DISABLE },
362/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +0100363 { NULL, 0, 0, 0 }
364 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000365
Simon Kelley28866e92011-02-14 20:19:14 +0000366
367#define ARG_DUP OPT_LAST
368#define ARG_ONE OPT_LAST + 1
369#define ARG_USED_CL OPT_LAST + 2
370#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000371
Simon Kelley1a6bca82008-07-11 11:11:42 +0100372static struct {
373 int opt;
374 unsigned int rept;
375 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000376 char * const desc;
377 char * const arg;
378} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000379 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
380 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100381 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000382 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
383 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
384 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100385 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
386 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
387 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
388 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
389 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000390 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
391 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100392 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000393 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
394 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000395 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000396 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100397 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000399 { '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 +0000400 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000401 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
402 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100403 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
404 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
405 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
406 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800407 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100408 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
409 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
411 { '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 +0000412 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100413 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000414 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100415 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
416 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
417 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
418 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
419 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
420 { 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 +0000421 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
422 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000423 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000424 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100425 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000426 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000427 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000428 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000429 { 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 +0000430 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000431 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000432 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
433 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
434 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
435 { 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 +0000436 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
437 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000438 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100439 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100440 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000441 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
442 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100443 { '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 +0000444 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
445 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100446 { '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 +0000447 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
448 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
449 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100450 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
451 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100452 { '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 +0100453 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000454 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100455 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
456 { '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 +0000457 { 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 +0000458 { 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 +0100459 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000460 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
461 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
462 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000463 { 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 +0000464 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000465 { '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 +0100466 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000467 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100468 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100469 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100470 { 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 +0100471 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100472 { 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 +0100473 { 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 +0100474 { 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 +0100475 { 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 +0000476 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000477 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100478 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100479 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100480 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000481 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100482 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000483 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100484 { 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 +0100485 { 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 +0000486 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100487 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100488 { 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 +0100489 { 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 +0100490 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100491 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
492 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000493 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000494 { 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 +0000495 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
496 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100497 { 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 +0000498 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100499 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
500 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
501 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000502 { 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 +0100503 { 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 +0000504 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100505 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100506 { 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 +0200507 { 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 +0100508 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000509 { 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 +0000510 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000511 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000512 { 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 +0000513 { 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 +0100514 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100515 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000516 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000517 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000518 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000519 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000520 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000521 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
522 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100523 { 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 +0100524 { 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 +0000525 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000526 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000527 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100528 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000529 { 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 +0000530 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Tarun Kundu41aa4882022-07-05 10:44:13 -0700531 { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>][,<reachable_time>][,<hop_limit>][,<retrans_time>]", gettext_noop("Set MTU, priority, resend-interval, router-lifetime, reachable_time, hop_limit and retrans_time"), NULL },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100532 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
533 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
534 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelleyb260d222021-03-12 21:57:57 +0000535 { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000536 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
537 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000538 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000539 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100540 { 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 +0000541 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100542 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
543 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000544 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Tarun Kundu024baf12022-06-28 10:56:47 -0700545 { LOPT_RA_ADV_DISABLE, ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to disable RA. This parameter should be subset of interfaces and should be provided after interfaces option"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100546 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000547};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000548
Josh Soref730c6742017-02-06 16:14:04 +0000549/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100550 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 +0100551 following sequence so that they map to themselves: it is therefore possible to call
552 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000553 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100554 couple of other places.
555 Note that space is included here so that
556 --dhcp-option=3, string
557 has five characters, whilst
558 --dhcp-option=3," string"
559 has six.
560*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100561
Simon Kelleyf2621c72007-04-29 19:47:21 +0100562static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100563
564static char hide_meta(char c)
565{
566 unsigned int i;
567
568 for (i = 0; i < (sizeof(meta) - 1); i++)
569 if (c == meta[i])
570 return (char)i;
571
572 return c;
573}
574
575static char unhide_meta(char cr)
576{
577 unsigned int c = cr;
578
579 if (c < (sizeof(meta) - 1))
580 cr = meta[c];
581
582 return cr;
583}
584
585static void unhide_metas(char *cp)
586{
587 if (cp)
588 for(; *cp; cp++)
589 *cp = unhide_meta(*cp);
590}
591
Simon Kelley824af852008-02-12 20:43:05 +0000592static void *opt_malloc(size_t size)
593{
594 void *ret;
595
596 if (mem_recover)
597 {
598 ret = whine_malloc(size);
599 if (!ret)
600 longjmp(mem_jmp, 1);
601 }
602 else
603 ret = safe_malloc(size);
604
605 return ret;
606}
607
Petr Menšík59e47032018-11-02 22:39:39 +0000608static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100609{
610 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000611 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100612
Petr Menšík59e47032018-11-02 22:39:39 +0000613 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100614 {
Petr Menšík59e47032018-11-02 22:39:39 +0000615 ret = opt_malloc(len+1);
616 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100617
618 /* restore hidden metachars */
619 unhide_metas(ret);
620 }
621
622 return ret;
623}
624
Simon Kelley3d8df262005-08-29 12:19:27 +0100625
Simon Kelleyf2621c72007-04-29 19:47:21 +0100626/* find next comma, split string with zero and eliminate spaces.
627 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000628
629static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100630{
631 char *comma, *p;
632
Simon Kelley73a08a22009-02-05 20:28:08 +0000633 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100634 return NULL;
635
636 p = comma;
637 *comma = ' ';
638
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100639 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100640
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100641 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100642 *p = 0;
643
644 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100645}
646
Simon Kelley73a08a22009-02-05 20:28:08 +0000647static char *split(char *s)
648{
649 return split_chr(s, ',');
650}
651
Simon Kelley1f15b812009-10-13 17:49:32 +0100652static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100653{
Simon Kelley1f15b812009-10-13 17:49:32 +0100654 char *ret;
655 int nomem;
656
Simon Kelley3d8df262005-08-29 12:19:27 +0100657 if (!s)
658 return 0;
659
660 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100661 if (!(ret = canonicalise(s, &nomem)) && nomem)
662 {
663 if (mem_recover)
664 longjmp(mem_jmp, 1);
665 else
666 die(_("could not get memory"), NULL, EC_NOMEM);
667 }
668
669 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100670}
671
672static int atoi_check(char *a, int *res)
673{
674 char *p;
675
676 if (!a)
677 return 0;
678
679 unhide_metas(a);
680
681 for (p = a; *p; p++)
682 if (*p < '0' || *p > '9')
683 return 0;
684
685 *res = atoi(a);
686 return 1;
687}
688
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100689static int atoi_check16(char *a, int *res)
690{
691 if (!(atoi_check(a, res)) ||
692 *res < 0 ||
693 *res > 0xffff)
694 return 0;
695
696 return 1;
697}
Simon Kelleyee415862014-02-11 11:07:22 +0000698
Simon Kelleyde73a492014-02-17 21:43:27 +0000699#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000700static int atoi_check8(char *a, int *res)
701{
702 if (!(atoi_check(a, res)) ||
703 *res < 0 ||
704 *res > 0xff)
705 return 0;
706
707 return 1;
708}
Simon Kelleyde73a492014-02-17 21:43:27 +0000709#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100710
711#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000712static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000713{
Simon Kelley824af852008-02-12 20:43:05 +0000714 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000715
716 if (txt)
717 {
718 size_t len = strlen(txt);
719 r->txt = opt_malloc(len+1);
720 r->len = len+1;
721 *(r->txt) = len;
722 memcpy((r->txt)+1, txt, len);
723 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100724
Simon Kelleyfec216d2014-03-27 20:54:34 +0000725 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000726 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000727 r->next = daemon->txt;
728 daemon->txt = r;
729 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000730}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100731#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000732
Simon Kelley849a8352006-06-09 21:02:31 +0100733static void do_usage(void)
734{
735 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000736 int i, j;
737
738 struct {
739 char handle;
740 int val;
741 } tab[] = {
742 { '$', CACHESIZ },
743 { '*', EDNS_PKTSZ },
744 { '&', MAXLEASES },
745 { '!', FTABSIZ },
746 { '#', TFTP_MAX_CONNECTIONS },
747 { '\0', 0 }
748 };
Simon Kelley849a8352006-06-09 21:02:31 +0100749
750 printf(_("Usage: dnsmasq [options]\n\n"));
751#ifndef HAVE_GETOPT_LONG
752 printf(_("Use short options only on the command line.\n"));
753#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100754 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100755
Simon Kelley1a6bca82008-07-11 11:11:42 +0100756 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100757 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100758 char *desc = usage[i].flagdesc;
759 char *eq = "=";
760
761 if (!desc || *desc == '[')
762 eq = "";
763
764 if (!desc)
765 desc = "";
766
767 for ( j = 0; opts[j].name; j++)
768 if (opts[j].val == usage[i].opt)
769 break;
770 if (usage[i].opt < 256)
771 sprintf(buff, "-%c, ", usage[i].opt);
772 else
773 sprintf(buff, " ");
774
775 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100776 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100777
Simon Kelley849a8352006-06-09 21:02:31 +0100778 if (usage[i].arg)
779 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000780 strcpy(buff, usage[i].arg);
781 for (j = 0; tab[j].handle; j++)
782 if (tab[j].handle == *(usage[i].arg))
783 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100784 }
Simon Kelley849a8352006-06-09 21:02:31 +0100785 printf(_(usage[i].desc), buff);
786 printf("\n");
787 }
788}
789
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100790#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000791#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
792#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100793
Ed Bardsleya7369be2015-08-05 21:17:18 +0100794static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
795{
796 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
797 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100798 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
799 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100800 else
801 return _("bad address");
802
803 return NULL;
804}
805
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100806char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
807{
808 int source_port = 0, serv_port = NAMESERVER_PORT;
809 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000810 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100811 int scope_index = 0;
812 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100813
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000814 if (!arg || strlen(arg) == 0)
815 {
816 *flags |= SERV_NO_ADDR;
817 *interface = 0;
818 return NULL;
819 }
820
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100821 if ((source = split_chr(arg, '@')) && /* is there a source. */
822 (portno = split_chr(source, '#')) &&
823 !atoi_check16(portno, &source_port))
824 return _("bad port");
825
826 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
827 !atoi_check16(portno, &serv_port))
828 return _("bad port");
829
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100830 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100831
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000832 if (source) {
833 interface_opt = split_chr(source, '@');
834
835 if (interface_opt)
836 {
837#if defined(SO_BINDTODEVICE)
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000838 safe_strncpy(interface, source, IF_NAMESIZE);
839 source = interface_opt;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000840#else
841 return _("interface binding not supported");
842#endif
843 }
844 }
845
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100846 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100847 {
848 addr->in.sin_port = htons(serv_port);
849 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
850#ifdef HAVE_SOCKADDR_SA_LEN
851 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
852#endif
853 source_addr->in.sin_addr.s_addr = INADDR_ANY;
854 source_addr->in.sin_port = htons(daemon->query_port);
855
856 if (source)
857 {
858 if (flags)
859 *flags |= SERV_HAS_SOURCE;
860 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100861 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100862 {
863#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000864 if (interface_opt)
865 return _("interface can only be specified once");
866
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100867 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200868 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100869#else
870 return _("interface binding not supported");
871#endif
872 }
873 }
874 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100875 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
876 {
877 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
878 return _("bad interface name");
879
880 addr->in6.sin6_port = htons(serv_port);
881 addr->in6.sin6_scope_id = scope_index;
882 source_addr->in6.sin6_addr = in6addr_any;
883 source_addr->in6.sin6_port = htons(daemon->query_port);
884 source_addr->in6.sin6_scope_id = 0;
885 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
886 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
887#ifdef HAVE_SOCKADDR_SA_LEN
888 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
889#endif
890 if (source)
891 {
892 if (flags)
893 *flags |= SERV_HAS_SOURCE;
894 source_addr->in6.sin6_port = htons(source_port);
895 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
896 {
897#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000898 if (interface_opt)
899 return _("interface can only be specified once");
900
901 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200902 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100903#else
904 return _("interface binding not supported");
905#endif
906 }
907 }
908 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100909 else
910 return _("bad address");
911
912 return NULL;
913}
914
Simon Kelleyde73a492014-02-17 21:43:27 +0000915static struct server *add_rev4(struct in_addr addr, int msize)
916{
917 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000918 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000919 char *p;
920
921 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000922 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
923
924 switch (msize)
925 {
926 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100927 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000928 /* fall through */
929 case 24:
930 p += sprintf(p, "%d.", (a >> 8) & 0xff);
931 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000932 case 16:
933 p += sprintf(p, "%d.", (a >> 16) & 0xff);
934 /* fall through */
935 case 8:
936 p += sprintf(p, "%d.", (a >> 24) & 0xff);
937 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000938 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000939 free(serv->domain);
940 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000941 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000942 }
943
944 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000945
946 serv->flags = SERV_HAS_DOMAIN;
947 serv->next = daemon->servers;
948 daemon->servers = serv;
949
950 return serv;
951
952}
953
954static struct server *add_rev6(struct in6_addr *addr, int msize)
955{
956 struct server *serv = opt_malloc(sizeof(struct server));
957 char *p;
958 int i;
959
960 memset(serv, 0, sizeof(struct server));
961 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
962
963 for (i = msize-1; i >= 0; i -= 4)
964 {
965 int dig = ((unsigned char *)addr)[i>>3];
966 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
967 }
968 p += sprintf(p, "ip6.arpa");
969
970 serv->flags = SERV_HAS_DOMAIN;
971 serv->next = daemon->servers;
972 daemon->servers = serv;
973
974 return serv;
975}
976
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000977#ifdef HAVE_DHCP
978
979static int is_tag_prefix(char *arg)
980{
981 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
982 return 1;
983
984 return 0;
985}
986
987static char *set_prefix(char *arg)
988{
989 if (strstr(arg, "set:") == arg)
990 return arg+4;
991
992 return arg;
993}
994
Simon Kelley52ec7832020-02-07 21:05:54 +0000995static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000996{
997 struct dhcp_netid *tt;
998 tt = opt_malloc(sizeof (struct dhcp_netid));
999 tt->net = opt_string_alloc(net);
1000 tt->next = next;
1001 return tt;
1002}
1003
1004static void dhcp_netid_free(struct dhcp_netid *nid)
1005{
1006 while (nid)
1007 {
1008 struct dhcp_netid *tmp = nid;
1009 nid = nid->next;
1010 free(tmp->net);
1011 free(tmp);
1012 }
1013}
1014
1015/* Parse one or more tag:s before parameters.
1016 * Moves arg to the end of tags. */
1017static struct dhcp_netid * dhcp_tags(char **arg)
1018{
1019 struct dhcp_netid *id = NULL;
1020
1021 while (is_tag_prefix(*arg))
1022 {
1023 char *comma = split(*arg);
1024 id = dhcp_netid_create((*arg)+4, id);
1025 *arg = comma;
1026 };
1027 if (!*arg)
1028 {
1029 dhcp_netid_free(id);
1030 id = NULL;
1031 }
1032 return id;
1033}
1034
1035static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1036{
1037 while (netid)
1038 {
1039 struct dhcp_netid_list *tmplist = netid;
1040 netid = netid->next;
1041 dhcp_netid_free(tmplist->list);
1042 free(tmplist);
1043 }
1044}
1045
1046static void dhcp_config_free(struct dhcp_config *config)
1047{
1048 if (config)
1049 {
1050 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001051
Petr Menšík59e47032018-11-02 22:39:39 +00001052 while (hwaddr)
1053 {
1054 struct hwaddr_config *tmp = hwaddr;
1055 hwaddr = hwaddr->next;
1056 free(tmp);
1057 }
Simon Kelley137286e2020-02-06 22:09:30 +00001058
Petr Menšík59e47032018-11-02 22:39:39 +00001059 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001060 dhcp_netid_free(config->filter);
1061
Petr Menšík59e47032018-11-02 22:39:39 +00001062 if (config->flags & CONFIG_CLID)
1063 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001064
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001065#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001066 if (config->flags & CONFIG_ADDR6)
1067 {
1068 struct addrlist *addr, *tmp;
1069
1070 for (addr = config->addr6; addr; addr = tmp)
1071 {
1072 tmp = addr->next;
1073 free(addr);
1074 }
1075 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001076#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001077
Petr Menšík59e47032018-11-02 22:39:39 +00001078 free(config);
1079 }
1080}
1081
1082static void dhcp_context_free(struct dhcp_context *ctx)
1083{
1084 if (ctx)
1085 {
1086 dhcp_netid_free(ctx->filter);
1087 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001088#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001089 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001090#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001091 free(ctx);
1092 }
1093}
1094
1095static void dhcp_opt_free(struct dhcp_opt *opt)
1096{
1097 if (opt->flags & DHOPT_VENDOR)
1098 free(opt->u.vendor_class);
1099 dhcp_netid_free(opt->netid);
1100 free(opt->val);
1101 free(opt);
1102}
1103
1104
Simon Kelley832af0b2007-01-21 20:01:28 +00001105/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001106static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001107{
Simon Kelley824af852008-02-12 20:43:05 +00001108 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001109 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001110 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001111 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001112 u16 opt_len = 0;
1113 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001114 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001115
1116 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001117 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001118 new->netid = NULL;
1119 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001120 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001121
Simon Kelleyf2621c72007-04-29 19:47:21 +01001122 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001123 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001124 comma = split(arg);
1125
1126 for (cp = arg; *cp; cp++)
1127 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001128 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001129
1130 if (!*cp)
1131 {
1132 new->opt = atoi(arg);
1133 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001134 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001135 break;
1136 }
1137
1138 if (strstr(arg, "option:") == arg)
1139 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001140 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1141 {
1142 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1143 /* option:<optname> must follow tag and vendor string. */
1144 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1145 option_ok = 1;
1146 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001147 break;
1148 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001149#ifdef HAVE_DHCP6
1150 else if (strstr(arg, "option6:") == arg)
1151 {
1152 for (cp = arg+8; *cp; cp++)
1153 if (*cp < '0' || *cp > '9')
1154 break;
1155
1156 if (!*cp)
1157 {
1158 new->opt = atoi(arg+8);
1159 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001160 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001161 }
1162 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001163 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001164 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1165 {
1166 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1167 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1168 option_ok = 1;
1169 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001170 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001171 /* option6:<opt>|<optname> must follow tag and vendor string. */
1172 is6 = 1;
1173 break;
1174 }
1175#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001176 else if (strstr(arg, "vendor:") == arg)
1177 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001178 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1179 new->flags |= DHOPT_VENDOR;
1180 }
1181 else if (strstr(arg, "encap:") == arg)
1182 {
1183 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001184 new->flags |= DHOPT_ENCAPSULATE;
1185 }
Simon Kelley316e2732010-01-22 20:16:09 +00001186 else if (strstr(arg, "vi-encap:") == arg)
1187 {
1188 new->u.encap = atoi(arg+9);
1189 new->flags |= DHOPT_RFC3925;
1190 if (flags == DHOPT_MATCH)
1191 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001192 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001193 break;
1194 }
1195 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001196 else
1197 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001198 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001199 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1200 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001201 }
1202
1203 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001204 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001205
1206#ifdef HAVE_DHCP6
1207 if (is6)
1208 {
1209 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001210 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001211
1212 if (opt_len == 0 &&
1213 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001214 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001215 }
1216 else
1217#endif
1218 if (opt_len == 0 &&
1219 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001220 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001221
Simon Kelley316e2732010-01-22 20:16:09 +00001222 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001223 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001224 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001225
1226 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001227 {
1228 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001229 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001230 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001231 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001232 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001233 dots = 0;
1234 for (cp = comma; (c = *cp); cp++)
1235 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001236 {
1237 addrs++;
1238 is_dec = is_hex = 0;
1239 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001240 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001241 {
1242 digs++;
1243 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001244 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001245 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001246 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001247 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001248 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001249 if (cp == comma) /* leading / means a pathname */
1250 is_addr = 0;
1251 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001252 else if (c == '.')
1253 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001254 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001255 dots++;
1256 }
1257 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001258 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001259 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001260 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001261 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001262 {
1263 is_addr = 0;
1264 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001265 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001266 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001267 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001268 *cp = 0;
1269 }
1270 else
1271 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001272 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001273 (c >='a' && c <= 'f') ||
1274 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001275 {
1276 is_hex = 0;
1277 if (c != '[' && c != ']')
1278 is_addr6 = 0;
1279 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001280 }
Simon Kelley28866e92011-02-14 20:19:14 +00001281 else
1282 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001283
Simon Kelley28866e92011-02-14 20:19:14 +00001284 if (!found_dig)
1285 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001286
1287 if (!found_colon)
1288 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001289
1290#ifdef HAVE_DHCP6
1291 /* NTP server option takes hex, addresses or FQDN */
1292 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1293 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1294#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001295
Simon Kelleyf2621c72007-04-29 19:47:21 +01001296 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001297 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001298 {
1299 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001300
1301 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001302 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001303
1304 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001305 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001306 }
Simon Kelley28866e92011-02-14 20:19:14 +00001307 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001308 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1309 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001310
1311 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1312 {
1313 int val, fac = 1;
1314
1315 switch (comma[strlen(comma) - 1])
1316 {
Simon Kelley42243212012-07-20 15:19:18 +01001317 case 'w':
1318 case 'W':
1319 fac *= 7;
1320 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001321 case 'd':
1322 case 'D':
1323 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001324 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001325 case 'h':
1326 case 'H':
1327 fac *= 60;
1328 /* fall through */
1329 case 'm':
1330 case 'M':
1331 fac *= 60;
1332 /* fall through */
1333 case 's':
1334 case 'S':
1335 comma[strlen(comma) - 1] = 0;
1336 }
1337
1338 new->len = 4;
1339 new->val = opt_malloc(4);
1340 val = atoi(comma);
1341 *((int *)new->val) = htonl(val * fac);
1342 }
1343 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001344 {
1345 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001346 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001347 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1348 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001349 }
1350 else if (is_dec)
1351 {
1352 int i, val = atoi(comma);
1353 /* assume numeric arg is 1 byte except for
1354 options where it is known otherwise.
1355 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001356 if (opt_len != 0)
1357 new->len = opt_len;
1358 else if (val & 0xffff0000)
1359 new->len = 4;
1360 else if (val & 0xff00)
1361 new->len = 2;
1362 else
1363 new->len = 1;
1364
Simon Kelley832af0b2007-01-21 20:01:28 +00001365 if (lenchar == 'b')
1366 new->len = 1;
1367 else if (lenchar == 's')
1368 new->len = 2;
1369 else if (lenchar == 'i')
1370 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001371
Simon Kelley824af852008-02-12 20:43:05 +00001372 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001373 for (i=0; i<new->len; i++)
1374 new->val[i] = val>>((new->len - i - 1)*8);
1375 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001376 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001377 {
1378 struct in_addr in;
1379 unsigned char *op;
1380 char *slash;
1381 /* max length of address/subnet descriptor is five bytes,
1382 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001383 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001384 new->flags |= DHOPT_ADDR;
1385
Simon Kelley572b41e2011-02-18 18:11:18 +00001386 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1387 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001388 {
Simon Kelley6b010842007-02-12 20:32:07 +00001389 *(op++) = 1; /* RFC 3361 "enc byte" */
1390 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001391 }
1392 while (addrs--)
1393 {
1394 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001395 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001396 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001397 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001398 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001399 if (!slash)
1400 {
1401 memcpy(op, &in, INADDRSZ);
1402 op += INADDRSZ;
1403 }
1404 else
1405 {
1406 unsigned char *p = (unsigned char *)&in;
1407 int netsize = atoi(slash);
1408 *op++ = netsize;
1409 if (netsize > 0)
1410 *op++ = *p++;
1411 if (netsize > 8)
1412 *op++ = *p++;
1413 if (netsize > 16)
1414 *op++ = *p++;
1415 if (netsize > 24)
1416 *op++ = *p++;
1417 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1418 }
1419 }
1420 new->len = op - new->val;
1421 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001422 else if (is_addr6 && is6)
1423 {
1424 unsigned char *op;
1425 new->val = op = opt_malloc(16 * addrs);
1426 new->flags |= DHOPT_ADDR6;
1427 while (addrs--)
1428 {
1429 cp = comma;
1430 comma = split(cp);
1431
1432 /* check for [1234::7] */
1433 if (*cp == '[')
1434 cp++;
1435 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1436 cp[strlen(cp)-1] = 0;
1437
1438 if (inet_pton(AF_INET6, cp, op))
1439 {
1440 op += IN6ADDRSZ;
1441 continue;
1442 }
Petr Menšík59e47032018-11-02 22:39:39 +00001443
1444 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001445 }
1446 new->len = op - new->val;
1447 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001448 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001449 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001450 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001451 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001452 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001453 {
1454 /* dns search, RFC 3397, or SIP, RFC 3361 */
1455 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001456 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001457 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001458 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001459
1460 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001461 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001462
1463 while (arg && *arg)
1464 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001465 char *in, *dom = NULL;
1466 size_t domlen = 1;
1467 /* Allow "." as an empty domain */
1468 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001469 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001470 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001471 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001472
Simon Kelleyc52e1892010-06-07 22:01:39 +01001473 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001474 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001475
1476 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001477 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001478 {
1479 memcpy(newp, m, header_size + len);
1480 free(m);
1481 }
Simon Kelley824af852008-02-12 20:43:05 +00001482 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001483 p = m + header_size;
1484 q = p + len;
1485
1486 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001487 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001488 {
1489 unsigned char *cp = q++;
1490 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001491 for (j = 0; *in && (*in != '.'); in++, j++)
1492 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001493 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001494 if (*in)
1495 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001496 }
1497 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001498 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001499
Simon Kelley832af0b2007-01-21 20:01:28 +00001500 /* Now tail-compress using earlier names. */
1501 newlen = q - p;
1502 for (tail = p + len; *tail; tail += (*tail) + 1)
1503 for (r = p; r - p < (int)len; r += (*r) + 1)
1504 if (strcmp((char *)r, (char *)tail) == 0)
1505 {
1506 PUTSHORT((r - p) | 0xc000, tail);
1507 newlen = tail - p;
1508 goto end;
1509 }
1510 end:
1511 len = newlen;
1512
1513 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001514 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001515 }
1516
1517 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001518 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001519 m[0] = 0;
1520 new->len = (int) len + header_size;
1521 new->val = m;
1522 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001523#ifdef HAVE_DHCP6
1524 else if (comma && (opt_len & OT_CSTRING))
1525 {
1526 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001527 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001528 unsigned char *p, *newp;
1529
Simon Kelley40ef23b2012-03-13 21:59:28 +00001530 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001531 if (comma[i] == ',')
1532 commas++;
1533
1534 newp = opt_malloc(strlen(comma)+(2*commas));
1535 p = newp;
1536 arg = comma;
1537 comma = split(arg);
1538
1539 while (arg && *arg)
1540 {
1541 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001542 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001543 PUTSHORT(len, p);
1544 memcpy(p, arg, len);
1545 p += len;
1546
1547 arg = comma;
1548 comma = split(arg);
1549 }
1550
1551 new->val = newp;
1552 new->len = p - newp;
1553 }
1554 else if (comma && (opt_len & OT_RFC1035_NAME))
1555 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001556 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001557 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001558 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001559 arg = comma;
1560 comma = split(arg);
1561
1562 while (arg && *arg)
1563 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001564 char *dom = canonicalise_opt(arg);
1565 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001566 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001567
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001568 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001569
1570 if (p)
1571 {
1572 memcpy(newp, p, len);
1573 free(p);
1574 }
1575
1576 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001577 q = p + len;
1578 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001579 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001580 if (is6 && new->opt == OPTION6_NTP_SERVER)
1581 {
1582 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1583 PUTSHORT(end - q - 2, q);
1584 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001585 len = end - p;
1586 free(dom);
1587
Simon Kelley4cb1b322012-02-06 14:30:41 +00001588 arg = comma;
1589 comma = split(arg);
1590 }
1591
Simon Kelley18f0fb02012-03-31 21:18:55 +01001592 new->val = p;
1593 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001594 }
1595#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001596 else
1597 {
1598 new->len = strlen(comma);
1599 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001600 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001601 new->flags |= DHOPT_STRING;
1602 }
1603 }
1604 }
1605
Simon Kelley4cb1b322012-02-06 14:30:41 +00001606 if (!is6 &&
1607 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001608 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001609 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001610 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001611
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001612 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001613 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001614 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1615 !new->netid ||
1616 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001617 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001618
1619 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001620 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001621 new->next = daemon->dhcp_match6;
1622 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001623 }
1624 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001625 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001626 new->next = daemon->dhcp_match;
1627 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001628 }
Simon Kelley824af852008-02-12 20:43:05 +00001629 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001630 else if (is6)
1631 {
1632 new->next = daemon->dhcp_opts6;
1633 daemon->dhcp_opts6 = new;
1634 }
1635 else
1636 {
1637 new->next = daemon->dhcp_opts;
1638 daemon->dhcp_opts = new;
1639 }
1640
1641 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001642on_error:
1643 dhcp_opt_free(new);
1644 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001645}
1646
Simon Kelley7622fc02009-06-04 20:32:05 +01001647#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001648
Simon Kelley28866e92011-02-14 20:19:14 +00001649void set_option_bool(unsigned int opt)
1650{
Petr Menšík24b87602018-10-24 22:30:18 +01001651 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001652}
1653
Simon Kelley2b5bae92012-06-26 16:55:23 +01001654void reset_option_bool(unsigned int opt)
1655{
Petr Menšík24b87602018-10-24 22:30:18 +01001656 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001657}
1658
Petr Menšík59e47032018-11-02 22:39:39 +00001659static void server_list_free(struct server *list)
1660{
1661 while (list)
1662 {
1663 struct server *tmp = list;
1664 list = list->next;
1665 free(tmp);
1666 }
1667}
1668
Simon Kelley7b1eae42014-02-20 13:43:28 +00001669static 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 +01001670{
1671 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001672 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001673
Simon Kelley832af0b2007-01-21 20:01:28 +00001674 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001675 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001676
Simon Kelley1a6bca82008-07-11 11:11:42 +01001677 for (i=0; usage[i].opt != 0; i++)
1678 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001679 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001680 int rept = usage[i].rept;
1681
Simon Kelley28866e92011-02-14 20:19:14 +00001682 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001683 {
1684 /* command line */
1685 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001686 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001687 if (rept == ARG_ONE)
1688 usage[i].rept = ARG_USED_CL;
1689 }
1690 else
1691 {
1692 /* allow file to override command line */
1693 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001694 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001695 if (rept == ARG_USED_CL || rept == ARG_ONE)
1696 usage[i].rept = ARG_USED_FILE;
1697 }
1698
1699 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1700 {
Simon Kelley28866e92011-02-14 20:19:14 +00001701 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001702 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001703 }
1704
1705 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001706 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001707
Simon Kelley849a8352006-06-09 21:02:31 +01001708 switch (option)
1709 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001710 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001711 {
Simon Kelley824af852008-02-12 20:43:05 +00001712 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001713 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001714 {
Simon Kelley28866e92011-02-14 20:19:14 +00001715 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001716 free(file);
1717 }
Simon Kelley849a8352006-06-09 21:02:31 +01001718 break;
1719 }
1720
Simon Kelleyf2621c72007-04-29 19:47:21 +01001721 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001722 {
1723 DIR *dir_stream;
1724 struct dirent *ent;
1725 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001726 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001727 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001728 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001729 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001730
Simon Kelley1f15b812009-10-13 17:49:32 +01001731 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001732 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001733 break;
1734
Simon Kelley1f15b812009-10-13 17:49:32 +01001735 for (arg = comma; arg; arg = comma)
1736 {
1737 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001738 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001739 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001740 li = opt_malloc(sizeof(struct list));
1741 if (*arg == '*')
1742 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001743 /* "*" with no suffix is a no-op */
1744 if (arg[1] == 0)
1745 free(li);
1746 else
1747 {
1748 li->next = match_suffix;
1749 match_suffix = li;
1750 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001751 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001752 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001753 }
1754 else
1755 {
1756 li->next = ignore_suffix;
1757 ignore_suffix = li;
1758 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001759 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001760 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001761 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001762 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001763
Simon Kelley849a8352006-06-09 21:02:31 +01001764 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001765 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001766
Simon Kelley849a8352006-06-09 21:02:31 +01001767 while ((ent = readdir(dir_stream)))
1768 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001769 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001770 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001771
1772 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001773 if (len == 0 ||
1774 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001775 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1776 ent->d_name[0] == '.')
1777 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001778
Simon Kelley3e1551a2014-09-09 21:46:07 +01001779 if (match_suffix)
1780 {
1781 for (li = match_suffix; li; li = li->next)
1782 {
1783 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001784 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001785 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001786 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001787 break;
1788 }
1789 if (!li)
1790 continue;
1791 }
1792
Simon Kelley1f15b812009-10-13 17:49:32 +01001793 for (li = ignore_suffix; li; li = li->next)
1794 {
1795 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001796 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001797 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001798 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001799 break;
1800 }
1801 if (li)
1802 continue;
1803
Simon Kelley824af852008-02-12 20:43:05 +00001804 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001805 strcpy(path, directory);
1806 strcat(path, "/");
1807 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001808
Simon Kelley39595cf2013-02-04 21:40:07 +00001809 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001810 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001811 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001812
Simon Kelley39595cf2013-02-04 21:40:07 +00001813 /* only reg files allowed. */
1814 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001815 {
1816 /* sort files into order. */
1817 struct list **up, *new = opt_malloc(sizeof(struct list));
1818 new->name = path;
1819
1820 for (up = &files, li = files; li; up = &li->next, li = li->next)
1821 if (strcmp(li->name, path) >=0)
1822 break;
1823
1824 new->next = li;
1825 *up = new;
1826 }
1827
Simon Kelley849a8352006-06-09 21:02:31 +01001828 }
Simon Kelleyab538832020-01-10 20:44:48 +00001829
1830 for (li = files; li; li = li->next)
1831 one_file(li->name, 0);
1832
Simon Kelley849a8352006-06-09 21:02:31 +01001833 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001834 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001835 for(; ignore_suffix; ignore_suffix = li)
1836 {
1837 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001838 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001839 free(ignore_suffix);
1840 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001841 for(; match_suffix; match_suffix = li)
1842 {
1843 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001844 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001845 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001846 }
Simon Kelleyab538832020-01-10 20:44:48 +00001847 for(; files; files = li)
1848 {
1849 li = files->next;
1850 free(files->name);
1851 free(files);
1852 }
Simon Kelley849a8352006-06-09 21:02:31 +01001853 break;
1854 }
1855
Simon Kelleyed4c0762013-10-08 20:46:34 +01001856 case LOPT_ADD_SBNET: /* --add-subnet */
1857 set_option_bool(OPT_CLIENT_SUBNET);
1858 if (arg)
1859 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001860 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001861 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001862
1863 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1864 if ((end = split_chr(arg, '/')))
1865 {
1866 /* has subnet+len */
1867 err = parse_mysockaddr(arg, &new->addr);
1868 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001869 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001870 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001871 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001872 new->addr_used = 1;
1873 }
1874 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001875 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001876
1877 daemon->add_subnet4 = new;
1878
Ed Bardsleya7369be2015-08-05 21:17:18 +01001879 if (comma)
1880 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001881 new = opt_malloc(sizeof(struct mysubnet));
1882 if ((end = split_chr(comma, '/')))
1883 {
1884 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001885 err = parse_mysockaddr(comma, &new->addr);
1886 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001887 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001888 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001889 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001890 new->addr_used = 1;
1891 }
1892 else
1893 {
1894 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001895 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001896 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001897
1898 daemon->add_subnet6 = new;
1899 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001900 }
1901 break;
1902
Simon Kelleyad094272012-08-10 17:10:54 +01001903 case '1': /* --enable-dbus */
1904 set_option_bool(OPT_DBUS);
1905 if (arg)
1906 daemon->dbus_name = opt_string_alloc(arg);
1907 else
1908 daemon->dbus_name = DNSMASQ_SERVICE;
1909 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001910
1911 case LOPT_UBUS: /* --enable-ubus */
1912 set_option_bool(OPT_UBUS);
1913 if (arg)
1914 daemon->ubus_name = opt_string_alloc(arg);
1915 else
1916 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1917 break;
1918
Simon Kelleyf2621c72007-04-29 19:47:21 +01001919 case '8': /* --log-facility */
1920 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001921 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001922 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001923 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001924 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001925#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001926 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001927#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001928 for (i = 0; facilitynames[i].c_name; i++)
1929 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1930 break;
1931
1932 if (facilitynames[i].c_name)
1933 daemon->log_fac = facilitynames[i].c_val;
1934 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001935 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001936#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001937 }
1938 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001939
Simon Kelleyf2621c72007-04-29 19:47:21 +01001940 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001941 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001942 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001943
Simon Kelleyf2621c72007-04-29 19:47:21 +01001944 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001945 {
Simon Kelley824af852008-02-12 20:43:05 +00001946 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001947 struct resolvc *new, *list = daemon->resolv_files;
1948
1949 if (list && list->is_default)
1950 {
1951 /* replace default resolv file - possibly with nothing */
1952 if (name)
1953 {
1954 list->is_default = 0;
1955 list->name = name;
1956 }
1957 else
1958 list = NULL;
1959 }
1960 else if (name)
1961 {
Simon Kelley824af852008-02-12 20:43:05 +00001962 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001963 new->next = list;
1964 new->name = name;
1965 new->is_default = 0;
1966 new->mtime = 0;
1967 new->logged = 0;
1968 list = new;
1969 }
1970 daemon->resolv_files = list;
1971 break;
1972 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001973
1974 case LOPT_SERVERS_FILE:
1975 daemon->servers_file = opt_string_alloc(arg);
1976 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001977
Simon Kelleyf2621c72007-04-29 19:47:21 +01001978 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001979 {
1980 int pref = 1;
1981 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001982 char *name, *target = NULL;
1983
Simon Kelleyf2621c72007-04-29 19:47:21 +01001984 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001985 {
1986 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001987 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001988 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001989 }
1990
Simon Kelley1f15b812009-10-13 17:49:32 +01001991 if (!(name = canonicalise_opt(arg)) ||
1992 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001993 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001994
Simon Kelley824af852008-02-12 20:43:05 +00001995 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001996 new->next = daemon->mxnames;
1997 daemon->mxnames = new;
1998 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001999 new->name = name;
2000 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01002001 new->weight = pref;
2002 break;
2003 }
2004
Simon Kelleyf2621c72007-04-29 19:47:21 +01002005 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01002006 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002007 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01002008 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002009
Simon Kelley6b173352018-05-08 18:32:14 +01002010 case LOPT_DUMPFILE: /* --dumpfile */
2011 daemon->dump_file = opt_string_alloc(arg);
2012 break;
2013
2014 case LOPT_DUMPMASK: /* --dumpmask */
2015 daemon->dump_mask = strtol(arg, NULL, 0);
2016 break;
2017
Simon Kelley7622fc02009-06-04 20:32:05 +01002018#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002019 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002020 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002021 break;
2022
Simon Kelleyc72daea2012-01-05 21:33:27 +00002023 /* Sorry about the gross pre-processor abuse */
2024 case '6': /* --dhcp-script */
2025 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002026# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002027 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002028# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002029 if (option == LOPT_LUASCRIPT)
2030# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002031 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002032# else
2033 daemon->luascript = opt_string_alloc(arg);
2034# endif
2035 else
2036 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002037# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002038 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002039#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002040
Simon Kelley70d18732015-01-31 19:59:29 +00002041 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2042 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2043 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2044 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2045 case LOPT_HOST_INOTIFY: /* --hostsdir */
2046 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002047 {
Simon Kelley824af852008-02-12 20:43:05 +00002048 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002049 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002050 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002051 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002052 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002053 if (option == 'H')
2054 {
2055 new->next = daemon->addn_hosts;
2056 daemon->addn_hosts = new;
2057 }
2058 else if (option == LOPT_DHCP_HOST)
2059 {
2060 new->next = daemon->dhcp_hosts_file;
2061 daemon->dhcp_hosts_file = new;
2062 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002063 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002064 {
2065 new->next = daemon->dhcp_opts_file;
2066 daemon->dhcp_opts_file = new;
2067 }
Simon Kelley70d18732015-01-31 19:59:29 +00002068 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002069 {
Simon Kelley70d18732015-01-31 19:59:29 +00002070 new->next = daemon->dynamic_dirs;
2071 daemon->dynamic_dirs = new;
2072 if (option == LOPT_DHCP_INOTIFY)
2073 new->flags |= AH_DHCP_HST;
2074 else if (option == LOPT_DHOPT_INOTIFY)
2075 new->flags |= AH_DHCP_OPT;
2076 else if (option == LOPT_HOST_INOTIFY)
2077 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002078 }
2079
Simon Kelley849a8352006-06-09 21:02:31 +01002080 break;
2081 }
2082
Simon Kelley4f7b3042012-11-28 21:27:02 +00002083 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002084 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002085
Simon Kelley4f7b3042012-11-28 21:27:02 +00002086 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002087
2088 while ((arg = comma))
2089 {
2090 struct iname *new = opt_malloc(sizeof(struct iname));
2091 comma = split(arg);
2092 new->name = NULL;
2093 unhide_metas(arg);
2094 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2095 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002096 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2097 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002098 else
2099 {
2100 char *fam = split_chr(arg, '/');
2101 new->name = opt_string_alloc(arg);
2102 new->addr.sa.sa_family = 0;
2103 if (fam)
2104 {
2105 if (strcmp(fam, "4") == 0)
2106 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002107 else if (strcmp(fam, "6") == 0)
2108 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002109 else
Petr Menšík59e47032018-11-02 22:39:39 +00002110 {
2111 free(new->name);
2112 ret_err_free(gen_err, new);
2113 }
Simon Kelley08933472018-10-05 16:34:35 +01002114 }
2115 }
2116 new->next = daemon->authinterface;
2117 daemon->authinterface = new;
2118 };
Simon Kelley429798f2012-12-10 20:45:53 +00002119
Simon Kelley4f7b3042012-11-28 21:27:02 +00002120 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002121
2122 case LOPT_AUTHSFS: /* --auth-sec-servers */
2123 {
2124 struct name_list *new;
2125
2126 do {
2127 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002128 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002129 new->name = opt_string_alloc(arg);
2130 new->next = daemon->secondary_forward_server;
2131 daemon->secondary_forward_server = new;
2132 arg = comma;
2133 } while (arg);
2134 break;
2135 }
2136
Simon Kelley4f7b3042012-11-28 21:27:02 +00002137 case LOPT_AUTHZONE: /* --auth-zone */
2138 {
2139 struct auth_zone *new;
2140
2141 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002142
Simon Kelley429798f2012-12-10 20:45:53 +00002143 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002144 new->domain = opt_string_alloc(arg);
2145 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002146 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002147 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002148 new->next = daemon->auth_zones;
2149 daemon->auth_zones = new;
2150
2151 while ((arg = comma))
2152 {
2153 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002154 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002155 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002156 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002157 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002158
2159 comma = split(arg);
2160 prefix = split_chr(arg, '/');
2161
2162 if (prefix && !atoi_check(prefix, &prefixlen))
2163 ret_err(gen_err);
2164
Mathias Kresin094bfae2016-07-24 14:15:22 +01002165 if (strstr(arg, "exclude:") == arg)
2166 {
2167 is_exclude = 1;
2168 arg = arg+8;
2169 }
2170
Simon Kelleycc921df2019-01-02 22:48:59 +00002171 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002172 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002173 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002174 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002175 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002176 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002177 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002178 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002179 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002180 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002181 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002182 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002183 else
2184 {
2185 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2186 name->name = opt_string_alloc(arg);
2187 name->flags = AUTH4 | AUTH6;
2188 name->next = new->interface_names;
2189 new->interface_names = name;
2190 if (prefix)
2191 {
2192 if (prefixlen == 4)
2193 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002194 else if (prefixlen == 6)
2195 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002196 else
2197 ret_err(gen_err);
2198 }
2199 }
2200
2201 if (subnet)
2202 {
2203 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002204
2205 if (is_exclude)
2206 {
2207 subnet->next = new->exclude;
2208 new->exclude = subnet;
2209 }
2210 else
2211 {
2212 subnet->next = new->subnet;
2213 new->subnet = subnet;
2214 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002215 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002216 }
2217 break;
2218 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002219
Simon Kelley4f7b3042012-11-28 21:27:02 +00002220 case LOPT_AUTHSOA: /* --auth-soa */
2221 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002222 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002223 if (comma)
2224 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002225 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002226 arg = comma;
2227 comma = split(arg);
2228 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002229 for (cp = daemon->hostmaster; *cp; cp++)
2230 if (*cp == '@')
2231 *cp = '.';
2232
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_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002238 if (comma)
2239 {
2240 arg = comma;
2241 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002242 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002243 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002244 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002245 }
2246 }
2247 }
2248
2249 break;
2250
Simon Kelley2bb73af2013-04-24 17:38:19 +01002251 case 's': /* --domain */
2252 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002253 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002254 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002255 else
Simon Kelley9009d742008-11-14 20:04:27 +00002256 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002257 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002258 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002259 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002260 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002261 else
2262 {
Simon Kelley9009d742008-11-14 20:04:27 +00002263 if (comma)
2264 {
Simon Kelley429798f2012-12-10 20:45:53 +00002265 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002266 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002267
Simon Kelley48fd1c42013-04-25 09:49:38 +01002268 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002269 new->indexed = 0;
2270
Simon Kelley9009d742008-11-14 20:04:27 +00002271 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002272 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002273 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002274 int msize;
2275
Simon Kelley28866e92011-02-14 20:19:14 +00002276 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002277 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002278 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002279 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002280 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002281 int mask = (1 << (32 - msize)) - 1;
2282 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002283 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2284 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002285 if (arg)
2286 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002287 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002288 {
2289 if (!(new->prefix = canonicalise_opt(arg)) ||
2290 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002291 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002292 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002293 else if (strcmp(arg, "local") != 0 ||
2294 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002295 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002296 else
2297 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002298 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002299 local=/xxx.yyy.zzz.in-addr.arpa/ */
2300 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002301 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002302 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002303
Simon Kelleyde73a492014-02-17 21:43:27 +00002304 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002305
2306 /* local=/<domain>/ */
2307 serv = opt_malloc(sizeof(struct server));
2308 memset(serv, 0, sizeof(struct server));
2309 serv->domain = d;
2310 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2311 serv->next = daemon->servers;
2312 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002313 }
2314 }
Simon Kelley9009d742008-11-14 20:04:27 +00002315 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002316 else if (inet_pton(AF_INET6, comma, &new->start6))
2317 {
2318 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2319 u64 addrpart = addr6part(&new->start6);
2320 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002321
Simon Kelleyd74942a2012-02-07 20:51:56 +00002322 /* prefix==64 overflows the mask calculation above */
2323 if (msize == 64)
2324 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002325
Simon Kelleyd74942a2012-02-07 20:51:56 +00002326 new->end6 = new->start6;
2327 setaddr6part(&new->start6, addrpart & ~mask);
2328 setaddr6part(&new->end6, addrpart | mask);
2329
2330 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002331 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002332 else if (arg)
2333 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002334 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002335 {
2336 if (!(new->prefix = canonicalise_opt(arg)) ||
2337 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002338 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002339 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002340 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002341 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002342 else
2343 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002344 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002345 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002346 struct server *serv = add_rev6(&new->start6, msize);
2347 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002348
2349 /* local=/<domain>/ */
2350 serv = opt_malloc(sizeof(struct server));
2351 memset(serv, 0, sizeof(struct server));
2352 serv->domain = d;
2353 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2354 serv->next = daemon->servers;
2355 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002356 }
2357 }
2358 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002359 else
Petr Menšík59e47032018-11-02 22:39:39 +00002360 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002361 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002362 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002363 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002364 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002365 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002366 prefstr = split(arg);
2367
Simon Kelleyd74942a2012-02-07 20:51:56 +00002368 if (inet_pton(AF_INET, comma, &new->start))
2369 {
2370 new->is6 = 0;
2371 if (!arg)
2372 new->end.s_addr = new->start.s_addr;
2373 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002374 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002375 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002376 else if (inet_pton(AF_INET6, comma, &new->start6))
2377 {
2378 new->is6 = 1;
2379 if (!arg)
2380 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2381 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002382 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002383 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002384 else
Petr Menšík59e47032018-11-02 22:39:39 +00002385 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002386
2387 if (option != 's' && prefstr)
2388 {
2389 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2390 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002391 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002392 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002393 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002394
2395 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002396 if (option == 's')
2397 {
2398 new->next = daemon->cond_domain;
2399 daemon->cond_domain = new;
2400 }
2401 else
2402 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002403 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002404 new->next = daemon->synth_domains;
2405 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002406 if (new->prefix &&
2407 (star = strrchr(new->prefix, '*'))
2408 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002409 {
2410 *star = 0;
2411 new->indexed = 1;
2412 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002413 }
Simon Kelley9009d742008-11-14 20:04:27 +00002414 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002415 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002416 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002417 else
2418 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002419 }
2420 }
Simon Kelley849a8352006-06-09 21:02:31 +01002421 break;
2422
Simon Kelley1e505122016-01-25 21:29:23 +00002423 case LOPT_CPE_ID: /* --add-dns-client */
2424 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002425 daemon->dns_client_id = opt_string_alloc(arg);
2426 break;
2427
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002428 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002429 if (!arg)
2430 set_option_bool(OPT_ADD_MAC);
2431 else
2432 {
2433 unhide_metas(arg);
2434 if (strcmp(arg, "base64") == 0)
2435 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002436 else if (strcmp(arg, "text") == 0)
2437 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002438 else
2439 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002440 }
2441 break;
2442
Simon Kelleyf2621c72007-04-29 19:47:21 +01002443 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002444 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002445 break;
2446
Simon Kelleyf2621c72007-04-29 19:47:21 +01002447 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002448 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002449 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002450 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002451
Simon Kelley7622fc02009-06-04 20:32:05 +01002452#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002453 case LOPT_SCRIPTUSR: /* --scriptuser */
2454 daemon->scriptuser = opt_string_alloc(arg);
2455 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002456#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002457
Simon Kelleyf2621c72007-04-29 19:47:21 +01002458 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002459 do {
Simon Kelley824af852008-02-12 20:43:05 +00002460 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002461 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002462 new->next = daemon->if_names;
2463 daemon->if_names = new;
2464 /* new->name may be NULL if someone does
2465 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002466 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002467 new->used = 0;
Tarun Kundu024baf12022-06-28 10:56:47 -07002468 new->is_ra_adv_dis = false;
Simon Kelley849a8352006-06-09 21:02:31 +01002469 arg = comma;
2470 } while (arg);
2471 break;
Tarun Kundu024baf12022-06-28 10:56:47 -07002472
2473 case LOPT_RA_ADV_DISABLE: ; /* --ra-adv-disable */
2474 struct iname *tmp;
2475 do {
2476 comma = split(arg);
2477 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
2478 {
2479 if (strcmp(tmp->name, arg) == 0) {
2480 tmp->is_ra_adv_dis = true;
2481 }
2482 }
2483 arg = comma;
2484 } while (arg);
2485 break;
2486
Simon Kelley2937f8a2013-07-29 19:49:07 +01002487 case LOPT_TFTP: /* --enable-tftp */
2488 set_option_bool(OPT_TFTP);
2489 if (!arg)
2490 break;
2491 /* fall through */
2492
Simon Kelleyf2621c72007-04-29 19:47:21 +01002493 case 'I': /* --except-interface */
2494 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002495 do {
Simon Kelley824af852008-02-12 20:43:05 +00002496 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002497 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002498 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002499 if (option == 'I')
2500 {
2501 new->next = daemon->if_except;
2502 daemon->if_except = new;
2503 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002504 else if (option == LOPT_TFTP)
2505 {
2506 new->next = daemon->tftp_interfaces;
2507 daemon->tftp_interfaces = new;
2508 }
Simon Kelley849a8352006-06-09 21:02:31 +01002509 else
2510 {
2511 new->next = daemon->dhcp_except;
2512 daemon->dhcp_except = new;
2513 }
2514 arg = comma;
2515 } while (arg);
2516 break;
2517
Simon Kelleyf2621c72007-04-29 19:47:21 +01002518 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002519 case LOPT_IGNORE_ADDR: /* --ignore-address */
2520 {
Simon Kelley849a8352006-06-09 21:02:31 +01002521 struct in_addr addr;
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002522 int prefix = 32;
Simon Kelley849a8352006-06-09 21:02:31 +01002523 unhide_metas(arg);
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002524
2525 if (!arg ||
2526 ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
2527 (inet_pton(AF_INET, arg, &addr) != 1))
2528 ret_err(gen_err); /* error */
2529 else
Simon Kelley849a8352006-06-09 21:02:31 +01002530 {
Simon Kelley824af852008-02-12 20:43:05 +00002531 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002532 if (option == 'B')
2533 {
2534 baddr->next = daemon->bogus_addr;
2535 daemon->bogus_addr = baddr;
2536 }
2537 else
2538 {
2539 baddr->next = daemon->ignore_addr;
2540 daemon->ignore_addr = baddr;
2541 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002542 baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
2543 baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
Simon Kelley849a8352006-06-09 21:02:31 +01002544 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002545 break;
2546 }
Simon Kelley849a8352006-06-09 21:02:31 +01002547
Simon Kelleyf2621c72007-04-29 19:47:21 +01002548 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002549 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002550 do {
Simon Kelley824af852008-02-12 20:43:05 +00002551 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002552 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002553 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002554 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002555 {
2556 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002557 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002558#ifdef HAVE_SOCKADDR_SA_LEN
2559 new->addr.in.sin_len = sizeof(new->addr.in);
2560#endif
2561 }
Simon Kelley849a8352006-06-09 21:02:31 +01002562 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2563 {
2564 new->addr.sa.sa_family = AF_INET6;
2565 new->addr.in6.sin6_flowinfo = 0;
2566 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002567 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002568#ifdef HAVE_SOCKADDR_SA_LEN
2569 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2570#endif
2571 }
Simon Kelley849a8352006-06-09 21:02:31 +01002572 else
Petr Menšík59e47032018-11-02 22:39:39 +00002573 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002574
2575 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002576 if (option == 'a')
2577 {
2578 new->next = daemon->if_addrs;
2579 daemon->if_addrs = new;
2580 }
2581 else
2582 {
2583 new->next = daemon->auth_peers;
2584 daemon->auth_peers = new;
2585 }
Simon Kelley849a8352006-06-09 21:02:31 +01002586 arg = comma;
2587 } while (arg);
2588 break;
2589
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002590 case 'S': /* --server */
2591 case LOPT_LOCAL: /* --local */
2592 case 'A': /* --address */
2593 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002594 {
2595 struct server *serv, *newlist = NULL;
2596
2597 unhide_metas(arg);
2598
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002599 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002600 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002601 int rebind = !(*arg == '/');
2602 char *end = NULL;
2603 if (!rebind)
2604 arg++;
2605 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002606 {
2607 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002608 /* elide leading dots - they are implied in the search algorithm */
2609 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002610 /* # matches everything and becomes a zero length domain string */
2611 if (strcmp(arg, "#") == 0)
2612 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002613 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002614 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002615 serv = opt_malloc(sizeof(struct server));
2616 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002617 serv->next = newlist;
2618 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002619 serv->domain = domain;
2620 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002621 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002622 if (rebind)
2623 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002624 }
2625 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002626 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002627 }
2628 else
2629 {
Simon Kelley824af852008-02-12 20:43:05 +00002630 newlist = opt_malloc(sizeof(struct server));
2631 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002632#ifdef HAVE_LOOP
2633 newlist->uid = rand32();
2634#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002635 }
2636
Simon Kelley7b1eae42014-02-20 13:43:28 +00002637 if (servers_only && option == 'S')
2638 newlist->flags |= SERV_FROM_FILE;
2639
Simon Kelley849a8352006-06-09 21:02:31 +01002640 if (option == 'A')
2641 {
2642 newlist->flags |= SERV_LITERAL_ADDRESS;
2643 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002644 {
2645 server_list_free(newlist);
2646 ret_err(gen_err);
2647 }
Simon Kelley849a8352006-06-09 21:02:31 +01002648 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002649 else if (option == LOPT_NO_REBIND)
2650 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002651
2652 if (!arg || !*arg)
2653 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002654 if (!(newlist->flags & SERV_NO_REBIND))
2655 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002656 }
2657
2658 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002659 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002660 else
2661 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002662 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2663 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002664 {
2665 server_list_free(newlist);
2666 ret_err(err);
2667 }
Simon Kelley849a8352006-06-09 21:02:31 +01002668 }
2669
Simon Kelleyf2621c72007-04-29 19:47:21 +01002670 serv = newlist;
2671 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002672 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002673 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002674 serv->next->addr = serv->addr;
2675 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002676 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002677 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002678 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002679 serv->next = daemon->servers;
2680 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002681 break;
2682 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002683
Simon Kelleyde73a492014-02-17 21:43:27 +00002684 case LOPT_REV_SERV: /* --rev-server */
2685 {
2686 char *string;
2687 int size;
2688 struct server *serv;
2689 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002690 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002691
2692 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002693 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002694 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002695
2696 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002697
Simon Kelleya9b022a2020-02-11 21:58:59 +00002698 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2699 ret_err(gen_err);
2700
Simon Kelleyde73a492014-02-17 21:43:27 +00002701 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002702 {
2703 serv = add_rev4(addr4, size);
2704 if (!serv)
2705 ret_err(_("bad prefix"));
2706 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002707 else if (inet_pton(AF_INET6, arg, &addr6))
2708 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002709 else
2710 ret_err(gen_err);
2711
2712 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2713
2714 if (string)
2715 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002716
2717 if (servers_only)
2718 serv->flags |= SERV_FROM_FILE;
2719
Simon Kelleyde73a492014-02-17 21:43:27 +00002720 break;
2721 }
2722
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002723 case LOPT_IPSET: /* --ipset */
2724#ifndef HAVE_IPSET
2725 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2726 break;
2727#else
2728 {
2729 struct ipsets ipsets_head;
2730 struct ipsets *ipsets = &ipsets_head;
2731 int size;
2732 char *end;
2733 char **sets, **sets_pos;
2734 memset(ipsets, 0, sizeof(struct ipsets));
2735 unhide_metas(arg);
2736 if (arg && *arg == '/')
2737 {
2738 arg++;
2739 while ((end = split_chr(arg, '/')))
2740 {
2741 char *domain = NULL;
2742 /* elide leading dots - they are implied in the search algorithm */
2743 while (*arg == '.')
2744 arg++;
2745 /* # matches everything and becomes a zero length domain string */
2746 if (strcmp(arg, "#") == 0 || !*arg)
2747 domain = "";
2748 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002749 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002750 ipsets->next = opt_malloc(sizeof(struct ipsets));
2751 ipsets = ipsets->next;
2752 memset(ipsets, 0, sizeof(struct ipsets));
2753 ipsets->domain = domain;
2754 arg = end;
2755 }
2756 }
2757 else
2758 {
2759 ipsets->next = opt_malloc(sizeof(struct ipsets));
2760 ipsets = ipsets->next;
2761 memset(ipsets, 0, sizeof(struct ipsets));
2762 ipsets->domain = "";
2763 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002764
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002765 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002766 ret_err(gen_err);
2767
2768 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002769 if (*end == ',')
2770 ++size;
2771
2772 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2773
2774 do {
2775 end = split(arg);
2776 *sets_pos++ = opt_string_alloc(arg);
2777 arg = end;
2778 } while (end);
2779 *sets_pos = 0;
2780 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2781 ipsets->next->sets = sets;
2782 ipsets->next = daemon->ipsets;
2783 daemon->ipsets = ipsets_head.next;
2784
2785 break;
2786 }
2787#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002788
Simon Kelleyf2621c72007-04-29 19:47:21 +01002789 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002790 {
2791 int size;
2792
2793 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002794 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002795 else
2796 {
2797 /* zero is OK, and means no caching. */
2798
2799 if (size < 0)
2800 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002801
2802 /* Note that for very large cache sizes, the malloc()
2803 will overflow. For the size of the cache record
2804 at the time this was noted, the value of "very large"
2805 was 46684428. Limit to an order of magnitude less than
2806 that to be safe from changes to the cache record. */
2807 if (size > 5000000)
2808 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002809
2810 daemon->cachesize = size;
2811 }
2812 break;
2813 }
2814
Simon Kelleyf2621c72007-04-29 19:47:21 +01002815 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002816 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002817 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002818 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002819
Simon Kelley1a6bca82008-07-11 11:11:42 +01002820 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002821 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002822 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002823 break;
2824
Hans Dedecker926332a2016-01-23 10:48:12 +00002825 case LOPT_MAXPORT: /* --max-port */
2826 if (!atoi_check16(arg, &daemon->max_port))
2827 ret_err(gen_err);
2828 break;
2829
Simon Kelleyf2621c72007-04-29 19:47:21 +01002830 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002831 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002832 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002833 break;
2834
Simon Kelley25cf5e32015-01-09 15:53:03 +00002835 case 'q': /* --log-queries */
2836 set_option_bool(OPT_LOG);
2837 if (arg && strcmp(arg, "extra") == 0)
2838 set_option_bool(OPT_EXTRALOG);
2839 break;
2840
Simon Kelleyf2621c72007-04-29 19:47:21 +01002841 case LOPT_MAX_LOGS: /* --log-async */
2842 daemon->max_logs = LOG_MAX; /* default */
2843 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002844 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002845 else if (daemon->max_logs > 100)
2846 daemon->max_logs = 100;
2847 break;
2848
2849 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002850 {
2851 int i;
2852 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002853 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002854 daemon->edns_pktsz = (unsigned short)i;
2855 break;
2856 }
2857
Simon Kelleyf2621c72007-04-29 19:47:21 +01002858 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002859 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002860 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002861 /* if explicitly set to zero, use single OS ephemeral port
2862 and disable random ports */
2863 if (daemon->query_port == 0)
2864 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002865 break;
2866
Simon Kelley824af852008-02-12 20:43:05 +00002867 case 'T': /* --local-ttl */
2868 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002869 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002870 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002871 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002872 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002873 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002874 {
2875 int ttl;
2876 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002877 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002878 else if (option == LOPT_NEGTTL)
2879 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002880 else if (option == LOPT_MAXTTL)
2881 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002882 else if (option == LOPT_MINCTTL)
2883 {
2884 if (ttl > TTL_FLOOR_LIMIT)
2885 ttl = TTL_FLOOR_LIMIT;
2886 daemon->min_cache_ttl = (unsigned long)ttl;
2887 }
Simon Kelley1d860412012-09-20 20:48:04 +01002888 else if (option == LOPT_MAXCTTL)
2889 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002890 else if (option == LOPT_AUTHTTL)
2891 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002892 else if (option == LOPT_DHCPTTL)
2893 {
2894 daemon->dhcp_ttl = (unsigned long)ttl;
2895 daemon->use_dhcp_ttl = 1;
2896 }
Simon Kelley849a8352006-06-09 21:02:31 +01002897 else
2898 daemon->local_ttl = (unsigned long)ttl;
2899 break;
2900 }
2901
Simon Kelley7622fc02009-06-04 20:32:05 +01002902#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002903 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002904 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002905 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002906 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002907#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002908
Simon Kelley7622fc02009-06-04 20:32:05 +01002909#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002910 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002911 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002912 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002913 break;
2914
Simon Kelleybec366b2016-02-24 22:03:26 +00002915 case LOPT_TFTP_MTU: /* --tftp-mtu */
2916 if (!atoi_check(arg, &daemon->tftp_mtu))
2917 ret_err(gen_err);
2918 break;
2919
Simon Kelley824af852008-02-12 20:43:05 +00002920 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002921 comma = split(arg);
2922 if (comma)
2923 {
2924 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2925 new->interface = opt_string_alloc(comma);
2926 new->prefix = opt_string_alloc(arg);
2927 new->next = daemon->if_prefix;
2928 daemon->if_prefix = new;
2929 }
2930 else
2931 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002932 break;
2933
Simon Kelley824af852008-02-12 20:43:05 +00002934 case LOPT_TFTPPORTS: /* --tftp-port-range */
2935 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002936 !atoi_check16(arg, &daemon->start_tftp_port) ||
2937 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002938 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002939
2940 if (daemon->start_tftp_port > daemon->end_tftp_port)
2941 {
2942 int tmp = daemon->start_tftp_port;
2943 daemon->start_tftp_port = daemon->end_tftp_port;
2944 daemon->end_tftp_port = tmp;
2945 }
2946
2947 break;
Floris Bos60704f52017-04-09 22:22:49 +01002948
2949 case LOPT_APREF: /* --tftp-unique-root */
2950 if (!arg || strcasecmp(arg, "ip") == 0)
2951 set_option_bool(OPT_TFTP_APREF_IP);
2952 else if (strcasecmp(arg, "mac") == 0)
2953 set_option_bool(OPT_TFTP_APREF_MAC);
2954 else
2955 ret_err(gen_err);
2956 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002957#endif
Simon Kelley824af852008-02-12 20:43:05 +00002958
Simon Kelleyf2621c72007-04-29 19:47:21 +01002959 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002960 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002961 struct dhcp_bridge *new;
2962
Simon Kelley316e2732010-01-22 20:16:09 +00002963 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002964 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002965
Simon Kelley22cd8602018-01-14 22:57:14 +00002966 for (new = daemon->bridges; new; new = new->next)
2967 if (strcmp(new->iface, arg) == 0)
2968 break;
2969
2970 if (!new)
2971 {
2972 new = opt_malloc(sizeof(struct dhcp_bridge));
2973 strcpy(new->iface, arg);
2974 new->alias = NULL;
2975 new->next = daemon->bridges;
2976 daemon->bridges = new;
2977 }
2978
Simon Kelley832af0b2007-01-21 20:01:28 +00002979 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002980 arg = comma;
2981 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002982 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002983 {
Simon Kelley824af852008-02-12 20:43:05 +00002984 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002985 b->next = new->alias;
2986 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002987 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002988 }
2989 } while (comma);
2990
2991 break;
2992 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002993
Simon Kelley7622fc02009-06-04 20:32:05 +01002994#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002995 case LOPT_SHARED_NET: /* --shared-network */
2996 {
2997 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2998
2999#ifdef HAVE_DHCP6
3000 new->shared_addr.s_addr = 0;
3001#endif
3002 new->if_index = 0;
3003
3004 if (!(comma = split(arg)))
3005 {
3006 snerr:
3007 free(new);
3008 ret_err(_("bad shared-network"));
3009 }
3010
3011 if (inet_pton(AF_INET, comma, &new->shared_addr))
3012 {
3013 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
3014 !(new->if_index = if_nametoindex(arg)))
3015 goto snerr;
3016 }
3017#ifdef HAVE_DHCP6
3018 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
3019 {
3020 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
3021 !(new->if_index = if_nametoindex(arg)))
3022 goto snerr;
3023 }
3024#endif
3025 else
3026 goto snerr;
3027
3028 new->next = daemon->shared_networks;
3029 daemon->shared_networks = new;
3030 break;
3031 }
3032
Simon Kelleyf2621c72007-04-29 19:47:21 +01003033 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01003034 {
3035 int k, leasepos = 2;
Kyle Swenson9d238072021-12-01 08:13:17 -07003036 char *cp, *a[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003037 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003038
Simon Kelley52b92f42012-01-22 16:05:15 +00003039 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003040
Simon Kelley849a8352006-06-09 21:02:31 +01003041 while(1)
3042 {
3043 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003044 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3045 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3046 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003047 break;
3048
Simon Kelleyf2621c72007-04-29 19:47:21 +01003049 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003050 {
Kyle Swenson545712c2021-11-17 12:25:04 -07003051/* CRADLEPOINT */
3052 if (strstr(arg, "forcedinterface:") == arg)
3053 new->forcedinterface = opt_string_alloc(arg+16);
3054 else if (strstr(arg, "forcedaddress:") == arg)
3055 new->forcedaddress.s_addr = inet_addr(arg+14);
3056 else if (is_tag_prefix(arg))
3057/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +01003058 {
Simon Kelley0c387192013-09-05 10:21:12 +01003059 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003060 if (arg[4])
3061 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003062 }
3063 else
3064 {
3065 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003066 {
3067 dhcp_context_free(new);
3068 ret_err(_("only one tag allowed"));
3069 }
Simon Kelley849a8352006-06-09 21:02:31 +01003070 else
Petr Menšík59e47032018-11-02 22:39:39 +00003071 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003072 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003073 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003074 }
3075 else
3076 {
3077 a[0] = arg;
3078 break;
3079 }
3080 }
3081
Kyle Swenson9d238072021-12-01 08:13:17 -07003082 for (k = 1; k < 9; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003083 if (!(a[k] = split(a[k-1])))
3084 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003085
Simon Kelley52b92f42012-01-22 16:05:15 +00003086 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003087 {
3088 dhcp_context_free(new);
3089 ret_err(_("bad dhcp-range"));
3090 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003091
3092 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003093 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003094 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003095 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003096 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003097 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003098 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003099 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003100 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003101 new->flags |= CONTEXT_PROXY;
3102 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003103 {
3104 dhcp_context_free(new);
3105 ret_err(_("bad dhcp-range"));
3106 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003107
3108 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3109 {
3110 struct in_addr tmp = new->start;
3111 new->start = new->end;
3112 new->end = tmp;
3113 }
3114
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003115 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003116 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003117 {
3118 new->flags |= CONTEXT_NETMASK;
3119 leasepos = 3;
3120 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003121 {
3122 dhcp_context_free(new);
3123 ret_err(_("inconsistent DHCP range"));
3124 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003125
Simon Kelley52b92f42012-01-22 16:05:15 +00003126
Simon Kelleyfa794662016-03-03 20:33:54 +00003127 if (k >= 4 && strchr(a[3], '.') &&
3128 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3129 {
3130 new->flags |= CONTEXT_BRDCAST;
3131 leasepos = 4;
3132 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003133 }
Simon Kelley849a8352006-06-09 21:02:31 +01003134 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003135#ifdef HAVE_DHCP6
3136 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003137 {
Petr Menšík59e47032018-11-02 22:39:39 +00003138 const char *err = NULL;
3139
Simon Kelley89500e32013-09-20 16:29:20 +01003140 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003141 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003142 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003143 new->lease_time = DEFLEASE6;
Kyle Swenson9d238072021-12-01 08:13:17 -07003144 new->valid_lifetime = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003145 new->next = daemon->dhcp6;
3146 daemon->dhcp6 = new;
3147
Simon Kelley30cd9662012-03-25 20:44:38 +01003148 for (leasepos = 1; leasepos < k; leasepos++)
3149 {
3150 if (strcmp(a[leasepos], "static") == 0)
3151 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3152 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003153 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003154 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003155 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003156 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3157 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003158 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003159 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003160 else if (strcmp(a[leasepos], "off-link") == 0)
3161 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003162 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3163 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003164 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3165 {
3166 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3167 new->flags |= CONTEXT_TEMPLATE;
3168 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003169 else
3170 break;
3171 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003172
Simon Kelley52b92f42012-01-22 16:05:15 +00003173 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003174 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003175 {
3176 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003177 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003178 if (!(*cp >= '0' && *cp <= '9'))
3179 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003180 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003181 {
3182 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003183 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003184 }
3185 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003186
Petr Menšík59e47032018-11-02 22:39:39 +00003187 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003188 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003189 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003190 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003191 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003192 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003193 }
Petr Menšík59e47032018-11-02 22:39:39 +00003194 else if (new->prefix < 64)
3195 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003196
Petr Menšík59e47032018-11-02 22:39:39 +00003197 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3198 err=(_("inconsistent DHCPv6 range"));
3199
3200 if (err)
3201 {
3202 dhcp_context_free(new);
3203 ret_err(err);
3204 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003205
3206 /* dhcp-range=:: enables DHCP stateless on any interface */
3207 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3208 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003209
3210 if (new->flags & CONTEXT_TEMPLATE)
3211 {
3212 struct in6_addr zero;
3213 memset(&zero, 0, sizeof(zero));
3214 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003215 {
3216 dhcp_context_free(new);
3217 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3218 }
Simon Kelley66409192013-08-01 20:19:32 +01003219 }
3220
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003221 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003222 {
3223 struct in6_addr tmp = new->start6;
3224 new->start6 = new->end6;
3225 new->end6 = tmp;
3226 }
Simon Kelley849a8352006-06-09 21:02:31 +01003227 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003228#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003229 else
Petr Menšík59e47032018-11-02 22:39:39 +00003230 {
3231 dhcp_context_free(new);
3232 ret_err(_("bad dhcp-range"));
3233 }
Simon Kelley849a8352006-06-09 21:02:31 +01003234
Simon Kelley30cd9662012-03-25 20:44:38 +01003235 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003236 {
Kyle Swenson9d238072021-12-01 08:13:17 -07003237
3238#ifndef HAVE_DHCP6
Simon Kelleyfa794662016-03-03 20:33:54 +00003239 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003240 {
3241 dhcp_context_free(new);
3242 ret_err(_("bad dhcp-range"));
3243 }
Kyle Swenson9d238072021-12-01 08:13:17 -07003244#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003245 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003246 {
3247 new->lease_time = 0xffffffff;
3248 new->flags |= CONTEXT_SETLEASE;
3249 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003250 else if (strcmp(a[leasepos], "deprecated") == 0)
3251 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003252 else
3253 {
3254 int fac = 1;
3255 if (strlen(a[leasepos]) > 0)
3256 {
3257 switch (a[leasepos][strlen(a[leasepos]) - 1])
3258 {
Simon Kelley42243212012-07-20 15:19:18 +01003259 case 'w':
3260 case 'W':
3261 fac *= 7;
3262 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003263 case 'd':
3264 case 'D':
3265 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003266 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003267 case 'h':
3268 case 'H':
3269 fac *= 60;
3270 /* fall through */
3271 case 'm':
3272 case 'M':
3273 fac *= 60;
3274 /* fall through */
3275 case 's':
3276 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003277 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003278 }
3279
Simon Kelleybe379862012-12-23 12:01:39 +00003280 for (cp = a[leasepos]; *cp; cp++)
3281 if (!(*cp >= '0' && *cp <= '9'))
3282 break;
3283
Kyle Swenson9d238072021-12-01 08:13:17 -07003284 if (*cp)
Petr Menšík59e47032018-11-02 22:39:39 +00003285 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003286
Simon Kelley849a8352006-06-09 21:02:31 +01003287 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003288 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003289 /* Leases of a minute or less confuse
3290 some clients, notably Apple's */
3291 if (new->lease_time < 120)
3292 new->lease_time = 120;
3293 }
3294 }
Kyle Swenson9d238072021-12-01 08:13:17 -07003295 leasepos++;
Simon Kelley849a8352006-06-09 21:02:31 +01003296 }
Kyle Swenson9d238072021-12-01 08:13:17 -07003297#ifdef HAVE_DHCP6
3298 if (leasepos < k)
3299 {
3300 if (leasepos != k-1)
3301 {
3302 dhcp_context_free(new);
3303 ret_err(_("bad dhcp-range"));
3304 }
3305 if (strcmp(a[leasepos], "infinite") == 0)
3306 {
3307 new->valid_lifetime = 0xffffffff;
3308 new->flags |= CONTEXT_SETVALID;
3309 }
3310 else
3311 {
3312 int fac = 1;
3313 if (strlen(a[leasepos]) > 0)
3314 {
3315 switch (a[leasepos][strlen(a[leasepos]) - 1])
3316 {
3317 case 'w':
3318 case 'W':
3319 fac *= 7;
3320 /* fall through */
3321 case 'd':
3322 case 'D':
3323 fac *= 24;
3324 /* fall through */
3325 case 'h':
3326 case 'H':
3327 fac *= 60;
3328 /* fall through */
3329 case 'm':
3330 case 'M':
3331 fac *= 60;
3332 /* fall through */
3333 case 's':
3334 case 'S':
3335 a[leasepos][strlen(a[leasepos]) - 1] = 0;
3336 }
3337
3338 for (cp = a[leasepos]; *cp; cp++)
3339 if (!(*cp >= '0' && *cp <= '9'))
3340 break;
Simon Kelley4d85e402020-07-12 22:45:46 +01003341
Kyle Swenson9d238072021-12-01 08:13:17 -07003342 if (*cp || (leasepos+1 < k))
3343 ret_err_free(_("bad dhcp-range"), new);
3344
3345 new->valid_lifetime = atoi(a[leasepos]) * fac;
3346 new->flags |= CONTEXT_SETVALID;
3347 }
3348 }
3349 leasepos++;
3350 }
3351#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003352 break;
3353 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003354
Simon Kelley5aabfc72007-08-29 11:24:47 +01003355 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003356 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003357 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003358 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003359 struct in_addr in;
3360
Simon Kelley824af852008-02-12 20:43:05 +00003361 new = opt_malloc(sizeof(struct dhcp_config));
3362
Simon Kelley849a8352006-06-09 21:02:31 +01003363 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003364 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3365 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003366 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003367 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003368 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003369#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003370 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003371#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003372
Simon Kelley137286e2020-02-06 22:09:30 +00003373 while (arg)
3374 {
3375 comma = split(arg);
3376 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3377 {
3378 if ((arg[0] == 'i' || arg[0] == 'I') &&
3379 (arg[1] == 'd' || arg[1] == 'D') &&
3380 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003381 {
Simon Kelley137286e2020-02-06 22:09:30 +00003382 if (arg[3] == '*')
3383 new->flags |= CONFIG_NOCLID;
3384 else
3385 {
3386 int len;
3387 arg += 3; /* dump id: */
3388 if (strchr(arg, ':'))
3389 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3390 else
3391 {
3392 unhide_metas(arg);
3393 len = (int) strlen(arg);
3394 }
3395
3396 if (len == -1)
3397 {
3398 dhcp_config_free(new);
3399 ret_err(_("bad hex constant"));
3400 }
3401 else if ((new->clid = opt_malloc(len)))
3402 {
3403 new->flags |= CONFIG_CLID;
3404 new->clid_len = len;
3405 memcpy(new->clid, arg, len);
3406 }
3407 }
3408 }
3409 /* dhcp-host has strange backwards-compat needs. */
3410 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3411 {
3412 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3413 newlist->next = new->netid;
3414 new->netid = newlist;
3415 newlist->list = dhcp_netid_create(arg+4, NULL);
3416 }
3417 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003418 new->filter = dhcp_netid_create(arg+4, new->filter);
3419
Simon Kelley137286e2020-02-06 22:09:30 +00003420#ifdef HAVE_DHCP6
3421 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3422 {
3423 char *pref;
3424 struct in6_addr in6;
3425 struct addrlist *new_addr;
3426
3427 arg[strlen(arg)-1] = 0;
3428 arg++;
3429 pref = split_chr(arg, '/');
3430
3431 if (!inet_pton(AF_INET6, arg, &in6))
3432 {
3433 dhcp_config_free(new);
3434 ret_err(_("bad IPv6 address"));
3435 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003436
Simon Kelley137286e2020-02-06 22:09:30 +00003437 new_addr = opt_malloc(sizeof(struct addrlist));
3438 new_addr->next = new->addr6;
3439 new_addr->flags = 0;
3440 new_addr->addr.addr6 = in6;
3441 new->addr6 = new_addr;
3442
3443 if (pref)
3444 {
3445 u64 addrpart = addr6part(&in6);
3446
3447 if (!atoi_check(pref, &new_addr->prefixlen) ||
3448 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003449 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003450 {
3451 dhcp_config_free(new);
3452 ret_err(_("bad IPv6 prefix"));
3453 }
3454
3455 new_addr->flags |= ADDRLIST_PREFIX;
3456 }
3457
3458 for (i= 0; i < 8; i++)
3459 if (in6.s6_addr[i] != 0)
3460 break;
3461
3462 /* set WILDCARD if network part all zeros */
3463 if (i == 8)
3464 new_addr->flags |= ADDRLIST_WILDCARD;
3465
3466 new->flags |= CONFIG_ADDR6;
3467 }
3468#endif
3469 else
3470 {
3471 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3472 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3473 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3474 {
3475 free(newhw);
3476 dhcp_config_free(new);
3477 ret_err(_("bad hex constant"));
3478 }
3479 else
3480 {
3481 newhw->next = new->hwaddr;
3482 new->hwaddr = newhw;
3483 }
3484 }
3485 }
3486 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3487 {
3488 struct dhcp_config *configs;
3489
3490 new->addr = in;
3491 new->flags |= CONFIG_ADDR;
3492
3493 /* If the same IP appears in more than one host config, then DISCOVER
3494 for one of the hosts will get the address, but REQUEST will be NAKed,
3495 since the address is reserved by the other one -> protocol loop. */
3496 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3497 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003498 {
Simon Kelley137286e2020-02-06 22:09:30 +00003499 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3500 return 0;
3501 }
3502 }
3503 else
3504 {
3505 char *cp, *lastp = NULL, last = 0;
3506 int fac = 1, isdig = 0;
3507
3508 if (strlen(arg) > 1)
3509 {
3510 lastp = arg + strlen(arg) - 1;
3511 last = *lastp;
3512 switch (last)
3513 {
3514 case 'w':
3515 case 'W':
3516 fac *= 7;
3517 /* fall through */
3518 case 'd':
3519 case 'D':
3520 fac *= 24;
3521 /* fall through */
3522 case 'h':
3523 case 'H':
3524 fac *= 60;
3525 /* fall through */
3526 case 'm':
3527 case 'M':
3528 fac *= 60;
3529 /* fall through */
3530 case 's':
3531 case 'S':
3532 *lastp = 0;
3533 }
3534 }
3535
3536 for (cp = arg; *cp; cp++)
3537 if (isdigit((unsigned char)*cp))
3538 isdig = 1;
3539 else if (*cp != ' ')
3540 break;
3541
3542 if (*cp)
3543 {
3544 if (lastp)
3545 *lastp = last;
3546 if (strcmp(arg, "infinite") == 0)
3547 {
3548 new->lease_time = 0xffffffff;
3549 new->flags |= CONFIG_TIME;
3550 }
3551 else if (strcmp(arg, "ignore") == 0)
3552 new->flags |= CONFIG_DISABLE;
3553 else
3554 {
3555 if (!(new->hostname = canonicalise_opt(arg)) ||
3556 !legal_hostname(new->hostname))
3557 {
3558 dhcp_config_free(new);
3559 ret_err(_("bad DHCP host name"));
3560 }
3561
3562 new->flags |= CONFIG_NAME;
3563 new->domain = strip_hostname(new->hostname);
3564 }
3565 }
3566 else if (isdig)
3567 {
3568 new->lease_time = atoi(arg) * fac;
3569 /* Leases of a minute or less confuse
3570 some clients, notably Apple's */
3571 if (new->lease_time < 120)
3572 new->lease_time = 120;
3573 new->flags |= CONFIG_TIME;
3574 }
3575 }
3576
3577 arg = comma;
3578 }
3579
Simon Kelley5aabfc72007-08-29 11:24:47 +01003580 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003581 break;
3582 }
Simon Kelley137286e2020-02-06 22:09:30 +00003583
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003584 case LOPT_TAG_IF: /* --tag-if */
3585 {
3586 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3587
3588 new->tag = NULL;
3589 new->set = NULL;
3590 new->next = NULL;
3591
3592 /* preserve order */
3593 if (!daemon->tag_if)
3594 daemon->tag_if = new;
3595 else
3596 {
3597 struct tag_if *tmp;
3598 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3599 tmp->next = new;
3600 }
3601
3602 while (arg)
3603 {
3604 size_t len;
3605
3606 comma = split(arg);
3607 len = strlen(arg);
3608
3609 if (len < 5)
3610 {
3611 new->set = NULL;
3612 break;
3613 }
3614 else
3615 {
Petr Menšík59e47032018-11-02 22:39:39 +00003616 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003617
3618 if (strstr(arg, "set:") == arg)
3619 {
3620 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3621 newlist->next = new->set;
3622 new->set = newlist;
3623 newlist->list = newtag;
3624 }
3625 else if (strstr(arg, "tag:") == arg)
3626 {
3627 newtag->next = new->tag;
3628 new->tag = newtag;
3629 }
3630 else
3631 {
3632 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003633 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003634 break;
3635 }
3636 }
3637
3638 arg = comma;
3639 }
3640
3641 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003642 {
3643 dhcp_netid_free(new->tag);
3644 dhcp_netid_list_free(new->set);
3645 ret_err_free(_("bad tag-if"), new);
3646 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003647
3648 break;
3649 }
3650
Simon Kelley849a8352006-06-09 21:02:31 +01003651
Simon Kelley73a08a22009-02-05 20:28:08 +00003652 case 'O': /* --dhcp-option */
3653 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003654 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003655 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003656 return parse_dhcp_opt(errstr, arg,
3657 option == LOPT_FORCE ? DHOPT_FORCE :
3658 (option == LOPT_MATCH ? DHOPT_MATCH :
3659 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003660
3661 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3662 {
3663 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3664 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3665 ssize_t len;
3666
3667 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3668 ret_err(gen_err);
3669
3670 new->wildcard = 0;
3671 new->netid = id;
3672 id->net = opt_string_alloc(set_prefix(arg));
3673
3674 if (comma[len-1] == '*')
3675 {
3676 comma[len-1] = 0;
3677 new->wildcard = 1;
3678 }
3679 new->name = opt_string_alloc(comma);
3680
3681 new->next = daemon->dhcp_name_match;
3682 daemon->dhcp_name_match = new;
3683
3684 break;
3685 }
3686
Simon Kelleyf2621c72007-04-29 19:47:21 +01003687 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003688 {
Petr Menšík59e47032018-11-02 22:39:39 +00003689 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003690
Petr Menšík137e9f82018-12-16 21:25:29 +00003691 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003692 {
3693 ret_err(gen_err);
3694 }
Simon Kelley849a8352006-06-09 21:02:31 +01003695 else
3696 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003697 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003698 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003699 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003700 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003701 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003702 dhcp_next_server.s_addr = 0;
3703 if (comma)
3704 {
3705 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003706 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003707 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003708 if (comma)
3709 {
3710 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003711 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3712 {
3713 /*
3714 * The user may have specified the tftp hostname here.
3715 * save it so that it can be resolved/looked up during
3716 * actual dhcp_reply().
3717 */
3718
3719 tftp_sname = opt_string_alloc(comma);
3720 dhcp_next_server.s_addr = 0;
3721 }
Simon Kelley849a8352006-06-09 21:02:31 +01003722 }
3723 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003724
3725 new = opt_malloc(sizeof(struct dhcp_boot));
3726 new->file = dhcp_file;
3727 new->sname = dhcp_sname;
3728 new->tftp_sname = tftp_sname;
3729 new->next_server = dhcp_next_server;
3730 new->netid = id;
3731 new->next = daemon->boot_config;
3732 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003733 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003734
Simon Kelley849a8352006-06-09 21:02:31 +01003735 break;
3736 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003737
Floris Bos503c6092017-04-09 23:07:13 +01003738 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3739 {
Petr Menšík59e47032018-11-02 22:39:39 +00003740 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003741
Petr Menšík137e9f82018-12-16 21:25:29 +00003742 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003743 {
3744 ret_err(gen_err);
3745 }
Floris Bos503c6092017-04-09 23:07:13 +01003746 else
3747 {
3748 struct delay_config *new;
3749 int delay;
3750 if (!atoi_check(arg, &delay))
3751 ret_err(gen_err);
3752
3753 new = opt_malloc(sizeof(struct delay_config));
3754 new->delay = delay;
3755 new->netid = id;
3756 new->next = daemon->delay_conf;
3757 daemon->delay_conf = new;
3758 }
3759
3760 break;
3761 }
3762
Simon Kelley7622fc02009-06-04 20:32:05 +01003763 case LOPT_PXE_PROMT: /* --pxe-prompt */
3764 {
3765 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3766 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003767
Simon Kelley7622fc02009-06-04 20:32:05 +01003768 new->netid = NULL;
3769 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003770 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003771
Petr Menšík137e9f82018-12-16 21:25:29 +00003772 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003773 {
3774 dhcp_opt_free(new);
3775 ret_err(gen_err);
3776 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003777 else
3778 {
3779 comma = split(arg);
3780 unhide_metas(arg);
3781 new->len = strlen(arg) + 1;
3782 new->val = opt_malloc(new->len);
3783 memcpy(new->val + 1, arg, new->len - 1);
3784
Wang Shanker4ded9622020-12-04 10:17:35 +08003785 new->u.vendor_class = NULL;
3786 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003787
3788 if (comma && atoi_check(comma, &timeout))
3789 *(new->val) = timeout;
3790 else
3791 *(new->val) = 255;
3792
3793 new->next = daemon->dhcp_opts;
3794 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003795 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003796 }
3797
3798 break;
3799 }
3800
3801 case LOPT_PXE_SERV: /* --pxe-service */
3802 {
3803 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3804 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003805 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3806 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003807 static int boottype = 32768;
3808
3809 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003810 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003811 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003812 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003813
Simon Kelley7622fc02009-06-04 20:32:05 +01003814 if (arg && (comma = split(arg)))
3815 {
3816 for (i = 0; CSA[i]; i++)
3817 if (strcasecmp(CSA[i], arg) == 0)
3818 break;
3819
3820 if (CSA[i] || atoi_check(arg, &i))
3821 {
3822 arg = comma;
3823 comma = split(arg);
3824
3825 new->CSA = i;
3826 new->menu = opt_string_alloc(arg);
3827
Simon Kelley316e2732010-01-22 20:16:09 +00003828 if (!comma)
3829 {
3830 new->type = 0; /* local boot */
3831 new->basename = NULL;
3832 }
3833 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003834 {
3835 arg = comma;
3836 comma = split(arg);
3837 if (atoi_check(arg, &i))
3838 {
3839 new->type = i;
3840 new->basename = NULL;
3841 }
3842 else
3843 {
3844 new->type = boottype++;
3845 new->basename = opt_string_alloc(arg);
3846 }
3847
Simon Kelley751d6f42012-02-10 15:24:51 +00003848 if (comma)
3849 {
3850 if (!inet_pton(AF_INET, comma, &new->server))
3851 {
3852 new->server.s_addr = 0;
3853 new->sname = opt_string_alloc(comma);
3854 }
3855
3856 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003857 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003858
Simon Kelley316e2732010-01-22 20:16:09 +00003859 /* Order matters */
3860 new->next = NULL;
3861 if (!daemon->pxe_services)
3862 daemon->pxe_services = new;
3863 else
3864 {
3865 struct pxe_service *s;
3866 for (s = daemon->pxe_services; s->next; s = s->next);
3867 s->next = new;
3868 }
3869
3870 daemon->enable_pxe = 1;
3871 break;
3872
Simon Kelley7622fc02009-06-04 20:32:05 +01003873 }
3874 }
3875
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003876 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003877 }
3878
Simon Kelleyf2621c72007-04-29 19:47:21 +01003879 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003880 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003881 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003882 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003883 else
3884 {
Simon Kelley824af852008-02-12 20:43:05 +00003885 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003886 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003887 unhide_metas(comma);
3888 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003889 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003890 {
3891 free(new->netid.net);
3892 ret_err_free(gen_err, new);
3893 }
Simon Kelley28866e92011-02-14 20:19:14 +00003894 else
3895 {
3896 new->next = daemon->dhcp_macs;
3897 daemon->dhcp_macs = new;
3898 }
Simon Kelley849a8352006-06-09 21:02:31 +01003899 }
3900 }
3901 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003902
Simon Kelleyf2621c72007-04-29 19:47:21 +01003903 case 'U': /* --dhcp-vendorclass */
3904 case 'j': /* --dhcp-userclass */
3905 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3906 case LOPT_REMOTE: /* --dhcp-remoteid */
3907 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003908 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003909 unsigned char *p;
3910 int dig = 0;
3911 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3912
3913 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003914 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003915
3916 new->netid.net = opt_string_alloc(set_prefix(arg));
3917 /* check for hex string - must digits may include : must not have nothing else,
3918 only allowed for agent-options. */
3919
3920 arg = comma;
3921 if ((comma = split(arg)))
3922 {
3923 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003924 {
3925 free(new->netid.net);
3926 ret_err_free(gen_err, new);
3927 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003928 else
3929 new->enterprise = atoi(arg+11);
3930 }
3931 else
3932 comma = arg;
3933
3934 for (p = (unsigned char *)comma; *p; p++)
3935 if (isxdigit(*p))
3936 dig = 1;
3937 else if (*p != ':')
3938 break;
3939 unhide_metas(comma);
3940 if (option == 'U' || option == 'j' || *p || !dig)
3941 {
3942 new->len = strlen(comma);
3943 new->data = opt_malloc(new->len);
3944 memcpy(new->data, comma, new->len);
3945 }
3946 else
3947 {
3948 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3949 new->data = opt_malloc(new->len);
3950 memcpy(new->data, comma, new->len);
3951 }
3952
3953 switch (option)
3954 {
3955 case 'j':
3956 new->match_type = MATCH_USER;
3957 break;
3958 case 'U':
3959 new->match_type = MATCH_VENDOR;
3960 break;
3961 case LOPT_CIRCUIT:
3962 new->match_type = MATCH_CIRCUIT;
3963 break;
3964 case LOPT_REMOTE:
3965 new->match_type = MATCH_REMOTE;
3966 break;
3967 case LOPT_SUBSCR:
3968 new->match_type = MATCH_SUBSCRIBER;
3969 break;
3970 }
3971 new->next = daemon->dhcp_vendors;
3972 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003973
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003974 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003975 }
3976
Simon Kelley9e038942008-05-30 20:06:34 +01003977 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3978 if (!arg)
3979 {
3980 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3981 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3982 }
3983 else
3984 {
3985 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003986 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3987 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003988 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003989 if (!comma)
3990 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3991 }
3992 break;
3993
Simon Kelley824af852008-02-12 20:43:05 +00003994 case 'J': /* --dhcp-ignore */
3995 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3996 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003997 case '3': /* --bootp-dynamic */
3998 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003999 {
Simon Kelley824af852008-02-12 20:43:05 +00004000 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01004001 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004002 if (option == 'J')
4003 {
4004 new->next = daemon->dhcp_ignore;
4005 daemon->dhcp_ignore = new;
4006 }
Simon Kelley824af852008-02-12 20:43:05 +00004007 else if (option == LOPT_BROADCAST)
4008 {
4009 new->next = daemon->force_broadcast;
4010 daemon->force_broadcast = new;
4011 }
Simon Kelley9009d742008-11-14 20:04:27 +00004012 else if (option == '3')
4013 {
4014 new->next = daemon->bootp_dynamic;
4015 daemon->bootp_dynamic = new;
4016 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004017 else if (option == LOPT_GEN_NAMES)
4018 {
4019 new->next = daemon->dhcp_gen_names;
4020 daemon->dhcp_gen_names = new;
4021 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004022 else
4023 {
4024 new->next = daemon->dhcp_ignore_names;
4025 daemon->dhcp_ignore_names = new;
4026 }
4027
4028 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004029 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00004030 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01004031 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00004032 }
Simon Kelley849a8352006-06-09 21:02:31 +01004033
4034 new->list = list;
4035 break;
4036 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004037
4038 case LOPT_PROXY: /* --dhcp-proxy */
4039 daemon->override = 1;
4040 while (arg) {
4041 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
4042 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004043 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00004044 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004045 new->next = daemon->override_relays;
4046 daemon->override_relays = new;
4047 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08004048 }
4049 break;
4050
4051 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
4052 {
4053 while (arg) {
4054 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
4055 comma = split(arg);
4056 new->data = opt_string_alloc(arg);
4057 new->next = daemon->dhcp_pxe_vendors;
4058 daemon->dhcp_pxe_vendors = new;
4059 arg = comma;
4060 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004061 }
4062 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01004063
4064 case LOPT_RELAY: /* --dhcp-relay */
4065 {
4066 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
4067 comma = split(arg);
4068 new->interface = opt_string_alloc(split(comma));
4069 new->iface_index = 0;
4070 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
4071 {
4072 new->next = daemon->relay4;
4073 daemon->relay4 = new;
4074 }
4075#ifdef HAVE_DHCP6
4076 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
4077 {
4078 new->next = daemon->relay6;
4079 daemon->relay6 = new;
4080 }
4081#endif
4082 else
Petr Menšík59e47032018-11-02 22:39:39 +00004083 {
4084 free(new->interface);
4085 ret_err_free(_("Bad dhcp-relay"), new);
4086 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01004087
4088 break;
4089 }
4090
Simon Kelley7622fc02009-06-04 20:32:05 +01004091#endif
Simon Kelley849a8352006-06-09 21:02:31 +01004092
Simon Kelley8b372702012-03-09 17:45:10 +00004093#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004094 case LOPT_RA_PARAM: /* --ra-param */
4095 if ((comma = split(arg)))
Tarun Kundu024baf12022-06-28 10:56:47 -07004096 {
4097 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
4098 new->lifetime = -1;
4099 new->prio = 0;
4100 new->mtu = 0;
4101 new->mtu_name = NULL;
4102 new->name = opt_string_alloc(arg);
4103 new->reachable_time = 0;
4104 new->hop_limit = 0;
Tarun Kundu41aa4882022-07-05 10:44:13 -07004105 new->retrans_time = 0;
Tarun Kundu024baf12022-06-28 10:56:47 -07004106 if (strcasestr(comma, "mtu:") == comma)
4107 {
4108 arg = comma + 4;
4109 if (!(comma = split(comma)))
4110 goto err;
4111 if (!strcasecmp(arg, "off"))
4112 new->mtu = -1;
4113 else if (!atoi_check(arg, &new->mtu))
4114 new->mtu_name = opt_string_alloc(arg);
4115 else if (new->mtu < 1280)
4116 goto err;
4117 }
4118 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4119 {
4120 if (*comma == 'l' || *comma == 'L')
4121 new->prio = 0x18;
4122 else
4123 new->prio = 0x08;
4124 comma = split(comma);
4125 }
4126 arg = split(comma);
4127 if (comma && !atoi_check(comma, &new->interval))
4128 goto err;
4129 comma = split(arg);
4130 if (arg && !atoi_check(arg, &new->lifetime))
4131 goto err;
4132 arg = split(comma);
Tarun Kundu41aa4882022-07-05 10:44:13 -07004133 if (comma && !atoi_check(comma, (int *)&new->reachable_time))
4134 goto err;
4135 comma = split(arg);
4136 if ((arg && !atoi_check(arg, (int *)&new->hop_limit)) ||
4137 (comma && (!atoi_check(comma, (int *)&new->retrans_time))))
Petr Menšík59e47032018-11-02 22:39:39 +00004138 {
David Flamand005c46d2017-04-11 11:49:54 +01004139err:
Tarun Kundu024baf12022-06-28 10:56:47 -07004140 free(new->name);
4141 ret_err_free(_("bad RA-params"), new);
Petr Menšík59e47032018-11-02 22:39:39 +00004142 }
Tarun Kundu024baf12022-06-28 10:56:47 -07004143 if (new->hop_limit != 0)
4144 {
4145 setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
4146 &new->hop_limit, sizeof(new->hop_limit));
4147 }
4148
4149 new->next = daemon->ra_interfaces;
4150 daemon->ra_interfaces = new;
4151 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004152 break;
4153
Simon Kelley8b372702012-03-09 17:45:10 +00004154 case LOPT_DUID: /* --dhcp-duid */
4155 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004156 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004157 else
4158 {
4159 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4160 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4161 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4162 }
4163 break;
4164#endif
4165
Simon Kelleyf2621c72007-04-29 19:47:21 +01004166 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004167 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004168 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004169 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004170 struct doctor *new = opt_malloc(sizeof(struct doctor));
4171 new->next = daemon->doctors;
4172 daemon->doctors = new;
4173 new->mask.s_addr = 0xffffffff;
4174 new->end.s_addr = 0;
4175
Simon Kelley849a8352006-06-09 21:02:31 +01004176 if ((a[0] = arg))
4177 for (k = 1; k < 3; k++)
4178 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004179 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004180 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004181 unhide_metas(a[k]);
4182 }
Simon Kelley849a8352006-06-09 21:02:31 +01004183
Simon Kelley73a08a22009-02-05 20:28:08 +00004184 dash = split_chr(a[0], '-');
4185
Simon Kelley849a8352006-06-09 21:02:31 +01004186 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004187 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004188 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4189 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4190 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004191
Simon Kelley73a08a22009-02-05 20:28:08 +00004192 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004193 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004194 !is_same_net(new->in, new->end, new->mask) ||
4195 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004196 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004197
4198 break;
4199 }
4200
Simon Kelleyf2621c72007-04-29 19:47:21 +01004201 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004202 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004203 {
4204 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004205 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004206
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004207 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004208
Simon Kelley824af852008-02-12 20:43:05 +00004209 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004210 memset(new, 0, sizeof(struct interface_name));
4211 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004212
Simon Kelleyf2621c72007-04-29 19:47:21 +01004213 /* Add to the end of the list, so that first name
4214 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004215 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004216 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004217
4218 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004219 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004220 if (inet_pton(AF_INET, arg, &new->proto4))
4221 new->flags |= INP4;
4222 else if (inet_pton(AF_INET6, arg, &new->proto6))
4223 new->flags |= INP6;
4224 else
4225 break;
4226
4227 arg = comma;
4228 }
4229
4230 if ((comma = split_chr(arg, '/')))
4231 {
4232 if (strcmp(comma, "4") == 0)
4233 new->flags &= ~IN6;
4234 else if (strcmp(comma, "6") == 0)
4235 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004236 else
Petr Menšík59e47032018-11-02 22:39:39 +00004237 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004238 }
4239
4240 new->intr = opt_string_alloc(arg);
4241
4242 if (option == LOPT_DYNHOST)
4243 {
4244 if (!(new->flags & (INP4 | INP6)))
4245 ret_err(_("missing address in dynamic host"));
4246
4247 if (!(new->flags & IN4) || !(new->flags & IN6))
4248 arg = NULL; /* provoke error below */
4249
4250 new->flags &= ~(IN4 | IN6);
4251 }
4252 else
4253 {
4254 if (new->flags & (INP4 | INP6))
4255 arg = NULL; /* provoke error below */
4256 }
4257
4258 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4259 ret_err(option == LOPT_DYNHOST ?
4260 _("bad dynamic host") : _("bad interface name"));
4261
Simon Kelleyf2621c72007-04-29 19:47:21 +01004262 break;
4263 }
Simon Kelley9009d742008-11-14 20:04:27 +00004264
4265 case LOPT_CNAME: /* --cname */
4266 {
4267 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004268 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004269 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004270
Simon Kelleya1d973f2016-12-22 22:09:50 +00004271 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004272 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004273 pen = last;
4274 last = comma;
4275 }
4276
4277 if (!pen)
4278 ret_err(_("bad CNAME"));
4279
4280 if (pen != arg && atoi_check(last, &ttl))
4281 last = pen;
4282
4283 target = canonicalise_opt(last);
4284
4285 while (arg != last)
4286 {
Petr Menšík56f06232018-03-06 23:13:32 +00004287 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004288 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004289
4290 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004291 {
4292 free(target);
4293 free(alias);
4294 ret_err(_("bad CNAME"));
4295 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004296
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004297 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004298 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004299 {
4300 free(target);
4301 free(alias);
4302 ret_err(_("duplicate CNAME"));
4303 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004304 new = opt_malloc(sizeof(struct cname));
4305 new->next = daemon->cnames;
4306 daemon->cnames = new;
4307 new->alias = alias;
4308 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004309 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004310
Petr Menšík56f06232018-03-06 23:13:32 +00004311 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004312 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004313
Simon Kelley9009d742008-11-14 20:04:27 +00004314 break;
4315 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004316
4317 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004318 {
4319 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004320 char *dom, *target = NULL;
4321
Simon Kelleyf2621c72007-04-29 19:47:21 +01004322 comma = split(arg);
4323
Simon Kelley1f15b812009-10-13 17:49:32 +01004324 if (!(dom = canonicalise_opt(arg)) ||
4325 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004326 {
4327 free(dom);
4328 free(target);
4329 ret_err(_("bad PTR record"));
4330 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004331 else
4332 {
4333 new = opt_malloc(sizeof(struct ptr_record));
4334 new->next = daemon->ptr;
4335 daemon->ptr = new;
4336 new->name = dom;
4337 new->ptr = target;
4338 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004339 break;
4340 }
4341
Simon Kelley1a6bca82008-07-11 11:11:42 +01004342 case LOPT_NAPTR: /* --naptr-record */
4343 {
4344 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4345 int k = 0;
4346 struct naptr *new;
4347 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004348 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004349
4350 if ((a[0] = arg))
4351 for (k = 1; k < 7; k++)
4352 if (!(a[k] = split(a[k-1])))
4353 break;
4354
4355
4356 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004357 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004358 !atoi_check16(a[1], &order) ||
4359 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004360 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004361 {
4362 free(name);
4363 free(replace);
4364 ret_err(_("bad NAPTR record"));
4365 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004366 else
4367 {
4368 new = opt_malloc(sizeof(struct naptr));
4369 new->next = daemon->naptr;
4370 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004371 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004372 new->flags = opt_string_alloc(a[3]);
4373 new->services = opt_string_alloc(a[4]);
4374 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004375 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004376 new->order = order;
4377 new->pref = pref;
4378 }
4379 break;
4380 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004381
4382 case LOPT_RR: /* dns-rr */
4383 {
4384 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004385 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004386 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004387 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004388
4389 comma = split(arg);
4390 data = split(comma);
4391
4392 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004393 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004394
Petr Menšík59e47032018-11-02 22:39:39 +00004395 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004396 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004397 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004398 {
4399 free(new->name);
4400 ret_err_free(_("bad RR record"), new);
4401 }
4402
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004403 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004404 new->class = class;
4405 new->next = daemon->rr;
4406 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004407
4408 if (data)
4409 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004410 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004411 new->len = len;
4412 memcpy(new->txt, data, len);
4413 }
4414
4415 break;
4416 }
4417
Simon Kelley974a6d02018-08-23 23:01:16 +01004418 case LOPT_CAA: /* --caa-record */
4419 {
4420 struct txt_record *new;
4421 char *tag, *value;
4422 int flags;
4423
4424 comma = split(arg);
4425 tag = split(comma);
4426 value = split(tag);
4427
4428 new = opt_malloc(sizeof(struct txt_record));
4429 new->next = daemon->rr;
4430 daemon->rr = new;
4431
4432 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4433 ret_err(_("bad CAA record"));
4434
4435 unhide_metas(tag);
4436 unhide_metas(value);
4437
4438 new->len = strlen(tag) + strlen(value) + 2;
4439 new->txt = opt_malloc(new->len);
4440 new->txt[0] = flags;
4441 new->txt[1] = strlen(tag);
4442 memcpy(&new->txt[2], tag, strlen(tag));
4443 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4444 new->class = T_CAA;
4445
4446 break;
4447 }
4448
Simon Kelleyf2621c72007-04-29 19:47:21 +01004449 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004450 {
4451 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004452 unsigned char *p, *cnt;
4453 size_t len;
4454
4455 comma = split(arg);
4456
Simon Kelley824af852008-02-12 20:43:05 +00004457 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004458 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004459 new->stat = 0;
4460
Simon Kelley1f15b812009-10-13 17:49:32 +01004461 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004462 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004463
Petr Menšík59e47032018-11-02 22:39:39 +00004464 new->next = daemon->txt;
4465 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004466 len = comma ? strlen(comma) : 0;
4467 len += (len/255) + 1; /* room for extra counts */
4468 new->txt = p = opt_malloc(len);
4469
4470 cnt = p++;
4471 *cnt = 0;
4472
4473 while (comma && *comma)
4474 {
4475 unsigned char c = (unsigned char)*comma++;
4476
4477 if (c == ',' || *cnt == 255)
4478 {
4479 if (c != ',')
4480 comma--;
4481 cnt = p++;
4482 *cnt = 0;
4483 }
4484 else
4485 {
4486 *p++ = unhide_meta(c);
4487 (*cnt)++;
4488 }
4489 }
4490
4491 new->len = p - new->txt;
4492
Simon Kelley849a8352006-06-09 21:02:31 +01004493 break;
4494 }
4495
Simon Kelleyf2621c72007-04-29 19:47:21 +01004496 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004497 {
4498 int port = 1, priority = 0, weight = 0;
4499 char *name, *target = NULL;
4500 struct mx_srv_record *new;
4501
Simon Kelleyf2621c72007-04-29 19:47:21 +01004502 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004503
Simon Kelley1f15b812009-10-13 17:49:32 +01004504 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004505 ret_err(_("bad SRV record"));
4506
Simon Kelley849a8352006-06-09 21:02:31 +01004507 if (comma)
4508 {
4509 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004510 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004511 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004512 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004513
Simon Kelley849a8352006-06-09 21:02:31 +01004514 if (comma)
4515 {
4516 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004517 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004518 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004519 {
4520 free(name);
4521 ret_err_free(_("invalid port number"), target);
4522 }
Simon Kelley824af852008-02-12 20:43:05 +00004523
Simon Kelley849a8352006-06-09 21:02:31 +01004524 if (comma)
4525 {
4526 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004527 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004528 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004529 {
4530 free(name);
4531 ret_err_free(_("invalid priority"), target);
4532 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004533 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004534 {
4535 free(name);
4536 ret_err_free(_("invalid weight"), target);
4537 }
Simon Kelley849a8352006-06-09 21:02:31 +01004538 }
4539 }
4540 }
4541
Simon Kelley824af852008-02-12 20:43:05 +00004542 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004543 new->next = daemon->mxnames;
4544 daemon->mxnames = new;
4545 new->issrv = 1;
4546 new->name = name;
4547 new->target = target;
4548 new->srvport = port;
4549 new->priority = priority;
4550 new->weight = weight;
4551 break;
4552 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004553
Simon Kelleye759d422012-03-16 13:18:57 +00004554 case LOPT_HOST_REC: /* --host-record */
4555 {
Petr Menšík59e47032018-11-02 22:39:39 +00004556 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004557
Simon Kelleye759d422012-03-16 13:18:57 +00004558 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004559 ret_err(_("Bad host-record"));
4560
Petr Menšík59e47032018-11-02 22:39:39 +00004561 new = opt_malloc(sizeof(struct host_record));
4562 memset(new, 0, sizeof(struct host_record));
4563 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004564 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004565
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004566 while (arg)
4567 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004568 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004569 char *dig;
4570
4571 for (dig = arg; *dig != 0; dig++)
4572 if (*dig < '0' || *dig > '9')
4573 break;
4574 if (*dig == 0)
4575 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004576 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004577 {
4578 new->addr = addr.addr4;
4579 new->flags |= HR_4;
4580 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004581 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004582 {
4583 new->addr6 = addr.addr6;
4584 new->flags |= HR_6;
4585 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004586 else
4587 {
4588 int nomem;
4589 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004590 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004591 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004592 {
4593 struct name_list *tmp = new->names, *next;
4594 for (tmp = new->names; tmp; tmp = next)
4595 {
4596 next = tmp->next;
4597 free(tmp);
4598 }
4599 ret_err_free(_("Bad name in host-record"), new);
4600 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004601
Petr Menšík59e47032018-11-02 22:39:39 +00004602 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004603 nl->name = canon;
4604 /* keep order, so that PTR record goes to first name */
4605 nl->next = NULL;
4606 if (!new->names)
4607 new->names = nl;
4608 else
4609 {
4610 struct name_list *tmp;
4611 for (tmp = new->names; tmp->next; tmp = tmp->next);
4612 tmp->next = nl;
4613 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004614 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004615
4616 arg = comma;
4617 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004618 }
Simon Kelleye759d422012-03-16 13:18:57 +00004619
4620 /* Keep list order */
4621 if (!daemon->host_records_tail)
4622 daemon->host_records = new;
4623 else
4624 daemon->host_records_tail->next = new;
4625 new->next = NULL;
4626 daemon->host_records_tail = new;
4627 break;
4628 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004629
4630#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004631 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004632 daemon->timestamp_file = opt_string_alloc(arg);
4633 break;
4634
Simon Kelleyf3e57872018-07-20 21:10:48 +01004635 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004636 if (arg)
4637 {
4638 if (strcmp(arg, "no") == 0)
4639 set_option_bool(OPT_DNSSEC_IGN_NS);
4640 else
4641 ret_err(_("bad value for dnssec-check-unsigned"));
4642 }
4643 break;
4644
Simon Kelleyf3e57872018-07-20 21:10:48 +01004645 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004646 {
Simon Kelleyee415862014-02-11 11:07:22 +00004647 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4648 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4649 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004650
4651 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004652 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004653
Simon Kelleycbf13a22014-01-25 17:59:14 +00004654 if ((comma = split(arg)) && (algo = split(comma)))
4655 {
4656 int class = 0;
4657 if (strcmp(comma, "IN") == 0)
4658 class = C_IN;
4659 else if (strcmp(comma, "CH") == 0)
4660 class = C_CHAOS;
4661 else if (strcmp(comma, "HS") == 0)
4662 class = C_HESIOD;
4663
4664 if (class != 0)
4665 {
4666 new->class = class;
4667 comma = algo;
4668 algo = split(comma);
4669 }
4670 }
4671
Simon Kelleyee415862014-02-11 11:07:22 +00004672 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4673 !atoi_check16(comma, &new->keytag) ||
4674 !atoi_check8(algo, &new->algo) ||
4675 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004676 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004677 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004678
Simon Kelley0fc2f312014-01-08 10:26:58 +00004679 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004680 len = (2*strlen(keyhex))+1;
4681 new->digest = opt_malloc(len);
4682 unhide_metas(keyhex);
4683 /* 4034: "Whitespace is allowed within digits" */
4684 for (cp = keyhex; *cp; )
4685 if (isspace(*cp))
4686 for (cp1 = cp; *cp1; cp1++)
4687 *cp1 = *(cp1+1);
4688 else
4689 cp++;
4690 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004691 {
4692 free(new->name);
4693 ret_err_free(_("bad HEX in trust anchor"), new);
4694 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004695
Simon Kelleyee415862014-02-11 11:07:22 +00004696 new->next = daemon->ds;
4697 daemon->ds = new;
4698
Simon Kelley0fc2f312014-01-08 10:26:58 +00004699 break;
4700 }
4701#endif
Kyle Swenson545712c2021-11-17 12:25:04 -07004702/* CRADLEPOINT */
4703/* This option (OPT_EDNS_OPTION) is required to tack a(n) owner record onto a DNS request:
4704 * Option Code: Owner (reserved) 4 (0x00 0x04)
4705 * Option Length: 19 ("Cradlepoint" + device_id (obtained from OpenDNS)) (0x00 0x13)
4706 * Option Data: "Cradlepoint" + device_id (an 4 byte identifier)
4707 *
4708 * The EDNS option is passed in like this:
4709 * --edns-option=4,CradlepointAABBCCDD
4710 */
4711 case LOPT_EDNS_OPTION:
4712 {
4713 unsigned char *tmp;
4714 int option_code, option_len = 0;
4715
4716 comma = split(arg);
4717
4718 if (*arg == 0 || !atoi_check16(arg, &option_code))
4719 {
4720 option = '?';
4721 ret_err(_("invalid EDNS option code"));
4722 break;
4723 }
4724
4725 if (comma &&
4726 (option_len = str_unescape_c_string(comma, comma)) <= 0)
4727 {
4728 option = '?';
4729 ret_err(_("invalid EDNS option data"));
4730 break;
4731 }
4732 tmp = opt_malloc(daemon->edns_options_len + 4 + option_len);
4733
4734 if (daemon->edns_options_len > 0)
4735 {
4736 memcpy(tmp, daemon->edns_options, daemon->edns_options_len);
4737 free(daemon->edns_options);
4738 }
4739 daemon->edns_options = tmp;
4740 tmp += daemon->edns_options_len;
4741 PUTSHORT(option_code, tmp);
4742 PUTSHORT(option_len, tmp);
4743 memcpy(tmp, comma, option_len);
4744 daemon->edns_options_len += 4 + option_len;
4745 my_syslog(LOG_DEBUG, _("EDNS Option %d added length %d"), option_code,
4746 daemon->edns_options_len);
4747
4748 break;
4749 }
4750
4751 case LOPT_SSID_MAP:
4752 {
4753 char *err;
4754
4755 unhide_metas(arg);
4756
4757 if ((err = opendns_parse_device_id_opt(arg)) != NULL) {
4758 /* int vs pointer warning return err; */
4759 ret_err(_("opendns parse device id failed"));
4760 }
4761
4762 break;
4763 }
Kyle Swensonba77fe82021-11-19 09:33:46 -07004764 case LOPT_EDNS_RESTRICT:
4765 {
4766 set_option_bool(OPT_EDNS_RESTRICT);
4767 break;
4768 }
Kyle Swenson545712c2021-11-17 12:25:04 -07004769/* CRADLEPOINT */
4770
Simon Kelley7622fc02009-06-04 20:32:05 +01004771 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004772 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004773
Simon Kelley849a8352006-06-09 21:02:31 +01004774 }
Simon Kelley824af852008-02-12 20:43:05 +00004775
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004776 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004777}
4778
Simon Kelley28866e92011-02-14 20:19:14 +00004779static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004780{
Simon Kelley824af852008-02-12 20:43:05 +00004781 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004782 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004783
4784 while (fgets(buff, MAXDNAME, f))
4785 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004786 int white, i;
4787 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004788 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004789 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004790
Simon Kelley824af852008-02-12 20:43:05 +00004791 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004792 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004793 {
4794 if (setjmp(mem_jmp))
4795 continue;
4796 mem_recover = 1;
4797 }
4798
Simon Kelley13dee6f2017-02-28 16:51:58 +00004799 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004800 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004801 errmess = NULL;
4802
Simon Kelley849a8352006-06-09 21:02:31 +01004803 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4804 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004805 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004806 {
4807 if (*p == '"')
4808 {
4809 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004810
Simon Kelley849a8352006-06-09 21:02:31 +01004811 for(; *p && *p != '"'; p++)
4812 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004813 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004814 {
4815 if (p[1] == 't')
4816 p[1] = '\t';
4817 else if (p[1] == 'n')
4818 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004819 else if (p[1] == 'b')
4820 p[1] = '\b';
4821 else if (p[1] == 'r')
4822 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004823 else if (p[1] == 'e') /* escape */
4824 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004825 memmove(p, p+1, strlen(p+1)+1);
4826 }
4827 *p = hide_meta(*p);
4828 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004829
4830 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004831 {
4832 errmess = _("missing \"");
4833 goto oops;
4834 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004835
4836 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004837 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004838
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004839 if (isspace(*p))
4840 {
4841 *p = ' ';
4842 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004843 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004844 else
4845 {
4846 if (white && *p == '#')
4847 {
4848 *p = 0;
4849 break;
4850 }
4851 white = 0;
4852 }
Simon Kelley849a8352006-06-09 21:02:31 +01004853 }
4854
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004855
4856 /* strip leading spaces */
4857 for (start = buff; *start && *start == ' '; start++);
4858
4859 /* strip trailing spaces */
4860 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4861
4862 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004863 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004864 else
4865 start[len] = 0;
4866
Simon Kelley611ebc52012-07-16 16:23:46 +01004867 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004868 arg = start;
4869 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004870 {
4871 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004872 for (arg = p+1; *arg == ' '; arg++);
4873 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004874 *p = 0;
4875 }
4876 else
4877 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004878
Simon Kelley611ebc52012-07-16 16:23:46 +01004879 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004880 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004881 for (option = 0, i = 0; opts[i].name; i++)
4882 if (strcmp(opts[i].name, start) == 0)
4883 {
4884 option = opts[i].val;
4885 break;
4886 }
4887
4888 if (!option)
4889 errmess = _("bad option");
4890 else if (opts[i].has_arg == 0 && arg)
4891 errmess = _("extraneous parameter");
4892 else if (opts[i].has_arg == 1 && !arg)
4893 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004894 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4895 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004896 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004897
4898 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004899 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004900 strcpy(daemon->namebuff, errmess);
4901
Simon Kelley9bafdc62018-08-21 22:53:38 +01004902 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004903 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004904 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004905 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004906 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004907 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004908 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004909 }
Simon Kelley849a8352006-06-09 21:02:31 +01004910 }
4911
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004912 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004913 fclose(f);
4914}
4915
Simon Kelley4f7bb572018-03-08 18:47:08 +00004916#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004917int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004918{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004919 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4920
Simon Kelley70d18732015-01-31 19:59:29 +00004921 if (flags & AH_DHCP_HST)
4922 return one_file(file, LOPT_BANK);
4923 else if (flags & AH_DHCP_OPT)
4924 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004925
Simon Kelley70d18732015-01-31 19:59:29 +00004926 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004927}
4928#endif
4929
Simon Kelley395eb712012-07-06 22:07:05 +01004930static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004931{
4932 FILE *f;
4933 int nofile_ok = 0;
4934 static int read_stdin = 0;
4935 static struct fileread {
4936 dev_t dev;
4937 ino_t ino;
4938 struct fileread *next;
4939 } *filesread = NULL;
4940
4941 if (hard_opt == '7')
4942 {
4943 /* default conf-file reading */
4944 hard_opt = 0;
4945 nofile_ok = 1;
4946 }
4947
4948 if (hard_opt == 0 && strcmp(file, "-") == 0)
4949 {
4950 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004951 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004952 read_stdin = 1;
4953 file = "stdin";
4954 f = stdin;
4955 }
4956 else
4957 {
4958 /* ignore repeated files. */
4959 struct stat statbuf;
4960
4961 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4962 {
4963 struct fileread *r;
4964
4965 for (r = filesread; r; r = r->next)
4966 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004967 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004968
4969 r = safe_malloc(sizeof(struct fileread));
4970 r->next = filesread;
4971 filesread = r;
4972 r->dev = statbuf.st_dev;
4973 r->ino = statbuf.st_ino;
4974 }
4975
4976 if (!(f = fopen(file, "r")))
4977 {
4978 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004979 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004980 else
4981 {
4982 char *str = _("cannot read %s: %s");
4983 if (hard_opt != 0)
4984 {
4985 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004986 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004987 }
4988 else
4989 die(str, file, EC_FILE);
4990 }
4991 }
4992 }
4993
4994 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004995 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004996}
4997
4998/* expand any name which is a directory */
4999struct hostsfile *expand_filelist(struct hostsfile *list)
5000{
Simon Kelley19c51cf2014-03-18 22:38:30 +00005001 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00005002 struct hostsfile *ah;
5003
Simon Kelley19c51cf2014-03-18 22:38:30 +00005004 /* find largest used index */
5005 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00005006 {
5007 if (i <= ah->index)
5008 i = ah->index + 1;
5009
5010 if (ah->flags & AH_DIR)
5011 ah->flags |= AH_INACTIVE;
5012 else
5013 ah->flags &= ~AH_INACTIVE;
5014 }
5015
5016 for (ah = list; ah; ah = ah->next)
5017 if (!(ah->flags & AH_INACTIVE))
5018 {
5019 struct stat buf;
5020 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
5021 {
5022 DIR *dir_stream;
5023 struct dirent *ent;
5024
5025 /* don't read this as a file */
5026 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00005027
Simon Kelley28866e92011-02-14 20:19:14 +00005028 if (!(dir_stream = opendir(ah->fname)))
5029 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
5030 ah->fname, strerror(errno));
5031 else
5032 {
5033 while ((ent = readdir(dir_stream)))
5034 {
5035 size_t lendir = strlen(ah->fname);
5036 size_t lenfile = strlen(ent->d_name);
5037 struct hostsfile *ah1;
5038 char *path;
5039
5040 /* ignore emacs backups and dotfiles */
5041 if (lenfile == 0 ||
5042 ent->d_name[lenfile - 1] == '~' ||
5043 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
5044 ent->d_name[0] == '.')
5045 continue;
5046
5047 /* see if we have an existing record.
5048 dir is ah->fname
5049 file is ent->d_name
5050 path to match is ah1->fname */
5051
5052 for (ah1 = list; ah1; ah1 = ah1->next)
5053 {
5054 if (lendir < strlen(ah1->fname) &&
5055 strstr(ah1->fname, ah->fname) == ah1->fname &&
5056 ah1->fname[lendir] == '/' &&
5057 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
5058 {
5059 ah1->flags &= ~AH_INACTIVE;
5060 break;
5061 }
5062 }
5063
5064 /* make new record */
5065 if (!ah1)
5066 {
5067 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
5068 continue;
5069
5070 if (!(path = whine_malloc(lendir + lenfile + 2)))
5071 {
5072 free(ah1);
5073 continue;
5074 }
5075
5076 strcpy(path, ah->fname);
5077 strcat(path, "/");
5078 strcat(path, ent->d_name);
5079 ah1->fname = path;
5080 ah1->index = i++;
5081 ah1->flags = AH_DIR;
5082 ah1->next = list;
5083 list = ah1;
5084 }
5085
5086 /* inactivate record if not regular file */
5087 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
5088 ah1->flags |= AH_INACTIVE;
5089
5090 }
5091 closedir(dir_stream);
5092 }
5093 }
5094 }
5095
5096 return list;
5097}
5098
Simon Kelley7b1eae42014-02-20 13:43:28 +00005099void read_servers_file(void)
5100{
5101 FILE *f;
5102
5103 if (!(f = fopen(daemon->servers_file, "r")))
5104 {
5105 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
5106 return;
5107 }
5108
5109 mark_servers(SERV_FROM_FILE);
5110 cleanup_servers();
5111
5112 read_file(daemon->servers_file, f, LOPT_REV_SERV);
5113}
5114
Simon Kelley28866e92011-02-14 20:19:14 +00005115
Simon Kelley7622fc02009-06-04 20:32:05 +01005116#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00005117static void clear_dynamic_conf(void)
5118{
5119 struct dhcp_config *configs, *cp, **up;
5120
5121 /* remove existing... */
5122 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
5123 {
5124 cp = configs->next;
5125
5126 if (configs->flags & CONFIG_BANK)
5127 {
5128 struct hwaddr_config *mac, *tmp;
5129 struct dhcp_netid_list *list, *tmplist;
5130
5131 for (mac = configs->hwaddr; mac; mac = tmp)
5132 {
5133 tmp = mac->next;
5134 free(mac);
5135 }
5136
5137 if (configs->flags & CONFIG_CLID)
5138 free(configs->clid);
5139
5140 for (list = configs->netid; list; list = tmplist)
5141 {
5142 free(list->list);
5143 tmplist = list->next;
5144 free(list);
5145 }
5146
5147 if (configs->flags & CONFIG_NAME)
5148 free(configs->hostname);
5149
5150 *up = configs->next;
5151 free(configs);
5152 }
5153 else
5154 up = &configs->next;
5155 }
5156}
5157
5158static void clear_dynamic_opt(void)
5159{
5160 struct dhcp_opt *opts, *cp, **up;
5161 struct dhcp_netid *id, *next;
5162
5163 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
5164 {
5165 cp = opts->next;
5166
5167 if (opts->flags & DHOPT_BANK)
5168 {
5169 if ((opts->flags & DHOPT_VENDOR))
5170 free(opts->u.vendor_class);
5171 free(opts->val);
5172 for (id = opts->netid; id; id = next)
5173 {
5174 next = id->next;
5175 free(id->net);
5176 free(id);
5177 }
5178 *up = opts->next;
5179 free(opts);
5180 }
5181 else
5182 up = &opts->next;
5183 }
5184}
5185
Simon Kelley824af852008-02-12 20:43:05 +00005186void reread_dhcp(void)
5187{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005188 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005189
Simon Kelley4f7bb572018-03-08 18:47:08 +00005190 /* Do these even if there is no daemon->dhcp_hosts_file or
5191 daemon->dhcp_opts_file since entries may have been created by the
5192 inotify dynamic file reading system. */
5193
5194 clear_dynamic_conf();
5195 clear_dynamic_opt();
5196
5197 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005198 {
Simon Kelley28866e92011-02-14 20:19:14 +00005199 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5200 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005201 if (!(hf->flags & AH_INACTIVE))
5202 {
5203 if (one_file(hf->fname, LOPT_BANK))
5204 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5205 }
Simon Kelley824af852008-02-12 20:43:05 +00005206 }
5207
5208 if (daemon->dhcp_opts_file)
5209 {
Simon Kelley28866e92011-02-14 20:19:14 +00005210 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5211 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5212 if (!(hf->flags & AH_INACTIVE))
5213 {
Simon Kelley395eb712012-07-06 22:07:05 +01005214 if (one_file(hf->fname, LOPT_OPTS))
5215 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005216 }
Simon Kelley824af852008-02-12 20:43:05 +00005217 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005218
5219# ifdef HAVE_INOTIFY
5220 /* Setup notify and read pre-existing files. */
5221 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5222# endif
Simon Kelley824af852008-02-12 20:43:05 +00005223}
Simon Kelley7622fc02009-06-04 20:32:05 +01005224#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005225
Simon Kelley5aabfc72007-08-29 11:24:47 +01005226void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005227{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005228 size_t argbuf_size = MAXDNAME;
5229 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005230 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005231 int option, testmode = 0;
5232 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005233
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005234 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005235
Simon Kelley824af852008-02-12 20:43:05 +00005236 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005237 memset(daemon, 0, sizeof(struct daemon));
5238 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005239
Simon Kelley3be34542004-09-11 19:12:13 +01005240 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005241 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005242 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005243 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005244 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5245 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005246 daemon->default_resolv.is_default = 1;
5247 daemon->default_resolv.name = RESOLVFILE;
5248 daemon->resolv_files = &daemon->default_resolv;
5249 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005250 daemon->runfile = RUNFILE;
5251 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005252 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005253 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005254 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005255 daemon->auth_ttl = AUTH_TTL;
5256 daemon->soa_refresh = SOA_REFRESH;
5257 daemon->soa_retry = SOA_RETRY;
5258 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley4a8c0982021-03-26 21:19:39 +00005259
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005260#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005261 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5262 add_txt("authors.bind", "Simon Kelley", 0);
5263 add_txt("copyright.bind", COPYRIGHT, 0);
5264 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5265 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5266 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5267 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5268 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5269#ifdef HAVE_AUTH
5270 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5271#endif
5272 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005273#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005274
Simon Kelley849a8352006-06-09 21:02:31 +01005275 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005276 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005277#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005278 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005279#else
Simon Kelley849a8352006-06-09 21:02:31 +01005280 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005281#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005282
5283 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005284 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005285 for (; optind < argc; optind++)
5286 {
5287 unsigned char *c = (unsigned char *)argv[optind];
5288 for (; *c != 0; c++)
5289 if (!isspace(*c))
5290 die(_("junk found in command line"), NULL, EC_BADCONF);
5291 }
Simon Kelley28866e92011-02-14 20:19:14 +00005292 break;
5293 }
5294
Simon Kelley849a8352006-06-09 21:02:31 +01005295 /* Copy optarg so that argv doesn't get changed */
5296 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005297 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005298 if (strlen(optarg) >= argbuf_size)
5299 {
5300 free(argbuf);
5301 argbuf_size = strlen(optarg) + 1;
5302 argbuf = opt_malloc(argbuf_size);
5303 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005304 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005305 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005306 }
5307 else
5308 arg = NULL;
5309
5310 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005311 if (option == LOPT_TEST)
5312 testmode = 1;
5313 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005314 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005315#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005316 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005317 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005318#ifdef HAVE_DHCP6
5319 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5320 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005321#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005322 else
5323#endif
5324 do_usage();
5325
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005326 exit(0);
5327 }
Simon Kelley849a8352006-06-09 21:02:31 +01005328 else if (option == 'v')
5329 {
5330 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005331 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005332 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5333 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005334 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005335 exit(0);
5336 }
Simon Kelley849a8352006-06-09 21:02:31 +01005337 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005338 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005339 if (!conffile)
5340 conffile = opt_string_alloc(arg);
5341 else
5342 {
5343 char *extra = opt_string_alloc(arg);
5344 one_file(extra, 0);
5345 free(extra);
5346 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005347 }
Simon Kelley849a8352006-06-09 21:02:31 +01005348 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005349 {
Simon Kelley26128d22004-11-14 16:43:54 +00005350#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005351 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005352#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005353 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005354#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005355 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005356 }
5357 }
Simon Kelley849a8352006-06-09 21:02:31 +01005358
Neil Jerram3bd4c472018-01-18 22:49:38 +00005359 free(argbuf);
5360
Simon Kelley849a8352006-06-09 21:02:31 +01005361 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005362 {
Petr Menšík59e47032018-11-02 22:39:39 +00005363 one_file(conffile, 0);
5364 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005365 }
Petr Menšík59e47032018-11-02 22:39:39 +00005366 else
5367 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005368
Simon Kelley1a6bca82008-07-11 11:11:42 +01005369 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005370 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005371 {
5372 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005373 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005374 if (!(tmp->flags & SERV_HAS_SOURCE))
5375 {
5376 if (tmp->source_addr.sa.sa_family == AF_INET)
5377 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005378 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5379 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005380 }
5381 }
5382
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005383 if (daemon->host_records)
5384 {
5385 struct host_record *hr;
5386
5387 for (hr = daemon->host_records; hr; hr = hr->next)
5388 if (hr->ttl == -1)
5389 hr->ttl = daemon->local_ttl;
5390 }
5391
5392 if (daemon->cnames)
5393 {
Simon Kelley903df072017-01-19 17:22:00 +00005394 struct cname *cn, *cn2, *cn3;
5395
5396#define NOLOOP 1
5397#define TESTLOOP 2
5398
Simon Kelley157d8cf2019-10-25 17:46:49 +01005399 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005400 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005401 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005402 {
5403 if (cn->ttl == -1)
5404 cn->ttl = daemon->local_ttl;
5405 cn->flag = 0;
5406 cn->targetp = NULL;
5407 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5408 if (hostname_isequal(cn->target, cn2->alias))
5409 {
5410 cn->targetp = cn2;
5411 break;
5412 }
5413 }
5414
5415 /* Find any CNAME loops.*/
5416 for (cn = daemon->cnames; cn; cn = cn->next)
5417 {
5418 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5419 {
5420 if (cn2->flag == NOLOOP)
5421 break;
5422
5423 if (cn2->flag == TESTLOOP)
5424 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5425
5426 cn2->flag = TESTLOOP;
5427 }
5428
5429 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5430 cn3->flag = NOLOOP;
5431 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005432 }
5433
Simon Kelley3be34542004-09-11 19:12:13 +01005434 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005435 {
5436 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005437 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005438 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005439 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005440 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005441 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005442 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005443
5444 /* create default, if not specified */
5445 if (daemon->authserver && !daemon->hostmaster)
5446 {
5447 strcpy(buff, "hostmaster.");
5448 strcat(buff, daemon->authserver);
5449 daemon->hostmaster = opt_string_alloc(buff);
5450 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005451
5452 if (!daemon->dhcp_pxe_vendors)
5453 {
5454 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5455 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5456 daemon->dhcp_pxe_vendors->next = NULL;
5457 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005458
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005459 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005460 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005461 {
Simon Kelley0a852542005-03-23 20:28:59 +00005462 struct mx_srv_record *mx;
5463
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005464 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005465 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005466
Simon Kelley0a852542005-03-23 20:28:59 +00005467 for (mx = daemon->mxnames; mx; mx = mx->next)
5468 if (!mx->issrv && hostname_isequal(mx->name, buff))
5469 break;
5470
Simon Kelley28866e92011-02-14 20:19:14 +00005471 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005472 {
Simon Kelley824af852008-02-12 20:43:05 +00005473 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005474 mx->next = daemon->mxnames;
5475 mx->issrv = 0;
5476 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005477 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005478 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005479 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005480
Simon Kelley3be34542004-09-11 19:12:13 +01005481 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005482 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005483
5484 for (mx = daemon->mxnames; mx; mx = mx->next)
5485 if (!mx->issrv && !mx->target)
5486 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005487 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005488
Simon Kelley28866e92011-02-14 20:19:14 +00005489 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005490 daemon->resolv_files &&
5491 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005492 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005493 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005494
Simon Kelley28866e92011-02-14 20:19:14 +00005495 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005496 {
5497 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005498 FILE *f;
5499
Simon Kelley28866e92011-02-14 20:19:14 +00005500 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005501 !daemon->resolv_files ||
5502 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005503 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005504
Simon Kelley3be34542004-09-11 19:12:13 +01005505 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005506 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005507
5508 while ((line = fgets(buff, MAXDNAME, f)))
5509 {
5510 char *token = strtok(line, " \t\n\r");
5511
5512 if (!token || strcmp(token, "search") != 0)
5513 continue;
5514
5515 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005516 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005517 break;
5518 }
Simon Kelley3be34542004-09-11 19:12:13 +01005519
Simon Kelleyde379512004-06-22 20:23:33 +01005520 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005521
Simon Kelley3be34542004-09-11 19:12:13 +01005522 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005523 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005524 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005525
5526 if (daemon->domain_suffix)
5527 {
5528 /* add domain for any srv record without one. */
5529 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005530
Simon Kelley3d8df262005-08-29 12:19:27 +01005531 for (srv = daemon->mxnames; srv; srv = srv->next)
5532 if (srv->issrv &&
5533 strchr(srv->name, '.') &&
5534 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5535 {
5536 strcpy(buff, srv->name);
5537 strcat(buff, ".");
5538 strcat(buff, daemon->domain_suffix);
5539 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005540 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005541 }
5542 }
Simon Kelley28866e92011-02-14 20:19:14 +00005543 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005544 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005545
Simon Kelleyc8a80482014-03-05 14:29:54 +00005546 /* If there's access-control config, then ignore --local-service, it's intended
5547 as a system default to keep otherwise unconfigured installations safe. */
5548 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5549 reset_option_bool(OPT_LOCAL_SERVICE);
5550
Simon Kelley7622fc02009-06-04 20:32:05 +01005551 if (testmode)
5552 {
5553 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5554 exit(0);
5555 }
Simon Kelley849a8352006-06-09 21:02:31 +01005556}