blob: ed17e3d377c86d488a95cb5747a0b83d46b00a1d [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 */
Simon Kelley849a8352006-06-09 21:02:31 +0100182#ifdef HAVE_GETOPT_LONG
183static const struct option opts[] =
184#else
185static const struct myoption opts[] =
186#endif
187 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100188 { "version", 0, 0, 'v' },
189 { "no-hosts", 0, 0, 'h' },
190 { "no-poll", 0, 0, 'n' },
191 { "help", 0, 0, 'w' },
192 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000193 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100194 { "user", 2, 0, 'u' },
195 { "group", 2, 0, 'g' },
196 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000197 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100198 { "mx-host", 1, 0, 'm' },
199 { "mx-target", 1, 0, 't' },
200 { "cache-size", 2, 0, 'c' },
201 { "port", 1, 0, 'p' },
202 { "dhcp-leasefile", 2, 0, 'l' },
203 { "dhcp-lease", 1, 0, 'l' },
204 { "dhcp-host", 1, 0, 'G' },
205 { "dhcp-range", 1, 0, 'F' },
206 { "dhcp-option", 1, 0, 'O' },
207 { "dhcp-boot", 1, 0, 'M' },
208 { "domain", 1, 0, 's' },
209 { "domain-suffix", 1, 0, 's' },
210 { "interface", 1, 0, 'i' },
211 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000212 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100213 { "bogus-priv", 0, 0, 'b' },
214 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000215 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100216 { "selfmx", 0, 0, 'e' },
217 { "filterwin2k", 0, 0, 'f' },
218 { "pid-file", 2, 0, 'x' },
219 { "strict-order", 0, 0, 'o' },
220 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000221 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100222 { "local", 1, 0, LOPT_LOCAL },
223 { "address", 1, 0, 'A' },
224 { "conf-file", 2, 0, 'C' },
225 { "no-resolv", 0, 0, 'R' },
226 { "expand-hosts", 0, 0, 'E' },
227 { "localmx", 0, 0, 'L' },
228 { "local-ttl", 1, 0, 'T' },
229 { "no-negcache", 0, 0, 'N' },
230 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000231 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100232 { "query-port", 1, 0, 'Q' },
233 { "except-interface", 1, 0, 'I' },
234 { "no-dhcp-interface", 1, 0, '2' },
235 { "domain-needed", 0, 0, 'D' },
236 { "dhcp-lease-max", 1, 0, 'X' },
237 { "bind-interfaces", 0, 0, 'z' },
238 { "read-ethers", 0, 0, 'Z' },
239 { "alias", 1, 0, 'V' },
240 { "dhcp-vendorclass", 1, 0, 'U' },
241 { "dhcp-userclass", 1, 0, 'j' },
242 { "dhcp-ignore", 1, 0, 'J' },
243 { "edns-packet-max", 1, 0, 'P' },
244 { "keep-in-foreground", 0, 0, 'k' },
245 { "dhcp-authoritative", 0, 0, 'K' },
246 { "srv-host", 1, 0, 'W' },
247 { "localise-queries", 0, 0, 'y' },
248 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100249 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100250 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100251 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100252 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100253 { "bootp-dynamic", 2, 0, '3' },
254 { "dhcp-mac", 1, 0, '4' },
255 { "no-ping", 0, 0, '5' },
256 { "dhcp-script", 1, 0, '6' },
257 { "conf-dir", 1, 0, '7' },
258 { "log-facility", 1, 0 ,'8' },
259 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000260 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100261 { "dns-forward-max", 1, 0, '0' },
262 { "clear-on-reload", 0, 0, LOPT_RELOAD },
263 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100264 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100265 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100266 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100267 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100268 { "tftp-root", 1, 0, LOPT_PREFIX },
269 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000270 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100271 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000272 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100273 { "ptr-record", 1, 0, LOPT_PTR },
274 { "naptr-record", 1, 0, LOPT_NAPTR },
275 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000276 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100277 { "dhcp-option-force", 1, 0, LOPT_FORCE },
278 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
279 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
280 { "log-async", 2, 0, LOPT_MAX_LOGS },
281 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
282 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
283 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800284 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100285 { "interface-name", 1, 0, LOPT_INTNAME },
286 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
287 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000288 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000289 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100290 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
291 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
292 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100293 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100294 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100295 { "dhcp-match", 1, 0, LOPT_MATCH },
296 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100297 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100298 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100299 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000300 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100301 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100302 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
303 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
304 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000305 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100306 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
307 { "cname", 1, 0, LOPT_CNAME },
308 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
309 { "pxe-service", 1, 0, LOPT_PXE_SERV },
310 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100311 { "tag-if", 1, 0, LOPT_TAG_IF },
312 { "dhcp-proxy", 2, 0, LOPT_PROXY },
313 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
314 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000315 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100316 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000317 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000318 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100319 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
320 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000321 { "dhcp-client-update", 0, 0, LOPT_FQDN },
322 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000323 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000324 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000325 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100326 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000327 { "auth-zone", 1, 0, LOPT_AUTHZONE },
328 { "auth-server", 1, 0, LOPT_AUTHSERV },
329 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
330 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000331 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000332 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000333 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100334 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200335 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000336 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000337 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100338 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000339 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000340 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100341 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100342 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100343 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
344 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
345 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100346 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000347 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000348 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100349 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000350 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100351 { "dumpfile", 1, 0, LOPT_DUMPFILE },
352 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200353 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000354 { "dynamic-host", 1, 0, LOPT_DYNHOST },
Simon Kelleyb260d222021-03-12 21:57:57 +0000355 { "log-debug", 0, 0, LOPT_LOG_DEBUG },
Kyle Swenson545712c2021-11-17 12:25:04 -0700356/* CRADLEPOINT */
357 { "edns-option", 1, 0, LOPT_EDNS_OPTION },
358 { "ssid-map", 1, 0, LOPT_SSID_MAP },
Kyle Swensonba77fe82021-11-19 09:33:46 -0700359 { "edns-restrict", 0, 0, LOPT_EDNS_RESTRICT },
Kyle Swenson545712c2021-11-17 12:25:04 -0700360/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +0100361 { NULL, 0, 0, 0 }
362 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000363
Simon Kelley28866e92011-02-14 20:19:14 +0000364
365#define ARG_DUP OPT_LAST
366#define ARG_ONE OPT_LAST + 1
367#define ARG_USED_CL OPT_LAST + 2
368#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000369
Simon Kelley1a6bca82008-07-11 11:11:42 +0100370static struct {
371 int opt;
372 unsigned int rept;
373 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000374 char * const desc;
375 char * const arg;
376} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000377 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
378 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100379 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000380 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
381 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
382 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100383 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
384 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
385 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
386 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
387 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000388 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
389 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100390 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000391 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
392 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000393 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000394 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100395 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100396 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000397 { '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 +0000398 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000399 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
400 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100401 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
402 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
403 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
404 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800405 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100406 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
407 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100408 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
409 { '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 +0000410 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100411 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000412 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100413 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
414 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
415 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
416 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
417 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
418 { 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 +0000419 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
420 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000421 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000422 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100423 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000424 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000425 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000426 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000427 { 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 +0000428 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000429 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000430 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
431 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
432 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
433 { 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 +0000434 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
435 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000436 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100437 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100438 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000439 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
440 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100441 { '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 +0000442 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
443 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100444 { '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 +0000445 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
446 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
447 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100448 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
449 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100450 { '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 +0100451 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000452 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100453 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
454 { '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 +0000455 { 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 +0000456 { 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 +0100457 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000458 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
459 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
460 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000461 { 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 +0000462 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000463 { '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 +0100464 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000465 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100466 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100467 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100468 { 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 +0100469 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100470 { 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 +0100471 { 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 +0100472 { 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 +0100473 { 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 +0000474 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000475 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100476 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100477 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100478 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000479 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100480 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000481 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100482 { 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 +0100483 { 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 +0000484 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100485 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100486 { 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 +0100487 { 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 +0100488 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100489 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
490 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000491 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000492 { 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 +0000493 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
494 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100495 { 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 +0000496 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100497 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
498 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
499 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000500 { 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 +0100501 { 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 +0000502 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100503 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100504 { 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 +0200505 { 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 +0100506 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000507 { 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 +0000508 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000509 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000510 { 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 +0000511 { 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 +0100512 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100513 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000514 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000515 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000516 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000517 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000518 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000519 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
520 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100521 { 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 +0100522 { 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 +0000523 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000524 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000525 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100526 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000527 { 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 +0000528 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100529 { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100530 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
531 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
532 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelleyb260d222021-03-12 21:57:57 +0000533 { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000534 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
535 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000536 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000537 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100538 { 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 +0000539 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100540 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
541 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000542 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100543 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000544};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000545
Josh Soref730c6742017-02-06 16:14:04 +0000546/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100547 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 +0100548 following sequence so that they map to themselves: it is therefore possible to call
549 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000550 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100551 couple of other places.
552 Note that space is included here so that
553 --dhcp-option=3, string
554 has five characters, whilst
555 --dhcp-option=3," string"
556 has six.
557*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100558
Simon Kelleyf2621c72007-04-29 19:47:21 +0100559static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100560
561static char hide_meta(char c)
562{
563 unsigned int i;
564
565 for (i = 0; i < (sizeof(meta) - 1); i++)
566 if (c == meta[i])
567 return (char)i;
568
569 return c;
570}
571
572static char unhide_meta(char cr)
573{
574 unsigned int c = cr;
575
576 if (c < (sizeof(meta) - 1))
577 cr = meta[c];
578
579 return cr;
580}
581
582static void unhide_metas(char *cp)
583{
584 if (cp)
585 for(; *cp; cp++)
586 *cp = unhide_meta(*cp);
587}
588
Simon Kelley824af852008-02-12 20:43:05 +0000589static void *opt_malloc(size_t size)
590{
591 void *ret;
592
593 if (mem_recover)
594 {
595 ret = whine_malloc(size);
596 if (!ret)
597 longjmp(mem_jmp, 1);
598 }
599 else
600 ret = safe_malloc(size);
601
602 return ret;
603}
604
Petr Menšík59e47032018-11-02 22:39:39 +0000605static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100606{
607 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000608 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100609
Petr Menšík59e47032018-11-02 22:39:39 +0000610 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100611 {
Petr Menšík59e47032018-11-02 22:39:39 +0000612 ret = opt_malloc(len+1);
613 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100614
615 /* restore hidden metachars */
616 unhide_metas(ret);
617 }
618
619 return ret;
620}
621
Simon Kelley3d8df262005-08-29 12:19:27 +0100622
Simon Kelleyf2621c72007-04-29 19:47:21 +0100623/* find next comma, split string with zero and eliminate spaces.
624 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000625
626static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100627{
628 char *comma, *p;
629
Simon Kelley73a08a22009-02-05 20:28:08 +0000630 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100631 return NULL;
632
633 p = comma;
634 *comma = ' ';
635
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100636 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100637
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100638 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100639 *p = 0;
640
641 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100642}
643
Simon Kelley73a08a22009-02-05 20:28:08 +0000644static char *split(char *s)
645{
646 return split_chr(s, ',');
647}
648
Simon Kelley1f15b812009-10-13 17:49:32 +0100649static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100650{
Simon Kelley1f15b812009-10-13 17:49:32 +0100651 char *ret;
652 int nomem;
653
Simon Kelley3d8df262005-08-29 12:19:27 +0100654 if (!s)
655 return 0;
656
657 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100658 if (!(ret = canonicalise(s, &nomem)) && nomem)
659 {
660 if (mem_recover)
661 longjmp(mem_jmp, 1);
662 else
663 die(_("could not get memory"), NULL, EC_NOMEM);
664 }
665
666 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100667}
668
669static int atoi_check(char *a, int *res)
670{
671 char *p;
672
673 if (!a)
674 return 0;
675
676 unhide_metas(a);
677
678 for (p = a; *p; p++)
679 if (*p < '0' || *p > '9')
680 return 0;
681
682 *res = atoi(a);
683 return 1;
684}
685
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100686static int atoi_check16(char *a, int *res)
687{
688 if (!(atoi_check(a, res)) ||
689 *res < 0 ||
690 *res > 0xffff)
691 return 0;
692
693 return 1;
694}
Simon Kelleyee415862014-02-11 11:07:22 +0000695
Simon Kelleyde73a492014-02-17 21:43:27 +0000696#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000697static int atoi_check8(char *a, int *res)
698{
699 if (!(atoi_check(a, res)) ||
700 *res < 0 ||
701 *res > 0xff)
702 return 0;
703
704 return 1;
705}
Simon Kelleyde73a492014-02-17 21:43:27 +0000706#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100707
708#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000709static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000710{
Simon Kelley824af852008-02-12 20:43:05 +0000711 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000712
713 if (txt)
714 {
715 size_t len = strlen(txt);
716 r->txt = opt_malloc(len+1);
717 r->len = len+1;
718 *(r->txt) = len;
719 memcpy((r->txt)+1, txt, len);
720 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100721
Simon Kelleyfec216d2014-03-27 20:54:34 +0000722 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000723 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000724 r->next = daemon->txt;
725 daemon->txt = r;
726 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000727}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100728#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000729
Simon Kelley849a8352006-06-09 21:02:31 +0100730static void do_usage(void)
731{
732 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000733 int i, j;
734
735 struct {
736 char handle;
737 int val;
738 } tab[] = {
739 { '$', CACHESIZ },
740 { '*', EDNS_PKTSZ },
741 { '&', MAXLEASES },
742 { '!', FTABSIZ },
743 { '#', TFTP_MAX_CONNECTIONS },
744 { '\0', 0 }
745 };
Simon Kelley849a8352006-06-09 21:02:31 +0100746
747 printf(_("Usage: dnsmasq [options]\n\n"));
748#ifndef HAVE_GETOPT_LONG
749 printf(_("Use short options only on the command line.\n"));
750#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100751 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100752
Simon Kelley1a6bca82008-07-11 11:11:42 +0100753 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100754 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100755 char *desc = usage[i].flagdesc;
756 char *eq = "=";
757
758 if (!desc || *desc == '[')
759 eq = "";
760
761 if (!desc)
762 desc = "";
763
764 for ( j = 0; opts[j].name; j++)
765 if (opts[j].val == usage[i].opt)
766 break;
767 if (usage[i].opt < 256)
768 sprintf(buff, "-%c, ", usage[i].opt);
769 else
770 sprintf(buff, " ");
771
772 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100773 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100774
Simon Kelley849a8352006-06-09 21:02:31 +0100775 if (usage[i].arg)
776 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000777 strcpy(buff, usage[i].arg);
778 for (j = 0; tab[j].handle; j++)
779 if (tab[j].handle == *(usage[i].arg))
780 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100781 }
Simon Kelley849a8352006-06-09 21:02:31 +0100782 printf(_(usage[i].desc), buff);
783 printf("\n");
784 }
785}
786
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100787#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000788#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
789#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100790
Ed Bardsleya7369be2015-08-05 21:17:18 +0100791static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
792{
793 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
794 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100795 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
796 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100797 else
798 return _("bad address");
799
800 return NULL;
801}
802
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100803char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
804{
805 int source_port = 0, serv_port = NAMESERVER_PORT;
806 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000807 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100808 int scope_index = 0;
809 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100810
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000811 if (!arg || strlen(arg) == 0)
812 {
813 *flags |= SERV_NO_ADDR;
814 *interface = 0;
815 return NULL;
816 }
817
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100818 if ((source = split_chr(arg, '@')) && /* is there a source. */
819 (portno = split_chr(source, '#')) &&
820 !atoi_check16(portno, &source_port))
821 return _("bad port");
822
823 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
824 !atoi_check16(portno, &serv_port))
825 return _("bad port");
826
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100827 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100828
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000829 if (source) {
830 interface_opt = split_chr(source, '@');
831
832 if (interface_opt)
833 {
834#if defined(SO_BINDTODEVICE)
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000835 safe_strncpy(interface, source, IF_NAMESIZE);
836 source = interface_opt;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000837#else
838 return _("interface binding not supported");
839#endif
840 }
841 }
842
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100843 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100844 {
845 addr->in.sin_port = htons(serv_port);
846 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
847#ifdef HAVE_SOCKADDR_SA_LEN
848 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
849#endif
850 source_addr->in.sin_addr.s_addr = INADDR_ANY;
851 source_addr->in.sin_port = htons(daemon->query_port);
852
853 if (source)
854 {
855 if (flags)
856 *flags |= SERV_HAS_SOURCE;
857 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100858 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100859 {
860#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000861 if (interface_opt)
862 return _("interface can only be specified once");
863
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100864 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200865 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100866#else
867 return _("interface binding not supported");
868#endif
869 }
870 }
871 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100872 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
873 {
874 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
875 return _("bad interface name");
876
877 addr->in6.sin6_port = htons(serv_port);
878 addr->in6.sin6_scope_id = scope_index;
879 source_addr->in6.sin6_addr = in6addr_any;
880 source_addr->in6.sin6_port = htons(daemon->query_port);
881 source_addr->in6.sin6_scope_id = 0;
882 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
883 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
884#ifdef HAVE_SOCKADDR_SA_LEN
885 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
886#endif
887 if (source)
888 {
889 if (flags)
890 *flags |= SERV_HAS_SOURCE;
891 source_addr->in6.sin6_port = htons(source_port);
892 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
893 {
894#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000895 if (interface_opt)
896 return _("interface can only be specified once");
897
898 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200899 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100900#else
901 return _("interface binding not supported");
902#endif
903 }
904 }
905 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100906 else
907 return _("bad address");
908
909 return NULL;
910}
911
Simon Kelleyde73a492014-02-17 21:43:27 +0000912static struct server *add_rev4(struct in_addr addr, int msize)
913{
914 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000915 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000916 char *p;
917
918 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000919 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
920
921 switch (msize)
922 {
923 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100924 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000925 /* fall through */
926 case 24:
927 p += sprintf(p, "%d.", (a >> 8) & 0xff);
928 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000929 case 16:
930 p += sprintf(p, "%d.", (a >> 16) & 0xff);
931 /* fall through */
932 case 8:
933 p += sprintf(p, "%d.", (a >> 24) & 0xff);
934 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000935 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000936 free(serv->domain);
937 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000938 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000939 }
940
941 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000942
943 serv->flags = SERV_HAS_DOMAIN;
944 serv->next = daemon->servers;
945 daemon->servers = serv;
946
947 return serv;
948
949}
950
951static struct server *add_rev6(struct in6_addr *addr, int msize)
952{
953 struct server *serv = opt_malloc(sizeof(struct server));
954 char *p;
955 int i;
956
957 memset(serv, 0, sizeof(struct server));
958 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
959
960 for (i = msize-1; i >= 0; i -= 4)
961 {
962 int dig = ((unsigned char *)addr)[i>>3];
963 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
964 }
965 p += sprintf(p, "ip6.arpa");
966
967 serv->flags = SERV_HAS_DOMAIN;
968 serv->next = daemon->servers;
969 daemon->servers = serv;
970
971 return serv;
972}
973
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000974#ifdef HAVE_DHCP
975
976static int is_tag_prefix(char *arg)
977{
978 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
979 return 1;
980
981 return 0;
982}
983
984static char *set_prefix(char *arg)
985{
986 if (strstr(arg, "set:") == arg)
987 return arg+4;
988
989 return arg;
990}
991
Simon Kelley52ec7832020-02-07 21:05:54 +0000992static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000993{
994 struct dhcp_netid *tt;
995 tt = opt_malloc(sizeof (struct dhcp_netid));
996 tt->net = opt_string_alloc(net);
997 tt->next = next;
998 return tt;
999}
1000
1001static void dhcp_netid_free(struct dhcp_netid *nid)
1002{
1003 while (nid)
1004 {
1005 struct dhcp_netid *tmp = nid;
1006 nid = nid->next;
1007 free(tmp->net);
1008 free(tmp);
1009 }
1010}
1011
1012/* Parse one or more tag:s before parameters.
1013 * Moves arg to the end of tags. */
1014static struct dhcp_netid * dhcp_tags(char **arg)
1015{
1016 struct dhcp_netid *id = NULL;
1017
1018 while (is_tag_prefix(*arg))
1019 {
1020 char *comma = split(*arg);
1021 id = dhcp_netid_create((*arg)+4, id);
1022 *arg = comma;
1023 };
1024 if (!*arg)
1025 {
1026 dhcp_netid_free(id);
1027 id = NULL;
1028 }
1029 return id;
1030}
1031
1032static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1033{
1034 while (netid)
1035 {
1036 struct dhcp_netid_list *tmplist = netid;
1037 netid = netid->next;
1038 dhcp_netid_free(tmplist->list);
1039 free(tmplist);
1040 }
1041}
1042
1043static void dhcp_config_free(struct dhcp_config *config)
1044{
1045 if (config)
1046 {
1047 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001048
Petr Menšík59e47032018-11-02 22:39:39 +00001049 while (hwaddr)
1050 {
1051 struct hwaddr_config *tmp = hwaddr;
1052 hwaddr = hwaddr->next;
1053 free(tmp);
1054 }
Simon Kelley137286e2020-02-06 22:09:30 +00001055
Petr Menšík59e47032018-11-02 22:39:39 +00001056 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001057 dhcp_netid_free(config->filter);
1058
Petr Menšík59e47032018-11-02 22:39:39 +00001059 if (config->flags & CONFIG_CLID)
1060 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001061
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001062#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001063 if (config->flags & CONFIG_ADDR6)
1064 {
1065 struct addrlist *addr, *tmp;
1066
1067 for (addr = config->addr6; addr; addr = tmp)
1068 {
1069 tmp = addr->next;
1070 free(addr);
1071 }
1072 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001073#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001074
Petr Menšík59e47032018-11-02 22:39:39 +00001075 free(config);
1076 }
1077}
1078
1079static void dhcp_context_free(struct dhcp_context *ctx)
1080{
1081 if (ctx)
1082 {
1083 dhcp_netid_free(ctx->filter);
1084 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001085#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001086 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001087#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001088 free(ctx);
1089 }
1090}
1091
1092static void dhcp_opt_free(struct dhcp_opt *opt)
1093{
1094 if (opt->flags & DHOPT_VENDOR)
1095 free(opt->u.vendor_class);
1096 dhcp_netid_free(opt->netid);
1097 free(opt->val);
1098 free(opt);
1099}
1100
1101
Simon Kelley832af0b2007-01-21 20:01:28 +00001102/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001103static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001104{
Simon Kelley824af852008-02-12 20:43:05 +00001105 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001106 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001107 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001108 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001109 u16 opt_len = 0;
1110 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001111 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001112
1113 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001114 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001115 new->netid = NULL;
1116 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001117 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001118
Simon Kelleyf2621c72007-04-29 19:47:21 +01001119 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001120 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001121 comma = split(arg);
1122
1123 for (cp = arg; *cp; cp++)
1124 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001125 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001126
1127 if (!*cp)
1128 {
1129 new->opt = atoi(arg);
1130 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001131 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001132 break;
1133 }
1134
1135 if (strstr(arg, "option:") == arg)
1136 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001137 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1138 {
1139 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1140 /* option:<optname> must follow tag and vendor string. */
1141 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1142 option_ok = 1;
1143 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001144 break;
1145 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001146#ifdef HAVE_DHCP6
1147 else if (strstr(arg, "option6:") == arg)
1148 {
1149 for (cp = arg+8; *cp; cp++)
1150 if (*cp < '0' || *cp > '9')
1151 break;
1152
1153 if (!*cp)
1154 {
1155 new->opt = atoi(arg+8);
1156 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001157 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001158 }
1159 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001160 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001161 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1162 {
1163 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1164 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1165 option_ok = 1;
1166 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001167 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001168 /* option6:<opt>|<optname> must follow tag and vendor string. */
1169 is6 = 1;
1170 break;
1171 }
1172#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001173 else if (strstr(arg, "vendor:") == arg)
1174 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001175 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1176 new->flags |= DHOPT_VENDOR;
1177 }
1178 else if (strstr(arg, "encap:") == arg)
1179 {
1180 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001181 new->flags |= DHOPT_ENCAPSULATE;
1182 }
Simon Kelley316e2732010-01-22 20:16:09 +00001183 else if (strstr(arg, "vi-encap:") == arg)
1184 {
1185 new->u.encap = atoi(arg+9);
1186 new->flags |= DHOPT_RFC3925;
1187 if (flags == DHOPT_MATCH)
1188 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001189 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001190 break;
1191 }
1192 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001193 else
1194 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001195 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001196 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1197 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001198 }
1199
1200 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001201 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001202
1203#ifdef HAVE_DHCP6
1204 if (is6)
1205 {
1206 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001207 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001208
1209 if (opt_len == 0 &&
1210 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001211 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001212 }
1213 else
1214#endif
1215 if (opt_len == 0 &&
1216 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001217 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001218
Simon Kelley316e2732010-01-22 20:16:09 +00001219 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001220 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001221 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001222
1223 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001224 {
1225 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001226 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001227 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001228 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001229 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001230 dots = 0;
1231 for (cp = comma; (c = *cp); cp++)
1232 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001233 {
1234 addrs++;
1235 is_dec = is_hex = 0;
1236 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001237 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001238 {
1239 digs++;
1240 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001241 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001242 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001243 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001244 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001245 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001246 if (cp == comma) /* leading / means a pathname */
1247 is_addr = 0;
1248 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001249 else if (c == '.')
1250 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001251 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001252 dots++;
1253 }
1254 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001255 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001256 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001257 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001258 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001259 {
1260 is_addr = 0;
1261 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001262 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001263 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001264 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001265 *cp = 0;
1266 }
1267 else
1268 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001269 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001270 (c >='a' && c <= 'f') ||
1271 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001272 {
1273 is_hex = 0;
1274 if (c != '[' && c != ']')
1275 is_addr6 = 0;
1276 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001277 }
Simon Kelley28866e92011-02-14 20:19:14 +00001278 else
1279 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001280
Simon Kelley28866e92011-02-14 20:19:14 +00001281 if (!found_dig)
1282 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001283
1284 if (!found_colon)
1285 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001286
1287#ifdef HAVE_DHCP6
1288 /* NTP server option takes hex, addresses or FQDN */
1289 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1290 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1291#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001292
Simon Kelleyf2621c72007-04-29 19:47:21 +01001293 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001294 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001295 {
1296 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001297
1298 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001299 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001300
1301 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001302 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001303 }
Simon Kelley28866e92011-02-14 20:19:14 +00001304 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001305 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1306 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001307
1308 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1309 {
1310 int val, fac = 1;
1311
1312 switch (comma[strlen(comma) - 1])
1313 {
Simon Kelley42243212012-07-20 15:19:18 +01001314 case 'w':
1315 case 'W':
1316 fac *= 7;
1317 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001318 case 'd':
1319 case 'D':
1320 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001321 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001322 case 'h':
1323 case 'H':
1324 fac *= 60;
1325 /* fall through */
1326 case 'm':
1327 case 'M':
1328 fac *= 60;
1329 /* fall through */
1330 case 's':
1331 case 'S':
1332 comma[strlen(comma) - 1] = 0;
1333 }
1334
1335 new->len = 4;
1336 new->val = opt_malloc(4);
1337 val = atoi(comma);
1338 *((int *)new->val) = htonl(val * fac);
1339 }
1340 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001341 {
1342 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001343 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001344 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1345 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001346 }
1347 else if (is_dec)
1348 {
1349 int i, val = atoi(comma);
1350 /* assume numeric arg is 1 byte except for
1351 options where it is known otherwise.
1352 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001353 if (opt_len != 0)
1354 new->len = opt_len;
1355 else if (val & 0xffff0000)
1356 new->len = 4;
1357 else if (val & 0xff00)
1358 new->len = 2;
1359 else
1360 new->len = 1;
1361
Simon Kelley832af0b2007-01-21 20:01:28 +00001362 if (lenchar == 'b')
1363 new->len = 1;
1364 else if (lenchar == 's')
1365 new->len = 2;
1366 else if (lenchar == 'i')
1367 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001368
Simon Kelley824af852008-02-12 20:43:05 +00001369 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001370 for (i=0; i<new->len; i++)
1371 new->val[i] = val>>((new->len - i - 1)*8);
1372 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001373 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001374 {
1375 struct in_addr in;
1376 unsigned char *op;
1377 char *slash;
1378 /* max length of address/subnet descriptor is five bytes,
1379 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001380 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001381 new->flags |= DHOPT_ADDR;
1382
Simon Kelley572b41e2011-02-18 18:11:18 +00001383 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1384 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001385 {
Simon Kelley6b010842007-02-12 20:32:07 +00001386 *(op++) = 1; /* RFC 3361 "enc byte" */
1387 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001388 }
1389 while (addrs--)
1390 {
1391 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001392 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001393 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001394 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001395 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001396 if (!slash)
1397 {
1398 memcpy(op, &in, INADDRSZ);
1399 op += INADDRSZ;
1400 }
1401 else
1402 {
1403 unsigned char *p = (unsigned char *)&in;
1404 int netsize = atoi(slash);
1405 *op++ = netsize;
1406 if (netsize > 0)
1407 *op++ = *p++;
1408 if (netsize > 8)
1409 *op++ = *p++;
1410 if (netsize > 16)
1411 *op++ = *p++;
1412 if (netsize > 24)
1413 *op++ = *p++;
1414 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1415 }
1416 }
1417 new->len = op - new->val;
1418 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001419 else if (is_addr6 && is6)
1420 {
1421 unsigned char *op;
1422 new->val = op = opt_malloc(16 * addrs);
1423 new->flags |= DHOPT_ADDR6;
1424 while (addrs--)
1425 {
1426 cp = comma;
1427 comma = split(cp);
1428
1429 /* check for [1234::7] */
1430 if (*cp == '[')
1431 cp++;
1432 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1433 cp[strlen(cp)-1] = 0;
1434
1435 if (inet_pton(AF_INET6, cp, op))
1436 {
1437 op += IN6ADDRSZ;
1438 continue;
1439 }
Petr Menšík59e47032018-11-02 22:39:39 +00001440
1441 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001442 }
1443 new->len = op - new->val;
1444 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001445 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001446 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001447 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001448 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001449 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001450 {
1451 /* dns search, RFC 3397, or SIP, RFC 3361 */
1452 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001453 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001454 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001455 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001456
1457 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001458 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001459
1460 while (arg && *arg)
1461 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001462 char *in, *dom = NULL;
1463 size_t domlen = 1;
1464 /* Allow "." as an empty domain */
1465 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001466 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001467 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001468 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001469
Simon Kelleyc52e1892010-06-07 22:01:39 +01001470 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001471 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001472
1473 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001474 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001475 {
1476 memcpy(newp, m, header_size + len);
1477 free(m);
1478 }
Simon Kelley824af852008-02-12 20:43:05 +00001479 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001480 p = m + header_size;
1481 q = p + len;
1482
1483 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001484 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001485 {
1486 unsigned char *cp = q++;
1487 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001488 for (j = 0; *in && (*in != '.'); in++, j++)
1489 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001490 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001491 if (*in)
1492 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001493 }
1494 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001495 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001496
Simon Kelley832af0b2007-01-21 20:01:28 +00001497 /* Now tail-compress using earlier names. */
1498 newlen = q - p;
1499 for (tail = p + len; *tail; tail += (*tail) + 1)
1500 for (r = p; r - p < (int)len; r += (*r) + 1)
1501 if (strcmp((char *)r, (char *)tail) == 0)
1502 {
1503 PUTSHORT((r - p) | 0xc000, tail);
1504 newlen = tail - p;
1505 goto end;
1506 }
1507 end:
1508 len = newlen;
1509
1510 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001511 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001512 }
1513
1514 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001515 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001516 m[0] = 0;
1517 new->len = (int) len + header_size;
1518 new->val = m;
1519 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001520#ifdef HAVE_DHCP6
1521 else if (comma && (opt_len & OT_CSTRING))
1522 {
1523 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001524 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001525 unsigned char *p, *newp;
1526
Simon Kelley40ef23b2012-03-13 21:59:28 +00001527 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001528 if (comma[i] == ',')
1529 commas++;
1530
1531 newp = opt_malloc(strlen(comma)+(2*commas));
1532 p = newp;
1533 arg = comma;
1534 comma = split(arg);
1535
1536 while (arg && *arg)
1537 {
1538 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001539 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001540 PUTSHORT(len, p);
1541 memcpy(p, arg, len);
1542 p += len;
1543
1544 arg = comma;
1545 comma = split(arg);
1546 }
1547
1548 new->val = newp;
1549 new->len = p - newp;
1550 }
1551 else if (comma && (opt_len & OT_RFC1035_NAME))
1552 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001553 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001554 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001555 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001556 arg = comma;
1557 comma = split(arg);
1558
1559 while (arg && *arg)
1560 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001561 char *dom = canonicalise_opt(arg);
1562 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001563 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001564
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001565 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001566
1567 if (p)
1568 {
1569 memcpy(newp, p, len);
1570 free(p);
1571 }
1572
1573 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001574 q = p + len;
1575 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001576 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001577 if (is6 && new->opt == OPTION6_NTP_SERVER)
1578 {
1579 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1580 PUTSHORT(end - q - 2, q);
1581 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001582 len = end - p;
1583 free(dom);
1584
Simon Kelley4cb1b322012-02-06 14:30:41 +00001585 arg = comma;
1586 comma = split(arg);
1587 }
1588
Simon Kelley18f0fb02012-03-31 21:18:55 +01001589 new->val = p;
1590 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001591 }
1592#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001593 else
1594 {
1595 new->len = strlen(comma);
1596 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001597 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001598 new->flags |= DHOPT_STRING;
1599 }
1600 }
1601 }
1602
Simon Kelley4cb1b322012-02-06 14:30:41 +00001603 if (!is6 &&
1604 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001605 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001606 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001607 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001608
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001609 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001610 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001611 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1612 !new->netid ||
1613 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001614 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001615
1616 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001617 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001618 new->next = daemon->dhcp_match6;
1619 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001620 }
1621 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001622 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001623 new->next = daemon->dhcp_match;
1624 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001625 }
Simon Kelley824af852008-02-12 20:43:05 +00001626 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001627 else if (is6)
1628 {
1629 new->next = daemon->dhcp_opts6;
1630 daemon->dhcp_opts6 = new;
1631 }
1632 else
1633 {
1634 new->next = daemon->dhcp_opts;
1635 daemon->dhcp_opts = new;
1636 }
1637
1638 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001639on_error:
1640 dhcp_opt_free(new);
1641 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001642}
1643
Simon Kelley7622fc02009-06-04 20:32:05 +01001644#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001645
Simon Kelley28866e92011-02-14 20:19:14 +00001646void set_option_bool(unsigned int opt)
1647{
Petr Menšík24b87602018-10-24 22:30:18 +01001648 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001649}
1650
Simon Kelley2b5bae92012-06-26 16:55:23 +01001651void reset_option_bool(unsigned int opt)
1652{
Petr Menšík24b87602018-10-24 22:30:18 +01001653 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001654}
1655
Petr Menšík59e47032018-11-02 22:39:39 +00001656static void server_list_free(struct server *list)
1657{
1658 while (list)
1659 {
1660 struct server *tmp = list;
1661 list = list->next;
1662 free(tmp);
1663 }
1664}
1665
Simon Kelley7b1eae42014-02-20 13:43:28 +00001666static 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 +01001667{
1668 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001669 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001670
Simon Kelley832af0b2007-01-21 20:01:28 +00001671 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001672 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001673
Simon Kelley1a6bca82008-07-11 11:11:42 +01001674 for (i=0; usage[i].opt != 0; i++)
1675 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001676 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001677 int rept = usage[i].rept;
1678
Simon Kelley28866e92011-02-14 20:19:14 +00001679 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001680 {
1681 /* command line */
1682 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001683 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001684 if (rept == ARG_ONE)
1685 usage[i].rept = ARG_USED_CL;
1686 }
1687 else
1688 {
1689 /* allow file to override command line */
1690 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001691 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001692 if (rept == ARG_USED_CL || rept == ARG_ONE)
1693 usage[i].rept = ARG_USED_FILE;
1694 }
1695
1696 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1697 {
Simon Kelley28866e92011-02-14 20:19:14 +00001698 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001699 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001700 }
1701
1702 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001703 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001704
Simon Kelley849a8352006-06-09 21:02:31 +01001705 switch (option)
1706 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001707 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001708 {
Simon Kelley824af852008-02-12 20:43:05 +00001709 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001710 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001711 {
Simon Kelley28866e92011-02-14 20:19:14 +00001712 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001713 free(file);
1714 }
Simon Kelley849a8352006-06-09 21:02:31 +01001715 break;
1716 }
1717
Simon Kelleyf2621c72007-04-29 19:47:21 +01001718 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001719 {
1720 DIR *dir_stream;
1721 struct dirent *ent;
1722 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001723 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001724 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001725 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001726 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001727
Simon Kelley1f15b812009-10-13 17:49:32 +01001728 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001729 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001730 break;
1731
Simon Kelley1f15b812009-10-13 17:49:32 +01001732 for (arg = comma; arg; arg = comma)
1733 {
1734 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001735 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001736 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001737 li = opt_malloc(sizeof(struct list));
1738 if (*arg == '*')
1739 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001740 /* "*" with no suffix is a no-op */
1741 if (arg[1] == 0)
1742 free(li);
1743 else
1744 {
1745 li->next = match_suffix;
1746 match_suffix = li;
1747 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001748 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001749 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001750 }
1751 else
1752 {
1753 li->next = ignore_suffix;
1754 ignore_suffix = li;
1755 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001756 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001757 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001758 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001759 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001760
Simon Kelley849a8352006-06-09 21:02:31 +01001761 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001762 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001763
Simon Kelley849a8352006-06-09 21:02:31 +01001764 while ((ent = readdir(dir_stream)))
1765 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001766 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001767 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001768
1769 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001770 if (len == 0 ||
1771 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001772 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1773 ent->d_name[0] == '.')
1774 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001775
Simon Kelley3e1551a2014-09-09 21:46:07 +01001776 if (match_suffix)
1777 {
1778 for (li = match_suffix; li; li = li->next)
1779 {
1780 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001781 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001782 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001783 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001784 break;
1785 }
1786 if (!li)
1787 continue;
1788 }
1789
Simon Kelley1f15b812009-10-13 17:49:32 +01001790 for (li = ignore_suffix; li; li = li->next)
1791 {
1792 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001793 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001794 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001795 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001796 break;
1797 }
1798 if (li)
1799 continue;
1800
Simon Kelley824af852008-02-12 20:43:05 +00001801 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001802 strcpy(path, directory);
1803 strcat(path, "/");
1804 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001805
Simon Kelley39595cf2013-02-04 21:40:07 +00001806 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001807 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001808 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001809
Simon Kelley39595cf2013-02-04 21:40:07 +00001810 /* only reg files allowed. */
1811 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001812 {
1813 /* sort files into order. */
1814 struct list **up, *new = opt_malloc(sizeof(struct list));
1815 new->name = path;
1816
1817 for (up = &files, li = files; li; up = &li->next, li = li->next)
1818 if (strcmp(li->name, path) >=0)
1819 break;
1820
1821 new->next = li;
1822 *up = new;
1823 }
1824
Simon Kelley849a8352006-06-09 21:02:31 +01001825 }
Simon Kelleyab538832020-01-10 20:44:48 +00001826
1827 for (li = files; li; li = li->next)
1828 one_file(li->name, 0);
1829
Simon Kelley849a8352006-06-09 21:02:31 +01001830 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001831 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001832 for(; ignore_suffix; ignore_suffix = li)
1833 {
1834 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001835 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001836 free(ignore_suffix);
1837 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001838 for(; match_suffix; match_suffix = li)
1839 {
1840 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001841 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001842 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001843 }
Simon Kelleyab538832020-01-10 20:44:48 +00001844 for(; files; files = li)
1845 {
1846 li = files->next;
1847 free(files->name);
1848 free(files);
1849 }
Simon Kelley849a8352006-06-09 21:02:31 +01001850 break;
1851 }
1852
Simon Kelleyed4c0762013-10-08 20:46:34 +01001853 case LOPT_ADD_SBNET: /* --add-subnet */
1854 set_option_bool(OPT_CLIENT_SUBNET);
1855 if (arg)
1856 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001857 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001858 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001859
1860 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1861 if ((end = split_chr(arg, '/')))
1862 {
1863 /* has subnet+len */
1864 err = parse_mysockaddr(arg, &new->addr);
1865 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001866 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001867 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001868 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001869 new->addr_used = 1;
1870 }
1871 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001872 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001873
1874 daemon->add_subnet4 = new;
1875
Ed Bardsleya7369be2015-08-05 21:17:18 +01001876 if (comma)
1877 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001878 new = opt_malloc(sizeof(struct mysubnet));
1879 if ((end = split_chr(comma, '/')))
1880 {
1881 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001882 err = parse_mysockaddr(comma, &new->addr);
1883 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001884 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001885 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001886 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001887 new->addr_used = 1;
1888 }
1889 else
1890 {
1891 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001892 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001893 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001894
1895 daemon->add_subnet6 = new;
1896 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001897 }
1898 break;
1899
Simon Kelleyad094272012-08-10 17:10:54 +01001900 case '1': /* --enable-dbus */
1901 set_option_bool(OPT_DBUS);
1902 if (arg)
1903 daemon->dbus_name = opt_string_alloc(arg);
1904 else
1905 daemon->dbus_name = DNSMASQ_SERVICE;
1906 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001907
1908 case LOPT_UBUS: /* --enable-ubus */
1909 set_option_bool(OPT_UBUS);
1910 if (arg)
1911 daemon->ubus_name = opt_string_alloc(arg);
1912 else
1913 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1914 break;
1915
Simon Kelleyf2621c72007-04-29 19:47:21 +01001916 case '8': /* --log-facility */
1917 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001918 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001919 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001920 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001921 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001922#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001923 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001924#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001925 for (i = 0; facilitynames[i].c_name; i++)
1926 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1927 break;
1928
1929 if (facilitynames[i].c_name)
1930 daemon->log_fac = facilitynames[i].c_val;
1931 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001932 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001933#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001934 }
1935 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001936
Simon Kelleyf2621c72007-04-29 19:47:21 +01001937 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001938 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001939 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001940
Simon Kelleyf2621c72007-04-29 19:47:21 +01001941 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001942 {
Simon Kelley824af852008-02-12 20:43:05 +00001943 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001944 struct resolvc *new, *list = daemon->resolv_files;
1945
1946 if (list && list->is_default)
1947 {
1948 /* replace default resolv file - possibly with nothing */
1949 if (name)
1950 {
1951 list->is_default = 0;
1952 list->name = name;
1953 }
1954 else
1955 list = NULL;
1956 }
1957 else if (name)
1958 {
Simon Kelley824af852008-02-12 20:43:05 +00001959 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001960 new->next = list;
1961 new->name = name;
1962 new->is_default = 0;
1963 new->mtime = 0;
1964 new->logged = 0;
1965 list = new;
1966 }
1967 daemon->resolv_files = list;
1968 break;
1969 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001970
1971 case LOPT_SERVERS_FILE:
1972 daemon->servers_file = opt_string_alloc(arg);
1973 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001974
Simon Kelleyf2621c72007-04-29 19:47:21 +01001975 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001976 {
1977 int pref = 1;
1978 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001979 char *name, *target = NULL;
1980
Simon Kelleyf2621c72007-04-29 19:47:21 +01001981 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001982 {
1983 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001984 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001985 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001986 }
1987
Simon Kelley1f15b812009-10-13 17:49:32 +01001988 if (!(name = canonicalise_opt(arg)) ||
1989 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001990 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001991
Simon Kelley824af852008-02-12 20:43:05 +00001992 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001993 new->next = daemon->mxnames;
1994 daemon->mxnames = new;
1995 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001996 new->name = name;
1997 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001998 new->weight = pref;
1999 break;
2000 }
2001
Simon Kelleyf2621c72007-04-29 19:47:21 +01002002 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01002003 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002004 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01002005 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002006
Simon Kelley6b173352018-05-08 18:32:14 +01002007 case LOPT_DUMPFILE: /* --dumpfile */
2008 daemon->dump_file = opt_string_alloc(arg);
2009 break;
2010
2011 case LOPT_DUMPMASK: /* --dumpmask */
2012 daemon->dump_mask = strtol(arg, NULL, 0);
2013 break;
2014
Simon Kelley7622fc02009-06-04 20:32:05 +01002015#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002016 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002017 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002018 break;
2019
Simon Kelleyc72daea2012-01-05 21:33:27 +00002020 /* Sorry about the gross pre-processor abuse */
2021 case '6': /* --dhcp-script */
2022 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002023# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002024 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002025# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002026 if (option == LOPT_LUASCRIPT)
2027# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002028 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002029# else
2030 daemon->luascript = opt_string_alloc(arg);
2031# endif
2032 else
2033 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002034# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002035 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002036#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002037
Simon Kelley70d18732015-01-31 19:59:29 +00002038 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2039 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2040 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2041 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2042 case LOPT_HOST_INOTIFY: /* --hostsdir */
2043 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002044 {
Simon Kelley824af852008-02-12 20:43:05 +00002045 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002046 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002047 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002048 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002049 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002050 if (option == 'H')
2051 {
2052 new->next = daemon->addn_hosts;
2053 daemon->addn_hosts = new;
2054 }
2055 else if (option == LOPT_DHCP_HOST)
2056 {
2057 new->next = daemon->dhcp_hosts_file;
2058 daemon->dhcp_hosts_file = new;
2059 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002060 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002061 {
2062 new->next = daemon->dhcp_opts_file;
2063 daemon->dhcp_opts_file = new;
2064 }
Simon Kelley70d18732015-01-31 19:59:29 +00002065 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002066 {
Simon Kelley70d18732015-01-31 19:59:29 +00002067 new->next = daemon->dynamic_dirs;
2068 daemon->dynamic_dirs = new;
2069 if (option == LOPT_DHCP_INOTIFY)
2070 new->flags |= AH_DHCP_HST;
2071 else if (option == LOPT_DHOPT_INOTIFY)
2072 new->flags |= AH_DHCP_OPT;
2073 else if (option == LOPT_HOST_INOTIFY)
2074 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002075 }
2076
Simon Kelley849a8352006-06-09 21:02:31 +01002077 break;
2078 }
2079
Simon Kelley4f7b3042012-11-28 21:27:02 +00002080 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002081 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002082
Simon Kelley4f7b3042012-11-28 21:27:02 +00002083 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002084
2085 while ((arg = comma))
2086 {
2087 struct iname *new = opt_malloc(sizeof(struct iname));
2088 comma = split(arg);
2089 new->name = NULL;
2090 unhide_metas(arg);
2091 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2092 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002093 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2094 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002095 else
2096 {
2097 char *fam = split_chr(arg, '/');
2098 new->name = opt_string_alloc(arg);
2099 new->addr.sa.sa_family = 0;
2100 if (fam)
2101 {
2102 if (strcmp(fam, "4") == 0)
2103 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002104 else if (strcmp(fam, "6") == 0)
2105 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002106 else
Petr Menšík59e47032018-11-02 22:39:39 +00002107 {
2108 free(new->name);
2109 ret_err_free(gen_err, new);
2110 }
Simon Kelley08933472018-10-05 16:34:35 +01002111 }
2112 }
2113 new->next = daemon->authinterface;
2114 daemon->authinterface = new;
2115 };
Simon Kelley429798f2012-12-10 20:45:53 +00002116
Simon Kelley4f7b3042012-11-28 21:27:02 +00002117 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002118
2119 case LOPT_AUTHSFS: /* --auth-sec-servers */
2120 {
2121 struct name_list *new;
2122
2123 do {
2124 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002125 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002126 new->name = opt_string_alloc(arg);
2127 new->next = daemon->secondary_forward_server;
2128 daemon->secondary_forward_server = new;
2129 arg = comma;
2130 } while (arg);
2131 break;
2132 }
2133
Simon Kelley4f7b3042012-11-28 21:27:02 +00002134 case LOPT_AUTHZONE: /* --auth-zone */
2135 {
2136 struct auth_zone *new;
2137
2138 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002139
Simon Kelley429798f2012-12-10 20:45:53 +00002140 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002141 new->domain = opt_string_alloc(arg);
2142 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002143 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002144 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002145 new->next = daemon->auth_zones;
2146 daemon->auth_zones = new;
2147
2148 while ((arg = comma))
2149 {
2150 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002151 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002152 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002153 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002154 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002155
2156 comma = split(arg);
2157 prefix = split_chr(arg, '/');
2158
2159 if (prefix && !atoi_check(prefix, &prefixlen))
2160 ret_err(gen_err);
2161
Mathias Kresin094bfae2016-07-24 14:15:22 +01002162 if (strstr(arg, "exclude:") == arg)
2163 {
2164 is_exclude = 1;
2165 arg = arg+8;
2166 }
2167
Simon Kelleycc921df2019-01-02 22:48:59 +00002168 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002169 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002170 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002171 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002172 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002173 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002174 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002175 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002176 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002177 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002178 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002179 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002180 else
2181 {
2182 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2183 name->name = opt_string_alloc(arg);
2184 name->flags = AUTH4 | AUTH6;
2185 name->next = new->interface_names;
2186 new->interface_names = name;
2187 if (prefix)
2188 {
2189 if (prefixlen == 4)
2190 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002191 else if (prefixlen == 6)
2192 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002193 else
2194 ret_err(gen_err);
2195 }
2196 }
2197
2198 if (subnet)
2199 {
2200 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002201
2202 if (is_exclude)
2203 {
2204 subnet->next = new->exclude;
2205 new->exclude = subnet;
2206 }
2207 else
2208 {
2209 subnet->next = new->subnet;
2210 new->subnet = subnet;
2211 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002212 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002213 }
2214 break;
2215 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002216
Simon Kelley4f7b3042012-11-28 21:27:02 +00002217 case LOPT_AUTHSOA: /* --auth-soa */
2218 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002219 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002220 if (comma)
2221 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002222 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002223 arg = comma;
2224 comma = split(arg);
2225 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002226 for (cp = daemon->hostmaster; *cp; cp++)
2227 if (*cp == '@')
2228 *cp = '.';
2229
Simon Kelley4f7b3042012-11-28 21:27:02 +00002230 if (comma)
2231 {
2232 arg = comma;
2233 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002234 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002235 if (comma)
2236 {
2237 arg = comma;
2238 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002239 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002240 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002241 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002242 }
2243 }
2244 }
2245
2246 break;
2247
Simon Kelley2bb73af2013-04-24 17:38:19 +01002248 case 's': /* --domain */
2249 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002250 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002251 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002252 else
Simon Kelley9009d742008-11-14 20:04:27 +00002253 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002254 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002255 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002256 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002257 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002258 else
2259 {
Simon Kelley9009d742008-11-14 20:04:27 +00002260 if (comma)
2261 {
Simon Kelley429798f2012-12-10 20:45:53 +00002262 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002263 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002264
Simon Kelley48fd1c42013-04-25 09:49:38 +01002265 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002266 new->indexed = 0;
2267
Simon Kelley9009d742008-11-14 20:04:27 +00002268 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002269 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002270 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002271 int msize;
2272
Simon Kelley28866e92011-02-14 20:19:14 +00002273 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002274 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002275 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002276 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002277 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002278 int mask = (1 << (32 - msize)) - 1;
2279 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002280 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2281 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002282 if (arg)
2283 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002284 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002285 {
2286 if (!(new->prefix = canonicalise_opt(arg)) ||
2287 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002288 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002289 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002290 else if (strcmp(arg, "local") != 0 ||
2291 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002292 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002293 else
2294 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002295 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002296 local=/xxx.yyy.zzz.in-addr.arpa/ */
2297 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002298 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002299 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002300
Simon Kelleyde73a492014-02-17 21:43:27 +00002301 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002302
2303 /* local=/<domain>/ */
2304 serv = opt_malloc(sizeof(struct server));
2305 memset(serv, 0, sizeof(struct server));
2306 serv->domain = d;
2307 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2308 serv->next = daemon->servers;
2309 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002310 }
2311 }
Simon Kelley9009d742008-11-14 20:04:27 +00002312 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002313 else if (inet_pton(AF_INET6, comma, &new->start6))
2314 {
2315 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2316 u64 addrpart = addr6part(&new->start6);
2317 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002318
Simon Kelleyd74942a2012-02-07 20:51:56 +00002319 /* prefix==64 overflows the mask calculation above */
2320 if (msize == 64)
2321 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002322
Simon Kelleyd74942a2012-02-07 20:51:56 +00002323 new->end6 = new->start6;
2324 setaddr6part(&new->start6, addrpart & ~mask);
2325 setaddr6part(&new->end6, addrpart | mask);
2326
2327 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002328 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002329 else if (arg)
2330 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002331 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002332 {
2333 if (!(new->prefix = canonicalise_opt(arg)) ||
2334 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002335 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002336 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002337 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002338 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002339 else
2340 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002341 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002342 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002343 struct server *serv = add_rev6(&new->start6, msize);
2344 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002345
2346 /* local=/<domain>/ */
2347 serv = opt_malloc(sizeof(struct server));
2348 memset(serv, 0, sizeof(struct server));
2349 serv->domain = d;
2350 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2351 serv->next = daemon->servers;
2352 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002353 }
2354 }
2355 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002356 else
Petr Menšík59e47032018-11-02 22:39:39 +00002357 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002358 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002359 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002360 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002361 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002362 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002363 prefstr = split(arg);
2364
Simon Kelleyd74942a2012-02-07 20:51:56 +00002365 if (inet_pton(AF_INET, comma, &new->start))
2366 {
2367 new->is6 = 0;
2368 if (!arg)
2369 new->end.s_addr = new->start.s_addr;
2370 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002371 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002372 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002373 else if (inet_pton(AF_INET6, comma, &new->start6))
2374 {
2375 new->is6 = 1;
2376 if (!arg)
2377 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2378 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002379 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002380 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002381 else
Petr Menšík59e47032018-11-02 22:39:39 +00002382 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002383
2384 if (option != 's' && prefstr)
2385 {
2386 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2387 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002388 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002389 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002390 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002391
2392 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002393 if (option == 's')
2394 {
2395 new->next = daemon->cond_domain;
2396 daemon->cond_domain = new;
2397 }
2398 else
2399 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002400 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002401 new->next = daemon->synth_domains;
2402 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002403 if (new->prefix &&
2404 (star = strrchr(new->prefix, '*'))
2405 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002406 {
2407 *star = 0;
2408 new->indexed = 1;
2409 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002410 }
Simon Kelley9009d742008-11-14 20:04:27 +00002411 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002412 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002413 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002414 else
2415 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002416 }
2417 }
Simon Kelley849a8352006-06-09 21:02:31 +01002418 break;
2419
Simon Kelley1e505122016-01-25 21:29:23 +00002420 case LOPT_CPE_ID: /* --add-dns-client */
2421 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002422 daemon->dns_client_id = opt_string_alloc(arg);
2423 break;
2424
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002425 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002426 if (!arg)
2427 set_option_bool(OPT_ADD_MAC);
2428 else
2429 {
2430 unhide_metas(arg);
2431 if (strcmp(arg, "base64") == 0)
2432 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002433 else if (strcmp(arg, "text") == 0)
2434 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002435 else
2436 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002437 }
2438 break;
2439
Simon Kelleyf2621c72007-04-29 19:47:21 +01002440 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002441 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002442 break;
2443
Simon Kelleyf2621c72007-04-29 19:47:21 +01002444 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002445 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002446 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002447 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002448
Simon Kelley7622fc02009-06-04 20:32:05 +01002449#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002450 case LOPT_SCRIPTUSR: /* --scriptuser */
2451 daemon->scriptuser = opt_string_alloc(arg);
2452 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002453#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002454
Simon Kelleyf2621c72007-04-29 19:47:21 +01002455 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002456 do {
Simon Kelley824af852008-02-12 20:43:05 +00002457 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002458 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002459 new->next = daemon->if_names;
2460 daemon->if_names = new;
2461 /* new->name may be NULL if someone does
2462 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002463 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002464 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002465 arg = comma;
2466 } while (arg);
2467 break;
2468
Simon Kelley2937f8a2013-07-29 19:49:07 +01002469 case LOPT_TFTP: /* --enable-tftp */
2470 set_option_bool(OPT_TFTP);
2471 if (!arg)
2472 break;
2473 /* fall through */
2474
Simon Kelleyf2621c72007-04-29 19:47:21 +01002475 case 'I': /* --except-interface */
2476 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002477 do {
Simon Kelley824af852008-02-12 20:43:05 +00002478 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002479 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002480 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002481 if (option == 'I')
2482 {
2483 new->next = daemon->if_except;
2484 daemon->if_except = new;
2485 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002486 else if (option == LOPT_TFTP)
2487 {
2488 new->next = daemon->tftp_interfaces;
2489 daemon->tftp_interfaces = new;
2490 }
Simon Kelley849a8352006-06-09 21:02:31 +01002491 else
2492 {
2493 new->next = daemon->dhcp_except;
2494 daemon->dhcp_except = new;
2495 }
2496 arg = comma;
2497 } while (arg);
2498 break;
2499
Simon Kelleyf2621c72007-04-29 19:47:21 +01002500 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002501 case LOPT_IGNORE_ADDR: /* --ignore-address */
2502 {
Simon Kelley849a8352006-06-09 21:02:31 +01002503 struct in_addr addr;
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002504 int prefix = 32;
Simon Kelley849a8352006-06-09 21:02:31 +01002505 unhide_metas(arg);
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002506
2507 if (!arg ||
2508 ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
2509 (inet_pton(AF_INET, arg, &addr) != 1))
2510 ret_err(gen_err); /* error */
2511 else
Simon Kelley849a8352006-06-09 21:02:31 +01002512 {
Simon Kelley824af852008-02-12 20:43:05 +00002513 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002514 if (option == 'B')
2515 {
2516 baddr->next = daemon->bogus_addr;
2517 daemon->bogus_addr = baddr;
2518 }
2519 else
2520 {
2521 baddr->next = daemon->ignore_addr;
2522 daemon->ignore_addr = baddr;
2523 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002524 baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
2525 baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
Simon Kelley849a8352006-06-09 21:02:31 +01002526 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002527 break;
2528 }
Simon Kelley849a8352006-06-09 21:02:31 +01002529
Simon Kelleyf2621c72007-04-29 19:47:21 +01002530 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002531 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002532 do {
Simon Kelley824af852008-02-12 20:43:05 +00002533 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002534 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002535 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002536 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002537 {
2538 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002539 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002540#ifdef HAVE_SOCKADDR_SA_LEN
2541 new->addr.in.sin_len = sizeof(new->addr.in);
2542#endif
2543 }
Simon Kelley849a8352006-06-09 21:02:31 +01002544 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2545 {
2546 new->addr.sa.sa_family = AF_INET6;
2547 new->addr.in6.sin6_flowinfo = 0;
2548 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002549 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002550#ifdef HAVE_SOCKADDR_SA_LEN
2551 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2552#endif
2553 }
Simon Kelley849a8352006-06-09 21:02:31 +01002554 else
Petr Menšík59e47032018-11-02 22:39:39 +00002555 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002556
2557 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002558 if (option == 'a')
2559 {
2560 new->next = daemon->if_addrs;
2561 daemon->if_addrs = new;
2562 }
2563 else
2564 {
2565 new->next = daemon->auth_peers;
2566 daemon->auth_peers = new;
2567 }
Simon Kelley849a8352006-06-09 21:02:31 +01002568 arg = comma;
2569 } while (arg);
2570 break;
2571
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002572 case 'S': /* --server */
2573 case LOPT_LOCAL: /* --local */
2574 case 'A': /* --address */
2575 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002576 {
2577 struct server *serv, *newlist = NULL;
2578
2579 unhide_metas(arg);
2580
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002581 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002582 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002583 int rebind = !(*arg == '/');
2584 char *end = NULL;
2585 if (!rebind)
2586 arg++;
2587 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002588 {
2589 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002590 /* elide leading dots - they are implied in the search algorithm */
2591 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002592 /* # matches everything and becomes a zero length domain string */
2593 if (strcmp(arg, "#") == 0)
2594 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002595 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002596 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002597 serv = opt_malloc(sizeof(struct server));
2598 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002599 serv->next = newlist;
2600 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002601 serv->domain = domain;
2602 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002603 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002604 if (rebind)
2605 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002606 }
2607 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002608 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002609 }
2610 else
2611 {
Simon Kelley824af852008-02-12 20:43:05 +00002612 newlist = opt_malloc(sizeof(struct server));
2613 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002614#ifdef HAVE_LOOP
2615 newlist->uid = rand32();
2616#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002617 }
2618
Simon Kelley7b1eae42014-02-20 13:43:28 +00002619 if (servers_only && option == 'S')
2620 newlist->flags |= SERV_FROM_FILE;
2621
Simon Kelley849a8352006-06-09 21:02:31 +01002622 if (option == 'A')
2623 {
2624 newlist->flags |= SERV_LITERAL_ADDRESS;
2625 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002626 {
2627 server_list_free(newlist);
2628 ret_err(gen_err);
2629 }
Simon Kelley849a8352006-06-09 21:02:31 +01002630 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002631 else if (option == LOPT_NO_REBIND)
2632 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002633
2634 if (!arg || !*arg)
2635 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002636 if (!(newlist->flags & SERV_NO_REBIND))
2637 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002638 }
2639
2640 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002641 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002642 else
2643 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002644 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2645 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002646 {
2647 server_list_free(newlist);
2648 ret_err(err);
2649 }
Simon Kelley849a8352006-06-09 21:02:31 +01002650 }
2651
Simon Kelleyf2621c72007-04-29 19:47:21 +01002652 serv = newlist;
2653 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002654 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002655 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002656 serv->next->addr = serv->addr;
2657 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002658 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002659 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002660 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002661 serv->next = daemon->servers;
2662 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002663 break;
2664 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002665
Simon Kelleyde73a492014-02-17 21:43:27 +00002666 case LOPT_REV_SERV: /* --rev-server */
2667 {
2668 char *string;
2669 int size;
2670 struct server *serv;
2671 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002672 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002673
2674 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002675 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002676 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002677
2678 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002679
Simon Kelleya9b022a2020-02-11 21:58:59 +00002680 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2681 ret_err(gen_err);
2682
Simon Kelleyde73a492014-02-17 21:43:27 +00002683 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002684 {
2685 serv = add_rev4(addr4, size);
2686 if (!serv)
2687 ret_err(_("bad prefix"));
2688 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002689 else if (inet_pton(AF_INET6, arg, &addr6))
2690 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002691 else
2692 ret_err(gen_err);
2693
2694 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2695
2696 if (string)
2697 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002698
2699 if (servers_only)
2700 serv->flags |= SERV_FROM_FILE;
2701
Simon Kelleyde73a492014-02-17 21:43:27 +00002702 break;
2703 }
2704
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002705 case LOPT_IPSET: /* --ipset */
2706#ifndef HAVE_IPSET
2707 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2708 break;
2709#else
2710 {
2711 struct ipsets ipsets_head;
2712 struct ipsets *ipsets = &ipsets_head;
2713 int size;
2714 char *end;
2715 char **sets, **sets_pos;
2716 memset(ipsets, 0, sizeof(struct ipsets));
2717 unhide_metas(arg);
2718 if (arg && *arg == '/')
2719 {
2720 arg++;
2721 while ((end = split_chr(arg, '/')))
2722 {
2723 char *domain = NULL;
2724 /* elide leading dots - they are implied in the search algorithm */
2725 while (*arg == '.')
2726 arg++;
2727 /* # matches everything and becomes a zero length domain string */
2728 if (strcmp(arg, "#") == 0 || !*arg)
2729 domain = "";
2730 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002731 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002732 ipsets->next = opt_malloc(sizeof(struct ipsets));
2733 ipsets = ipsets->next;
2734 memset(ipsets, 0, sizeof(struct ipsets));
2735 ipsets->domain = domain;
2736 arg = end;
2737 }
2738 }
2739 else
2740 {
2741 ipsets->next = opt_malloc(sizeof(struct ipsets));
2742 ipsets = ipsets->next;
2743 memset(ipsets, 0, sizeof(struct ipsets));
2744 ipsets->domain = "";
2745 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002746
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002747 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002748 ret_err(gen_err);
2749
2750 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002751 if (*end == ',')
2752 ++size;
2753
2754 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2755
2756 do {
2757 end = split(arg);
2758 *sets_pos++ = opt_string_alloc(arg);
2759 arg = end;
2760 } while (end);
2761 *sets_pos = 0;
2762 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2763 ipsets->next->sets = sets;
2764 ipsets->next = daemon->ipsets;
2765 daemon->ipsets = ipsets_head.next;
2766
2767 break;
2768 }
2769#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002770
Simon Kelleyf2621c72007-04-29 19:47:21 +01002771 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002772 {
2773 int size;
2774
2775 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002776 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002777 else
2778 {
2779 /* zero is OK, and means no caching. */
2780
2781 if (size < 0)
2782 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002783
2784 /* Note that for very large cache sizes, the malloc()
2785 will overflow. For the size of the cache record
2786 at the time this was noted, the value of "very large"
2787 was 46684428. Limit to an order of magnitude less than
2788 that to be safe from changes to the cache record. */
2789 if (size > 5000000)
2790 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002791
2792 daemon->cachesize = size;
2793 }
2794 break;
2795 }
2796
Simon Kelleyf2621c72007-04-29 19:47:21 +01002797 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002798 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002799 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002800 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002801
Simon Kelley1a6bca82008-07-11 11:11:42 +01002802 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002803 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002804 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002805 break;
2806
Hans Dedecker926332a2016-01-23 10:48:12 +00002807 case LOPT_MAXPORT: /* --max-port */
2808 if (!atoi_check16(arg, &daemon->max_port))
2809 ret_err(gen_err);
2810 break;
2811
Simon Kelleyf2621c72007-04-29 19:47:21 +01002812 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002813 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002814 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002815 break;
2816
Simon Kelley25cf5e32015-01-09 15:53:03 +00002817 case 'q': /* --log-queries */
2818 set_option_bool(OPT_LOG);
2819 if (arg && strcmp(arg, "extra") == 0)
2820 set_option_bool(OPT_EXTRALOG);
2821 break;
2822
Simon Kelleyf2621c72007-04-29 19:47:21 +01002823 case LOPT_MAX_LOGS: /* --log-async */
2824 daemon->max_logs = LOG_MAX; /* default */
2825 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002826 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002827 else if (daemon->max_logs > 100)
2828 daemon->max_logs = 100;
2829 break;
2830
2831 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002832 {
2833 int i;
2834 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002835 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002836 daemon->edns_pktsz = (unsigned short)i;
2837 break;
2838 }
2839
Simon Kelleyf2621c72007-04-29 19:47:21 +01002840 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002841 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002842 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002843 /* if explicitly set to zero, use single OS ephemeral port
2844 and disable random ports */
2845 if (daemon->query_port == 0)
2846 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002847 break;
2848
Simon Kelley824af852008-02-12 20:43:05 +00002849 case 'T': /* --local-ttl */
2850 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002851 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002852 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002853 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002854 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002855 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002856 {
2857 int ttl;
2858 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002859 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002860 else if (option == LOPT_NEGTTL)
2861 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002862 else if (option == LOPT_MAXTTL)
2863 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002864 else if (option == LOPT_MINCTTL)
2865 {
2866 if (ttl > TTL_FLOOR_LIMIT)
2867 ttl = TTL_FLOOR_LIMIT;
2868 daemon->min_cache_ttl = (unsigned long)ttl;
2869 }
Simon Kelley1d860412012-09-20 20:48:04 +01002870 else if (option == LOPT_MAXCTTL)
2871 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002872 else if (option == LOPT_AUTHTTL)
2873 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002874 else if (option == LOPT_DHCPTTL)
2875 {
2876 daemon->dhcp_ttl = (unsigned long)ttl;
2877 daemon->use_dhcp_ttl = 1;
2878 }
Simon Kelley849a8352006-06-09 21:02:31 +01002879 else
2880 daemon->local_ttl = (unsigned long)ttl;
2881 break;
2882 }
2883
Simon Kelley7622fc02009-06-04 20:32:05 +01002884#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002885 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002886 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002887 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002888 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002889#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002890
Simon Kelley7622fc02009-06-04 20:32:05 +01002891#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002892 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002893 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002894 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002895 break;
2896
Simon Kelleybec366b2016-02-24 22:03:26 +00002897 case LOPT_TFTP_MTU: /* --tftp-mtu */
2898 if (!atoi_check(arg, &daemon->tftp_mtu))
2899 ret_err(gen_err);
2900 break;
2901
Simon Kelley824af852008-02-12 20:43:05 +00002902 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002903 comma = split(arg);
2904 if (comma)
2905 {
2906 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2907 new->interface = opt_string_alloc(comma);
2908 new->prefix = opt_string_alloc(arg);
2909 new->next = daemon->if_prefix;
2910 daemon->if_prefix = new;
2911 }
2912 else
2913 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002914 break;
2915
Simon Kelley824af852008-02-12 20:43:05 +00002916 case LOPT_TFTPPORTS: /* --tftp-port-range */
2917 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002918 !atoi_check16(arg, &daemon->start_tftp_port) ||
2919 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002920 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002921
2922 if (daemon->start_tftp_port > daemon->end_tftp_port)
2923 {
2924 int tmp = daemon->start_tftp_port;
2925 daemon->start_tftp_port = daemon->end_tftp_port;
2926 daemon->end_tftp_port = tmp;
2927 }
2928
2929 break;
Floris Bos60704f52017-04-09 22:22:49 +01002930
2931 case LOPT_APREF: /* --tftp-unique-root */
2932 if (!arg || strcasecmp(arg, "ip") == 0)
2933 set_option_bool(OPT_TFTP_APREF_IP);
2934 else if (strcasecmp(arg, "mac") == 0)
2935 set_option_bool(OPT_TFTP_APREF_MAC);
2936 else
2937 ret_err(gen_err);
2938 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002939#endif
Simon Kelley824af852008-02-12 20:43:05 +00002940
Simon Kelleyf2621c72007-04-29 19:47:21 +01002941 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002942 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002943 struct dhcp_bridge *new;
2944
Simon Kelley316e2732010-01-22 20:16:09 +00002945 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002946 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002947
Simon Kelley22cd8602018-01-14 22:57:14 +00002948 for (new = daemon->bridges; new; new = new->next)
2949 if (strcmp(new->iface, arg) == 0)
2950 break;
2951
2952 if (!new)
2953 {
2954 new = opt_malloc(sizeof(struct dhcp_bridge));
2955 strcpy(new->iface, arg);
2956 new->alias = NULL;
2957 new->next = daemon->bridges;
2958 daemon->bridges = new;
2959 }
2960
Simon Kelley832af0b2007-01-21 20:01:28 +00002961 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002962 arg = comma;
2963 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002964 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002965 {
Simon Kelley824af852008-02-12 20:43:05 +00002966 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002967 b->next = new->alias;
2968 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002969 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002970 }
2971 } while (comma);
2972
2973 break;
2974 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002975
Simon Kelley7622fc02009-06-04 20:32:05 +01002976#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002977 case LOPT_SHARED_NET: /* --shared-network */
2978 {
2979 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2980
2981#ifdef HAVE_DHCP6
2982 new->shared_addr.s_addr = 0;
2983#endif
2984 new->if_index = 0;
2985
2986 if (!(comma = split(arg)))
2987 {
2988 snerr:
2989 free(new);
2990 ret_err(_("bad shared-network"));
2991 }
2992
2993 if (inet_pton(AF_INET, comma, &new->shared_addr))
2994 {
2995 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2996 !(new->if_index = if_nametoindex(arg)))
2997 goto snerr;
2998 }
2999#ifdef HAVE_DHCP6
3000 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
3001 {
3002 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
3003 !(new->if_index = if_nametoindex(arg)))
3004 goto snerr;
3005 }
3006#endif
3007 else
3008 goto snerr;
3009
3010 new->next = daemon->shared_networks;
3011 daemon->shared_networks = new;
3012 break;
3013 }
3014
Simon Kelleyf2621c72007-04-29 19:47:21 +01003015 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01003016 {
3017 int k, leasepos = 2;
Kyle Swenson9d238072021-12-01 08:13:17 -07003018 char *cp, *a[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003019 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003020
Simon Kelley52b92f42012-01-22 16:05:15 +00003021 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003022
Simon Kelley849a8352006-06-09 21:02:31 +01003023 while(1)
3024 {
3025 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003026 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3027 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3028 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003029 break;
3030
Simon Kelleyf2621c72007-04-29 19:47:21 +01003031 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003032 {
Kyle Swenson545712c2021-11-17 12:25:04 -07003033/* CRADLEPOINT */
3034 if (strstr(arg, "forcedinterface:") == arg)
3035 new->forcedinterface = opt_string_alloc(arg+16);
3036 else if (strstr(arg, "forcedaddress:") == arg)
3037 new->forcedaddress.s_addr = inet_addr(arg+14);
3038 else if (is_tag_prefix(arg))
3039/* CRADLEPOINT */
Simon Kelley849a8352006-06-09 21:02:31 +01003040 {
Simon Kelley0c387192013-09-05 10:21:12 +01003041 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003042 if (arg[4])
3043 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003044 }
3045 else
3046 {
3047 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003048 {
3049 dhcp_context_free(new);
3050 ret_err(_("only one tag allowed"));
3051 }
Simon Kelley849a8352006-06-09 21:02:31 +01003052 else
Petr Menšík59e47032018-11-02 22:39:39 +00003053 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003054 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003055 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003056 }
3057 else
3058 {
3059 a[0] = arg;
3060 break;
3061 }
3062 }
3063
Kyle Swenson9d238072021-12-01 08:13:17 -07003064 for (k = 1; k < 9; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003065 if (!(a[k] = split(a[k-1])))
3066 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003067
Simon Kelley52b92f42012-01-22 16:05:15 +00003068 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003069 {
3070 dhcp_context_free(new);
3071 ret_err(_("bad dhcp-range"));
3072 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003073
3074 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003075 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003076 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003077 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003078 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003079 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003080 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003081 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003082 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003083 new->flags |= CONTEXT_PROXY;
3084 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003085 {
3086 dhcp_context_free(new);
3087 ret_err(_("bad dhcp-range"));
3088 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003089
3090 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3091 {
3092 struct in_addr tmp = new->start;
3093 new->start = new->end;
3094 new->end = tmp;
3095 }
3096
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003097 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003098 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003099 {
3100 new->flags |= CONTEXT_NETMASK;
3101 leasepos = 3;
3102 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003103 {
3104 dhcp_context_free(new);
3105 ret_err(_("inconsistent DHCP range"));
3106 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003107
Simon Kelley52b92f42012-01-22 16:05:15 +00003108
Simon Kelleyfa794662016-03-03 20:33:54 +00003109 if (k >= 4 && strchr(a[3], '.') &&
3110 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3111 {
3112 new->flags |= CONTEXT_BRDCAST;
3113 leasepos = 4;
3114 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003115 }
Simon Kelley849a8352006-06-09 21:02:31 +01003116 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003117#ifdef HAVE_DHCP6
3118 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003119 {
Petr Menšík59e47032018-11-02 22:39:39 +00003120 const char *err = NULL;
3121
Simon Kelley89500e32013-09-20 16:29:20 +01003122 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003123 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003124 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003125 new->lease_time = DEFLEASE6;
Kyle Swenson9d238072021-12-01 08:13:17 -07003126 new->valid_lifetime = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003127 new->next = daemon->dhcp6;
3128 daemon->dhcp6 = new;
3129
Simon Kelley30cd9662012-03-25 20:44:38 +01003130 for (leasepos = 1; leasepos < k; leasepos++)
3131 {
3132 if (strcmp(a[leasepos], "static") == 0)
3133 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3134 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003135 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003136 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003137 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003138 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3139 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003140 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003141 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003142 else if (strcmp(a[leasepos], "off-link") == 0)
3143 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003144 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3145 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003146 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3147 {
3148 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3149 new->flags |= CONTEXT_TEMPLATE;
3150 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003151 else
3152 break;
3153 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003154
Simon Kelley52b92f42012-01-22 16:05:15 +00003155 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003156 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003157 {
3158 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003159 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003160 if (!(*cp >= '0' && *cp <= '9'))
3161 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003162 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003163 {
3164 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003165 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003166 }
3167 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003168
Petr Menšík59e47032018-11-02 22:39:39 +00003169 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003170 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003171 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003172 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003173 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003174 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003175 }
Petr Menšík59e47032018-11-02 22:39:39 +00003176 else if (new->prefix < 64)
3177 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003178
Petr Menšík59e47032018-11-02 22:39:39 +00003179 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3180 err=(_("inconsistent DHCPv6 range"));
3181
3182 if (err)
3183 {
3184 dhcp_context_free(new);
3185 ret_err(err);
3186 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003187
3188 /* dhcp-range=:: enables DHCP stateless on any interface */
3189 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3190 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003191
3192 if (new->flags & CONTEXT_TEMPLATE)
3193 {
3194 struct in6_addr zero;
3195 memset(&zero, 0, sizeof(zero));
3196 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003197 {
3198 dhcp_context_free(new);
3199 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3200 }
Simon Kelley66409192013-08-01 20:19:32 +01003201 }
3202
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003203 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003204 {
3205 struct in6_addr tmp = new->start6;
3206 new->start6 = new->end6;
3207 new->end6 = tmp;
3208 }
Simon Kelley849a8352006-06-09 21:02:31 +01003209 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003210#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003211 else
Petr Menšík59e47032018-11-02 22:39:39 +00003212 {
3213 dhcp_context_free(new);
3214 ret_err(_("bad dhcp-range"));
3215 }
Simon Kelley849a8352006-06-09 21:02:31 +01003216
Simon Kelley30cd9662012-03-25 20:44:38 +01003217 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003218 {
Kyle Swenson9d238072021-12-01 08:13:17 -07003219
3220#ifndef HAVE_DHCP6
Simon Kelleyfa794662016-03-03 20:33:54 +00003221 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003222 {
3223 dhcp_context_free(new);
3224 ret_err(_("bad dhcp-range"));
3225 }
Kyle Swenson9d238072021-12-01 08:13:17 -07003226#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003227 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003228 {
3229 new->lease_time = 0xffffffff;
3230 new->flags |= CONTEXT_SETLEASE;
3231 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003232 else if (strcmp(a[leasepos], "deprecated") == 0)
3233 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003234 else
3235 {
3236 int fac = 1;
3237 if (strlen(a[leasepos]) > 0)
3238 {
3239 switch (a[leasepos][strlen(a[leasepos]) - 1])
3240 {
Simon Kelley42243212012-07-20 15:19:18 +01003241 case 'w':
3242 case 'W':
3243 fac *= 7;
3244 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003245 case 'd':
3246 case 'D':
3247 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003248 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003249 case 'h':
3250 case 'H':
3251 fac *= 60;
3252 /* fall through */
3253 case 'm':
3254 case 'M':
3255 fac *= 60;
3256 /* fall through */
3257 case 's':
3258 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003259 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003260 }
3261
Simon Kelleybe379862012-12-23 12:01:39 +00003262 for (cp = a[leasepos]; *cp; cp++)
3263 if (!(*cp >= '0' && *cp <= '9'))
3264 break;
3265
Kyle Swenson9d238072021-12-01 08:13:17 -07003266 if (*cp)
Petr Menšík59e47032018-11-02 22:39:39 +00003267 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003268
Simon Kelley849a8352006-06-09 21:02:31 +01003269 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003270 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003271 /* Leases of a minute or less confuse
3272 some clients, notably Apple's */
3273 if (new->lease_time < 120)
3274 new->lease_time = 120;
3275 }
3276 }
Kyle Swenson9d238072021-12-01 08:13:17 -07003277 leasepos++;
Simon Kelley849a8352006-06-09 21:02:31 +01003278 }
Kyle Swenson9d238072021-12-01 08:13:17 -07003279#ifdef HAVE_DHCP6
3280 if (leasepos < k)
3281 {
3282 if (leasepos != k-1)
3283 {
3284 dhcp_context_free(new);
3285 ret_err(_("bad dhcp-range"));
3286 }
3287 if (strcmp(a[leasepos], "infinite") == 0)
3288 {
3289 new->valid_lifetime = 0xffffffff;
3290 new->flags |= CONTEXT_SETVALID;
3291 }
3292 else
3293 {
3294 int fac = 1;
3295 if (strlen(a[leasepos]) > 0)
3296 {
3297 switch (a[leasepos][strlen(a[leasepos]) - 1])
3298 {
3299 case 'w':
3300 case 'W':
3301 fac *= 7;
3302 /* fall through */
3303 case 'd':
3304 case 'D':
3305 fac *= 24;
3306 /* fall through */
3307 case 'h':
3308 case 'H':
3309 fac *= 60;
3310 /* fall through */
3311 case 'm':
3312 case 'M':
3313 fac *= 60;
3314 /* fall through */
3315 case 's':
3316 case 'S':
3317 a[leasepos][strlen(a[leasepos]) - 1] = 0;
3318 }
3319
3320 for (cp = a[leasepos]; *cp; cp++)
3321 if (!(*cp >= '0' && *cp <= '9'))
3322 break;
Simon Kelley4d85e402020-07-12 22:45:46 +01003323
Kyle Swenson9d238072021-12-01 08:13:17 -07003324 if (*cp || (leasepos+1 < k))
3325 ret_err_free(_("bad dhcp-range"), new);
3326
3327 new->valid_lifetime = atoi(a[leasepos]) * fac;
3328 new->flags |= CONTEXT_SETVALID;
3329 }
3330 }
3331 leasepos++;
3332 }
3333#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003334 break;
3335 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003336
Simon Kelley5aabfc72007-08-29 11:24:47 +01003337 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003338 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003339 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003340 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003341 struct in_addr in;
3342
Simon Kelley824af852008-02-12 20:43:05 +00003343 new = opt_malloc(sizeof(struct dhcp_config));
3344
Simon Kelley849a8352006-06-09 21:02:31 +01003345 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003346 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3347 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003348 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003349 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003350 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003351#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003352 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003353#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003354
Simon Kelley137286e2020-02-06 22:09:30 +00003355 while (arg)
3356 {
3357 comma = split(arg);
3358 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3359 {
3360 if ((arg[0] == 'i' || arg[0] == 'I') &&
3361 (arg[1] == 'd' || arg[1] == 'D') &&
3362 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003363 {
Simon Kelley137286e2020-02-06 22:09:30 +00003364 if (arg[3] == '*')
3365 new->flags |= CONFIG_NOCLID;
3366 else
3367 {
3368 int len;
3369 arg += 3; /* dump id: */
3370 if (strchr(arg, ':'))
3371 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3372 else
3373 {
3374 unhide_metas(arg);
3375 len = (int) strlen(arg);
3376 }
3377
3378 if (len == -1)
3379 {
3380 dhcp_config_free(new);
3381 ret_err(_("bad hex constant"));
3382 }
3383 else if ((new->clid = opt_malloc(len)))
3384 {
3385 new->flags |= CONFIG_CLID;
3386 new->clid_len = len;
3387 memcpy(new->clid, arg, len);
3388 }
3389 }
3390 }
3391 /* dhcp-host has strange backwards-compat needs. */
3392 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3393 {
3394 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3395 newlist->next = new->netid;
3396 new->netid = newlist;
3397 newlist->list = dhcp_netid_create(arg+4, NULL);
3398 }
3399 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003400 new->filter = dhcp_netid_create(arg+4, new->filter);
3401
Simon Kelley137286e2020-02-06 22:09:30 +00003402#ifdef HAVE_DHCP6
3403 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3404 {
3405 char *pref;
3406 struct in6_addr in6;
3407 struct addrlist *new_addr;
3408
3409 arg[strlen(arg)-1] = 0;
3410 arg++;
3411 pref = split_chr(arg, '/');
3412
3413 if (!inet_pton(AF_INET6, arg, &in6))
3414 {
3415 dhcp_config_free(new);
3416 ret_err(_("bad IPv6 address"));
3417 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003418
Simon Kelley137286e2020-02-06 22:09:30 +00003419 new_addr = opt_malloc(sizeof(struct addrlist));
3420 new_addr->next = new->addr6;
3421 new_addr->flags = 0;
3422 new_addr->addr.addr6 = in6;
3423 new->addr6 = new_addr;
3424
3425 if (pref)
3426 {
3427 u64 addrpart = addr6part(&in6);
3428
3429 if (!atoi_check(pref, &new_addr->prefixlen) ||
3430 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003431 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003432 {
3433 dhcp_config_free(new);
3434 ret_err(_("bad IPv6 prefix"));
3435 }
3436
3437 new_addr->flags |= ADDRLIST_PREFIX;
3438 }
3439
3440 for (i= 0; i < 8; i++)
3441 if (in6.s6_addr[i] != 0)
3442 break;
3443
3444 /* set WILDCARD if network part all zeros */
3445 if (i == 8)
3446 new_addr->flags |= ADDRLIST_WILDCARD;
3447
3448 new->flags |= CONFIG_ADDR6;
3449 }
3450#endif
3451 else
3452 {
3453 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3454 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3455 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3456 {
3457 free(newhw);
3458 dhcp_config_free(new);
3459 ret_err(_("bad hex constant"));
3460 }
3461 else
3462 {
3463 newhw->next = new->hwaddr;
3464 new->hwaddr = newhw;
3465 }
3466 }
3467 }
3468 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3469 {
3470 struct dhcp_config *configs;
3471
3472 new->addr = in;
3473 new->flags |= CONFIG_ADDR;
3474
3475 /* If the same IP appears in more than one host config, then DISCOVER
3476 for one of the hosts will get the address, but REQUEST will be NAKed,
3477 since the address is reserved by the other one -> protocol loop. */
3478 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3479 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003480 {
Simon Kelley137286e2020-02-06 22:09:30 +00003481 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3482 return 0;
3483 }
3484 }
3485 else
3486 {
3487 char *cp, *lastp = NULL, last = 0;
3488 int fac = 1, isdig = 0;
3489
3490 if (strlen(arg) > 1)
3491 {
3492 lastp = arg + strlen(arg) - 1;
3493 last = *lastp;
3494 switch (last)
3495 {
3496 case 'w':
3497 case 'W':
3498 fac *= 7;
3499 /* fall through */
3500 case 'd':
3501 case 'D':
3502 fac *= 24;
3503 /* fall through */
3504 case 'h':
3505 case 'H':
3506 fac *= 60;
3507 /* fall through */
3508 case 'm':
3509 case 'M':
3510 fac *= 60;
3511 /* fall through */
3512 case 's':
3513 case 'S':
3514 *lastp = 0;
3515 }
3516 }
3517
3518 for (cp = arg; *cp; cp++)
3519 if (isdigit((unsigned char)*cp))
3520 isdig = 1;
3521 else if (*cp != ' ')
3522 break;
3523
3524 if (*cp)
3525 {
3526 if (lastp)
3527 *lastp = last;
3528 if (strcmp(arg, "infinite") == 0)
3529 {
3530 new->lease_time = 0xffffffff;
3531 new->flags |= CONFIG_TIME;
3532 }
3533 else if (strcmp(arg, "ignore") == 0)
3534 new->flags |= CONFIG_DISABLE;
3535 else
3536 {
3537 if (!(new->hostname = canonicalise_opt(arg)) ||
3538 !legal_hostname(new->hostname))
3539 {
3540 dhcp_config_free(new);
3541 ret_err(_("bad DHCP host name"));
3542 }
3543
3544 new->flags |= CONFIG_NAME;
3545 new->domain = strip_hostname(new->hostname);
3546 }
3547 }
3548 else if (isdig)
3549 {
3550 new->lease_time = atoi(arg) * fac;
3551 /* Leases of a minute or less confuse
3552 some clients, notably Apple's */
3553 if (new->lease_time < 120)
3554 new->lease_time = 120;
3555 new->flags |= CONFIG_TIME;
3556 }
3557 }
3558
3559 arg = comma;
3560 }
3561
Simon Kelley5aabfc72007-08-29 11:24:47 +01003562 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003563 break;
3564 }
Simon Kelley137286e2020-02-06 22:09:30 +00003565
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003566 case LOPT_TAG_IF: /* --tag-if */
3567 {
3568 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3569
3570 new->tag = NULL;
3571 new->set = NULL;
3572 new->next = NULL;
3573
3574 /* preserve order */
3575 if (!daemon->tag_if)
3576 daemon->tag_if = new;
3577 else
3578 {
3579 struct tag_if *tmp;
3580 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3581 tmp->next = new;
3582 }
3583
3584 while (arg)
3585 {
3586 size_t len;
3587
3588 comma = split(arg);
3589 len = strlen(arg);
3590
3591 if (len < 5)
3592 {
3593 new->set = NULL;
3594 break;
3595 }
3596 else
3597 {
Petr Menšík59e47032018-11-02 22:39:39 +00003598 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003599
3600 if (strstr(arg, "set:") == arg)
3601 {
3602 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3603 newlist->next = new->set;
3604 new->set = newlist;
3605 newlist->list = newtag;
3606 }
3607 else if (strstr(arg, "tag:") == arg)
3608 {
3609 newtag->next = new->tag;
3610 new->tag = newtag;
3611 }
3612 else
3613 {
3614 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003615 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003616 break;
3617 }
3618 }
3619
3620 arg = comma;
3621 }
3622
3623 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003624 {
3625 dhcp_netid_free(new->tag);
3626 dhcp_netid_list_free(new->set);
3627 ret_err_free(_("bad tag-if"), new);
3628 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003629
3630 break;
3631 }
3632
Simon Kelley849a8352006-06-09 21:02:31 +01003633
Simon Kelley73a08a22009-02-05 20:28:08 +00003634 case 'O': /* --dhcp-option */
3635 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003636 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003637 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003638 return parse_dhcp_opt(errstr, arg,
3639 option == LOPT_FORCE ? DHOPT_FORCE :
3640 (option == LOPT_MATCH ? DHOPT_MATCH :
3641 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003642
3643 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3644 {
3645 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3646 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3647 ssize_t len;
3648
3649 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3650 ret_err(gen_err);
3651
3652 new->wildcard = 0;
3653 new->netid = id;
3654 id->net = opt_string_alloc(set_prefix(arg));
3655
3656 if (comma[len-1] == '*')
3657 {
3658 comma[len-1] = 0;
3659 new->wildcard = 1;
3660 }
3661 new->name = opt_string_alloc(comma);
3662
3663 new->next = daemon->dhcp_name_match;
3664 daemon->dhcp_name_match = new;
3665
3666 break;
3667 }
3668
Simon Kelleyf2621c72007-04-29 19:47:21 +01003669 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003670 {
Petr Menšík59e47032018-11-02 22:39:39 +00003671 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003672
Petr Menšík137e9f82018-12-16 21:25:29 +00003673 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003674 {
3675 ret_err(gen_err);
3676 }
Simon Kelley849a8352006-06-09 21:02:31 +01003677 else
3678 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003679 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003680 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003681 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003682 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003683 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003684 dhcp_next_server.s_addr = 0;
3685 if (comma)
3686 {
3687 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003688 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003689 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003690 if (comma)
3691 {
3692 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003693 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3694 {
3695 /*
3696 * The user may have specified the tftp hostname here.
3697 * save it so that it can be resolved/looked up during
3698 * actual dhcp_reply().
3699 */
3700
3701 tftp_sname = opt_string_alloc(comma);
3702 dhcp_next_server.s_addr = 0;
3703 }
Simon Kelley849a8352006-06-09 21:02:31 +01003704 }
3705 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003706
3707 new = opt_malloc(sizeof(struct dhcp_boot));
3708 new->file = dhcp_file;
3709 new->sname = dhcp_sname;
3710 new->tftp_sname = tftp_sname;
3711 new->next_server = dhcp_next_server;
3712 new->netid = id;
3713 new->next = daemon->boot_config;
3714 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003715 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003716
Simon Kelley849a8352006-06-09 21:02:31 +01003717 break;
3718 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003719
Floris Bos503c6092017-04-09 23:07:13 +01003720 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3721 {
Petr Menšík59e47032018-11-02 22:39:39 +00003722 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003723
Petr Menšík137e9f82018-12-16 21:25:29 +00003724 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003725 {
3726 ret_err(gen_err);
3727 }
Floris Bos503c6092017-04-09 23:07:13 +01003728 else
3729 {
3730 struct delay_config *new;
3731 int delay;
3732 if (!atoi_check(arg, &delay))
3733 ret_err(gen_err);
3734
3735 new = opt_malloc(sizeof(struct delay_config));
3736 new->delay = delay;
3737 new->netid = id;
3738 new->next = daemon->delay_conf;
3739 daemon->delay_conf = new;
3740 }
3741
3742 break;
3743 }
3744
Simon Kelley7622fc02009-06-04 20:32:05 +01003745 case LOPT_PXE_PROMT: /* --pxe-prompt */
3746 {
3747 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3748 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003749
Simon Kelley7622fc02009-06-04 20:32:05 +01003750 new->netid = NULL;
3751 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003752 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003753
Petr Menšík137e9f82018-12-16 21:25:29 +00003754 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003755 {
3756 dhcp_opt_free(new);
3757 ret_err(gen_err);
3758 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003759 else
3760 {
3761 comma = split(arg);
3762 unhide_metas(arg);
3763 new->len = strlen(arg) + 1;
3764 new->val = opt_malloc(new->len);
3765 memcpy(new->val + 1, arg, new->len - 1);
3766
Wang Shanker4ded9622020-12-04 10:17:35 +08003767 new->u.vendor_class = NULL;
3768 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003769
3770 if (comma && atoi_check(comma, &timeout))
3771 *(new->val) = timeout;
3772 else
3773 *(new->val) = 255;
3774
3775 new->next = daemon->dhcp_opts;
3776 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003777 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003778 }
3779
3780 break;
3781 }
3782
3783 case LOPT_PXE_SERV: /* --pxe-service */
3784 {
3785 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3786 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003787 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3788 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003789 static int boottype = 32768;
3790
3791 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003792 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003793 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003794 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003795
Simon Kelley7622fc02009-06-04 20:32:05 +01003796 if (arg && (comma = split(arg)))
3797 {
3798 for (i = 0; CSA[i]; i++)
3799 if (strcasecmp(CSA[i], arg) == 0)
3800 break;
3801
3802 if (CSA[i] || atoi_check(arg, &i))
3803 {
3804 arg = comma;
3805 comma = split(arg);
3806
3807 new->CSA = i;
3808 new->menu = opt_string_alloc(arg);
3809
Simon Kelley316e2732010-01-22 20:16:09 +00003810 if (!comma)
3811 {
3812 new->type = 0; /* local boot */
3813 new->basename = NULL;
3814 }
3815 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003816 {
3817 arg = comma;
3818 comma = split(arg);
3819 if (atoi_check(arg, &i))
3820 {
3821 new->type = i;
3822 new->basename = NULL;
3823 }
3824 else
3825 {
3826 new->type = boottype++;
3827 new->basename = opt_string_alloc(arg);
3828 }
3829
Simon Kelley751d6f42012-02-10 15:24:51 +00003830 if (comma)
3831 {
3832 if (!inet_pton(AF_INET, comma, &new->server))
3833 {
3834 new->server.s_addr = 0;
3835 new->sname = opt_string_alloc(comma);
3836 }
3837
3838 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003839 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003840
Simon Kelley316e2732010-01-22 20:16:09 +00003841 /* Order matters */
3842 new->next = NULL;
3843 if (!daemon->pxe_services)
3844 daemon->pxe_services = new;
3845 else
3846 {
3847 struct pxe_service *s;
3848 for (s = daemon->pxe_services; s->next; s = s->next);
3849 s->next = new;
3850 }
3851
3852 daemon->enable_pxe = 1;
3853 break;
3854
Simon Kelley7622fc02009-06-04 20:32:05 +01003855 }
3856 }
3857
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003858 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003859 }
3860
Simon Kelleyf2621c72007-04-29 19:47:21 +01003861 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003862 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003863 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003864 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003865 else
3866 {
Simon Kelley824af852008-02-12 20:43:05 +00003867 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003868 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003869 unhide_metas(comma);
3870 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003871 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003872 {
3873 free(new->netid.net);
3874 ret_err_free(gen_err, new);
3875 }
Simon Kelley28866e92011-02-14 20:19:14 +00003876 else
3877 {
3878 new->next = daemon->dhcp_macs;
3879 daemon->dhcp_macs = new;
3880 }
Simon Kelley849a8352006-06-09 21:02:31 +01003881 }
3882 }
3883 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003884
Simon Kelleyf2621c72007-04-29 19:47:21 +01003885 case 'U': /* --dhcp-vendorclass */
3886 case 'j': /* --dhcp-userclass */
3887 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3888 case LOPT_REMOTE: /* --dhcp-remoteid */
3889 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003890 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003891 unsigned char *p;
3892 int dig = 0;
3893 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3894
3895 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003896 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003897
3898 new->netid.net = opt_string_alloc(set_prefix(arg));
3899 /* check for hex string - must digits may include : must not have nothing else,
3900 only allowed for agent-options. */
3901
3902 arg = comma;
3903 if ((comma = split(arg)))
3904 {
3905 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003906 {
3907 free(new->netid.net);
3908 ret_err_free(gen_err, new);
3909 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003910 else
3911 new->enterprise = atoi(arg+11);
3912 }
3913 else
3914 comma = arg;
3915
3916 for (p = (unsigned char *)comma; *p; p++)
3917 if (isxdigit(*p))
3918 dig = 1;
3919 else if (*p != ':')
3920 break;
3921 unhide_metas(comma);
3922 if (option == 'U' || option == 'j' || *p || !dig)
3923 {
3924 new->len = strlen(comma);
3925 new->data = opt_malloc(new->len);
3926 memcpy(new->data, comma, new->len);
3927 }
3928 else
3929 {
3930 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3931 new->data = opt_malloc(new->len);
3932 memcpy(new->data, comma, new->len);
3933 }
3934
3935 switch (option)
3936 {
3937 case 'j':
3938 new->match_type = MATCH_USER;
3939 break;
3940 case 'U':
3941 new->match_type = MATCH_VENDOR;
3942 break;
3943 case LOPT_CIRCUIT:
3944 new->match_type = MATCH_CIRCUIT;
3945 break;
3946 case LOPT_REMOTE:
3947 new->match_type = MATCH_REMOTE;
3948 break;
3949 case LOPT_SUBSCR:
3950 new->match_type = MATCH_SUBSCRIBER;
3951 break;
3952 }
3953 new->next = daemon->dhcp_vendors;
3954 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003955
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003956 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003957 }
3958
Simon Kelley9e038942008-05-30 20:06:34 +01003959 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3960 if (!arg)
3961 {
3962 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3963 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3964 }
3965 else
3966 {
3967 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003968 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3969 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003970 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003971 if (!comma)
3972 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3973 }
3974 break;
3975
Simon Kelley824af852008-02-12 20:43:05 +00003976 case 'J': /* --dhcp-ignore */
3977 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3978 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003979 case '3': /* --bootp-dynamic */
3980 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003981 {
Simon Kelley824af852008-02-12 20:43:05 +00003982 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003983 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003984 if (option == 'J')
3985 {
3986 new->next = daemon->dhcp_ignore;
3987 daemon->dhcp_ignore = new;
3988 }
Simon Kelley824af852008-02-12 20:43:05 +00003989 else if (option == LOPT_BROADCAST)
3990 {
3991 new->next = daemon->force_broadcast;
3992 daemon->force_broadcast = new;
3993 }
Simon Kelley9009d742008-11-14 20:04:27 +00003994 else if (option == '3')
3995 {
3996 new->next = daemon->bootp_dynamic;
3997 daemon->bootp_dynamic = new;
3998 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003999 else if (option == LOPT_GEN_NAMES)
4000 {
4001 new->next = daemon->dhcp_gen_names;
4002 daemon->dhcp_gen_names = new;
4003 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004004 else
4005 {
4006 new->next = daemon->dhcp_ignore_names;
4007 daemon->dhcp_ignore_names = new;
4008 }
4009
4010 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004011 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00004012 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01004013 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00004014 }
Simon Kelley849a8352006-06-09 21:02:31 +01004015
4016 new->list = list;
4017 break;
4018 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004019
4020 case LOPT_PROXY: /* --dhcp-proxy */
4021 daemon->override = 1;
4022 while (arg) {
4023 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
4024 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004025 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00004026 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004027 new->next = daemon->override_relays;
4028 daemon->override_relays = new;
4029 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08004030 }
4031 break;
4032
4033 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
4034 {
4035 while (arg) {
4036 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
4037 comma = split(arg);
4038 new->data = opt_string_alloc(arg);
4039 new->next = daemon->dhcp_pxe_vendors;
4040 daemon->dhcp_pxe_vendors = new;
4041 arg = comma;
4042 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004043 }
4044 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01004045
4046 case LOPT_RELAY: /* --dhcp-relay */
4047 {
4048 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
4049 comma = split(arg);
4050 new->interface = opt_string_alloc(split(comma));
4051 new->iface_index = 0;
4052 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
4053 {
4054 new->next = daemon->relay4;
4055 daemon->relay4 = new;
4056 }
4057#ifdef HAVE_DHCP6
4058 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
4059 {
4060 new->next = daemon->relay6;
4061 daemon->relay6 = new;
4062 }
4063#endif
4064 else
Petr Menšík59e47032018-11-02 22:39:39 +00004065 {
4066 free(new->interface);
4067 ret_err_free(_("Bad dhcp-relay"), new);
4068 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01004069
4070 break;
4071 }
4072
Simon Kelley7622fc02009-06-04 20:32:05 +01004073#endif
Simon Kelley849a8352006-06-09 21:02:31 +01004074
Simon Kelley8b372702012-03-09 17:45:10 +00004075#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004076 case LOPT_RA_PARAM: /* --ra-param */
4077 if ((comma = split(arg)))
4078 {
4079 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
4080 new->lifetime = -1;
4081 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01004082 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004083 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004084 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004085 if (strcasestr(comma, "mtu:") == comma)
4086 {
4087 arg = comma + 4;
4088 if (!(comma = split(comma)))
4089 goto err;
4090 if (!strcasecmp(arg, "off"))
4091 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004092 else if (!atoi_check(arg, &new->mtu))
4093 new->mtu_name = opt_string_alloc(arg);
4094 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004095 goto err;
4096 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004097 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4098 {
4099 if (*comma == 'l' || *comma == 'L')
4100 new->prio = 0x18;
4101 else
4102 new->prio = 0x08;
4103 comma = split(comma);
4104 }
4105 arg = split(comma);
4106 if (!atoi_check(comma, &new->interval) ||
4107 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004108 {
David Flamand005c46d2017-04-11 11:49:54 +01004109err:
Petr Menšík59e47032018-11-02 22:39:39 +00004110 free(new->name);
4111 ret_err_free(_("bad RA-params"), new);
4112 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004113
4114 new->next = daemon->ra_interfaces;
4115 daemon->ra_interfaces = new;
4116 }
4117 break;
4118
Simon Kelley8b372702012-03-09 17:45:10 +00004119 case LOPT_DUID: /* --dhcp-duid */
4120 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004121 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004122 else
4123 {
4124 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4125 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4126 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4127 }
4128 break;
4129#endif
4130
Simon Kelleyf2621c72007-04-29 19:47:21 +01004131 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004132 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004133 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004134 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004135 struct doctor *new = opt_malloc(sizeof(struct doctor));
4136 new->next = daemon->doctors;
4137 daemon->doctors = new;
4138 new->mask.s_addr = 0xffffffff;
4139 new->end.s_addr = 0;
4140
Simon Kelley849a8352006-06-09 21:02:31 +01004141 if ((a[0] = arg))
4142 for (k = 1; k < 3; k++)
4143 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004144 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004145 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004146 unhide_metas(a[k]);
4147 }
Simon Kelley849a8352006-06-09 21:02:31 +01004148
Simon Kelley73a08a22009-02-05 20:28:08 +00004149 dash = split_chr(a[0], '-');
4150
Simon Kelley849a8352006-06-09 21:02:31 +01004151 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004152 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004153 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4154 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4155 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004156
Simon Kelley73a08a22009-02-05 20:28:08 +00004157 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004158 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004159 !is_same_net(new->in, new->end, new->mask) ||
4160 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004161 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004162
4163 break;
4164 }
4165
Simon Kelleyf2621c72007-04-29 19:47:21 +01004166 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004167 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004168 {
4169 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004170 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004171
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004172 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004173
Simon Kelley824af852008-02-12 20:43:05 +00004174 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004175 memset(new, 0, sizeof(struct interface_name));
4176 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004177
Simon Kelleyf2621c72007-04-29 19:47:21 +01004178 /* Add to the end of the list, so that first name
4179 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004180 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004181 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004182
4183 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004184 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004185 if (inet_pton(AF_INET, arg, &new->proto4))
4186 new->flags |= INP4;
4187 else if (inet_pton(AF_INET6, arg, &new->proto6))
4188 new->flags |= INP6;
4189 else
4190 break;
4191
4192 arg = comma;
4193 }
4194
4195 if ((comma = split_chr(arg, '/')))
4196 {
4197 if (strcmp(comma, "4") == 0)
4198 new->flags &= ~IN6;
4199 else if (strcmp(comma, "6") == 0)
4200 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004201 else
Petr Menšík59e47032018-11-02 22:39:39 +00004202 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004203 }
4204
4205 new->intr = opt_string_alloc(arg);
4206
4207 if (option == LOPT_DYNHOST)
4208 {
4209 if (!(new->flags & (INP4 | INP6)))
4210 ret_err(_("missing address in dynamic host"));
4211
4212 if (!(new->flags & IN4) || !(new->flags & IN6))
4213 arg = NULL; /* provoke error below */
4214
4215 new->flags &= ~(IN4 | IN6);
4216 }
4217 else
4218 {
4219 if (new->flags & (INP4 | INP6))
4220 arg = NULL; /* provoke error below */
4221 }
4222
4223 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4224 ret_err(option == LOPT_DYNHOST ?
4225 _("bad dynamic host") : _("bad interface name"));
4226
Simon Kelleyf2621c72007-04-29 19:47:21 +01004227 break;
4228 }
Simon Kelley9009d742008-11-14 20:04:27 +00004229
4230 case LOPT_CNAME: /* --cname */
4231 {
4232 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004233 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004234 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004235
Simon Kelleya1d973f2016-12-22 22:09:50 +00004236 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004237 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004238 pen = last;
4239 last = comma;
4240 }
4241
4242 if (!pen)
4243 ret_err(_("bad CNAME"));
4244
4245 if (pen != arg && atoi_check(last, &ttl))
4246 last = pen;
4247
4248 target = canonicalise_opt(last);
4249
4250 while (arg != last)
4251 {
Petr Menšík56f06232018-03-06 23:13:32 +00004252 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004253 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004254
4255 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004256 {
4257 free(target);
4258 free(alias);
4259 ret_err(_("bad CNAME"));
4260 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004261
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004262 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004263 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004264 {
4265 free(target);
4266 free(alias);
4267 ret_err(_("duplicate CNAME"));
4268 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004269 new = opt_malloc(sizeof(struct cname));
4270 new->next = daemon->cnames;
4271 daemon->cnames = new;
4272 new->alias = alias;
4273 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004274 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004275
Petr Menšík56f06232018-03-06 23:13:32 +00004276 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004277 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004278
Simon Kelley9009d742008-11-14 20:04:27 +00004279 break;
4280 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004281
4282 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004283 {
4284 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004285 char *dom, *target = NULL;
4286
Simon Kelleyf2621c72007-04-29 19:47:21 +01004287 comma = split(arg);
4288
Simon Kelley1f15b812009-10-13 17:49:32 +01004289 if (!(dom = canonicalise_opt(arg)) ||
4290 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004291 {
4292 free(dom);
4293 free(target);
4294 ret_err(_("bad PTR record"));
4295 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004296 else
4297 {
4298 new = opt_malloc(sizeof(struct ptr_record));
4299 new->next = daemon->ptr;
4300 daemon->ptr = new;
4301 new->name = dom;
4302 new->ptr = target;
4303 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004304 break;
4305 }
4306
Simon Kelley1a6bca82008-07-11 11:11:42 +01004307 case LOPT_NAPTR: /* --naptr-record */
4308 {
4309 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4310 int k = 0;
4311 struct naptr *new;
4312 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004313 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004314
4315 if ((a[0] = arg))
4316 for (k = 1; k < 7; k++)
4317 if (!(a[k] = split(a[k-1])))
4318 break;
4319
4320
4321 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004322 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004323 !atoi_check16(a[1], &order) ||
4324 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004325 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004326 {
4327 free(name);
4328 free(replace);
4329 ret_err(_("bad NAPTR record"));
4330 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004331 else
4332 {
4333 new = opt_malloc(sizeof(struct naptr));
4334 new->next = daemon->naptr;
4335 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004336 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004337 new->flags = opt_string_alloc(a[3]);
4338 new->services = opt_string_alloc(a[4]);
4339 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004340 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004341 new->order = order;
4342 new->pref = pref;
4343 }
4344 break;
4345 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004346
4347 case LOPT_RR: /* dns-rr */
4348 {
4349 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004350 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004351 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004352 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004353
4354 comma = split(arg);
4355 data = split(comma);
4356
4357 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004358 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004359
Petr Menšík59e47032018-11-02 22:39:39 +00004360 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004361 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004362 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004363 {
4364 free(new->name);
4365 ret_err_free(_("bad RR record"), new);
4366 }
4367
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004368 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004369 new->class = class;
4370 new->next = daemon->rr;
4371 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004372
4373 if (data)
4374 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004375 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004376 new->len = len;
4377 memcpy(new->txt, data, len);
4378 }
4379
4380 break;
4381 }
4382
Simon Kelley974a6d02018-08-23 23:01:16 +01004383 case LOPT_CAA: /* --caa-record */
4384 {
4385 struct txt_record *new;
4386 char *tag, *value;
4387 int flags;
4388
4389 comma = split(arg);
4390 tag = split(comma);
4391 value = split(tag);
4392
4393 new = opt_malloc(sizeof(struct txt_record));
4394 new->next = daemon->rr;
4395 daemon->rr = new;
4396
4397 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4398 ret_err(_("bad CAA record"));
4399
4400 unhide_metas(tag);
4401 unhide_metas(value);
4402
4403 new->len = strlen(tag) + strlen(value) + 2;
4404 new->txt = opt_malloc(new->len);
4405 new->txt[0] = flags;
4406 new->txt[1] = strlen(tag);
4407 memcpy(&new->txt[2], tag, strlen(tag));
4408 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4409 new->class = T_CAA;
4410
4411 break;
4412 }
4413
Simon Kelleyf2621c72007-04-29 19:47:21 +01004414 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004415 {
4416 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004417 unsigned char *p, *cnt;
4418 size_t len;
4419
4420 comma = split(arg);
4421
Simon Kelley824af852008-02-12 20:43:05 +00004422 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004423 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004424 new->stat = 0;
4425
Simon Kelley1f15b812009-10-13 17:49:32 +01004426 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004427 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004428
Petr Menšík59e47032018-11-02 22:39:39 +00004429 new->next = daemon->txt;
4430 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004431 len = comma ? strlen(comma) : 0;
4432 len += (len/255) + 1; /* room for extra counts */
4433 new->txt = p = opt_malloc(len);
4434
4435 cnt = p++;
4436 *cnt = 0;
4437
4438 while (comma && *comma)
4439 {
4440 unsigned char c = (unsigned char)*comma++;
4441
4442 if (c == ',' || *cnt == 255)
4443 {
4444 if (c != ',')
4445 comma--;
4446 cnt = p++;
4447 *cnt = 0;
4448 }
4449 else
4450 {
4451 *p++ = unhide_meta(c);
4452 (*cnt)++;
4453 }
4454 }
4455
4456 new->len = p - new->txt;
4457
Simon Kelley849a8352006-06-09 21:02:31 +01004458 break;
4459 }
4460
Simon Kelleyf2621c72007-04-29 19:47:21 +01004461 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004462 {
4463 int port = 1, priority = 0, weight = 0;
4464 char *name, *target = NULL;
4465 struct mx_srv_record *new;
4466
Simon Kelleyf2621c72007-04-29 19:47:21 +01004467 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004468
Simon Kelley1f15b812009-10-13 17:49:32 +01004469 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004470 ret_err(_("bad SRV record"));
4471
Simon Kelley849a8352006-06-09 21:02:31 +01004472 if (comma)
4473 {
4474 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004475 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004476 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004477 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004478
Simon Kelley849a8352006-06-09 21:02:31 +01004479 if (comma)
4480 {
4481 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004482 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004483 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004484 {
4485 free(name);
4486 ret_err_free(_("invalid port number"), target);
4487 }
Simon Kelley824af852008-02-12 20:43:05 +00004488
Simon Kelley849a8352006-06-09 21:02:31 +01004489 if (comma)
4490 {
4491 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004492 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004493 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004494 {
4495 free(name);
4496 ret_err_free(_("invalid priority"), target);
4497 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004498 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004499 {
4500 free(name);
4501 ret_err_free(_("invalid weight"), target);
4502 }
Simon Kelley849a8352006-06-09 21:02:31 +01004503 }
4504 }
4505 }
4506
Simon Kelley824af852008-02-12 20:43:05 +00004507 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004508 new->next = daemon->mxnames;
4509 daemon->mxnames = new;
4510 new->issrv = 1;
4511 new->name = name;
4512 new->target = target;
4513 new->srvport = port;
4514 new->priority = priority;
4515 new->weight = weight;
4516 break;
4517 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004518
Simon Kelleye759d422012-03-16 13:18:57 +00004519 case LOPT_HOST_REC: /* --host-record */
4520 {
Petr Menšík59e47032018-11-02 22:39:39 +00004521 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004522
Simon Kelleye759d422012-03-16 13:18:57 +00004523 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004524 ret_err(_("Bad host-record"));
4525
Petr Menšík59e47032018-11-02 22:39:39 +00004526 new = opt_malloc(sizeof(struct host_record));
4527 memset(new, 0, sizeof(struct host_record));
4528 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004529 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004530
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004531 while (arg)
4532 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004533 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004534 char *dig;
4535
4536 for (dig = arg; *dig != 0; dig++)
4537 if (*dig < '0' || *dig > '9')
4538 break;
4539 if (*dig == 0)
4540 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004541 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004542 {
4543 new->addr = addr.addr4;
4544 new->flags |= HR_4;
4545 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004546 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004547 {
4548 new->addr6 = addr.addr6;
4549 new->flags |= HR_6;
4550 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004551 else
4552 {
4553 int nomem;
4554 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004555 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004556 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004557 {
4558 struct name_list *tmp = new->names, *next;
4559 for (tmp = new->names; tmp; tmp = next)
4560 {
4561 next = tmp->next;
4562 free(tmp);
4563 }
4564 ret_err_free(_("Bad name in host-record"), new);
4565 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004566
Petr Menšík59e47032018-11-02 22:39:39 +00004567 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004568 nl->name = canon;
4569 /* keep order, so that PTR record goes to first name */
4570 nl->next = NULL;
4571 if (!new->names)
4572 new->names = nl;
4573 else
4574 {
4575 struct name_list *tmp;
4576 for (tmp = new->names; tmp->next; tmp = tmp->next);
4577 tmp->next = nl;
4578 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004579 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004580
4581 arg = comma;
4582 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004583 }
Simon Kelleye759d422012-03-16 13:18:57 +00004584
4585 /* Keep list order */
4586 if (!daemon->host_records_tail)
4587 daemon->host_records = new;
4588 else
4589 daemon->host_records_tail->next = new;
4590 new->next = NULL;
4591 daemon->host_records_tail = new;
4592 break;
4593 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004594
4595#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004596 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004597 daemon->timestamp_file = opt_string_alloc(arg);
4598 break;
4599
Simon Kelleyf3e57872018-07-20 21:10:48 +01004600 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004601 if (arg)
4602 {
4603 if (strcmp(arg, "no") == 0)
4604 set_option_bool(OPT_DNSSEC_IGN_NS);
4605 else
4606 ret_err(_("bad value for dnssec-check-unsigned"));
4607 }
4608 break;
4609
Simon Kelleyf3e57872018-07-20 21:10:48 +01004610 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004611 {
Simon Kelleyee415862014-02-11 11:07:22 +00004612 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4613 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4614 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004615
4616 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004617 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004618
Simon Kelleycbf13a22014-01-25 17:59:14 +00004619 if ((comma = split(arg)) && (algo = split(comma)))
4620 {
4621 int class = 0;
4622 if (strcmp(comma, "IN") == 0)
4623 class = C_IN;
4624 else if (strcmp(comma, "CH") == 0)
4625 class = C_CHAOS;
4626 else if (strcmp(comma, "HS") == 0)
4627 class = C_HESIOD;
4628
4629 if (class != 0)
4630 {
4631 new->class = class;
4632 comma = algo;
4633 algo = split(comma);
4634 }
4635 }
4636
Simon Kelleyee415862014-02-11 11:07:22 +00004637 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4638 !atoi_check16(comma, &new->keytag) ||
4639 !atoi_check8(algo, &new->algo) ||
4640 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004641 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004642 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004643
Simon Kelley0fc2f312014-01-08 10:26:58 +00004644 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004645 len = (2*strlen(keyhex))+1;
4646 new->digest = opt_malloc(len);
4647 unhide_metas(keyhex);
4648 /* 4034: "Whitespace is allowed within digits" */
4649 for (cp = keyhex; *cp; )
4650 if (isspace(*cp))
4651 for (cp1 = cp; *cp1; cp1++)
4652 *cp1 = *(cp1+1);
4653 else
4654 cp++;
4655 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004656 {
4657 free(new->name);
4658 ret_err_free(_("bad HEX in trust anchor"), new);
4659 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004660
Simon Kelleyee415862014-02-11 11:07:22 +00004661 new->next = daemon->ds;
4662 daemon->ds = new;
4663
Simon Kelley0fc2f312014-01-08 10:26:58 +00004664 break;
4665 }
4666#endif
Kyle Swenson545712c2021-11-17 12:25:04 -07004667/* CRADLEPOINT */
4668/* This option (OPT_EDNS_OPTION) is required to tack a(n) owner record onto a DNS request:
4669 * Option Code: Owner (reserved) 4 (0x00 0x04)
4670 * Option Length: 19 ("Cradlepoint" + device_id (obtained from OpenDNS)) (0x00 0x13)
4671 * Option Data: "Cradlepoint" + device_id (an 4 byte identifier)
4672 *
4673 * The EDNS option is passed in like this:
4674 * --edns-option=4,CradlepointAABBCCDD
4675 */
4676 case LOPT_EDNS_OPTION:
4677 {
4678 unsigned char *tmp;
4679 int option_code, option_len = 0;
4680
4681 comma = split(arg);
4682
4683 if (*arg == 0 || !atoi_check16(arg, &option_code))
4684 {
4685 option = '?';
4686 ret_err(_("invalid EDNS option code"));
4687 break;
4688 }
4689
4690 if (comma &&
4691 (option_len = str_unescape_c_string(comma, comma)) <= 0)
4692 {
4693 option = '?';
4694 ret_err(_("invalid EDNS option data"));
4695 break;
4696 }
4697 tmp = opt_malloc(daemon->edns_options_len + 4 + option_len);
4698
4699 if (daemon->edns_options_len > 0)
4700 {
4701 memcpy(tmp, daemon->edns_options, daemon->edns_options_len);
4702 free(daemon->edns_options);
4703 }
4704 daemon->edns_options = tmp;
4705 tmp += daemon->edns_options_len;
4706 PUTSHORT(option_code, tmp);
4707 PUTSHORT(option_len, tmp);
4708 memcpy(tmp, comma, option_len);
4709 daemon->edns_options_len += 4 + option_len;
4710 my_syslog(LOG_DEBUG, _("EDNS Option %d added length %d"), option_code,
4711 daemon->edns_options_len);
4712
4713 break;
4714 }
4715
4716 case LOPT_SSID_MAP:
4717 {
4718 char *err;
4719
4720 unhide_metas(arg);
4721
4722 if ((err = opendns_parse_device_id_opt(arg)) != NULL) {
4723 /* int vs pointer warning return err; */
4724 ret_err(_("opendns parse device id failed"));
4725 }
4726
4727 break;
4728 }
Kyle Swensonba77fe82021-11-19 09:33:46 -07004729 case LOPT_EDNS_RESTRICT:
4730 {
4731 set_option_bool(OPT_EDNS_RESTRICT);
4732 break;
4733 }
Kyle Swenson545712c2021-11-17 12:25:04 -07004734/* CRADLEPOINT */
4735
Simon Kelley7622fc02009-06-04 20:32:05 +01004736 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004737 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004738
Simon Kelley849a8352006-06-09 21:02:31 +01004739 }
Simon Kelley824af852008-02-12 20:43:05 +00004740
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004741 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004742}
4743
Simon Kelley28866e92011-02-14 20:19:14 +00004744static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004745{
Simon Kelley824af852008-02-12 20:43:05 +00004746 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004747 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004748
4749 while (fgets(buff, MAXDNAME, f))
4750 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004751 int white, i;
4752 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004753 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004754 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004755
Simon Kelley824af852008-02-12 20:43:05 +00004756 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004757 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004758 {
4759 if (setjmp(mem_jmp))
4760 continue;
4761 mem_recover = 1;
4762 }
4763
Simon Kelley13dee6f2017-02-28 16:51:58 +00004764 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004765 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004766 errmess = NULL;
4767
Simon Kelley849a8352006-06-09 21:02:31 +01004768 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4769 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004770 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004771 {
4772 if (*p == '"')
4773 {
4774 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004775
Simon Kelley849a8352006-06-09 21:02:31 +01004776 for(; *p && *p != '"'; p++)
4777 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004778 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004779 {
4780 if (p[1] == 't')
4781 p[1] = '\t';
4782 else if (p[1] == 'n')
4783 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004784 else if (p[1] == 'b')
4785 p[1] = '\b';
4786 else if (p[1] == 'r')
4787 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004788 else if (p[1] == 'e') /* escape */
4789 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004790 memmove(p, p+1, strlen(p+1)+1);
4791 }
4792 *p = hide_meta(*p);
4793 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004794
4795 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004796 {
4797 errmess = _("missing \"");
4798 goto oops;
4799 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004800
4801 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004802 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004803
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004804 if (isspace(*p))
4805 {
4806 *p = ' ';
4807 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004808 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004809 else
4810 {
4811 if (white && *p == '#')
4812 {
4813 *p = 0;
4814 break;
4815 }
4816 white = 0;
4817 }
Simon Kelley849a8352006-06-09 21:02:31 +01004818 }
4819
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004820
4821 /* strip leading spaces */
4822 for (start = buff; *start && *start == ' '; start++);
4823
4824 /* strip trailing spaces */
4825 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4826
4827 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004828 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004829 else
4830 start[len] = 0;
4831
Simon Kelley611ebc52012-07-16 16:23:46 +01004832 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004833 arg = start;
4834 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004835 {
4836 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004837 for (arg = p+1; *arg == ' '; arg++);
4838 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004839 *p = 0;
4840 }
4841 else
4842 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004843
Simon Kelley611ebc52012-07-16 16:23:46 +01004844 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004845 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004846 for (option = 0, i = 0; opts[i].name; i++)
4847 if (strcmp(opts[i].name, start) == 0)
4848 {
4849 option = opts[i].val;
4850 break;
4851 }
4852
4853 if (!option)
4854 errmess = _("bad option");
4855 else if (opts[i].has_arg == 0 && arg)
4856 errmess = _("extraneous parameter");
4857 else if (opts[i].has_arg == 1 && !arg)
4858 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004859 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4860 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004861 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004862
4863 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004864 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004865 strcpy(daemon->namebuff, errmess);
4866
Simon Kelley9bafdc62018-08-21 22:53:38 +01004867 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004868 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004869 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004870 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004871 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004872 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004873 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004874 }
Simon Kelley849a8352006-06-09 21:02:31 +01004875 }
4876
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004877 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004878 fclose(f);
4879}
4880
Simon Kelley4f7bb572018-03-08 18:47:08 +00004881#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004882int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004883{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004884 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4885
Simon Kelley70d18732015-01-31 19:59:29 +00004886 if (flags & AH_DHCP_HST)
4887 return one_file(file, LOPT_BANK);
4888 else if (flags & AH_DHCP_OPT)
4889 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004890
Simon Kelley70d18732015-01-31 19:59:29 +00004891 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004892}
4893#endif
4894
Simon Kelley395eb712012-07-06 22:07:05 +01004895static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004896{
4897 FILE *f;
4898 int nofile_ok = 0;
4899 static int read_stdin = 0;
4900 static struct fileread {
4901 dev_t dev;
4902 ino_t ino;
4903 struct fileread *next;
4904 } *filesread = NULL;
4905
4906 if (hard_opt == '7')
4907 {
4908 /* default conf-file reading */
4909 hard_opt = 0;
4910 nofile_ok = 1;
4911 }
4912
4913 if (hard_opt == 0 && strcmp(file, "-") == 0)
4914 {
4915 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004916 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004917 read_stdin = 1;
4918 file = "stdin";
4919 f = stdin;
4920 }
4921 else
4922 {
4923 /* ignore repeated files. */
4924 struct stat statbuf;
4925
4926 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4927 {
4928 struct fileread *r;
4929
4930 for (r = filesread; r; r = r->next)
4931 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004932 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004933
4934 r = safe_malloc(sizeof(struct fileread));
4935 r->next = filesread;
4936 filesread = r;
4937 r->dev = statbuf.st_dev;
4938 r->ino = statbuf.st_ino;
4939 }
4940
4941 if (!(f = fopen(file, "r")))
4942 {
4943 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004944 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004945 else
4946 {
4947 char *str = _("cannot read %s: %s");
4948 if (hard_opt != 0)
4949 {
4950 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004951 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004952 }
4953 else
4954 die(str, file, EC_FILE);
4955 }
4956 }
4957 }
4958
4959 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004960 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004961}
4962
4963/* expand any name which is a directory */
4964struct hostsfile *expand_filelist(struct hostsfile *list)
4965{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004966 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004967 struct hostsfile *ah;
4968
Simon Kelley19c51cf2014-03-18 22:38:30 +00004969 /* find largest used index */
4970 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004971 {
4972 if (i <= ah->index)
4973 i = ah->index + 1;
4974
4975 if (ah->flags & AH_DIR)
4976 ah->flags |= AH_INACTIVE;
4977 else
4978 ah->flags &= ~AH_INACTIVE;
4979 }
4980
4981 for (ah = list; ah; ah = ah->next)
4982 if (!(ah->flags & AH_INACTIVE))
4983 {
4984 struct stat buf;
4985 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4986 {
4987 DIR *dir_stream;
4988 struct dirent *ent;
4989
4990 /* don't read this as a file */
4991 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004992
Simon Kelley28866e92011-02-14 20:19:14 +00004993 if (!(dir_stream = opendir(ah->fname)))
4994 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4995 ah->fname, strerror(errno));
4996 else
4997 {
4998 while ((ent = readdir(dir_stream)))
4999 {
5000 size_t lendir = strlen(ah->fname);
5001 size_t lenfile = strlen(ent->d_name);
5002 struct hostsfile *ah1;
5003 char *path;
5004
5005 /* ignore emacs backups and dotfiles */
5006 if (lenfile == 0 ||
5007 ent->d_name[lenfile - 1] == '~' ||
5008 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
5009 ent->d_name[0] == '.')
5010 continue;
5011
5012 /* see if we have an existing record.
5013 dir is ah->fname
5014 file is ent->d_name
5015 path to match is ah1->fname */
5016
5017 for (ah1 = list; ah1; ah1 = ah1->next)
5018 {
5019 if (lendir < strlen(ah1->fname) &&
5020 strstr(ah1->fname, ah->fname) == ah1->fname &&
5021 ah1->fname[lendir] == '/' &&
5022 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
5023 {
5024 ah1->flags &= ~AH_INACTIVE;
5025 break;
5026 }
5027 }
5028
5029 /* make new record */
5030 if (!ah1)
5031 {
5032 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
5033 continue;
5034
5035 if (!(path = whine_malloc(lendir + lenfile + 2)))
5036 {
5037 free(ah1);
5038 continue;
5039 }
5040
5041 strcpy(path, ah->fname);
5042 strcat(path, "/");
5043 strcat(path, ent->d_name);
5044 ah1->fname = path;
5045 ah1->index = i++;
5046 ah1->flags = AH_DIR;
5047 ah1->next = list;
5048 list = ah1;
5049 }
5050
5051 /* inactivate record if not regular file */
5052 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
5053 ah1->flags |= AH_INACTIVE;
5054
5055 }
5056 closedir(dir_stream);
5057 }
5058 }
5059 }
5060
5061 return list;
5062}
5063
Simon Kelley7b1eae42014-02-20 13:43:28 +00005064void read_servers_file(void)
5065{
5066 FILE *f;
5067
5068 if (!(f = fopen(daemon->servers_file, "r")))
5069 {
5070 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
5071 return;
5072 }
5073
5074 mark_servers(SERV_FROM_FILE);
5075 cleanup_servers();
5076
5077 read_file(daemon->servers_file, f, LOPT_REV_SERV);
5078}
5079
Simon Kelley28866e92011-02-14 20:19:14 +00005080
Simon Kelley7622fc02009-06-04 20:32:05 +01005081#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00005082static void clear_dynamic_conf(void)
5083{
5084 struct dhcp_config *configs, *cp, **up;
5085
5086 /* remove existing... */
5087 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
5088 {
5089 cp = configs->next;
5090
5091 if (configs->flags & CONFIG_BANK)
5092 {
5093 struct hwaddr_config *mac, *tmp;
5094 struct dhcp_netid_list *list, *tmplist;
5095
5096 for (mac = configs->hwaddr; mac; mac = tmp)
5097 {
5098 tmp = mac->next;
5099 free(mac);
5100 }
5101
5102 if (configs->flags & CONFIG_CLID)
5103 free(configs->clid);
5104
5105 for (list = configs->netid; list; list = tmplist)
5106 {
5107 free(list->list);
5108 tmplist = list->next;
5109 free(list);
5110 }
5111
5112 if (configs->flags & CONFIG_NAME)
5113 free(configs->hostname);
5114
5115 *up = configs->next;
5116 free(configs);
5117 }
5118 else
5119 up = &configs->next;
5120 }
5121}
5122
5123static void clear_dynamic_opt(void)
5124{
5125 struct dhcp_opt *opts, *cp, **up;
5126 struct dhcp_netid *id, *next;
5127
5128 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
5129 {
5130 cp = opts->next;
5131
5132 if (opts->flags & DHOPT_BANK)
5133 {
5134 if ((opts->flags & DHOPT_VENDOR))
5135 free(opts->u.vendor_class);
5136 free(opts->val);
5137 for (id = opts->netid; id; id = next)
5138 {
5139 next = id->next;
5140 free(id->net);
5141 free(id);
5142 }
5143 *up = opts->next;
5144 free(opts);
5145 }
5146 else
5147 up = &opts->next;
5148 }
5149}
5150
Simon Kelley824af852008-02-12 20:43:05 +00005151void reread_dhcp(void)
5152{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005153 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005154
Simon Kelley4f7bb572018-03-08 18:47:08 +00005155 /* Do these even if there is no daemon->dhcp_hosts_file or
5156 daemon->dhcp_opts_file since entries may have been created by the
5157 inotify dynamic file reading system. */
5158
5159 clear_dynamic_conf();
5160 clear_dynamic_opt();
5161
5162 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005163 {
Simon Kelley28866e92011-02-14 20:19:14 +00005164 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5165 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005166 if (!(hf->flags & AH_INACTIVE))
5167 {
5168 if (one_file(hf->fname, LOPT_BANK))
5169 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5170 }
Simon Kelley824af852008-02-12 20:43:05 +00005171 }
5172
5173 if (daemon->dhcp_opts_file)
5174 {
Simon Kelley28866e92011-02-14 20:19:14 +00005175 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5176 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5177 if (!(hf->flags & AH_INACTIVE))
5178 {
Simon Kelley395eb712012-07-06 22:07:05 +01005179 if (one_file(hf->fname, LOPT_OPTS))
5180 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005181 }
Simon Kelley824af852008-02-12 20:43:05 +00005182 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005183
5184# ifdef HAVE_INOTIFY
5185 /* Setup notify and read pre-existing files. */
5186 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5187# endif
Simon Kelley824af852008-02-12 20:43:05 +00005188}
Simon Kelley7622fc02009-06-04 20:32:05 +01005189#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005190
Simon Kelley5aabfc72007-08-29 11:24:47 +01005191void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005192{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005193 size_t argbuf_size = MAXDNAME;
5194 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005195 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005196 int option, testmode = 0;
5197 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005198
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005199 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005200
Simon Kelley824af852008-02-12 20:43:05 +00005201 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005202 memset(daemon, 0, sizeof(struct daemon));
5203 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005204
Simon Kelley3be34542004-09-11 19:12:13 +01005205 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005206 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005207 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005208 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005209 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5210 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005211 daemon->default_resolv.is_default = 1;
5212 daemon->default_resolv.name = RESOLVFILE;
5213 daemon->resolv_files = &daemon->default_resolv;
5214 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005215 daemon->runfile = RUNFILE;
5216 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005217 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005218 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005219 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005220 daemon->auth_ttl = AUTH_TTL;
5221 daemon->soa_refresh = SOA_REFRESH;
5222 daemon->soa_retry = SOA_RETRY;
5223 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley4a8c0982021-03-26 21:19:39 +00005224
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005225#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005226 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5227 add_txt("authors.bind", "Simon Kelley", 0);
5228 add_txt("copyright.bind", COPYRIGHT, 0);
5229 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5230 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5231 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5232 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5233 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5234#ifdef HAVE_AUTH
5235 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5236#endif
5237 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005238#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005239
Simon Kelley849a8352006-06-09 21:02:31 +01005240 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005241 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005242#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005243 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005244#else
Simon Kelley849a8352006-06-09 21:02:31 +01005245 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005246#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005247
5248 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005249 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005250 for (; optind < argc; optind++)
5251 {
5252 unsigned char *c = (unsigned char *)argv[optind];
5253 for (; *c != 0; c++)
5254 if (!isspace(*c))
5255 die(_("junk found in command line"), NULL, EC_BADCONF);
5256 }
Simon Kelley28866e92011-02-14 20:19:14 +00005257 break;
5258 }
5259
Simon Kelley849a8352006-06-09 21:02:31 +01005260 /* Copy optarg so that argv doesn't get changed */
5261 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005262 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005263 if (strlen(optarg) >= argbuf_size)
5264 {
5265 free(argbuf);
5266 argbuf_size = strlen(optarg) + 1;
5267 argbuf = opt_malloc(argbuf_size);
5268 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005269 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005270 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005271 }
5272 else
5273 arg = NULL;
5274
5275 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005276 if (option == LOPT_TEST)
5277 testmode = 1;
5278 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005279 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005280#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005281 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005282 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005283#ifdef HAVE_DHCP6
5284 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5285 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005286#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005287 else
5288#endif
5289 do_usage();
5290
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005291 exit(0);
5292 }
Simon Kelley849a8352006-06-09 21:02:31 +01005293 else if (option == 'v')
5294 {
5295 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005296 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005297 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5298 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005299 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005300 exit(0);
5301 }
Simon Kelley849a8352006-06-09 21:02:31 +01005302 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005303 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005304 if (!conffile)
5305 conffile = opt_string_alloc(arg);
5306 else
5307 {
5308 char *extra = opt_string_alloc(arg);
5309 one_file(extra, 0);
5310 free(extra);
5311 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005312 }
Simon Kelley849a8352006-06-09 21:02:31 +01005313 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005314 {
Simon Kelley26128d22004-11-14 16:43:54 +00005315#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005316 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005317#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005318 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005319#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005320 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005321 }
5322 }
Simon Kelley849a8352006-06-09 21:02:31 +01005323
Neil Jerram3bd4c472018-01-18 22:49:38 +00005324 free(argbuf);
5325
Simon Kelley849a8352006-06-09 21:02:31 +01005326 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005327 {
Petr Menšík59e47032018-11-02 22:39:39 +00005328 one_file(conffile, 0);
5329 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005330 }
Petr Menšík59e47032018-11-02 22:39:39 +00005331 else
5332 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005333
Simon Kelley1a6bca82008-07-11 11:11:42 +01005334 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005335 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005336 {
5337 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005338 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005339 if (!(tmp->flags & SERV_HAS_SOURCE))
5340 {
5341 if (tmp->source_addr.sa.sa_family == AF_INET)
5342 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005343 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5344 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005345 }
5346 }
5347
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005348 if (daemon->host_records)
5349 {
5350 struct host_record *hr;
5351
5352 for (hr = daemon->host_records; hr; hr = hr->next)
5353 if (hr->ttl == -1)
5354 hr->ttl = daemon->local_ttl;
5355 }
5356
5357 if (daemon->cnames)
5358 {
Simon Kelley903df072017-01-19 17:22:00 +00005359 struct cname *cn, *cn2, *cn3;
5360
5361#define NOLOOP 1
5362#define TESTLOOP 2
5363
Simon Kelley157d8cf2019-10-25 17:46:49 +01005364 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005365 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005366 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005367 {
5368 if (cn->ttl == -1)
5369 cn->ttl = daemon->local_ttl;
5370 cn->flag = 0;
5371 cn->targetp = NULL;
5372 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5373 if (hostname_isequal(cn->target, cn2->alias))
5374 {
5375 cn->targetp = cn2;
5376 break;
5377 }
5378 }
5379
5380 /* Find any CNAME loops.*/
5381 for (cn = daemon->cnames; cn; cn = cn->next)
5382 {
5383 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5384 {
5385 if (cn2->flag == NOLOOP)
5386 break;
5387
5388 if (cn2->flag == TESTLOOP)
5389 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5390
5391 cn2->flag = TESTLOOP;
5392 }
5393
5394 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5395 cn3->flag = NOLOOP;
5396 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005397 }
5398
Simon Kelley3be34542004-09-11 19:12:13 +01005399 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005400 {
5401 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005402 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005403 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005404 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005405 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005406 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005407 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005408
5409 /* create default, if not specified */
5410 if (daemon->authserver && !daemon->hostmaster)
5411 {
5412 strcpy(buff, "hostmaster.");
5413 strcat(buff, daemon->authserver);
5414 daemon->hostmaster = opt_string_alloc(buff);
5415 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005416
5417 if (!daemon->dhcp_pxe_vendors)
5418 {
5419 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5420 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5421 daemon->dhcp_pxe_vendors->next = NULL;
5422 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005423
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005424 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005425 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005426 {
Simon Kelley0a852542005-03-23 20:28:59 +00005427 struct mx_srv_record *mx;
5428
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005429 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005430 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005431
Simon Kelley0a852542005-03-23 20:28:59 +00005432 for (mx = daemon->mxnames; mx; mx = mx->next)
5433 if (!mx->issrv && hostname_isequal(mx->name, buff))
5434 break;
5435
Simon Kelley28866e92011-02-14 20:19:14 +00005436 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005437 {
Simon Kelley824af852008-02-12 20:43:05 +00005438 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005439 mx->next = daemon->mxnames;
5440 mx->issrv = 0;
5441 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005442 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005443 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005444 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005445
Simon Kelley3be34542004-09-11 19:12:13 +01005446 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005447 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005448
5449 for (mx = daemon->mxnames; mx; mx = mx->next)
5450 if (!mx->issrv && !mx->target)
5451 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005452 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005453
Simon Kelley28866e92011-02-14 20:19:14 +00005454 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005455 daemon->resolv_files &&
5456 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005457 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005458 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005459
Simon Kelley28866e92011-02-14 20:19:14 +00005460 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005461 {
5462 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005463 FILE *f;
5464
Simon Kelley28866e92011-02-14 20:19:14 +00005465 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005466 !daemon->resolv_files ||
5467 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005468 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005469
Simon Kelley3be34542004-09-11 19:12:13 +01005470 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005471 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005472
5473 while ((line = fgets(buff, MAXDNAME, f)))
5474 {
5475 char *token = strtok(line, " \t\n\r");
5476
5477 if (!token || strcmp(token, "search") != 0)
5478 continue;
5479
5480 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005481 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005482 break;
5483 }
Simon Kelley3be34542004-09-11 19:12:13 +01005484
Simon Kelleyde379512004-06-22 20:23:33 +01005485 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005486
Simon Kelley3be34542004-09-11 19:12:13 +01005487 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005488 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005489 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005490
5491 if (daemon->domain_suffix)
5492 {
5493 /* add domain for any srv record without one. */
5494 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005495
Simon Kelley3d8df262005-08-29 12:19:27 +01005496 for (srv = daemon->mxnames; srv; srv = srv->next)
5497 if (srv->issrv &&
5498 strchr(srv->name, '.') &&
5499 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5500 {
5501 strcpy(buff, srv->name);
5502 strcat(buff, ".");
5503 strcat(buff, daemon->domain_suffix);
5504 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005505 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005506 }
5507 }
Simon Kelley28866e92011-02-14 20:19:14 +00005508 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005509 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005510
Simon Kelleyc8a80482014-03-05 14:29:54 +00005511 /* If there's access-control config, then ignore --local-service, it's intended
5512 as a system default to keep otherwise unconfigured installations safe. */
5513 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5514 reset_option_bool(OPT_LOCAL_SERVICE);
5515
Simon Kelley7622fc02009-06-04 20:32:05 +01005516 if (testmode)
5517 {
5518 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5519 exit(0);
5520 }
Simon Kelley849a8352006-06-09 21:02:31 +01005521}