blob: be3dcc949710aab66b2401d2a73a217e849e154f [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;
Simon Kelley8445f5d2012-12-17 21:54:08 +00003018 char *cp, *a[8] = { 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
Simon Kelley1f776932012-12-16 19:46:08 +00003064 for (k = 1; k < 8; 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;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003126 new->next = daemon->dhcp6;
3127 daemon->dhcp6 = new;
3128
Simon Kelley30cd9662012-03-25 20:44:38 +01003129 for (leasepos = 1; leasepos < k; leasepos++)
3130 {
3131 if (strcmp(a[leasepos], "static") == 0)
3132 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3133 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003134 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003135 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003136 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003137 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3138 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003139 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003140 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003141 else if (strcmp(a[leasepos], "off-link") == 0)
3142 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003143 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3144 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003145 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3146 {
3147 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3148 new->flags |= CONTEXT_TEMPLATE;
3149 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003150 else
3151 break;
3152 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003153
Simon Kelley52b92f42012-01-22 16:05:15 +00003154 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003155 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003156 {
3157 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003158 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003159 if (!(*cp >= '0' && *cp <= '9'))
3160 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003161 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003162 {
3163 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003164 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003165 }
3166 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003167
Petr Menšík59e47032018-11-02 22:39:39 +00003168 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003169 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003170 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003171 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003172 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003173 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003174 }
Petr Menšík59e47032018-11-02 22:39:39 +00003175 else if (new->prefix < 64)
3176 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003177
Petr Menšík59e47032018-11-02 22:39:39 +00003178 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3179 err=(_("inconsistent DHCPv6 range"));
3180
3181 if (err)
3182 {
3183 dhcp_context_free(new);
3184 ret_err(err);
3185 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003186
3187 /* dhcp-range=:: enables DHCP stateless on any interface */
3188 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3189 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003190
3191 if (new->flags & CONTEXT_TEMPLATE)
3192 {
3193 struct in6_addr zero;
3194 memset(&zero, 0, sizeof(zero));
3195 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003196 {
3197 dhcp_context_free(new);
3198 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3199 }
Simon Kelley66409192013-08-01 20:19:32 +01003200 }
3201
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003202 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003203 {
3204 struct in6_addr tmp = new->start6;
3205 new->start6 = new->end6;
3206 new->end6 = tmp;
3207 }
Simon Kelley849a8352006-06-09 21:02:31 +01003208 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003209#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003210 else
Petr Menšík59e47032018-11-02 22:39:39 +00003211 {
3212 dhcp_context_free(new);
3213 ret_err(_("bad dhcp-range"));
3214 }
Simon Kelley849a8352006-06-09 21:02:31 +01003215
Simon Kelley30cd9662012-03-25 20:44:38 +01003216 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003217 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003218 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003219 {
3220 dhcp_context_free(new);
3221 ret_err(_("bad dhcp-range"));
3222 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003223
Simon Kelley849a8352006-06-09 21:02:31 +01003224 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003225 {
3226 new->lease_time = 0xffffffff;
3227 new->flags |= CONTEXT_SETLEASE;
3228 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003229 else if (strcmp(a[leasepos], "deprecated") == 0)
3230 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003231 else
3232 {
3233 int fac = 1;
3234 if (strlen(a[leasepos]) > 0)
3235 {
3236 switch (a[leasepos][strlen(a[leasepos]) - 1])
3237 {
Simon Kelley42243212012-07-20 15:19:18 +01003238 case 'w':
3239 case 'W':
3240 fac *= 7;
3241 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003242 case 'd':
3243 case 'D':
3244 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003245 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003246 case 'h':
3247 case 'H':
3248 fac *= 60;
3249 /* fall through */
3250 case 'm':
3251 case 'M':
3252 fac *= 60;
3253 /* fall through */
3254 case 's':
3255 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003256 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003257 }
3258
Simon Kelleybe379862012-12-23 12:01:39 +00003259 for (cp = a[leasepos]; *cp; cp++)
3260 if (!(*cp >= '0' && *cp <= '9'))
3261 break;
3262
Simon Kelley54dae552013-02-05 17:55:10 +00003263 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003264 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003265
Simon Kelley849a8352006-06-09 21:02:31 +01003266 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003267 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003268 /* Leases of a minute or less confuse
3269 some clients, notably Apple's */
3270 if (new->lease_time < 120)
3271 new->lease_time = 120;
3272 }
3273 }
3274 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003275
Simon Kelley849a8352006-06-09 21:02:31 +01003276 break;
3277 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003278
Simon Kelley5aabfc72007-08-29 11:24:47 +01003279 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003280 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003281 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003282 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003283 struct in_addr in;
3284
Simon Kelley824af852008-02-12 20:43:05 +00003285 new = opt_malloc(sizeof(struct dhcp_config));
3286
Simon Kelley849a8352006-06-09 21:02:31 +01003287 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003288 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3289 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003290 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003291 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003292 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003293#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003294 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003295#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003296
Simon Kelley137286e2020-02-06 22:09:30 +00003297 while (arg)
3298 {
3299 comma = split(arg);
3300 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3301 {
3302 if ((arg[0] == 'i' || arg[0] == 'I') &&
3303 (arg[1] == 'd' || arg[1] == 'D') &&
3304 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003305 {
Simon Kelley137286e2020-02-06 22:09:30 +00003306 if (arg[3] == '*')
3307 new->flags |= CONFIG_NOCLID;
3308 else
3309 {
3310 int len;
3311 arg += 3; /* dump id: */
3312 if (strchr(arg, ':'))
3313 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3314 else
3315 {
3316 unhide_metas(arg);
3317 len = (int) strlen(arg);
3318 }
3319
3320 if (len == -1)
3321 {
3322 dhcp_config_free(new);
3323 ret_err(_("bad hex constant"));
3324 }
3325 else if ((new->clid = opt_malloc(len)))
3326 {
3327 new->flags |= CONFIG_CLID;
3328 new->clid_len = len;
3329 memcpy(new->clid, arg, len);
3330 }
3331 }
3332 }
3333 /* dhcp-host has strange backwards-compat needs. */
3334 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3335 {
3336 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3337 newlist->next = new->netid;
3338 new->netid = newlist;
3339 newlist->list = dhcp_netid_create(arg+4, NULL);
3340 }
3341 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003342 new->filter = dhcp_netid_create(arg+4, new->filter);
3343
Simon Kelley137286e2020-02-06 22:09:30 +00003344#ifdef HAVE_DHCP6
3345 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3346 {
3347 char *pref;
3348 struct in6_addr in6;
3349 struct addrlist *new_addr;
3350
3351 arg[strlen(arg)-1] = 0;
3352 arg++;
3353 pref = split_chr(arg, '/');
3354
3355 if (!inet_pton(AF_INET6, arg, &in6))
3356 {
3357 dhcp_config_free(new);
3358 ret_err(_("bad IPv6 address"));
3359 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003360
Simon Kelley137286e2020-02-06 22:09:30 +00003361 new_addr = opt_malloc(sizeof(struct addrlist));
3362 new_addr->next = new->addr6;
3363 new_addr->flags = 0;
3364 new_addr->addr.addr6 = in6;
3365 new->addr6 = new_addr;
3366
3367 if (pref)
3368 {
3369 u64 addrpart = addr6part(&in6);
3370
3371 if (!atoi_check(pref, &new_addr->prefixlen) ||
3372 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003373 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003374 {
3375 dhcp_config_free(new);
3376 ret_err(_("bad IPv6 prefix"));
3377 }
3378
3379 new_addr->flags |= ADDRLIST_PREFIX;
3380 }
3381
3382 for (i= 0; i < 8; i++)
3383 if (in6.s6_addr[i] != 0)
3384 break;
3385
3386 /* set WILDCARD if network part all zeros */
3387 if (i == 8)
3388 new_addr->flags |= ADDRLIST_WILDCARD;
3389
3390 new->flags |= CONFIG_ADDR6;
3391 }
3392#endif
3393 else
3394 {
3395 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3396 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3397 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3398 {
3399 free(newhw);
3400 dhcp_config_free(new);
3401 ret_err(_("bad hex constant"));
3402 }
3403 else
3404 {
3405 newhw->next = new->hwaddr;
3406 new->hwaddr = newhw;
3407 }
3408 }
3409 }
3410 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3411 {
3412 struct dhcp_config *configs;
3413
3414 new->addr = in;
3415 new->flags |= CONFIG_ADDR;
3416
3417 /* If the same IP appears in more than one host config, then DISCOVER
3418 for one of the hosts will get the address, but REQUEST will be NAKed,
3419 since the address is reserved by the other one -> protocol loop. */
3420 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3421 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003422 {
Simon Kelley137286e2020-02-06 22:09:30 +00003423 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3424 return 0;
3425 }
3426 }
3427 else
3428 {
3429 char *cp, *lastp = NULL, last = 0;
3430 int fac = 1, isdig = 0;
3431
3432 if (strlen(arg) > 1)
3433 {
3434 lastp = arg + strlen(arg) - 1;
3435 last = *lastp;
3436 switch (last)
3437 {
3438 case 'w':
3439 case 'W':
3440 fac *= 7;
3441 /* fall through */
3442 case 'd':
3443 case 'D':
3444 fac *= 24;
3445 /* fall through */
3446 case 'h':
3447 case 'H':
3448 fac *= 60;
3449 /* fall through */
3450 case 'm':
3451 case 'M':
3452 fac *= 60;
3453 /* fall through */
3454 case 's':
3455 case 'S':
3456 *lastp = 0;
3457 }
3458 }
3459
3460 for (cp = arg; *cp; cp++)
3461 if (isdigit((unsigned char)*cp))
3462 isdig = 1;
3463 else if (*cp != ' ')
3464 break;
3465
3466 if (*cp)
3467 {
3468 if (lastp)
3469 *lastp = last;
3470 if (strcmp(arg, "infinite") == 0)
3471 {
3472 new->lease_time = 0xffffffff;
3473 new->flags |= CONFIG_TIME;
3474 }
3475 else if (strcmp(arg, "ignore") == 0)
3476 new->flags |= CONFIG_DISABLE;
3477 else
3478 {
3479 if (!(new->hostname = canonicalise_opt(arg)) ||
3480 !legal_hostname(new->hostname))
3481 {
3482 dhcp_config_free(new);
3483 ret_err(_("bad DHCP host name"));
3484 }
3485
3486 new->flags |= CONFIG_NAME;
3487 new->domain = strip_hostname(new->hostname);
3488 }
3489 }
3490 else if (isdig)
3491 {
3492 new->lease_time = atoi(arg) * fac;
3493 /* Leases of a minute or less confuse
3494 some clients, notably Apple's */
3495 if (new->lease_time < 120)
3496 new->lease_time = 120;
3497 new->flags |= CONFIG_TIME;
3498 }
3499 }
3500
3501 arg = comma;
3502 }
3503
Simon Kelley5aabfc72007-08-29 11:24:47 +01003504 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003505 break;
3506 }
Simon Kelley137286e2020-02-06 22:09:30 +00003507
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003508 case LOPT_TAG_IF: /* --tag-if */
3509 {
3510 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3511
3512 new->tag = NULL;
3513 new->set = NULL;
3514 new->next = NULL;
3515
3516 /* preserve order */
3517 if (!daemon->tag_if)
3518 daemon->tag_if = new;
3519 else
3520 {
3521 struct tag_if *tmp;
3522 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3523 tmp->next = new;
3524 }
3525
3526 while (arg)
3527 {
3528 size_t len;
3529
3530 comma = split(arg);
3531 len = strlen(arg);
3532
3533 if (len < 5)
3534 {
3535 new->set = NULL;
3536 break;
3537 }
3538 else
3539 {
Petr Menšík59e47032018-11-02 22:39:39 +00003540 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003541
3542 if (strstr(arg, "set:") == arg)
3543 {
3544 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3545 newlist->next = new->set;
3546 new->set = newlist;
3547 newlist->list = newtag;
3548 }
3549 else if (strstr(arg, "tag:") == arg)
3550 {
3551 newtag->next = new->tag;
3552 new->tag = newtag;
3553 }
3554 else
3555 {
3556 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003557 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003558 break;
3559 }
3560 }
3561
3562 arg = comma;
3563 }
3564
3565 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003566 {
3567 dhcp_netid_free(new->tag);
3568 dhcp_netid_list_free(new->set);
3569 ret_err_free(_("bad tag-if"), new);
3570 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003571
3572 break;
3573 }
3574
Simon Kelley849a8352006-06-09 21:02:31 +01003575
Simon Kelley73a08a22009-02-05 20:28:08 +00003576 case 'O': /* --dhcp-option */
3577 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003578 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003579 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003580 return parse_dhcp_opt(errstr, arg,
3581 option == LOPT_FORCE ? DHOPT_FORCE :
3582 (option == LOPT_MATCH ? DHOPT_MATCH :
3583 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003584
3585 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3586 {
3587 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3588 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3589 ssize_t len;
3590
3591 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3592 ret_err(gen_err);
3593
3594 new->wildcard = 0;
3595 new->netid = id;
3596 id->net = opt_string_alloc(set_prefix(arg));
3597
3598 if (comma[len-1] == '*')
3599 {
3600 comma[len-1] = 0;
3601 new->wildcard = 1;
3602 }
3603 new->name = opt_string_alloc(comma);
3604
3605 new->next = daemon->dhcp_name_match;
3606 daemon->dhcp_name_match = new;
3607
3608 break;
3609 }
3610
Simon Kelleyf2621c72007-04-29 19:47:21 +01003611 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003612 {
Petr Menšík59e47032018-11-02 22:39:39 +00003613 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003614
Petr Menšík137e9f82018-12-16 21:25:29 +00003615 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003616 {
3617 ret_err(gen_err);
3618 }
Simon Kelley849a8352006-06-09 21:02:31 +01003619 else
3620 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003621 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003622 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003623 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003624 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003625 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003626 dhcp_next_server.s_addr = 0;
3627 if (comma)
3628 {
3629 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003630 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003631 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003632 if (comma)
3633 {
3634 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003635 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3636 {
3637 /*
3638 * The user may have specified the tftp hostname here.
3639 * save it so that it can be resolved/looked up during
3640 * actual dhcp_reply().
3641 */
3642
3643 tftp_sname = opt_string_alloc(comma);
3644 dhcp_next_server.s_addr = 0;
3645 }
Simon Kelley849a8352006-06-09 21:02:31 +01003646 }
3647 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003648
3649 new = opt_malloc(sizeof(struct dhcp_boot));
3650 new->file = dhcp_file;
3651 new->sname = dhcp_sname;
3652 new->tftp_sname = tftp_sname;
3653 new->next_server = dhcp_next_server;
3654 new->netid = id;
3655 new->next = daemon->boot_config;
3656 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003657 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003658
Simon Kelley849a8352006-06-09 21:02:31 +01003659 break;
3660 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003661
Floris Bos503c6092017-04-09 23:07:13 +01003662 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3663 {
Petr Menšík59e47032018-11-02 22:39:39 +00003664 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003665
Petr Menšík137e9f82018-12-16 21:25:29 +00003666 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003667 {
3668 ret_err(gen_err);
3669 }
Floris Bos503c6092017-04-09 23:07:13 +01003670 else
3671 {
3672 struct delay_config *new;
3673 int delay;
3674 if (!atoi_check(arg, &delay))
3675 ret_err(gen_err);
3676
3677 new = opt_malloc(sizeof(struct delay_config));
3678 new->delay = delay;
3679 new->netid = id;
3680 new->next = daemon->delay_conf;
3681 daemon->delay_conf = new;
3682 }
3683
3684 break;
3685 }
3686
Simon Kelley7622fc02009-06-04 20:32:05 +01003687 case LOPT_PXE_PROMT: /* --pxe-prompt */
3688 {
3689 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3690 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003691
Simon Kelley7622fc02009-06-04 20:32:05 +01003692 new->netid = NULL;
3693 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003694 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003695
Petr Menšík137e9f82018-12-16 21:25:29 +00003696 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003697 {
3698 dhcp_opt_free(new);
3699 ret_err(gen_err);
3700 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003701 else
3702 {
3703 comma = split(arg);
3704 unhide_metas(arg);
3705 new->len = strlen(arg) + 1;
3706 new->val = opt_malloc(new->len);
3707 memcpy(new->val + 1, arg, new->len - 1);
3708
Wang Shanker4ded9622020-12-04 10:17:35 +08003709 new->u.vendor_class = NULL;
3710 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003711
3712 if (comma && atoi_check(comma, &timeout))
3713 *(new->val) = timeout;
3714 else
3715 *(new->val) = 255;
3716
3717 new->next = daemon->dhcp_opts;
3718 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003719 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003720 }
3721
3722 break;
3723 }
3724
3725 case LOPT_PXE_SERV: /* --pxe-service */
3726 {
3727 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3728 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003729 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3730 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003731 static int boottype = 32768;
3732
3733 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003734 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003735 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003736 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003737
Simon Kelley7622fc02009-06-04 20:32:05 +01003738 if (arg && (comma = split(arg)))
3739 {
3740 for (i = 0; CSA[i]; i++)
3741 if (strcasecmp(CSA[i], arg) == 0)
3742 break;
3743
3744 if (CSA[i] || atoi_check(arg, &i))
3745 {
3746 arg = comma;
3747 comma = split(arg);
3748
3749 new->CSA = i;
3750 new->menu = opt_string_alloc(arg);
3751
Simon Kelley316e2732010-01-22 20:16:09 +00003752 if (!comma)
3753 {
3754 new->type = 0; /* local boot */
3755 new->basename = NULL;
3756 }
3757 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003758 {
3759 arg = comma;
3760 comma = split(arg);
3761 if (atoi_check(arg, &i))
3762 {
3763 new->type = i;
3764 new->basename = NULL;
3765 }
3766 else
3767 {
3768 new->type = boottype++;
3769 new->basename = opt_string_alloc(arg);
3770 }
3771
Simon Kelley751d6f42012-02-10 15:24:51 +00003772 if (comma)
3773 {
3774 if (!inet_pton(AF_INET, comma, &new->server))
3775 {
3776 new->server.s_addr = 0;
3777 new->sname = opt_string_alloc(comma);
3778 }
3779
3780 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003781 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003782
Simon Kelley316e2732010-01-22 20:16:09 +00003783 /* Order matters */
3784 new->next = NULL;
3785 if (!daemon->pxe_services)
3786 daemon->pxe_services = new;
3787 else
3788 {
3789 struct pxe_service *s;
3790 for (s = daemon->pxe_services; s->next; s = s->next);
3791 s->next = new;
3792 }
3793
3794 daemon->enable_pxe = 1;
3795 break;
3796
Simon Kelley7622fc02009-06-04 20:32:05 +01003797 }
3798 }
3799
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003800 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003801 }
3802
Simon Kelleyf2621c72007-04-29 19:47:21 +01003803 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003804 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003805 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003806 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003807 else
3808 {
Simon Kelley824af852008-02-12 20:43:05 +00003809 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003810 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003811 unhide_metas(comma);
3812 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003813 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003814 {
3815 free(new->netid.net);
3816 ret_err_free(gen_err, new);
3817 }
Simon Kelley28866e92011-02-14 20:19:14 +00003818 else
3819 {
3820 new->next = daemon->dhcp_macs;
3821 daemon->dhcp_macs = new;
3822 }
Simon Kelley849a8352006-06-09 21:02:31 +01003823 }
3824 }
3825 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003826
Simon Kelleyf2621c72007-04-29 19:47:21 +01003827 case 'U': /* --dhcp-vendorclass */
3828 case 'j': /* --dhcp-userclass */
3829 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3830 case LOPT_REMOTE: /* --dhcp-remoteid */
3831 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003832 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003833 unsigned char *p;
3834 int dig = 0;
3835 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3836
3837 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003838 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003839
3840 new->netid.net = opt_string_alloc(set_prefix(arg));
3841 /* check for hex string - must digits may include : must not have nothing else,
3842 only allowed for agent-options. */
3843
3844 arg = comma;
3845 if ((comma = split(arg)))
3846 {
3847 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003848 {
3849 free(new->netid.net);
3850 ret_err_free(gen_err, new);
3851 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003852 else
3853 new->enterprise = atoi(arg+11);
3854 }
3855 else
3856 comma = arg;
3857
3858 for (p = (unsigned char *)comma; *p; p++)
3859 if (isxdigit(*p))
3860 dig = 1;
3861 else if (*p != ':')
3862 break;
3863 unhide_metas(comma);
3864 if (option == 'U' || option == 'j' || *p || !dig)
3865 {
3866 new->len = strlen(comma);
3867 new->data = opt_malloc(new->len);
3868 memcpy(new->data, comma, new->len);
3869 }
3870 else
3871 {
3872 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3873 new->data = opt_malloc(new->len);
3874 memcpy(new->data, comma, new->len);
3875 }
3876
3877 switch (option)
3878 {
3879 case 'j':
3880 new->match_type = MATCH_USER;
3881 break;
3882 case 'U':
3883 new->match_type = MATCH_VENDOR;
3884 break;
3885 case LOPT_CIRCUIT:
3886 new->match_type = MATCH_CIRCUIT;
3887 break;
3888 case LOPT_REMOTE:
3889 new->match_type = MATCH_REMOTE;
3890 break;
3891 case LOPT_SUBSCR:
3892 new->match_type = MATCH_SUBSCRIBER;
3893 break;
3894 }
3895 new->next = daemon->dhcp_vendors;
3896 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003897
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003898 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003899 }
3900
Simon Kelley9e038942008-05-30 20:06:34 +01003901 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3902 if (!arg)
3903 {
3904 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3905 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3906 }
3907 else
3908 {
3909 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003910 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3911 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003912 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003913 if (!comma)
3914 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3915 }
3916 break;
3917
Simon Kelley824af852008-02-12 20:43:05 +00003918 case 'J': /* --dhcp-ignore */
3919 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3920 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003921 case '3': /* --bootp-dynamic */
3922 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003923 {
Simon Kelley824af852008-02-12 20:43:05 +00003924 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003925 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003926 if (option == 'J')
3927 {
3928 new->next = daemon->dhcp_ignore;
3929 daemon->dhcp_ignore = new;
3930 }
Simon Kelley824af852008-02-12 20:43:05 +00003931 else if (option == LOPT_BROADCAST)
3932 {
3933 new->next = daemon->force_broadcast;
3934 daemon->force_broadcast = new;
3935 }
Simon Kelley9009d742008-11-14 20:04:27 +00003936 else if (option == '3')
3937 {
3938 new->next = daemon->bootp_dynamic;
3939 daemon->bootp_dynamic = new;
3940 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003941 else if (option == LOPT_GEN_NAMES)
3942 {
3943 new->next = daemon->dhcp_gen_names;
3944 daemon->dhcp_gen_names = new;
3945 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003946 else
3947 {
3948 new->next = daemon->dhcp_ignore_names;
3949 daemon->dhcp_ignore_names = new;
3950 }
3951
3952 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003953 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003954 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003955 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003956 }
Simon Kelley849a8352006-06-09 21:02:31 +01003957
3958 new->list = list;
3959 break;
3960 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003961
3962 case LOPT_PROXY: /* --dhcp-proxy */
3963 daemon->override = 1;
3964 while (arg) {
3965 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3966 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003967 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003968 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003969 new->next = daemon->override_relays;
3970 daemon->override_relays = new;
3971 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003972 }
3973 break;
3974
3975 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3976 {
3977 while (arg) {
3978 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3979 comma = split(arg);
3980 new->data = opt_string_alloc(arg);
3981 new->next = daemon->dhcp_pxe_vendors;
3982 daemon->dhcp_pxe_vendors = new;
3983 arg = comma;
3984 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003985 }
3986 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003987
3988 case LOPT_RELAY: /* --dhcp-relay */
3989 {
3990 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3991 comma = split(arg);
3992 new->interface = opt_string_alloc(split(comma));
3993 new->iface_index = 0;
3994 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3995 {
3996 new->next = daemon->relay4;
3997 daemon->relay4 = new;
3998 }
3999#ifdef HAVE_DHCP6
4000 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
4001 {
4002 new->next = daemon->relay6;
4003 daemon->relay6 = new;
4004 }
4005#endif
4006 else
Petr Menšík59e47032018-11-02 22:39:39 +00004007 {
4008 free(new->interface);
4009 ret_err_free(_("Bad dhcp-relay"), new);
4010 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01004011
4012 break;
4013 }
4014
Simon Kelley7622fc02009-06-04 20:32:05 +01004015#endif
Simon Kelley849a8352006-06-09 21:02:31 +01004016
Simon Kelley8b372702012-03-09 17:45:10 +00004017#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004018 case LOPT_RA_PARAM: /* --ra-param */
4019 if ((comma = split(arg)))
4020 {
4021 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
4022 new->lifetime = -1;
4023 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01004024 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004025 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004026 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004027 if (strcasestr(comma, "mtu:") == comma)
4028 {
4029 arg = comma + 4;
4030 if (!(comma = split(comma)))
4031 goto err;
4032 if (!strcasecmp(arg, "off"))
4033 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004034 else if (!atoi_check(arg, &new->mtu))
4035 new->mtu_name = opt_string_alloc(arg);
4036 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004037 goto err;
4038 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004039 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4040 {
4041 if (*comma == 'l' || *comma == 'L')
4042 new->prio = 0x18;
4043 else
4044 new->prio = 0x08;
4045 comma = split(comma);
4046 }
4047 arg = split(comma);
4048 if (!atoi_check(comma, &new->interval) ||
4049 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004050 {
David Flamand005c46d2017-04-11 11:49:54 +01004051err:
Petr Menšík59e47032018-11-02 22:39:39 +00004052 free(new->name);
4053 ret_err_free(_("bad RA-params"), new);
4054 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004055
4056 new->next = daemon->ra_interfaces;
4057 daemon->ra_interfaces = new;
4058 }
4059 break;
4060
Simon Kelley8b372702012-03-09 17:45:10 +00004061 case LOPT_DUID: /* --dhcp-duid */
4062 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004063 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004064 else
4065 {
4066 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4067 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4068 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4069 }
4070 break;
4071#endif
4072
Simon Kelleyf2621c72007-04-29 19:47:21 +01004073 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004074 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004075 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004076 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004077 struct doctor *new = opt_malloc(sizeof(struct doctor));
4078 new->next = daemon->doctors;
4079 daemon->doctors = new;
4080 new->mask.s_addr = 0xffffffff;
4081 new->end.s_addr = 0;
4082
Simon Kelley849a8352006-06-09 21:02:31 +01004083 if ((a[0] = arg))
4084 for (k = 1; k < 3; k++)
4085 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004086 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004087 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004088 unhide_metas(a[k]);
4089 }
Simon Kelley849a8352006-06-09 21:02:31 +01004090
Simon Kelley73a08a22009-02-05 20:28:08 +00004091 dash = split_chr(a[0], '-');
4092
Simon Kelley849a8352006-06-09 21:02:31 +01004093 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004094 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004095 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4096 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4097 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004098
Simon Kelley73a08a22009-02-05 20:28:08 +00004099 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004100 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004101 !is_same_net(new->in, new->end, new->mask) ||
4102 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004103 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004104
4105 break;
4106 }
4107
Simon Kelleyf2621c72007-04-29 19:47:21 +01004108 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004109 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004110 {
4111 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004112 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004113
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004114 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004115
Simon Kelley824af852008-02-12 20:43:05 +00004116 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004117 memset(new, 0, sizeof(struct interface_name));
4118 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004119
Simon Kelleyf2621c72007-04-29 19:47:21 +01004120 /* Add to the end of the list, so that first name
4121 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004122 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004123 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004124
4125 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004126 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004127 if (inet_pton(AF_INET, arg, &new->proto4))
4128 new->flags |= INP4;
4129 else if (inet_pton(AF_INET6, arg, &new->proto6))
4130 new->flags |= INP6;
4131 else
4132 break;
4133
4134 arg = comma;
4135 }
4136
4137 if ((comma = split_chr(arg, '/')))
4138 {
4139 if (strcmp(comma, "4") == 0)
4140 new->flags &= ~IN6;
4141 else if (strcmp(comma, "6") == 0)
4142 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004143 else
Petr Menšík59e47032018-11-02 22:39:39 +00004144 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004145 }
4146
4147 new->intr = opt_string_alloc(arg);
4148
4149 if (option == LOPT_DYNHOST)
4150 {
4151 if (!(new->flags & (INP4 | INP6)))
4152 ret_err(_("missing address in dynamic host"));
4153
4154 if (!(new->flags & IN4) || !(new->flags & IN6))
4155 arg = NULL; /* provoke error below */
4156
4157 new->flags &= ~(IN4 | IN6);
4158 }
4159 else
4160 {
4161 if (new->flags & (INP4 | INP6))
4162 arg = NULL; /* provoke error below */
4163 }
4164
4165 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4166 ret_err(option == LOPT_DYNHOST ?
4167 _("bad dynamic host") : _("bad interface name"));
4168
Simon Kelleyf2621c72007-04-29 19:47:21 +01004169 break;
4170 }
Simon Kelley9009d742008-11-14 20:04:27 +00004171
4172 case LOPT_CNAME: /* --cname */
4173 {
4174 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004175 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004176 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004177
Simon Kelleya1d973f2016-12-22 22:09:50 +00004178 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004179 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004180 pen = last;
4181 last = comma;
4182 }
4183
4184 if (!pen)
4185 ret_err(_("bad CNAME"));
4186
4187 if (pen != arg && atoi_check(last, &ttl))
4188 last = pen;
4189
4190 target = canonicalise_opt(last);
4191
4192 while (arg != last)
4193 {
Petr Menšík56f06232018-03-06 23:13:32 +00004194 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004195 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004196
4197 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004198 {
4199 free(target);
4200 free(alias);
4201 ret_err(_("bad CNAME"));
4202 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004203
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004204 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004205 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004206 {
4207 free(target);
4208 free(alias);
4209 ret_err(_("duplicate CNAME"));
4210 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004211 new = opt_malloc(sizeof(struct cname));
4212 new->next = daemon->cnames;
4213 daemon->cnames = new;
4214 new->alias = alias;
4215 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004216 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004217
Petr Menšík56f06232018-03-06 23:13:32 +00004218 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004219 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004220
Simon Kelley9009d742008-11-14 20:04:27 +00004221 break;
4222 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004223
4224 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004225 {
4226 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004227 char *dom, *target = NULL;
4228
Simon Kelleyf2621c72007-04-29 19:47:21 +01004229 comma = split(arg);
4230
Simon Kelley1f15b812009-10-13 17:49:32 +01004231 if (!(dom = canonicalise_opt(arg)) ||
4232 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004233 {
4234 free(dom);
4235 free(target);
4236 ret_err(_("bad PTR record"));
4237 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004238 else
4239 {
4240 new = opt_malloc(sizeof(struct ptr_record));
4241 new->next = daemon->ptr;
4242 daemon->ptr = new;
4243 new->name = dom;
4244 new->ptr = target;
4245 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004246 break;
4247 }
4248
Simon Kelley1a6bca82008-07-11 11:11:42 +01004249 case LOPT_NAPTR: /* --naptr-record */
4250 {
4251 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4252 int k = 0;
4253 struct naptr *new;
4254 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004255 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004256
4257 if ((a[0] = arg))
4258 for (k = 1; k < 7; k++)
4259 if (!(a[k] = split(a[k-1])))
4260 break;
4261
4262
4263 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004264 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004265 !atoi_check16(a[1], &order) ||
4266 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004267 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004268 {
4269 free(name);
4270 free(replace);
4271 ret_err(_("bad NAPTR record"));
4272 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004273 else
4274 {
4275 new = opt_malloc(sizeof(struct naptr));
4276 new->next = daemon->naptr;
4277 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004278 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004279 new->flags = opt_string_alloc(a[3]);
4280 new->services = opt_string_alloc(a[4]);
4281 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004282 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004283 new->order = order;
4284 new->pref = pref;
4285 }
4286 break;
4287 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004288
4289 case LOPT_RR: /* dns-rr */
4290 {
4291 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004292 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004293 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004294 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004295
4296 comma = split(arg);
4297 data = split(comma);
4298
4299 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004300 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004301
Petr Menšík59e47032018-11-02 22:39:39 +00004302 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004303 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004304 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004305 {
4306 free(new->name);
4307 ret_err_free(_("bad RR record"), new);
4308 }
4309
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004310 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004311 new->class = class;
4312 new->next = daemon->rr;
4313 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004314
4315 if (data)
4316 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004317 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004318 new->len = len;
4319 memcpy(new->txt, data, len);
4320 }
4321
4322 break;
4323 }
4324
Simon Kelley974a6d02018-08-23 23:01:16 +01004325 case LOPT_CAA: /* --caa-record */
4326 {
4327 struct txt_record *new;
4328 char *tag, *value;
4329 int flags;
4330
4331 comma = split(arg);
4332 tag = split(comma);
4333 value = split(tag);
4334
4335 new = opt_malloc(sizeof(struct txt_record));
4336 new->next = daemon->rr;
4337 daemon->rr = new;
4338
4339 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4340 ret_err(_("bad CAA record"));
4341
4342 unhide_metas(tag);
4343 unhide_metas(value);
4344
4345 new->len = strlen(tag) + strlen(value) + 2;
4346 new->txt = opt_malloc(new->len);
4347 new->txt[0] = flags;
4348 new->txt[1] = strlen(tag);
4349 memcpy(&new->txt[2], tag, strlen(tag));
4350 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4351 new->class = T_CAA;
4352
4353 break;
4354 }
4355
Simon Kelleyf2621c72007-04-29 19:47:21 +01004356 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004357 {
4358 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004359 unsigned char *p, *cnt;
4360 size_t len;
4361
4362 comma = split(arg);
4363
Simon Kelley824af852008-02-12 20:43:05 +00004364 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004365 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004366 new->stat = 0;
4367
Simon Kelley1f15b812009-10-13 17:49:32 +01004368 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004369 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004370
Petr Menšík59e47032018-11-02 22:39:39 +00004371 new->next = daemon->txt;
4372 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004373 len = comma ? strlen(comma) : 0;
4374 len += (len/255) + 1; /* room for extra counts */
4375 new->txt = p = opt_malloc(len);
4376
4377 cnt = p++;
4378 *cnt = 0;
4379
4380 while (comma && *comma)
4381 {
4382 unsigned char c = (unsigned char)*comma++;
4383
4384 if (c == ',' || *cnt == 255)
4385 {
4386 if (c != ',')
4387 comma--;
4388 cnt = p++;
4389 *cnt = 0;
4390 }
4391 else
4392 {
4393 *p++ = unhide_meta(c);
4394 (*cnt)++;
4395 }
4396 }
4397
4398 new->len = p - new->txt;
4399
Simon Kelley849a8352006-06-09 21:02:31 +01004400 break;
4401 }
4402
Simon Kelleyf2621c72007-04-29 19:47:21 +01004403 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004404 {
4405 int port = 1, priority = 0, weight = 0;
4406 char *name, *target = NULL;
4407 struct mx_srv_record *new;
4408
Simon Kelleyf2621c72007-04-29 19:47:21 +01004409 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004410
Simon Kelley1f15b812009-10-13 17:49:32 +01004411 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004412 ret_err(_("bad SRV record"));
4413
Simon Kelley849a8352006-06-09 21:02:31 +01004414 if (comma)
4415 {
4416 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004417 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004418 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004419 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004420
Simon Kelley849a8352006-06-09 21:02:31 +01004421 if (comma)
4422 {
4423 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004424 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004425 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004426 {
4427 free(name);
4428 ret_err_free(_("invalid port number"), target);
4429 }
Simon Kelley824af852008-02-12 20:43:05 +00004430
Simon Kelley849a8352006-06-09 21:02:31 +01004431 if (comma)
4432 {
4433 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004434 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004435 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004436 {
4437 free(name);
4438 ret_err_free(_("invalid priority"), target);
4439 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004440 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004441 {
4442 free(name);
4443 ret_err_free(_("invalid weight"), target);
4444 }
Simon Kelley849a8352006-06-09 21:02:31 +01004445 }
4446 }
4447 }
4448
Simon Kelley824af852008-02-12 20:43:05 +00004449 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004450 new->next = daemon->mxnames;
4451 daemon->mxnames = new;
4452 new->issrv = 1;
4453 new->name = name;
4454 new->target = target;
4455 new->srvport = port;
4456 new->priority = priority;
4457 new->weight = weight;
4458 break;
4459 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004460
Simon Kelleye759d422012-03-16 13:18:57 +00004461 case LOPT_HOST_REC: /* --host-record */
4462 {
Petr Menšík59e47032018-11-02 22:39:39 +00004463 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004464
Simon Kelleye759d422012-03-16 13:18:57 +00004465 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004466 ret_err(_("Bad host-record"));
4467
Petr Menšík59e47032018-11-02 22:39:39 +00004468 new = opt_malloc(sizeof(struct host_record));
4469 memset(new, 0, sizeof(struct host_record));
4470 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004471 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004472
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004473 while (arg)
4474 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004475 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004476 char *dig;
4477
4478 for (dig = arg; *dig != 0; dig++)
4479 if (*dig < '0' || *dig > '9')
4480 break;
4481 if (*dig == 0)
4482 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004483 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004484 {
4485 new->addr = addr.addr4;
4486 new->flags |= HR_4;
4487 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004488 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004489 {
4490 new->addr6 = addr.addr6;
4491 new->flags |= HR_6;
4492 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004493 else
4494 {
4495 int nomem;
4496 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004497 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004498 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004499 {
4500 struct name_list *tmp = new->names, *next;
4501 for (tmp = new->names; tmp; tmp = next)
4502 {
4503 next = tmp->next;
4504 free(tmp);
4505 }
4506 ret_err_free(_("Bad name in host-record"), new);
4507 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004508
Petr Menšík59e47032018-11-02 22:39:39 +00004509 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004510 nl->name = canon;
4511 /* keep order, so that PTR record goes to first name */
4512 nl->next = NULL;
4513 if (!new->names)
4514 new->names = nl;
4515 else
4516 {
4517 struct name_list *tmp;
4518 for (tmp = new->names; tmp->next; tmp = tmp->next);
4519 tmp->next = nl;
4520 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004521 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004522
4523 arg = comma;
4524 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004525 }
Simon Kelleye759d422012-03-16 13:18:57 +00004526
4527 /* Keep list order */
4528 if (!daemon->host_records_tail)
4529 daemon->host_records = new;
4530 else
4531 daemon->host_records_tail->next = new;
4532 new->next = NULL;
4533 daemon->host_records_tail = new;
4534 break;
4535 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004536
4537#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004538 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004539 daemon->timestamp_file = opt_string_alloc(arg);
4540 break;
4541
Simon Kelleyf3e57872018-07-20 21:10:48 +01004542 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004543 if (arg)
4544 {
4545 if (strcmp(arg, "no") == 0)
4546 set_option_bool(OPT_DNSSEC_IGN_NS);
4547 else
4548 ret_err(_("bad value for dnssec-check-unsigned"));
4549 }
4550 break;
4551
Simon Kelleyf3e57872018-07-20 21:10:48 +01004552 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004553 {
Simon Kelleyee415862014-02-11 11:07:22 +00004554 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4555 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4556 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004557
4558 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004559 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004560
Simon Kelleycbf13a22014-01-25 17:59:14 +00004561 if ((comma = split(arg)) && (algo = split(comma)))
4562 {
4563 int class = 0;
4564 if (strcmp(comma, "IN") == 0)
4565 class = C_IN;
4566 else if (strcmp(comma, "CH") == 0)
4567 class = C_CHAOS;
4568 else if (strcmp(comma, "HS") == 0)
4569 class = C_HESIOD;
4570
4571 if (class != 0)
4572 {
4573 new->class = class;
4574 comma = algo;
4575 algo = split(comma);
4576 }
4577 }
4578
Simon Kelleyee415862014-02-11 11:07:22 +00004579 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4580 !atoi_check16(comma, &new->keytag) ||
4581 !atoi_check8(algo, &new->algo) ||
4582 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004583 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004584 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004585
Simon Kelley0fc2f312014-01-08 10:26:58 +00004586 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004587 len = (2*strlen(keyhex))+1;
4588 new->digest = opt_malloc(len);
4589 unhide_metas(keyhex);
4590 /* 4034: "Whitespace is allowed within digits" */
4591 for (cp = keyhex; *cp; )
4592 if (isspace(*cp))
4593 for (cp1 = cp; *cp1; cp1++)
4594 *cp1 = *(cp1+1);
4595 else
4596 cp++;
4597 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004598 {
4599 free(new->name);
4600 ret_err_free(_("bad HEX in trust anchor"), new);
4601 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004602
Simon Kelleyee415862014-02-11 11:07:22 +00004603 new->next = daemon->ds;
4604 daemon->ds = new;
4605
Simon Kelley0fc2f312014-01-08 10:26:58 +00004606 break;
4607 }
4608#endif
Kyle Swenson545712c2021-11-17 12:25:04 -07004609/* CRADLEPOINT */
4610/* This option (OPT_EDNS_OPTION) is required to tack a(n) owner record onto a DNS request:
4611 * Option Code: Owner (reserved) 4 (0x00 0x04)
4612 * Option Length: 19 ("Cradlepoint" + device_id (obtained from OpenDNS)) (0x00 0x13)
4613 * Option Data: "Cradlepoint" + device_id (an 4 byte identifier)
4614 *
4615 * The EDNS option is passed in like this:
4616 * --edns-option=4,CradlepointAABBCCDD
4617 */
4618 case LOPT_EDNS_OPTION:
4619 {
4620 unsigned char *tmp;
4621 int option_code, option_len = 0;
4622
4623 comma = split(arg);
4624
4625 if (*arg == 0 || !atoi_check16(arg, &option_code))
4626 {
4627 option = '?';
4628 ret_err(_("invalid EDNS option code"));
4629 break;
4630 }
4631
4632 if (comma &&
4633 (option_len = str_unescape_c_string(comma, comma)) <= 0)
4634 {
4635 option = '?';
4636 ret_err(_("invalid EDNS option data"));
4637 break;
4638 }
4639 tmp = opt_malloc(daemon->edns_options_len + 4 + option_len);
4640
4641 if (daemon->edns_options_len > 0)
4642 {
4643 memcpy(tmp, daemon->edns_options, daemon->edns_options_len);
4644 free(daemon->edns_options);
4645 }
4646 daemon->edns_options = tmp;
4647 tmp += daemon->edns_options_len;
4648 PUTSHORT(option_code, tmp);
4649 PUTSHORT(option_len, tmp);
4650 memcpy(tmp, comma, option_len);
4651 daemon->edns_options_len += 4 + option_len;
4652 my_syslog(LOG_DEBUG, _("EDNS Option %d added length %d"), option_code,
4653 daemon->edns_options_len);
4654
4655 break;
4656 }
4657
4658 case LOPT_SSID_MAP:
4659 {
4660 char *err;
4661
4662 unhide_metas(arg);
4663
4664 if ((err = opendns_parse_device_id_opt(arg)) != NULL) {
4665 /* int vs pointer warning return err; */
4666 ret_err(_("opendns parse device id failed"));
4667 }
4668
4669 break;
4670 }
Kyle Swensonba77fe82021-11-19 09:33:46 -07004671 case LOPT_EDNS_RESTRICT:
4672 {
4673 set_option_bool(OPT_EDNS_RESTRICT);
4674 break;
4675 }
Kyle Swenson545712c2021-11-17 12:25:04 -07004676/* CRADLEPOINT */
4677
Simon Kelley7622fc02009-06-04 20:32:05 +01004678 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004679 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004680
Simon Kelley849a8352006-06-09 21:02:31 +01004681 }
Simon Kelley824af852008-02-12 20:43:05 +00004682
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004683 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004684}
4685
Simon Kelley28866e92011-02-14 20:19:14 +00004686static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004687{
Simon Kelley824af852008-02-12 20:43:05 +00004688 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004689 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004690
4691 while (fgets(buff, MAXDNAME, f))
4692 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004693 int white, i;
4694 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004695 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004696 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004697
Simon Kelley824af852008-02-12 20:43:05 +00004698 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004699 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004700 {
4701 if (setjmp(mem_jmp))
4702 continue;
4703 mem_recover = 1;
4704 }
4705
Simon Kelley13dee6f2017-02-28 16:51:58 +00004706 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004707 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004708 errmess = NULL;
4709
Simon Kelley849a8352006-06-09 21:02:31 +01004710 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4711 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004712 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004713 {
4714 if (*p == '"')
4715 {
4716 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004717
Simon Kelley849a8352006-06-09 21:02:31 +01004718 for(; *p && *p != '"'; p++)
4719 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004720 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004721 {
4722 if (p[1] == 't')
4723 p[1] = '\t';
4724 else if (p[1] == 'n')
4725 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004726 else if (p[1] == 'b')
4727 p[1] = '\b';
4728 else if (p[1] == 'r')
4729 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004730 else if (p[1] == 'e') /* escape */
4731 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004732 memmove(p, p+1, strlen(p+1)+1);
4733 }
4734 *p = hide_meta(*p);
4735 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004736
4737 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004738 {
4739 errmess = _("missing \"");
4740 goto oops;
4741 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004742
4743 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004744 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004745
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004746 if (isspace(*p))
4747 {
4748 *p = ' ';
4749 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004750 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004751 else
4752 {
4753 if (white && *p == '#')
4754 {
4755 *p = 0;
4756 break;
4757 }
4758 white = 0;
4759 }
Simon Kelley849a8352006-06-09 21:02:31 +01004760 }
4761
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004762
4763 /* strip leading spaces */
4764 for (start = buff; *start && *start == ' '; start++);
4765
4766 /* strip trailing spaces */
4767 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4768
4769 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004770 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004771 else
4772 start[len] = 0;
4773
Simon Kelley611ebc52012-07-16 16:23:46 +01004774 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004775 arg = start;
4776 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004777 {
4778 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004779 for (arg = p+1; *arg == ' '; arg++);
4780 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004781 *p = 0;
4782 }
4783 else
4784 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004785
Simon Kelley611ebc52012-07-16 16:23:46 +01004786 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004787 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004788 for (option = 0, i = 0; opts[i].name; i++)
4789 if (strcmp(opts[i].name, start) == 0)
4790 {
4791 option = opts[i].val;
4792 break;
4793 }
4794
4795 if (!option)
4796 errmess = _("bad option");
4797 else if (opts[i].has_arg == 0 && arg)
4798 errmess = _("extraneous parameter");
4799 else if (opts[i].has_arg == 1 && !arg)
4800 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004801 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4802 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004803 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004804
4805 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004806 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004807 strcpy(daemon->namebuff, errmess);
4808
Simon Kelley9bafdc62018-08-21 22:53:38 +01004809 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004810 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004811 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004812 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004813 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004814 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004815 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004816 }
Simon Kelley849a8352006-06-09 21:02:31 +01004817 }
4818
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004819 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004820 fclose(f);
4821}
4822
Simon Kelley4f7bb572018-03-08 18:47:08 +00004823#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004824int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004825{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004826 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4827
Simon Kelley70d18732015-01-31 19:59:29 +00004828 if (flags & AH_DHCP_HST)
4829 return one_file(file, LOPT_BANK);
4830 else if (flags & AH_DHCP_OPT)
4831 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004832
Simon Kelley70d18732015-01-31 19:59:29 +00004833 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004834}
4835#endif
4836
Simon Kelley395eb712012-07-06 22:07:05 +01004837static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004838{
4839 FILE *f;
4840 int nofile_ok = 0;
4841 static int read_stdin = 0;
4842 static struct fileread {
4843 dev_t dev;
4844 ino_t ino;
4845 struct fileread *next;
4846 } *filesread = NULL;
4847
4848 if (hard_opt == '7')
4849 {
4850 /* default conf-file reading */
4851 hard_opt = 0;
4852 nofile_ok = 1;
4853 }
4854
4855 if (hard_opt == 0 && strcmp(file, "-") == 0)
4856 {
4857 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004858 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004859 read_stdin = 1;
4860 file = "stdin";
4861 f = stdin;
4862 }
4863 else
4864 {
4865 /* ignore repeated files. */
4866 struct stat statbuf;
4867
4868 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4869 {
4870 struct fileread *r;
4871
4872 for (r = filesread; r; r = r->next)
4873 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004874 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004875
4876 r = safe_malloc(sizeof(struct fileread));
4877 r->next = filesread;
4878 filesread = r;
4879 r->dev = statbuf.st_dev;
4880 r->ino = statbuf.st_ino;
4881 }
4882
4883 if (!(f = fopen(file, "r")))
4884 {
4885 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004886 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004887 else
4888 {
4889 char *str = _("cannot read %s: %s");
4890 if (hard_opt != 0)
4891 {
4892 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004893 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004894 }
4895 else
4896 die(str, file, EC_FILE);
4897 }
4898 }
4899 }
4900
4901 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004902 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004903}
4904
4905/* expand any name which is a directory */
4906struct hostsfile *expand_filelist(struct hostsfile *list)
4907{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004908 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004909 struct hostsfile *ah;
4910
Simon Kelley19c51cf2014-03-18 22:38:30 +00004911 /* find largest used index */
4912 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004913 {
4914 if (i <= ah->index)
4915 i = ah->index + 1;
4916
4917 if (ah->flags & AH_DIR)
4918 ah->flags |= AH_INACTIVE;
4919 else
4920 ah->flags &= ~AH_INACTIVE;
4921 }
4922
4923 for (ah = list; ah; ah = ah->next)
4924 if (!(ah->flags & AH_INACTIVE))
4925 {
4926 struct stat buf;
4927 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4928 {
4929 DIR *dir_stream;
4930 struct dirent *ent;
4931
4932 /* don't read this as a file */
4933 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004934
Simon Kelley28866e92011-02-14 20:19:14 +00004935 if (!(dir_stream = opendir(ah->fname)))
4936 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4937 ah->fname, strerror(errno));
4938 else
4939 {
4940 while ((ent = readdir(dir_stream)))
4941 {
4942 size_t lendir = strlen(ah->fname);
4943 size_t lenfile = strlen(ent->d_name);
4944 struct hostsfile *ah1;
4945 char *path;
4946
4947 /* ignore emacs backups and dotfiles */
4948 if (lenfile == 0 ||
4949 ent->d_name[lenfile - 1] == '~' ||
4950 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4951 ent->d_name[0] == '.')
4952 continue;
4953
4954 /* see if we have an existing record.
4955 dir is ah->fname
4956 file is ent->d_name
4957 path to match is ah1->fname */
4958
4959 for (ah1 = list; ah1; ah1 = ah1->next)
4960 {
4961 if (lendir < strlen(ah1->fname) &&
4962 strstr(ah1->fname, ah->fname) == ah1->fname &&
4963 ah1->fname[lendir] == '/' &&
4964 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4965 {
4966 ah1->flags &= ~AH_INACTIVE;
4967 break;
4968 }
4969 }
4970
4971 /* make new record */
4972 if (!ah1)
4973 {
4974 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4975 continue;
4976
4977 if (!(path = whine_malloc(lendir + lenfile + 2)))
4978 {
4979 free(ah1);
4980 continue;
4981 }
4982
4983 strcpy(path, ah->fname);
4984 strcat(path, "/");
4985 strcat(path, ent->d_name);
4986 ah1->fname = path;
4987 ah1->index = i++;
4988 ah1->flags = AH_DIR;
4989 ah1->next = list;
4990 list = ah1;
4991 }
4992
4993 /* inactivate record if not regular file */
4994 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4995 ah1->flags |= AH_INACTIVE;
4996
4997 }
4998 closedir(dir_stream);
4999 }
5000 }
5001 }
5002
5003 return list;
5004}
5005
Simon Kelley7b1eae42014-02-20 13:43:28 +00005006void read_servers_file(void)
5007{
5008 FILE *f;
5009
5010 if (!(f = fopen(daemon->servers_file, "r")))
5011 {
5012 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
5013 return;
5014 }
5015
5016 mark_servers(SERV_FROM_FILE);
5017 cleanup_servers();
5018
5019 read_file(daemon->servers_file, f, LOPT_REV_SERV);
5020}
5021
Simon Kelley28866e92011-02-14 20:19:14 +00005022
Simon Kelley7622fc02009-06-04 20:32:05 +01005023#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00005024static void clear_dynamic_conf(void)
5025{
5026 struct dhcp_config *configs, *cp, **up;
5027
5028 /* remove existing... */
5029 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
5030 {
5031 cp = configs->next;
5032
5033 if (configs->flags & CONFIG_BANK)
5034 {
5035 struct hwaddr_config *mac, *tmp;
5036 struct dhcp_netid_list *list, *tmplist;
5037
5038 for (mac = configs->hwaddr; mac; mac = tmp)
5039 {
5040 tmp = mac->next;
5041 free(mac);
5042 }
5043
5044 if (configs->flags & CONFIG_CLID)
5045 free(configs->clid);
5046
5047 for (list = configs->netid; list; list = tmplist)
5048 {
5049 free(list->list);
5050 tmplist = list->next;
5051 free(list);
5052 }
5053
5054 if (configs->flags & CONFIG_NAME)
5055 free(configs->hostname);
5056
5057 *up = configs->next;
5058 free(configs);
5059 }
5060 else
5061 up = &configs->next;
5062 }
5063}
5064
5065static void clear_dynamic_opt(void)
5066{
5067 struct dhcp_opt *opts, *cp, **up;
5068 struct dhcp_netid *id, *next;
5069
5070 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
5071 {
5072 cp = opts->next;
5073
5074 if (opts->flags & DHOPT_BANK)
5075 {
5076 if ((opts->flags & DHOPT_VENDOR))
5077 free(opts->u.vendor_class);
5078 free(opts->val);
5079 for (id = opts->netid; id; id = next)
5080 {
5081 next = id->next;
5082 free(id->net);
5083 free(id);
5084 }
5085 *up = opts->next;
5086 free(opts);
5087 }
5088 else
5089 up = &opts->next;
5090 }
5091}
5092
Simon Kelley824af852008-02-12 20:43:05 +00005093void reread_dhcp(void)
5094{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005095 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005096
Simon Kelley4f7bb572018-03-08 18:47:08 +00005097 /* Do these even if there is no daemon->dhcp_hosts_file or
5098 daemon->dhcp_opts_file since entries may have been created by the
5099 inotify dynamic file reading system. */
5100
5101 clear_dynamic_conf();
5102 clear_dynamic_opt();
5103
5104 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005105 {
Simon Kelley28866e92011-02-14 20:19:14 +00005106 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5107 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005108 if (!(hf->flags & AH_INACTIVE))
5109 {
5110 if (one_file(hf->fname, LOPT_BANK))
5111 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5112 }
Simon Kelley824af852008-02-12 20:43:05 +00005113 }
5114
5115 if (daemon->dhcp_opts_file)
5116 {
Simon Kelley28866e92011-02-14 20:19:14 +00005117 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5118 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5119 if (!(hf->flags & AH_INACTIVE))
5120 {
Simon Kelley395eb712012-07-06 22:07:05 +01005121 if (one_file(hf->fname, LOPT_OPTS))
5122 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005123 }
Simon Kelley824af852008-02-12 20:43:05 +00005124 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005125
5126# ifdef HAVE_INOTIFY
5127 /* Setup notify and read pre-existing files. */
5128 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5129# endif
Simon Kelley824af852008-02-12 20:43:05 +00005130}
Simon Kelley7622fc02009-06-04 20:32:05 +01005131#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005132
Simon Kelley5aabfc72007-08-29 11:24:47 +01005133void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005134{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005135 size_t argbuf_size = MAXDNAME;
5136 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005137 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005138 int option, testmode = 0;
5139 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005140
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005141 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005142
Simon Kelley824af852008-02-12 20:43:05 +00005143 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005144 memset(daemon, 0, sizeof(struct daemon));
5145 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005146
Simon Kelley3be34542004-09-11 19:12:13 +01005147 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005148 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005149 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005150 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005151 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5152 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005153 daemon->default_resolv.is_default = 1;
5154 daemon->default_resolv.name = RESOLVFILE;
5155 daemon->resolv_files = &daemon->default_resolv;
5156 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005157 daemon->runfile = RUNFILE;
5158 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005159 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005160 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005161 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005162 daemon->auth_ttl = AUTH_TTL;
5163 daemon->soa_refresh = SOA_REFRESH;
5164 daemon->soa_retry = SOA_RETRY;
5165 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley4a8c0982021-03-26 21:19:39 +00005166
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005167#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005168 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5169 add_txt("authors.bind", "Simon Kelley", 0);
5170 add_txt("copyright.bind", COPYRIGHT, 0);
5171 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5172 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5173 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5174 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5175 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5176#ifdef HAVE_AUTH
5177 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5178#endif
5179 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005180#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005181
Simon Kelley849a8352006-06-09 21:02:31 +01005182 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005183 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005184#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005185 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005186#else
Simon Kelley849a8352006-06-09 21:02:31 +01005187 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005188#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005189
5190 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005191 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005192 for (; optind < argc; optind++)
5193 {
5194 unsigned char *c = (unsigned char *)argv[optind];
5195 for (; *c != 0; c++)
5196 if (!isspace(*c))
5197 die(_("junk found in command line"), NULL, EC_BADCONF);
5198 }
Simon Kelley28866e92011-02-14 20:19:14 +00005199 break;
5200 }
5201
Simon Kelley849a8352006-06-09 21:02:31 +01005202 /* Copy optarg so that argv doesn't get changed */
5203 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005204 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005205 if (strlen(optarg) >= argbuf_size)
5206 {
5207 free(argbuf);
5208 argbuf_size = strlen(optarg) + 1;
5209 argbuf = opt_malloc(argbuf_size);
5210 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005211 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005212 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005213 }
5214 else
5215 arg = NULL;
5216
5217 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005218 if (option == LOPT_TEST)
5219 testmode = 1;
5220 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005221 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005222#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005223 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005224 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005225#ifdef HAVE_DHCP6
5226 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5227 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005228#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005229 else
5230#endif
5231 do_usage();
5232
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005233 exit(0);
5234 }
Simon Kelley849a8352006-06-09 21:02:31 +01005235 else if (option == 'v')
5236 {
5237 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005238 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005239 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5240 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005241 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005242 exit(0);
5243 }
Simon Kelley849a8352006-06-09 21:02:31 +01005244 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005245 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005246 if (!conffile)
5247 conffile = opt_string_alloc(arg);
5248 else
5249 {
5250 char *extra = opt_string_alloc(arg);
5251 one_file(extra, 0);
5252 free(extra);
5253 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005254 }
Simon Kelley849a8352006-06-09 21:02:31 +01005255 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005256 {
Simon Kelley26128d22004-11-14 16:43:54 +00005257#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005258 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005259#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005260 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005261#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005262 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005263 }
5264 }
Simon Kelley849a8352006-06-09 21:02:31 +01005265
Neil Jerram3bd4c472018-01-18 22:49:38 +00005266 free(argbuf);
5267
Simon Kelley849a8352006-06-09 21:02:31 +01005268 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005269 {
Petr Menšík59e47032018-11-02 22:39:39 +00005270 one_file(conffile, 0);
5271 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005272 }
Petr Menšík59e47032018-11-02 22:39:39 +00005273 else
5274 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005275
Simon Kelley1a6bca82008-07-11 11:11:42 +01005276 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005277 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005278 {
5279 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005280 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005281 if (!(tmp->flags & SERV_HAS_SOURCE))
5282 {
5283 if (tmp->source_addr.sa.sa_family == AF_INET)
5284 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005285 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5286 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005287 }
5288 }
5289
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005290 if (daemon->host_records)
5291 {
5292 struct host_record *hr;
5293
5294 for (hr = daemon->host_records; hr; hr = hr->next)
5295 if (hr->ttl == -1)
5296 hr->ttl = daemon->local_ttl;
5297 }
5298
5299 if (daemon->cnames)
5300 {
Simon Kelley903df072017-01-19 17:22:00 +00005301 struct cname *cn, *cn2, *cn3;
5302
5303#define NOLOOP 1
5304#define TESTLOOP 2
5305
Simon Kelley157d8cf2019-10-25 17:46:49 +01005306 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005307 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005308 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005309 {
5310 if (cn->ttl == -1)
5311 cn->ttl = daemon->local_ttl;
5312 cn->flag = 0;
5313 cn->targetp = NULL;
5314 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5315 if (hostname_isequal(cn->target, cn2->alias))
5316 {
5317 cn->targetp = cn2;
5318 break;
5319 }
5320 }
5321
5322 /* Find any CNAME loops.*/
5323 for (cn = daemon->cnames; cn; cn = cn->next)
5324 {
5325 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5326 {
5327 if (cn2->flag == NOLOOP)
5328 break;
5329
5330 if (cn2->flag == TESTLOOP)
5331 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5332
5333 cn2->flag = TESTLOOP;
5334 }
5335
5336 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5337 cn3->flag = NOLOOP;
5338 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005339 }
5340
Simon Kelley3be34542004-09-11 19:12:13 +01005341 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005342 {
5343 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005344 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005345 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005346 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005347 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005348 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005349 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005350
5351 /* create default, if not specified */
5352 if (daemon->authserver && !daemon->hostmaster)
5353 {
5354 strcpy(buff, "hostmaster.");
5355 strcat(buff, daemon->authserver);
5356 daemon->hostmaster = opt_string_alloc(buff);
5357 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005358
5359 if (!daemon->dhcp_pxe_vendors)
5360 {
5361 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5362 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5363 daemon->dhcp_pxe_vendors->next = NULL;
5364 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005365
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005366 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005367 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005368 {
Simon Kelley0a852542005-03-23 20:28:59 +00005369 struct mx_srv_record *mx;
5370
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005371 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005372 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005373
Simon Kelley0a852542005-03-23 20:28:59 +00005374 for (mx = daemon->mxnames; mx; mx = mx->next)
5375 if (!mx->issrv && hostname_isequal(mx->name, buff))
5376 break;
5377
Simon Kelley28866e92011-02-14 20:19:14 +00005378 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005379 {
Simon Kelley824af852008-02-12 20:43:05 +00005380 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005381 mx->next = daemon->mxnames;
5382 mx->issrv = 0;
5383 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005384 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005385 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005386 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005387
Simon Kelley3be34542004-09-11 19:12:13 +01005388 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005389 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005390
5391 for (mx = daemon->mxnames; mx; mx = mx->next)
5392 if (!mx->issrv && !mx->target)
5393 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005394 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005395
Simon Kelley28866e92011-02-14 20:19:14 +00005396 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005397 daemon->resolv_files &&
5398 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005399 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005400 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005401
Simon Kelley28866e92011-02-14 20:19:14 +00005402 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005403 {
5404 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005405 FILE *f;
5406
Simon Kelley28866e92011-02-14 20:19:14 +00005407 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005408 !daemon->resolv_files ||
5409 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005410 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005411
Simon Kelley3be34542004-09-11 19:12:13 +01005412 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005413 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005414
5415 while ((line = fgets(buff, MAXDNAME, f)))
5416 {
5417 char *token = strtok(line, " \t\n\r");
5418
5419 if (!token || strcmp(token, "search") != 0)
5420 continue;
5421
5422 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005423 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005424 break;
5425 }
Simon Kelley3be34542004-09-11 19:12:13 +01005426
Simon Kelleyde379512004-06-22 20:23:33 +01005427 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005428
Simon Kelley3be34542004-09-11 19:12:13 +01005429 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005430 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005431 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005432
5433 if (daemon->domain_suffix)
5434 {
5435 /* add domain for any srv record without one. */
5436 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005437
Simon Kelley3d8df262005-08-29 12:19:27 +01005438 for (srv = daemon->mxnames; srv; srv = srv->next)
5439 if (srv->issrv &&
5440 strchr(srv->name, '.') &&
5441 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5442 {
5443 strcpy(buff, srv->name);
5444 strcat(buff, ".");
5445 strcat(buff, daemon->domain_suffix);
5446 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005447 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005448 }
5449 }
Simon Kelley28866e92011-02-14 20:19:14 +00005450 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005451 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005452
Simon Kelleyc8a80482014-03-05 14:29:54 +00005453 /* If there's access-control config, then ignore --local-service, it's intended
5454 as a system default to keep otherwise unconfigured installations safe. */
5455 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5456 reset_option_bool(OPT_LOCAL_SERVICE);
5457
Simon Kelley7622fc02009-06-04 20:32:05 +01005458 if (testmode)
5459 {
5460 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5461 exit(0);
5462 }
Simon Kelley849a8352006-06-09 21:02:31 +01005463}