blob: 6de59143ff7af429dd94bba2ef75de7bd26acbe7 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley849a8352006-06-09 21:02:31 +010017/* define this to get facilitynames */
18#define SYSLOG_NAMES
Simon Kelley9e4abcb2004-01-22 19:47:41 +000019#include "dnsmasq.h"
Simon Kelley824af852008-02-12 20:43:05 +000020#include <setjmp.h>
21
Simon Kelley7622fc02009-06-04 20:32:05 +010022static volatile int mem_recover = 0;
23static jmp_buf mem_jmp;
Simon Kelley395eb712012-07-06 22:07:05 +010024static int one_file(char *file, int hard_opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010025
Simon Kelley824af852008-02-12 20:43:05 +000026/* Solaris headers don't have facility names. */
27#ifdef HAVE_SOLARIS_NETWORK
28static const struct {
29 char *c_name;
30 unsigned int c_val;
31} facilitynames[] = {
32 { "kern", LOG_KERN },
33 { "user", LOG_USER },
34 { "mail", LOG_MAIL },
35 { "daemon", LOG_DAEMON },
36 { "auth", LOG_AUTH },
37 { "syslog", LOG_SYSLOG },
38 { "lpr", LOG_LPR },
39 { "news", LOG_NEWS },
40 { "uucp", LOG_UUCP },
Simon Kelley824af852008-02-12 20:43:05 +000041 { "audit", LOG_AUDIT },
Simon Kelley824af852008-02-12 20:43:05 +000042 { "cron", LOG_CRON },
43 { "local0", LOG_LOCAL0 },
44 { "local1", LOG_LOCAL1 },
45 { "local2", LOG_LOCAL2 },
46 { "local3", LOG_LOCAL3 },
47 { "local4", LOG_LOCAL4 },
48 { "local5", LOG_LOCAL5 },
49 { "local6", LOG_LOCAL6 },
50 { "local7", LOG_LOCAL7 },
51 { NULL, 0 }
52};
53#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000054
Simon Kelley849a8352006-06-09 21:02:31 +010055#ifndef HAVE_GETOPT_LONG
Simon Kelley9e4abcb2004-01-22 19:47:41 +000056struct myoption {
57 const char *name;
58 int has_arg;
59 int *flag;
60 int val;
61};
Simon Kelley849a8352006-06-09 21:02:31 +010062#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000063
Simon Kelley9009d742008-11-14 20:04:27 +000064#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
Simon Kelley9e4abcb2004-01-22 19:47:41 +000065
Simon Kelley16972692006-10-16 20:04:18 +010066/* options which don't have a one-char version */
Simon Kelleye98bd522014-03-28 20:41:23 +000067#define LOPT_RELOAD 256
68#define LOPT_NO_NAMES 257
69#define LOPT_TFTP 258
70#define LOPT_SECURE 259
71#define LOPT_PREFIX 260
72#define LOPT_PTR 261
73#define LOPT_BRIDGE 262
74#define LOPT_TFTP_MAX 263
75#define LOPT_FORCE 264
76#define LOPT_NOBLOCK 265
77#define LOPT_LOG_OPTS 266
78#define LOPT_MAX_LOGS 267
79#define LOPT_CIRCUIT 268
80#define LOPT_REMOTE 269
81#define LOPT_SUBSCR 270
82#define LOPT_INTNAME 271
83#define LOPT_BANK 272
84#define LOPT_DHCP_HOST 273
85#define LOPT_APREF 274
86#define LOPT_OVERRIDE 275
87#define LOPT_TFTPPORTS 276
88#define LOPT_REBIND 277
89#define LOPT_NOLAST 278
90#define LOPT_OPTS 279
91#define LOPT_DHCP_OPTS 280
92#define LOPT_MATCH 281
93#define LOPT_BROADCAST 282
94#define LOPT_NEGTTL 283
95#define LOPT_ALTPORT 284
96#define LOPT_SCRIPTUSR 285
97#define LOPT_LOCAL 286
98#define LOPT_NAPTR 287
99#define LOPT_MINPORT 288
100#define LOPT_DHCP_FQDN 289
101#define LOPT_CNAME 290
102#define LOPT_PXE_PROMT 291
103#define LOPT_PXE_SERV 292
104#define LOPT_TEST 293
105#define LOPT_TAG_IF 294
106#define LOPT_PROXY 295
107#define LOPT_GEN_NAMES 296
108#define LOPT_MAXTTL 297
109#define LOPT_NO_REBIND 298
110#define LOPT_LOC_REBND 299
111#define LOPT_ADD_MAC 300
112#define LOPT_DNSSEC 301
113#define LOPT_INCR_ADDR 302
114#define LOPT_CONNTRACK 303
115#define LOPT_FQDN 304
116#define LOPT_LUASCRIPT 305
117#define LOPT_RA 306
118#define LOPT_DUID 307
119#define LOPT_HOST_REC 308
120#define LOPT_TFTP_LC 309
121#define LOPT_RR 310
122#define LOPT_CLVERBIND 311
123#define LOPT_MAXCTTL 312
124#define LOPT_AUTHZONE 313
125#define LOPT_AUTHSERV 314
126#define LOPT_AUTHTTL 315
127#define LOPT_AUTHSOA 316
128#define LOPT_AUTHSFS 317
129#define LOPT_AUTHPEER 318
130#define LOPT_IPSET 319
131#define LOPT_SYNTH 320
Simon Kelleye98bd522014-03-28 20:41:23 +0000132#define LOPT_RELAY 323
133#define LOPT_RA_PARAM 324
134#define LOPT_ADD_SBNET 325
135#define LOPT_QUIET_DHCP 326
136#define LOPT_QUIET_DHCP6 327
137#define LOPT_QUIET_RA 328
138#define LOPT_SEC_VALID 329
139#define LOPT_TRUST_ANCHOR 330
140#define LOPT_DNSSEC_DEBUG 331
141#define LOPT_REV_SERV 332
142#define LOPT_SERVERS_FILE 333
143#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000144#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000145#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100146#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000147#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000148#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000149#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000150#define LOPT_DHOPT_INOTIFY 341
151#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000152#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100153#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000154#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000155#define LOPT_CPE_ID 346
156#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000157#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000158#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100159#define LOPT_REPLY_DELAY 350
Simon Kelley734d5312018-03-23 23:09:53 +0000160#define LOPT_RAPID_COMMIT 351
Simon Kelley6b173352018-05-08 18:32:14 +0100161#define LOPT_DUMPFILE 352
162#define LOPT_DUMPMASK 353
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100163#define LOPT_UBUS 354
Simon Kelleyc8226202018-08-08 23:46:03 +0100164#define LOPT_NAME_MATCH 355
Simon Kelley974a6d02018-08-23 23:01:16 +0100165#define LOPT_CAA 356
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000166#define LOPT_SHARED_NET 357
Florent Fourcot13a58f92019-06-20 10:26:40 +0200167#define LOPT_IGNORE_CLID 358
Simon Kelley66f62652020-01-05 16:21:24 +0000168#define LOPT_SINGLE_PORT 359
Simon Kelleyee645822020-02-27 16:34:14 +0000169#define LOPT_SCRIPT_TIME 360
Wang Shanker4ded9622020-12-04 10:17:35 +0800170#define LOPT_PXE_VENDOR 361
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000171#define LOPT_DYNHOST 362
Simon Kelleyb260d222021-03-12 21:57:57 +0000172#define LOPT_LOG_DEBUG 363
Simon Kelleybec366b2016-02-24 22:03:26 +0000173
Simon Kelley849a8352006-06-09 21:02:31 +0100174#ifdef HAVE_GETOPT_LONG
175static const struct option opts[] =
176#else
177static const struct myoption opts[] =
178#endif
179 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100180 { "version", 0, 0, 'v' },
181 { "no-hosts", 0, 0, 'h' },
182 { "no-poll", 0, 0, 'n' },
183 { "help", 0, 0, 'w' },
184 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000185 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100186 { "user", 2, 0, 'u' },
187 { "group", 2, 0, 'g' },
188 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000189 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100190 { "mx-host", 1, 0, 'm' },
191 { "mx-target", 1, 0, 't' },
192 { "cache-size", 2, 0, 'c' },
193 { "port", 1, 0, 'p' },
194 { "dhcp-leasefile", 2, 0, 'l' },
195 { "dhcp-lease", 1, 0, 'l' },
196 { "dhcp-host", 1, 0, 'G' },
197 { "dhcp-range", 1, 0, 'F' },
198 { "dhcp-option", 1, 0, 'O' },
199 { "dhcp-boot", 1, 0, 'M' },
200 { "domain", 1, 0, 's' },
201 { "domain-suffix", 1, 0, 's' },
202 { "interface", 1, 0, 'i' },
203 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000204 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100205 { "bogus-priv", 0, 0, 'b' },
206 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000207 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100208 { "selfmx", 0, 0, 'e' },
209 { "filterwin2k", 0, 0, 'f' },
210 { "pid-file", 2, 0, 'x' },
211 { "strict-order", 0, 0, 'o' },
212 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000213 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 { "local", 1, 0, LOPT_LOCAL },
215 { "address", 1, 0, 'A' },
216 { "conf-file", 2, 0, 'C' },
217 { "no-resolv", 0, 0, 'R' },
218 { "expand-hosts", 0, 0, 'E' },
219 { "localmx", 0, 0, 'L' },
220 { "local-ttl", 1, 0, 'T' },
221 { "no-negcache", 0, 0, 'N' },
222 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000223 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100224 { "query-port", 1, 0, 'Q' },
225 { "except-interface", 1, 0, 'I' },
226 { "no-dhcp-interface", 1, 0, '2' },
227 { "domain-needed", 0, 0, 'D' },
228 { "dhcp-lease-max", 1, 0, 'X' },
229 { "bind-interfaces", 0, 0, 'z' },
230 { "read-ethers", 0, 0, 'Z' },
231 { "alias", 1, 0, 'V' },
232 { "dhcp-vendorclass", 1, 0, 'U' },
233 { "dhcp-userclass", 1, 0, 'j' },
234 { "dhcp-ignore", 1, 0, 'J' },
235 { "edns-packet-max", 1, 0, 'P' },
236 { "keep-in-foreground", 0, 0, 'k' },
237 { "dhcp-authoritative", 0, 0, 'K' },
238 { "srv-host", 1, 0, 'W' },
239 { "localise-queries", 0, 0, 'y' },
240 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100241 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100242 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100243 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100244 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100245 { "bootp-dynamic", 2, 0, '3' },
246 { "dhcp-mac", 1, 0, '4' },
247 { "no-ping", 0, 0, '5' },
248 { "dhcp-script", 1, 0, '6' },
249 { "conf-dir", 1, 0, '7' },
250 { "log-facility", 1, 0 ,'8' },
251 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000252 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100253 { "dns-forward-max", 1, 0, '0' },
254 { "clear-on-reload", 0, 0, LOPT_RELOAD },
255 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100256 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100257 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100258 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100259 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100260 { "tftp-root", 1, 0, LOPT_PREFIX },
261 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000262 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100263 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000264 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100265 { "ptr-record", 1, 0, LOPT_PTR },
266 { "naptr-record", 1, 0, LOPT_NAPTR },
267 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000268 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100269 { "dhcp-option-force", 1, 0, LOPT_FORCE },
270 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
271 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
272 { "log-async", 2, 0, LOPT_MAX_LOGS },
273 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
274 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
275 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
Wang Shanker4ded9622020-12-04 10:17:35 +0800276 { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100277 { "interface-name", 1, 0, LOPT_INTNAME },
278 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
279 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000280 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000281 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100282 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
283 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
284 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100285 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100286 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100287 { "dhcp-match", 1, 0, LOPT_MATCH },
288 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100289 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100290 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100291 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000292 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100293 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100294 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
295 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
296 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000297 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100298 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
299 { "cname", 1, 0, LOPT_CNAME },
300 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
301 { "pxe-service", 1, 0, LOPT_PXE_SERV },
302 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100303 { "tag-if", 1, 0, LOPT_TAG_IF },
304 { "dhcp-proxy", 2, 0, LOPT_PROXY },
305 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
306 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000307 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100308 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000309 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000310 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100311 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
312 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000313 { "dhcp-client-update", 0, 0, LOPT_FQDN },
314 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000315 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000316 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000317 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100318 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000319 { "auth-zone", 1, 0, LOPT_AUTHZONE },
320 { "auth-server", 1, 0, LOPT_AUTHSERV },
321 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
322 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000323 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000324 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000325 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100326 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200327 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000328 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000329 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100330 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000331 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000332 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100333 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100334 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100335 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
336 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
337 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100338 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000339 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000340 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100341 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000342 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100343 { "dumpfile", 1, 0, LOPT_DUMPFILE },
344 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200345 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000346 { "dynamic-host", 1, 0, LOPT_DYNHOST },
Simon Kelleyb260d222021-03-12 21:57:57 +0000347 { "log-debug", 0, 0, LOPT_LOG_DEBUG },
Simon Kelley849a8352006-06-09 21:02:31 +0100348 { NULL, 0, 0, 0 }
349 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000350
Simon Kelley28866e92011-02-14 20:19:14 +0000351
352#define ARG_DUP OPT_LAST
353#define ARG_ONE OPT_LAST + 1
354#define ARG_USED_CL OPT_LAST + 2
355#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356
Simon Kelley1a6bca82008-07-11 11:11:42 +0100357static struct {
358 int opt;
359 unsigned int rept;
360 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000361 char * const desc;
362 char * const arg;
363} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000364 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
365 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100366 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000367 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
368 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
369 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100370 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
371 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
372 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
373 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
374 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000375 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
376 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
379 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000380 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000381 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100382 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100383 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000384 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
Simon Kelley70d18732015-01-31 19:59:29 +0000385 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000386 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
387 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100388 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
389 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
390 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
391 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
Wang Shanker4ded9622020-12-04 10:17:35 +0800392 { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor class to match for PXE requests."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100393 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
394 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100395 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
396 { 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000397 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000399 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100400 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
401 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
402 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
403 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
404 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
405 { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000406 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
407 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000408 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000409 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000411 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000412 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000413 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000414 { LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000415 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000416 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000417 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
418 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
419 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
420 { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
RinSatsuki28de3872015-01-10 15:22:21 +0000421 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
422 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000423 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100424 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100425 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000426 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
427 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100428 { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp or --help dhcp6 for known DHCP options."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000429 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
430 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100431 { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000432 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
433 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
434 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100435 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
436 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100437 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100438 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000439 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100440 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
441 { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000442 { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000443 { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
Simon Kelley1a6bca82008-07-11 11:11:42 +0100444 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000445 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
446 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
447 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000448 { LOPT_SCRIPT_ARP, OPT_SCRIPT_ARP, NULL, gettext_noop("Call dhcp-script with changes to local ARP table."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000449 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000450 { '8', ARG_ONE, "<facility>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000452 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100453 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100454 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100455 { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100456 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100457 { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
Floris Bos60704f52017-04-09 22:22:49 +0100458 { LOPT_APREF, ARG_DUP, "[=ip|mac]", gettext_noop("Add client IP or hardware address to tftp-root."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100459 { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100460 { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000461 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000462 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100463 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100464 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100465 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000466 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100467 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000468 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100469 { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100470 { LOPT_LOC_REBND, OPT_LOCAL_REBIND, NULL, gettext_noop("Allow rebinding of 127.0.0.0/8, for RBL servers."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000471 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100472 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100473 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelleyc8226202018-08-08 23:46:03 +0100474 { LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100475 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100476 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
477 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000478 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000479 { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000480 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
481 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100482 { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000483 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100484 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
485 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
486 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000487 { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
Ed Bardsleya7369be2015-08-05 21:17:18 +0100488 { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000489 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100490 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100491 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200492 { LOPT_IGNORE_CLID, OPT_IGNORE_CLID, NULL, gettext_noop("Ignore client identifier option sent by DHCP clients."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100493 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000494 { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000495 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000496 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000497 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelleyb7cf7542021-03-04 16:54:14 +0000498 { LOPT_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), NULL },
Simon Kelley974a6d02018-08-23 23:01:16 +0100499 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100500 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000501 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000502 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000503 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000504 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000505 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000506 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
507 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100508 { LOPT_IPSET, ARG_DUP, "/<domain>[/<domain>...]/<ipset>...", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
Gildasa9bf81a2013-10-24 13:31:40 +0100509 { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
Simon Kelley3a237152013-12-12 12:15:50 +0000510 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000511 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000512 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100513 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000514 { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000515 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100516 { LOPT_RA_PARAM, ARG_DUP, "<iface>,[mtu:<value>|<interface>|off,][<prio>,]<intval>[,<lifetime>]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100517 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
518 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
519 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelleyb260d222021-03-12 21:57:57 +0000520 { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000521 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
522 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000523 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000524 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100525 { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
Simon Kelley734d5312018-03-23 23:09:53 +0000526 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100527 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
528 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000529 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100530 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000531};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000532
Josh Soref730c6742017-02-06 16:14:04 +0000533/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100534 character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
Simon Kelley3d8df262005-08-29 12:19:27 +0100535 following sequence so that they map to themselves: it is therefore possible to call
536 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000537 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100538 couple of other places.
539 Note that space is included here so that
540 --dhcp-option=3, string
541 has five characters, whilst
542 --dhcp-option=3," string"
543 has six.
544*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100545
Simon Kelleyf2621c72007-04-29 19:47:21 +0100546static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100547
548static char hide_meta(char c)
549{
550 unsigned int i;
551
552 for (i = 0; i < (sizeof(meta) - 1); i++)
553 if (c == meta[i])
554 return (char)i;
555
556 return c;
557}
558
559static char unhide_meta(char cr)
560{
561 unsigned int c = cr;
562
563 if (c < (sizeof(meta) - 1))
564 cr = meta[c];
565
566 return cr;
567}
568
569static void unhide_metas(char *cp)
570{
571 if (cp)
572 for(; *cp; cp++)
573 *cp = unhide_meta(*cp);
574}
575
Simon Kelley824af852008-02-12 20:43:05 +0000576static void *opt_malloc(size_t size)
577{
578 void *ret;
579
580 if (mem_recover)
581 {
582 ret = whine_malloc(size);
583 if (!ret)
584 longjmp(mem_jmp, 1);
585 }
586 else
587 ret = safe_malloc(size);
588
589 return ret;
590}
591
Petr Menšík59e47032018-11-02 22:39:39 +0000592static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100593{
594 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000595 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100596
Petr Menšík59e47032018-11-02 22:39:39 +0000597 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100598 {
Petr Menšík59e47032018-11-02 22:39:39 +0000599 ret = opt_malloc(len+1);
600 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100601
602 /* restore hidden metachars */
603 unhide_metas(ret);
604 }
605
606 return ret;
607}
608
Simon Kelley3d8df262005-08-29 12:19:27 +0100609
Simon Kelleyf2621c72007-04-29 19:47:21 +0100610/* find next comma, split string with zero and eliminate spaces.
611 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000612
613static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100614{
615 char *comma, *p;
616
Simon Kelley73a08a22009-02-05 20:28:08 +0000617 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100618 return NULL;
619
620 p = comma;
621 *comma = ' ';
622
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100623 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100624
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100625 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100626 *p = 0;
627
628 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100629}
630
Simon Kelley73a08a22009-02-05 20:28:08 +0000631static char *split(char *s)
632{
633 return split_chr(s, ',');
634}
635
Simon Kelley1f15b812009-10-13 17:49:32 +0100636static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100637{
Simon Kelley1f15b812009-10-13 17:49:32 +0100638 char *ret;
639 int nomem;
640
Simon Kelley3d8df262005-08-29 12:19:27 +0100641 if (!s)
642 return 0;
643
644 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100645 if (!(ret = canonicalise(s, &nomem)) && nomem)
646 {
647 if (mem_recover)
648 longjmp(mem_jmp, 1);
649 else
650 die(_("could not get memory"), NULL, EC_NOMEM);
651 }
652
653 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100654}
655
656static int atoi_check(char *a, int *res)
657{
658 char *p;
659
660 if (!a)
661 return 0;
662
663 unhide_metas(a);
664
665 for (p = a; *p; p++)
666 if (*p < '0' || *p > '9')
667 return 0;
668
669 *res = atoi(a);
670 return 1;
671}
672
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100673static int atoi_check16(char *a, int *res)
674{
675 if (!(atoi_check(a, res)) ||
676 *res < 0 ||
677 *res > 0xffff)
678 return 0;
679
680 return 1;
681}
Simon Kelleyee415862014-02-11 11:07:22 +0000682
Simon Kelleyde73a492014-02-17 21:43:27 +0000683#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000684static int atoi_check8(char *a, int *res)
685{
686 if (!(atoi_check(a, res)) ||
687 *res < 0 ||
688 *res > 0xff)
689 return 0;
690
691 return 1;
692}
Simon Kelleyde73a492014-02-17 21:43:27 +0000693#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100694
695#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000696static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000697{
Simon Kelley824af852008-02-12 20:43:05 +0000698 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000699
700 if (txt)
701 {
702 size_t len = strlen(txt);
703 r->txt = opt_malloc(len+1);
704 r->len = len+1;
705 *(r->txt) = len;
706 memcpy((r->txt)+1, txt, len);
707 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100708
Simon Kelleyfec216d2014-03-27 20:54:34 +0000709 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000710 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000711 r->next = daemon->txt;
712 daemon->txt = r;
713 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000714}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100715#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000716
Simon Kelley849a8352006-06-09 21:02:31 +0100717static void do_usage(void)
718{
719 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000720 int i, j;
721
722 struct {
723 char handle;
724 int val;
725 } tab[] = {
726 { '$', CACHESIZ },
727 { '*', EDNS_PKTSZ },
728 { '&', MAXLEASES },
729 { '!', FTABSIZ },
730 { '#', TFTP_MAX_CONNECTIONS },
731 { '\0', 0 }
732 };
Simon Kelley849a8352006-06-09 21:02:31 +0100733
734 printf(_("Usage: dnsmasq [options]\n\n"));
735#ifndef HAVE_GETOPT_LONG
736 printf(_("Use short options only on the command line.\n"));
737#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100738 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100739
Simon Kelley1a6bca82008-07-11 11:11:42 +0100740 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100741 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100742 char *desc = usage[i].flagdesc;
743 char *eq = "=";
744
745 if (!desc || *desc == '[')
746 eq = "";
747
748 if (!desc)
749 desc = "";
750
751 for ( j = 0; opts[j].name; j++)
752 if (opts[j].val == usage[i].opt)
753 break;
754 if (usage[i].opt < 256)
755 sprintf(buff, "-%c, ", usage[i].opt);
756 else
757 sprintf(buff, " ");
758
759 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100760 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100761
Simon Kelley849a8352006-06-09 21:02:31 +0100762 if (usage[i].arg)
763 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000764 strcpy(buff, usage[i].arg);
765 for (j = 0; tab[j].handle; j++)
766 if (tab[j].handle == *(usage[i].arg))
767 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100768 }
Simon Kelley849a8352006-06-09 21:02:31 +0100769 printf(_(usage[i].desc), buff);
770 printf("\n");
771 }
772}
773
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100774#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000775#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
776#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100777
Ed Bardsleya7369be2015-08-05 21:17:18 +0100778static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
779{
780 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
781 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100782 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
783 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100784 else
785 return _("bad address");
786
787 return NULL;
788}
789
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100790char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
791{
792 int source_port = 0, serv_port = NAMESERVER_PORT;
793 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000794 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100795 int scope_index = 0;
796 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100797
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000798 if (!arg || strlen(arg) == 0)
799 {
800 *flags |= SERV_NO_ADDR;
801 *interface = 0;
802 return NULL;
803 }
804
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100805 if ((source = split_chr(arg, '@')) && /* is there a source. */
806 (portno = split_chr(source, '#')) &&
807 !atoi_check16(portno, &source_port))
808 return _("bad port");
809
810 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
811 !atoi_check16(portno, &serv_port))
812 return _("bad port");
813
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100814 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100815
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000816 if (source) {
817 interface_opt = split_chr(source, '@');
818
819 if (interface_opt)
820 {
821#if defined(SO_BINDTODEVICE)
Simon Kelley74d4fcd2021-03-15 21:59:51 +0000822 safe_strncpy(interface, source, IF_NAMESIZE);
823 source = interface_opt;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000824#else
825 return _("interface binding not supported");
826#endif
827 }
828 }
829
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100830 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100831 {
832 addr->in.sin_port = htons(serv_port);
833 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
834#ifdef HAVE_SOCKADDR_SA_LEN
835 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
836#endif
837 source_addr->in.sin_addr.s_addr = INADDR_ANY;
838 source_addr->in.sin_port = htons(daemon->query_port);
839
840 if (source)
841 {
842 if (flags)
843 *flags |= SERV_HAS_SOURCE;
844 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100845 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100846 {
847#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000848 if (interface_opt)
849 return _("interface can only be specified once");
850
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100851 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200852 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100853#else
854 return _("interface binding not supported");
855#endif
856 }
857 }
858 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100859 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
860 {
861 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
862 return _("bad interface name");
863
864 addr->in6.sin6_port = htons(serv_port);
865 addr->in6.sin6_scope_id = scope_index;
866 source_addr->in6.sin6_addr = in6addr_any;
867 source_addr->in6.sin6_port = htons(daemon->query_port);
868 source_addr->in6.sin6_scope_id = 0;
869 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
870 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
871#ifdef HAVE_SOCKADDR_SA_LEN
872 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
873#endif
874 if (source)
875 {
876 if (flags)
877 *flags |= SERV_HAS_SOURCE;
878 source_addr->in6.sin6_port = htons(source_port);
879 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
880 {
881#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000882 if (interface_opt)
883 return _("interface can only be specified once");
884
885 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200886 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100887#else
888 return _("interface binding not supported");
889#endif
890 }
891 }
892 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100893 else
894 return _("bad address");
895
896 return NULL;
897}
898
Simon Kelleyde73a492014-02-17 21:43:27 +0000899static struct server *add_rev4(struct in_addr addr, int msize)
900{
901 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000902 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000903 char *p;
904
905 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000906 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
907
908 switch (msize)
909 {
910 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100911 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000912 /* fall through */
913 case 24:
914 p += sprintf(p, "%d.", (a >> 8) & 0xff);
915 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000916 case 16:
917 p += sprintf(p, "%d.", (a >> 16) & 0xff);
918 /* fall through */
919 case 8:
920 p += sprintf(p, "%d.", (a >> 24) & 0xff);
921 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000922 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000923 free(serv->domain);
924 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000925 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000926 }
927
928 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000929
930 serv->flags = SERV_HAS_DOMAIN;
931 serv->next = daemon->servers;
932 daemon->servers = serv;
933
934 return serv;
935
936}
937
938static struct server *add_rev6(struct in6_addr *addr, int msize)
939{
940 struct server *serv = opt_malloc(sizeof(struct server));
941 char *p;
942 int i;
943
944 memset(serv, 0, sizeof(struct server));
945 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
946
947 for (i = msize-1; i >= 0; i -= 4)
948 {
949 int dig = ((unsigned char *)addr)[i>>3];
950 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
951 }
952 p += sprintf(p, "ip6.arpa");
953
954 serv->flags = SERV_HAS_DOMAIN;
955 serv->next = daemon->servers;
956 daemon->servers = serv;
957
958 return serv;
959}
960
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000961#ifdef HAVE_DHCP
962
963static int is_tag_prefix(char *arg)
964{
965 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
966 return 1;
967
968 return 0;
969}
970
971static char *set_prefix(char *arg)
972{
973 if (strstr(arg, "set:") == arg)
974 return arg+4;
975
976 return arg;
977}
978
Simon Kelley52ec7832020-02-07 21:05:54 +0000979static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000980{
981 struct dhcp_netid *tt;
982 tt = opt_malloc(sizeof (struct dhcp_netid));
983 tt->net = opt_string_alloc(net);
984 tt->next = next;
985 return tt;
986}
987
988static void dhcp_netid_free(struct dhcp_netid *nid)
989{
990 while (nid)
991 {
992 struct dhcp_netid *tmp = nid;
993 nid = nid->next;
994 free(tmp->net);
995 free(tmp);
996 }
997}
998
999/* Parse one or more tag:s before parameters.
1000 * Moves arg to the end of tags. */
1001static struct dhcp_netid * dhcp_tags(char **arg)
1002{
1003 struct dhcp_netid *id = NULL;
1004
1005 while (is_tag_prefix(*arg))
1006 {
1007 char *comma = split(*arg);
1008 id = dhcp_netid_create((*arg)+4, id);
1009 *arg = comma;
1010 };
1011 if (!*arg)
1012 {
1013 dhcp_netid_free(id);
1014 id = NULL;
1015 }
1016 return id;
1017}
1018
1019static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1020{
1021 while (netid)
1022 {
1023 struct dhcp_netid_list *tmplist = netid;
1024 netid = netid->next;
1025 dhcp_netid_free(tmplist->list);
1026 free(tmplist);
1027 }
1028}
1029
1030static void dhcp_config_free(struct dhcp_config *config)
1031{
1032 if (config)
1033 {
1034 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001035
Petr Menšík59e47032018-11-02 22:39:39 +00001036 while (hwaddr)
1037 {
1038 struct hwaddr_config *tmp = hwaddr;
1039 hwaddr = hwaddr->next;
1040 free(tmp);
1041 }
Simon Kelley137286e2020-02-06 22:09:30 +00001042
Petr Menšík59e47032018-11-02 22:39:39 +00001043 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001044 dhcp_netid_free(config->filter);
1045
Petr Menšík59e47032018-11-02 22:39:39 +00001046 if (config->flags & CONFIG_CLID)
1047 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001048
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001049#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001050 if (config->flags & CONFIG_ADDR6)
1051 {
1052 struct addrlist *addr, *tmp;
1053
1054 for (addr = config->addr6; addr; addr = tmp)
1055 {
1056 tmp = addr->next;
1057 free(addr);
1058 }
1059 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001060#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001061
Petr Menšík59e47032018-11-02 22:39:39 +00001062 free(config);
1063 }
1064}
1065
1066static void dhcp_context_free(struct dhcp_context *ctx)
1067{
1068 if (ctx)
1069 {
1070 dhcp_netid_free(ctx->filter);
1071 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001072#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001073 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001074#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001075 free(ctx);
1076 }
1077}
1078
1079static void dhcp_opt_free(struct dhcp_opt *opt)
1080{
1081 if (opt->flags & DHOPT_VENDOR)
1082 free(opt->u.vendor_class);
1083 dhcp_netid_free(opt->netid);
1084 free(opt->val);
1085 free(opt);
1086}
1087
1088
Simon Kelley832af0b2007-01-21 20:01:28 +00001089/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001090static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001091{
Simon Kelley824af852008-02-12 20:43:05 +00001092 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001093 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001094 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001095 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001096 u16 opt_len = 0;
1097 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001098 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001099
1100 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001101 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001102 new->netid = NULL;
1103 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001104 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001105
Simon Kelleyf2621c72007-04-29 19:47:21 +01001106 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001107 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001108 comma = split(arg);
1109
1110 for (cp = arg; *cp; cp++)
1111 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001112 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001113
1114 if (!*cp)
1115 {
1116 new->opt = atoi(arg);
1117 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001118 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001119 break;
1120 }
1121
1122 if (strstr(arg, "option:") == arg)
1123 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001124 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1125 {
1126 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1127 /* option:<optname> must follow tag and vendor string. */
1128 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1129 option_ok = 1;
1130 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001131 break;
1132 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001133#ifdef HAVE_DHCP6
1134 else if (strstr(arg, "option6:") == arg)
1135 {
1136 for (cp = arg+8; *cp; cp++)
1137 if (*cp < '0' || *cp > '9')
1138 break;
1139
1140 if (!*cp)
1141 {
1142 new->opt = atoi(arg+8);
1143 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001144 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001145 }
1146 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001147 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001148 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1149 {
1150 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1151 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1152 option_ok = 1;
1153 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001154 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001155 /* option6:<opt>|<optname> must follow tag and vendor string. */
1156 is6 = 1;
1157 break;
1158 }
1159#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001160 else if (strstr(arg, "vendor:") == arg)
1161 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001162 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1163 new->flags |= DHOPT_VENDOR;
1164 }
1165 else if (strstr(arg, "encap:") == arg)
1166 {
1167 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001168 new->flags |= DHOPT_ENCAPSULATE;
1169 }
Simon Kelley316e2732010-01-22 20:16:09 +00001170 else if (strstr(arg, "vi-encap:") == arg)
1171 {
1172 new->u.encap = atoi(arg+9);
1173 new->flags |= DHOPT_RFC3925;
1174 if (flags == DHOPT_MATCH)
1175 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001176 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001177 break;
1178 }
1179 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001180 else
1181 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001182 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001183 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1184 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001185 }
1186
1187 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001188 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001189
1190#ifdef HAVE_DHCP6
1191 if (is6)
1192 {
1193 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001194 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001195
1196 if (opt_len == 0 &&
1197 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001198 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001199 }
1200 else
1201#endif
1202 if (opt_len == 0 &&
1203 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001204 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001205
Simon Kelley316e2732010-01-22 20:16:09 +00001206 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001207 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001208 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001209
1210 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001211 {
1212 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001213 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001214 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001215 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001216 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001217 dots = 0;
1218 for (cp = comma; (c = *cp); cp++)
1219 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001220 {
1221 addrs++;
1222 is_dec = is_hex = 0;
1223 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001224 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001225 {
1226 digs++;
1227 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001228 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001229 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001230 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001231 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001232 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001233 if (cp == comma) /* leading / means a pathname */
1234 is_addr = 0;
1235 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001236 else if (c == '.')
1237 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001238 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001239 dots++;
1240 }
1241 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001242 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001243 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001244 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001245 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001246 {
1247 is_addr = 0;
1248 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001249 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001250 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001251 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001252 *cp = 0;
1253 }
1254 else
1255 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001256 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001257 (c >='a' && c <= 'f') ||
1258 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001259 {
1260 is_hex = 0;
1261 if (c != '[' && c != ']')
1262 is_addr6 = 0;
1263 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001264 }
Simon Kelley28866e92011-02-14 20:19:14 +00001265 else
1266 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001267
Simon Kelley28866e92011-02-14 20:19:14 +00001268 if (!found_dig)
1269 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001270
1271 if (!found_colon)
1272 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001273
1274#ifdef HAVE_DHCP6
1275 /* NTP server option takes hex, addresses or FQDN */
1276 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1277 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1278#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001279
Simon Kelleyf2621c72007-04-29 19:47:21 +01001280 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001281 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001282 {
1283 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001284
1285 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001286 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001287
1288 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001289 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001290 }
Simon Kelley28866e92011-02-14 20:19:14 +00001291 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001292 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1293 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001294
1295 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1296 {
1297 int val, fac = 1;
1298
1299 switch (comma[strlen(comma) - 1])
1300 {
Simon Kelley42243212012-07-20 15:19:18 +01001301 case 'w':
1302 case 'W':
1303 fac *= 7;
1304 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001305 case 'd':
1306 case 'D':
1307 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001308 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001309 case 'h':
1310 case 'H':
1311 fac *= 60;
1312 /* fall through */
1313 case 'm':
1314 case 'M':
1315 fac *= 60;
1316 /* fall through */
1317 case 's':
1318 case 'S':
1319 comma[strlen(comma) - 1] = 0;
1320 }
1321
1322 new->len = 4;
1323 new->val = opt_malloc(4);
1324 val = atoi(comma);
1325 *((int *)new->val) = htonl(val * fac);
1326 }
1327 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001328 {
1329 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001330 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001331 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1332 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001333 }
1334 else if (is_dec)
1335 {
1336 int i, val = atoi(comma);
1337 /* assume numeric arg is 1 byte except for
1338 options where it is known otherwise.
1339 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001340 if (opt_len != 0)
1341 new->len = opt_len;
1342 else if (val & 0xffff0000)
1343 new->len = 4;
1344 else if (val & 0xff00)
1345 new->len = 2;
1346 else
1347 new->len = 1;
1348
Simon Kelley832af0b2007-01-21 20:01:28 +00001349 if (lenchar == 'b')
1350 new->len = 1;
1351 else if (lenchar == 's')
1352 new->len = 2;
1353 else if (lenchar == 'i')
1354 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001355
Simon Kelley824af852008-02-12 20:43:05 +00001356 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001357 for (i=0; i<new->len; i++)
1358 new->val[i] = val>>((new->len - i - 1)*8);
1359 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001360 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001361 {
1362 struct in_addr in;
1363 unsigned char *op;
1364 char *slash;
1365 /* max length of address/subnet descriptor is five bytes,
1366 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001367 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001368 new->flags |= DHOPT_ADDR;
1369
Simon Kelley572b41e2011-02-18 18:11:18 +00001370 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1371 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001372 {
Simon Kelley6b010842007-02-12 20:32:07 +00001373 *(op++) = 1; /* RFC 3361 "enc byte" */
1374 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001375 }
1376 while (addrs--)
1377 {
1378 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001379 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001380 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001381 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001382 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001383 if (!slash)
1384 {
1385 memcpy(op, &in, INADDRSZ);
1386 op += INADDRSZ;
1387 }
1388 else
1389 {
1390 unsigned char *p = (unsigned char *)&in;
1391 int netsize = atoi(slash);
1392 *op++ = netsize;
1393 if (netsize > 0)
1394 *op++ = *p++;
1395 if (netsize > 8)
1396 *op++ = *p++;
1397 if (netsize > 16)
1398 *op++ = *p++;
1399 if (netsize > 24)
1400 *op++ = *p++;
1401 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1402 }
1403 }
1404 new->len = op - new->val;
1405 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001406 else if (is_addr6 && is6)
1407 {
1408 unsigned char *op;
1409 new->val = op = opt_malloc(16 * addrs);
1410 new->flags |= DHOPT_ADDR6;
1411 while (addrs--)
1412 {
1413 cp = comma;
1414 comma = split(cp);
1415
1416 /* check for [1234::7] */
1417 if (*cp == '[')
1418 cp++;
1419 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1420 cp[strlen(cp)-1] = 0;
1421
1422 if (inet_pton(AF_INET6, cp, op))
1423 {
1424 op += IN6ADDRSZ;
1425 continue;
1426 }
Petr Menšík59e47032018-11-02 22:39:39 +00001427
1428 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001429 }
1430 new->len = op - new->val;
1431 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001432 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001433 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001434 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001435 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001436 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001437 {
1438 /* dns search, RFC 3397, or SIP, RFC 3361 */
1439 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001440 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001441 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001442 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001443
1444 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001445 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001446
1447 while (arg && *arg)
1448 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001449 char *in, *dom = NULL;
1450 size_t domlen = 1;
1451 /* Allow "." as an empty domain */
1452 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001453 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001454 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001455 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001456
Simon Kelleyc52e1892010-06-07 22:01:39 +01001457 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001458 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001459
1460 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001461 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001462 {
1463 memcpy(newp, m, header_size + len);
1464 free(m);
1465 }
Simon Kelley824af852008-02-12 20:43:05 +00001466 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001467 p = m + header_size;
1468 q = p + len;
1469
1470 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001471 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001472 {
1473 unsigned char *cp = q++;
1474 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001475 for (j = 0; *in && (*in != '.'); in++, j++)
1476 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001477 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001478 if (*in)
1479 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001480 }
1481 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001482 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001483
Simon Kelley832af0b2007-01-21 20:01:28 +00001484 /* Now tail-compress using earlier names. */
1485 newlen = q - p;
1486 for (tail = p + len; *tail; tail += (*tail) + 1)
1487 for (r = p; r - p < (int)len; r += (*r) + 1)
1488 if (strcmp((char *)r, (char *)tail) == 0)
1489 {
1490 PUTSHORT((r - p) | 0xc000, tail);
1491 newlen = tail - p;
1492 goto end;
1493 }
1494 end:
1495 len = newlen;
1496
1497 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001498 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001499 }
1500
1501 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001502 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001503 m[0] = 0;
1504 new->len = (int) len + header_size;
1505 new->val = m;
1506 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001507#ifdef HAVE_DHCP6
1508 else if (comma && (opt_len & OT_CSTRING))
1509 {
1510 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001511 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001512 unsigned char *p, *newp;
1513
Simon Kelley40ef23b2012-03-13 21:59:28 +00001514 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001515 if (comma[i] == ',')
1516 commas++;
1517
1518 newp = opt_malloc(strlen(comma)+(2*commas));
1519 p = newp;
1520 arg = comma;
1521 comma = split(arg);
1522
1523 while (arg && *arg)
1524 {
1525 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001526 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001527 PUTSHORT(len, p);
1528 memcpy(p, arg, len);
1529 p += len;
1530
1531 arg = comma;
1532 comma = split(arg);
1533 }
1534
1535 new->val = newp;
1536 new->len = p - newp;
1537 }
1538 else if (comma && (opt_len & OT_RFC1035_NAME))
1539 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001540 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001541 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001542 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001543 arg = comma;
1544 comma = split(arg);
1545
1546 while (arg && *arg)
1547 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001548 char *dom = canonicalise_opt(arg);
1549 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001550 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001551
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001552 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001553
1554 if (p)
1555 {
1556 memcpy(newp, p, len);
1557 free(p);
1558 }
1559
1560 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001561 q = p + len;
1562 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001563 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001564 if (is6 && new->opt == OPTION6_NTP_SERVER)
1565 {
1566 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1567 PUTSHORT(end - q - 2, q);
1568 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001569 len = end - p;
1570 free(dom);
1571
Simon Kelley4cb1b322012-02-06 14:30:41 +00001572 arg = comma;
1573 comma = split(arg);
1574 }
1575
Simon Kelley18f0fb02012-03-31 21:18:55 +01001576 new->val = p;
1577 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001578 }
1579#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001580 else
1581 {
1582 new->len = strlen(comma);
1583 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001584 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001585 new->flags |= DHOPT_STRING;
1586 }
1587 }
1588 }
1589
Simon Kelley4cb1b322012-02-06 14:30:41 +00001590 if (!is6 &&
1591 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001592 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001593 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001594 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001595
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001596 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001597 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001598 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1599 !new->netid ||
1600 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001601 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001602
1603 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001604 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001605 new->next = daemon->dhcp_match6;
1606 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001607 }
1608 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001609 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001610 new->next = daemon->dhcp_match;
1611 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001612 }
Simon Kelley824af852008-02-12 20:43:05 +00001613 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001614 else if (is6)
1615 {
1616 new->next = daemon->dhcp_opts6;
1617 daemon->dhcp_opts6 = new;
1618 }
1619 else
1620 {
1621 new->next = daemon->dhcp_opts;
1622 daemon->dhcp_opts = new;
1623 }
1624
1625 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001626on_error:
1627 dhcp_opt_free(new);
1628 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001629}
1630
Simon Kelley7622fc02009-06-04 20:32:05 +01001631#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001632
Simon Kelley28866e92011-02-14 20:19:14 +00001633void set_option_bool(unsigned int opt)
1634{
Petr Menšík24b87602018-10-24 22:30:18 +01001635 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001636}
1637
Simon Kelley2b5bae92012-06-26 16:55:23 +01001638void reset_option_bool(unsigned int opt)
1639{
Petr Menšík24b87602018-10-24 22:30:18 +01001640 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001641}
1642
Petr Menšík59e47032018-11-02 22:39:39 +00001643static void server_list_free(struct server *list)
1644{
1645 while (list)
1646 {
1647 struct server *tmp = list;
1648 list = list->next;
1649 free(tmp);
1650 }
1651}
1652
Simon Kelley7b1eae42014-02-20 13:43:28 +00001653static 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 +01001654{
1655 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001656 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001657
Simon Kelley832af0b2007-01-21 20:01:28 +00001658 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001659 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001660
Simon Kelley1a6bca82008-07-11 11:11:42 +01001661 for (i=0; usage[i].opt != 0; i++)
1662 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001663 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001664 int rept = usage[i].rept;
1665
Simon Kelley28866e92011-02-14 20:19:14 +00001666 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001667 {
1668 /* command line */
1669 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001670 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001671 if (rept == ARG_ONE)
1672 usage[i].rept = ARG_USED_CL;
1673 }
1674 else
1675 {
1676 /* allow file to override command line */
1677 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001678 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001679 if (rept == ARG_USED_CL || rept == ARG_ONE)
1680 usage[i].rept = ARG_USED_FILE;
1681 }
1682
1683 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1684 {
Simon Kelley28866e92011-02-14 20:19:14 +00001685 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001686 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001687 }
1688
1689 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001690 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001691
Simon Kelley849a8352006-06-09 21:02:31 +01001692 switch (option)
1693 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001694 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001695 {
Simon Kelley824af852008-02-12 20:43:05 +00001696 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001697 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001698 {
Simon Kelley28866e92011-02-14 20:19:14 +00001699 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001700 free(file);
1701 }
Simon Kelley849a8352006-06-09 21:02:31 +01001702 break;
1703 }
1704
Simon Kelleyf2621c72007-04-29 19:47:21 +01001705 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001706 {
1707 DIR *dir_stream;
1708 struct dirent *ent;
1709 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001710 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001711 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001712 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001713 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001714
Simon Kelley1f15b812009-10-13 17:49:32 +01001715 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001716 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001717 break;
1718
Simon Kelley1f15b812009-10-13 17:49:32 +01001719 for (arg = comma; arg; arg = comma)
1720 {
1721 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001722 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001723 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001724 li = opt_malloc(sizeof(struct list));
1725 if (*arg == '*')
1726 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001727 /* "*" with no suffix is a no-op */
1728 if (arg[1] == 0)
1729 free(li);
1730 else
1731 {
1732 li->next = match_suffix;
1733 match_suffix = li;
1734 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001735 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001736 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001737 }
1738 else
1739 {
1740 li->next = ignore_suffix;
1741 ignore_suffix = li;
1742 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001743 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001744 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001745 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001746 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001747
Simon Kelley849a8352006-06-09 21:02:31 +01001748 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001749 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001750
Simon Kelley849a8352006-06-09 21:02:31 +01001751 while ((ent = readdir(dir_stream)))
1752 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001753 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001754 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001755
1756 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001757 if (len == 0 ||
1758 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001759 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1760 ent->d_name[0] == '.')
1761 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001762
Simon Kelley3e1551a2014-09-09 21:46:07 +01001763 if (match_suffix)
1764 {
1765 for (li = match_suffix; li; li = li->next)
1766 {
1767 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001768 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001769 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001770 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001771 break;
1772 }
1773 if (!li)
1774 continue;
1775 }
1776
Simon Kelley1f15b812009-10-13 17:49:32 +01001777 for (li = ignore_suffix; li; li = li->next)
1778 {
1779 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001780 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001781 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001782 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001783 break;
1784 }
1785 if (li)
1786 continue;
1787
Simon Kelley824af852008-02-12 20:43:05 +00001788 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001789 strcpy(path, directory);
1790 strcat(path, "/");
1791 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001792
Simon Kelley39595cf2013-02-04 21:40:07 +00001793 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001794 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001795 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001796
Simon Kelley39595cf2013-02-04 21:40:07 +00001797 /* only reg files allowed. */
1798 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001799 {
1800 /* sort files into order. */
1801 struct list **up, *new = opt_malloc(sizeof(struct list));
1802 new->name = path;
1803
1804 for (up = &files, li = files; li; up = &li->next, li = li->next)
1805 if (strcmp(li->name, path) >=0)
1806 break;
1807
1808 new->next = li;
1809 *up = new;
1810 }
1811
Simon Kelley849a8352006-06-09 21:02:31 +01001812 }
Simon Kelleyab538832020-01-10 20:44:48 +00001813
1814 for (li = files; li; li = li->next)
1815 one_file(li->name, 0);
1816
Simon Kelley849a8352006-06-09 21:02:31 +01001817 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001818 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001819 for(; ignore_suffix; ignore_suffix = li)
1820 {
1821 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001822 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001823 free(ignore_suffix);
1824 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001825 for(; match_suffix; match_suffix = li)
1826 {
1827 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001828 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001829 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001830 }
Simon Kelleyab538832020-01-10 20:44:48 +00001831 for(; files; files = li)
1832 {
1833 li = files->next;
1834 free(files->name);
1835 free(files);
1836 }
Simon Kelley849a8352006-06-09 21:02:31 +01001837 break;
1838 }
1839
Simon Kelleyed4c0762013-10-08 20:46:34 +01001840 case LOPT_ADD_SBNET: /* --add-subnet */
1841 set_option_bool(OPT_CLIENT_SUBNET);
1842 if (arg)
1843 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001844 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001845 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001846
1847 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1848 if ((end = split_chr(arg, '/')))
1849 {
1850 /* has subnet+len */
1851 err = parse_mysockaddr(arg, &new->addr);
1852 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001853 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001854 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001855 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001856 new->addr_used = 1;
1857 }
1858 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001859 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001860
1861 daemon->add_subnet4 = new;
1862
Ed Bardsleya7369be2015-08-05 21:17:18 +01001863 if (comma)
1864 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001865 new = opt_malloc(sizeof(struct mysubnet));
1866 if ((end = split_chr(comma, '/')))
1867 {
1868 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001869 err = parse_mysockaddr(comma, &new->addr);
1870 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001871 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001872 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001873 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001874 new->addr_used = 1;
1875 }
1876 else
1877 {
1878 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001879 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001880 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001881
1882 daemon->add_subnet6 = new;
1883 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001884 }
1885 break;
1886
Simon Kelleyad094272012-08-10 17:10:54 +01001887 case '1': /* --enable-dbus */
1888 set_option_bool(OPT_DBUS);
1889 if (arg)
1890 daemon->dbus_name = opt_string_alloc(arg);
1891 else
1892 daemon->dbus_name = DNSMASQ_SERVICE;
1893 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001894
1895 case LOPT_UBUS: /* --enable-ubus */
1896 set_option_bool(OPT_UBUS);
1897 if (arg)
1898 daemon->ubus_name = opt_string_alloc(arg);
1899 else
1900 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1901 break;
1902
Simon Kelleyf2621c72007-04-29 19:47:21 +01001903 case '8': /* --log-facility */
1904 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001905 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001906 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001907 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001908 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001909#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001910 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001911#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001912 for (i = 0; facilitynames[i].c_name; i++)
1913 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1914 break;
1915
1916 if (facilitynames[i].c_name)
1917 daemon->log_fac = facilitynames[i].c_val;
1918 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001919 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001920#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001921 }
1922 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001923
Simon Kelleyf2621c72007-04-29 19:47:21 +01001924 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001925 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001926 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001927
Simon Kelleyf2621c72007-04-29 19:47:21 +01001928 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001929 {
Simon Kelley824af852008-02-12 20:43:05 +00001930 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001931 struct resolvc *new, *list = daemon->resolv_files;
1932
1933 if (list && list->is_default)
1934 {
1935 /* replace default resolv file - possibly with nothing */
1936 if (name)
1937 {
1938 list->is_default = 0;
1939 list->name = name;
1940 }
1941 else
1942 list = NULL;
1943 }
1944 else if (name)
1945 {
Simon Kelley824af852008-02-12 20:43:05 +00001946 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001947 new->next = list;
1948 new->name = name;
1949 new->is_default = 0;
1950 new->mtime = 0;
1951 new->logged = 0;
1952 list = new;
1953 }
1954 daemon->resolv_files = list;
1955 break;
1956 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001957
1958 case LOPT_SERVERS_FILE:
1959 daemon->servers_file = opt_string_alloc(arg);
1960 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001961
Simon Kelleyf2621c72007-04-29 19:47:21 +01001962 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001963 {
1964 int pref = 1;
1965 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001966 char *name, *target = NULL;
1967
Simon Kelleyf2621c72007-04-29 19:47:21 +01001968 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001969 {
1970 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001971 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001972 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001973 }
1974
Simon Kelley1f15b812009-10-13 17:49:32 +01001975 if (!(name = canonicalise_opt(arg)) ||
1976 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001977 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001978
Simon Kelley824af852008-02-12 20:43:05 +00001979 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001980 new->next = daemon->mxnames;
1981 daemon->mxnames = new;
1982 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001983 new->name = name;
1984 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001985 new->weight = pref;
1986 break;
1987 }
1988
Simon Kelleyf2621c72007-04-29 19:47:21 +01001989 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001990 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001991 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001992 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001993
Simon Kelley6b173352018-05-08 18:32:14 +01001994 case LOPT_DUMPFILE: /* --dumpfile */
1995 daemon->dump_file = opt_string_alloc(arg);
1996 break;
1997
1998 case LOPT_DUMPMASK: /* --dumpmask */
1999 daemon->dump_mask = strtol(arg, NULL, 0);
2000 break;
2001
Simon Kelley7622fc02009-06-04 20:32:05 +01002002#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002003 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00002004 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002005 break;
2006
Simon Kelleyc72daea2012-01-05 21:33:27 +00002007 /* Sorry about the gross pre-processor abuse */
2008 case '6': /* --dhcp-script */
2009 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002010# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002011 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002012# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002013 if (option == LOPT_LUASCRIPT)
2014# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002015 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002016# else
2017 daemon->luascript = opt_string_alloc(arg);
2018# endif
2019 else
2020 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002021# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002022 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002023#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002024
Simon Kelley70d18732015-01-31 19:59:29 +00002025 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2026 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2027 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2028 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2029 case LOPT_HOST_INOTIFY: /* --hostsdir */
2030 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002031 {
Simon Kelley824af852008-02-12 20:43:05 +00002032 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002033 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002034 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002035 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002036 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002037 if (option == 'H')
2038 {
2039 new->next = daemon->addn_hosts;
2040 daemon->addn_hosts = new;
2041 }
2042 else if (option == LOPT_DHCP_HOST)
2043 {
2044 new->next = daemon->dhcp_hosts_file;
2045 daemon->dhcp_hosts_file = new;
2046 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002047 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002048 {
2049 new->next = daemon->dhcp_opts_file;
2050 daemon->dhcp_opts_file = new;
2051 }
Simon Kelley70d18732015-01-31 19:59:29 +00002052 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002053 {
Simon Kelley70d18732015-01-31 19:59:29 +00002054 new->next = daemon->dynamic_dirs;
2055 daemon->dynamic_dirs = new;
2056 if (option == LOPT_DHCP_INOTIFY)
2057 new->flags |= AH_DHCP_HST;
2058 else if (option == LOPT_DHOPT_INOTIFY)
2059 new->flags |= AH_DHCP_OPT;
2060 else if (option == LOPT_HOST_INOTIFY)
2061 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002062 }
2063
Simon Kelley849a8352006-06-09 21:02:31 +01002064 break;
2065 }
2066
Simon Kelley4f7b3042012-11-28 21:27:02 +00002067 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002068 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002069
Simon Kelley4f7b3042012-11-28 21:27:02 +00002070 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002071
2072 while ((arg = comma))
2073 {
2074 struct iname *new = opt_malloc(sizeof(struct iname));
2075 comma = split(arg);
2076 new->name = NULL;
2077 unhide_metas(arg);
2078 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2079 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002080 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2081 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002082 else
2083 {
2084 char *fam = split_chr(arg, '/');
2085 new->name = opt_string_alloc(arg);
2086 new->addr.sa.sa_family = 0;
2087 if (fam)
2088 {
2089 if (strcmp(fam, "4") == 0)
2090 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002091 else if (strcmp(fam, "6") == 0)
2092 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002093 else
Petr Menšík59e47032018-11-02 22:39:39 +00002094 {
2095 free(new->name);
2096 ret_err_free(gen_err, new);
2097 }
Simon Kelley08933472018-10-05 16:34:35 +01002098 }
2099 }
2100 new->next = daemon->authinterface;
2101 daemon->authinterface = new;
2102 };
Simon Kelley429798f2012-12-10 20:45:53 +00002103
Simon Kelley4f7b3042012-11-28 21:27:02 +00002104 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002105
2106 case LOPT_AUTHSFS: /* --auth-sec-servers */
2107 {
2108 struct name_list *new;
2109
2110 do {
2111 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002112 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002113 new->name = opt_string_alloc(arg);
2114 new->next = daemon->secondary_forward_server;
2115 daemon->secondary_forward_server = new;
2116 arg = comma;
2117 } while (arg);
2118 break;
2119 }
2120
Simon Kelley4f7b3042012-11-28 21:27:02 +00002121 case LOPT_AUTHZONE: /* --auth-zone */
2122 {
2123 struct auth_zone *new;
2124
2125 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002126
Simon Kelley429798f2012-12-10 20:45:53 +00002127 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002128 new->domain = opt_string_alloc(arg);
2129 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002130 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002131 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002132 new->next = daemon->auth_zones;
2133 daemon->auth_zones = new;
2134
2135 while ((arg = comma))
2136 {
2137 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002138 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002139 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002140 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002141 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002142
2143 comma = split(arg);
2144 prefix = split_chr(arg, '/');
2145
2146 if (prefix && !atoi_check(prefix, &prefixlen))
2147 ret_err(gen_err);
2148
Mathias Kresin094bfae2016-07-24 14:15:22 +01002149 if (strstr(arg, "exclude:") == arg)
2150 {
2151 is_exclude = 1;
2152 arg = arg+8;
2153 }
2154
Simon Kelleycc921df2019-01-02 22:48:59 +00002155 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002156 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002157 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002158 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002159 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002160 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002161 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002162 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002163 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002164 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002165 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002166 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002167 else
2168 {
2169 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2170 name->name = opt_string_alloc(arg);
2171 name->flags = AUTH4 | AUTH6;
2172 name->next = new->interface_names;
2173 new->interface_names = name;
2174 if (prefix)
2175 {
2176 if (prefixlen == 4)
2177 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002178 else if (prefixlen == 6)
2179 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002180 else
2181 ret_err(gen_err);
2182 }
2183 }
2184
2185 if (subnet)
2186 {
2187 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002188
2189 if (is_exclude)
2190 {
2191 subnet->next = new->exclude;
2192 new->exclude = subnet;
2193 }
2194 else
2195 {
2196 subnet->next = new->subnet;
2197 new->subnet = subnet;
2198 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002199 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002200 }
2201 break;
2202 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002203
Simon Kelley4f7b3042012-11-28 21:27:02 +00002204 case LOPT_AUTHSOA: /* --auth-soa */
2205 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002206 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002207 if (comma)
2208 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002209 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002210 arg = comma;
2211 comma = split(arg);
2212 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002213 for (cp = daemon->hostmaster; *cp; cp++)
2214 if (*cp == '@')
2215 *cp = '.';
2216
Simon Kelley4f7b3042012-11-28 21:27:02 +00002217 if (comma)
2218 {
2219 arg = comma;
2220 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002221 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002222 if (comma)
2223 {
2224 arg = comma;
2225 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002226 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002227 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002228 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002229 }
2230 }
2231 }
2232
2233 break;
2234
Simon Kelley2bb73af2013-04-24 17:38:19 +01002235 case 's': /* --domain */
2236 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002237 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002238 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002239 else
Simon Kelley9009d742008-11-14 20:04:27 +00002240 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002241 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002242 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002243 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002244 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002245 else
2246 {
Simon Kelley9009d742008-11-14 20:04:27 +00002247 if (comma)
2248 {
Simon Kelley429798f2012-12-10 20:45:53 +00002249 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002250 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002251
Simon Kelley48fd1c42013-04-25 09:49:38 +01002252 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002253 new->indexed = 0;
2254
Simon Kelley9009d742008-11-14 20:04:27 +00002255 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002256 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002257 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002258 int msize;
2259
Simon Kelley28866e92011-02-14 20:19:14 +00002260 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002261 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002262 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002263 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002264 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002265 int mask = (1 << (32 - msize)) - 1;
2266 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002267 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2268 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002269 if (arg)
2270 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002271 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002272 {
2273 if (!(new->prefix = canonicalise_opt(arg)) ||
2274 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002275 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002276 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002277 else if (strcmp(arg, "local") != 0 ||
2278 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002279 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002280 else
2281 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002282 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002283 local=/xxx.yyy.zzz.in-addr.arpa/ */
2284 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002285 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002286 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002287
Simon Kelleyde73a492014-02-17 21:43:27 +00002288 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002289
2290 /* local=/<domain>/ */
2291 serv = opt_malloc(sizeof(struct server));
2292 memset(serv, 0, sizeof(struct server));
2293 serv->domain = d;
2294 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2295 serv->next = daemon->servers;
2296 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002297 }
2298 }
Simon Kelley9009d742008-11-14 20:04:27 +00002299 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002300 else if (inet_pton(AF_INET6, comma, &new->start6))
2301 {
2302 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2303 u64 addrpart = addr6part(&new->start6);
2304 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002305
Simon Kelleyd74942a2012-02-07 20:51:56 +00002306 /* prefix==64 overflows the mask calculation above */
2307 if (msize == 64)
2308 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002309
Simon Kelleyd74942a2012-02-07 20:51:56 +00002310 new->end6 = new->start6;
2311 setaddr6part(&new->start6, addrpart & ~mask);
2312 setaddr6part(&new->end6, addrpart | mask);
2313
2314 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002315 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002316 else if (arg)
2317 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002318 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002319 {
2320 if (!(new->prefix = canonicalise_opt(arg)) ||
2321 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002322 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002323 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002324 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002325 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002326 else
2327 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002328 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002329 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002330 struct server *serv = add_rev6(&new->start6, msize);
2331 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002332
2333 /* local=/<domain>/ */
2334 serv = opt_malloc(sizeof(struct server));
2335 memset(serv, 0, sizeof(struct server));
2336 serv->domain = d;
2337 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2338 serv->next = daemon->servers;
2339 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002340 }
2341 }
2342 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002343 else
Petr Menšík59e47032018-11-02 22:39:39 +00002344 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002345 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002346 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002347 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002348 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002349 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002350 prefstr = split(arg);
2351
Simon Kelleyd74942a2012-02-07 20:51:56 +00002352 if (inet_pton(AF_INET, comma, &new->start))
2353 {
2354 new->is6 = 0;
2355 if (!arg)
2356 new->end.s_addr = new->start.s_addr;
2357 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002358 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002359 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002360 else if (inet_pton(AF_INET6, comma, &new->start6))
2361 {
2362 new->is6 = 1;
2363 if (!arg)
2364 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2365 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002366 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002367 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002368 else
Petr Menšík59e47032018-11-02 22:39:39 +00002369 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002370
2371 if (option != 's' && prefstr)
2372 {
2373 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2374 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002375 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002376 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002377 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002378
2379 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002380 if (option == 's')
2381 {
2382 new->next = daemon->cond_domain;
2383 daemon->cond_domain = new;
2384 }
2385 else
2386 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002387 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002388 new->next = daemon->synth_domains;
2389 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002390 if (new->prefix &&
2391 (star = strrchr(new->prefix, '*'))
2392 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002393 {
2394 *star = 0;
2395 new->indexed = 1;
2396 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002397 }
Simon Kelley9009d742008-11-14 20:04:27 +00002398 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002399 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002400 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002401 else
2402 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002403 }
2404 }
Simon Kelley849a8352006-06-09 21:02:31 +01002405 break;
2406
Simon Kelley1e505122016-01-25 21:29:23 +00002407 case LOPT_CPE_ID: /* --add-dns-client */
2408 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002409 daemon->dns_client_id = opt_string_alloc(arg);
2410 break;
2411
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002412 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002413 if (!arg)
2414 set_option_bool(OPT_ADD_MAC);
2415 else
2416 {
2417 unhide_metas(arg);
2418 if (strcmp(arg, "base64") == 0)
2419 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002420 else if (strcmp(arg, "text") == 0)
2421 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002422 else
2423 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002424 }
2425 break;
2426
Simon Kelleyf2621c72007-04-29 19:47:21 +01002427 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002428 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002429 break;
2430
Simon Kelleyf2621c72007-04-29 19:47:21 +01002431 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002432 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002433 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002434 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002435
Simon Kelley7622fc02009-06-04 20:32:05 +01002436#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002437 case LOPT_SCRIPTUSR: /* --scriptuser */
2438 daemon->scriptuser = opt_string_alloc(arg);
2439 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002440#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002441
Simon Kelleyf2621c72007-04-29 19:47:21 +01002442 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002443 do {
Simon Kelley824af852008-02-12 20:43:05 +00002444 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002445 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002446 new->next = daemon->if_names;
2447 daemon->if_names = new;
2448 /* new->name may be NULL if someone does
2449 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002450 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002451 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002452 arg = comma;
2453 } while (arg);
2454 break;
2455
Simon Kelley2937f8a2013-07-29 19:49:07 +01002456 case LOPT_TFTP: /* --enable-tftp */
2457 set_option_bool(OPT_TFTP);
2458 if (!arg)
2459 break;
2460 /* fall through */
2461
Simon Kelleyf2621c72007-04-29 19:47:21 +01002462 case 'I': /* --except-interface */
2463 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002464 do {
Simon Kelley824af852008-02-12 20:43:05 +00002465 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002466 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002467 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002468 if (option == 'I')
2469 {
2470 new->next = daemon->if_except;
2471 daemon->if_except = new;
2472 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002473 else if (option == LOPT_TFTP)
2474 {
2475 new->next = daemon->tftp_interfaces;
2476 daemon->tftp_interfaces = new;
2477 }
Simon Kelley849a8352006-06-09 21:02:31 +01002478 else
2479 {
2480 new->next = daemon->dhcp_except;
2481 daemon->dhcp_except = new;
2482 }
2483 arg = comma;
2484 } while (arg);
2485 break;
2486
Simon Kelleyf2621c72007-04-29 19:47:21 +01002487 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002488 case LOPT_IGNORE_ADDR: /* --ignore-address */
2489 {
Simon Kelley849a8352006-06-09 21:02:31 +01002490 struct in_addr addr;
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002491 int prefix = 32;
Simon Kelley849a8352006-06-09 21:02:31 +01002492 unhide_metas(arg);
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002493
2494 if (!arg ||
2495 ((comma = split_chr(arg, '/')) && !atoi_check(comma, &prefix)) ||
2496 (inet_pton(AF_INET, arg, &addr) != 1))
2497 ret_err(gen_err); /* error */
2498 else
Simon Kelley849a8352006-06-09 21:02:31 +01002499 {
Simon Kelley824af852008-02-12 20:43:05 +00002500 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002501 if (option == 'B')
2502 {
2503 baddr->next = daemon->bogus_addr;
2504 daemon->bogus_addr = baddr;
2505 }
2506 else
2507 {
2508 baddr->next = daemon->ignore_addr;
2509 daemon->ignore_addr = baddr;
2510 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002511 baddr->mask.s_addr = htonl(~((1 << (32 - prefix)) - 1));
2512 baddr->addr.s_addr = addr.s_addr & baddr->mask.s_addr;
Simon Kelley849a8352006-06-09 21:02:31 +01002513 }
Simon Kelley9eaa91b2021-03-17 20:31:06 +00002514 break;
2515 }
Simon Kelley849a8352006-06-09 21:02:31 +01002516
Simon Kelleyf2621c72007-04-29 19:47:21 +01002517 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002518 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002519 do {
Simon Kelley824af852008-02-12 20:43:05 +00002520 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002521 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002522 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002523 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002524 {
2525 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002526 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002527#ifdef HAVE_SOCKADDR_SA_LEN
2528 new->addr.in.sin_len = sizeof(new->addr.in);
2529#endif
2530 }
Simon Kelley849a8352006-06-09 21:02:31 +01002531 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2532 {
2533 new->addr.sa.sa_family = AF_INET6;
2534 new->addr.in6.sin6_flowinfo = 0;
2535 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002536 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002537#ifdef HAVE_SOCKADDR_SA_LEN
2538 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2539#endif
2540 }
Simon Kelley849a8352006-06-09 21:02:31 +01002541 else
Petr Menšík59e47032018-11-02 22:39:39 +00002542 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002543
2544 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002545 if (option == 'a')
2546 {
2547 new->next = daemon->if_addrs;
2548 daemon->if_addrs = new;
2549 }
2550 else
2551 {
2552 new->next = daemon->auth_peers;
2553 daemon->auth_peers = new;
2554 }
Simon Kelley849a8352006-06-09 21:02:31 +01002555 arg = comma;
2556 } while (arg);
2557 break;
2558
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002559 case 'S': /* --server */
2560 case LOPT_LOCAL: /* --local */
2561 case 'A': /* --address */
2562 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002563 {
2564 struct server *serv, *newlist = NULL;
2565
2566 unhide_metas(arg);
2567
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002568 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002569 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002570 int rebind = !(*arg == '/');
2571 char *end = NULL;
2572 if (!rebind)
2573 arg++;
2574 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002575 {
2576 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002577 /* elide leading dots - they are implied in the search algorithm */
2578 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002579 /* # matches everything and becomes a zero length domain string */
2580 if (strcmp(arg, "#") == 0)
2581 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002582 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002583 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002584 serv = opt_malloc(sizeof(struct server));
2585 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002586 serv->next = newlist;
2587 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002588 serv->domain = domain;
2589 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002590 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002591 if (rebind)
2592 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002593 }
2594 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002595 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002596 }
2597 else
2598 {
Simon Kelley824af852008-02-12 20:43:05 +00002599 newlist = opt_malloc(sizeof(struct server));
2600 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002601#ifdef HAVE_LOOP
2602 newlist->uid = rand32();
2603#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002604 }
2605
Simon Kelley7b1eae42014-02-20 13:43:28 +00002606 if (servers_only && option == 'S')
2607 newlist->flags |= SERV_FROM_FILE;
2608
Simon Kelley849a8352006-06-09 21:02:31 +01002609 if (option == 'A')
2610 {
2611 newlist->flags |= SERV_LITERAL_ADDRESS;
2612 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002613 {
2614 server_list_free(newlist);
2615 ret_err(gen_err);
2616 }
Simon Kelley849a8352006-06-09 21:02:31 +01002617 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002618 else if (option == LOPT_NO_REBIND)
2619 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002620
2621 if (!arg || !*arg)
2622 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002623 if (!(newlist->flags & SERV_NO_REBIND))
2624 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002625 }
2626
2627 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002628 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002629 else
2630 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002631 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2632 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002633 {
2634 server_list_free(newlist);
2635 ret_err(err);
2636 }
Simon Kelley849a8352006-06-09 21:02:31 +01002637 }
2638
Simon Kelleyf2621c72007-04-29 19:47:21 +01002639 serv = newlist;
2640 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002641 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002642 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002643 serv->next->addr = serv->addr;
2644 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002645 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002646 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002647 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002648 serv->next = daemon->servers;
2649 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002650 break;
2651 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002652
Simon Kelleyde73a492014-02-17 21:43:27 +00002653 case LOPT_REV_SERV: /* --rev-server */
2654 {
2655 char *string;
2656 int size;
2657 struct server *serv;
2658 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002659 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002660
2661 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002662 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002663 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002664
2665 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002666
Simon Kelleya9b022a2020-02-11 21:58:59 +00002667 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2668 ret_err(gen_err);
2669
Simon Kelleyde73a492014-02-17 21:43:27 +00002670 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002671 {
2672 serv = add_rev4(addr4, size);
2673 if (!serv)
2674 ret_err(_("bad prefix"));
2675 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002676 else if (inet_pton(AF_INET6, arg, &addr6))
2677 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002678 else
2679 ret_err(gen_err);
2680
2681 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2682
2683 if (string)
2684 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002685
2686 if (servers_only)
2687 serv->flags |= SERV_FROM_FILE;
2688
Simon Kelleyde73a492014-02-17 21:43:27 +00002689 break;
2690 }
2691
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002692 case LOPT_IPSET: /* --ipset */
2693#ifndef HAVE_IPSET
2694 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2695 break;
2696#else
2697 {
2698 struct ipsets ipsets_head;
2699 struct ipsets *ipsets = &ipsets_head;
2700 int size;
2701 char *end;
2702 char **sets, **sets_pos;
2703 memset(ipsets, 0, sizeof(struct ipsets));
2704 unhide_metas(arg);
2705 if (arg && *arg == '/')
2706 {
2707 arg++;
2708 while ((end = split_chr(arg, '/')))
2709 {
2710 char *domain = NULL;
2711 /* elide leading dots - they are implied in the search algorithm */
2712 while (*arg == '.')
2713 arg++;
2714 /* # matches everything and becomes a zero length domain string */
2715 if (strcmp(arg, "#") == 0 || !*arg)
2716 domain = "";
2717 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002718 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002719 ipsets->next = opt_malloc(sizeof(struct ipsets));
2720 ipsets = ipsets->next;
2721 memset(ipsets, 0, sizeof(struct ipsets));
2722 ipsets->domain = domain;
2723 arg = end;
2724 }
2725 }
2726 else
2727 {
2728 ipsets->next = opt_malloc(sizeof(struct ipsets));
2729 ipsets = ipsets->next;
2730 memset(ipsets, 0, sizeof(struct ipsets));
2731 ipsets->domain = "";
2732 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002733
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002734 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002735 ret_err(gen_err);
2736
2737 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002738 if (*end == ',')
2739 ++size;
2740
2741 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2742
2743 do {
2744 end = split(arg);
2745 *sets_pos++ = opt_string_alloc(arg);
2746 arg = end;
2747 } while (end);
2748 *sets_pos = 0;
2749 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2750 ipsets->next->sets = sets;
2751 ipsets->next = daemon->ipsets;
2752 daemon->ipsets = ipsets_head.next;
2753
2754 break;
2755 }
2756#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002757
Simon Kelleyf2621c72007-04-29 19:47:21 +01002758 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002759 {
2760 int size;
2761
2762 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002763 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002764 else
2765 {
2766 /* zero is OK, and means no caching. */
2767
2768 if (size < 0)
2769 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002770
2771 /* Note that for very large cache sizes, the malloc()
2772 will overflow. For the size of the cache record
2773 at the time this was noted, the value of "very large"
2774 was 46684428. Limit to an order of magnitude less than
2775 that to be safe from changes to the cache record. */
2776 if (size > 5000000)
2777 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002778
2779 daemon->cachesize = size;
2780 }
2781 break;
2782 }
2783
Simon Kelleyf2621c72007-04-29 19:47:21 +01002784 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002785 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002786 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002787 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002788
Simon Kelley1a6bca82008-07-11 11:11:42 +01002789 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002790 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002791 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002792 break;
2793
Hans Dedecker926332a2016-01-23 10:48:12 +00002794 case LOPT_MAXPORT: /* --max-port */
2795 if (!atoi_check16(arg, &daemon->max_port))
2796 ret_err(gen_err);
2797 break;
2798
Simon Kelleyf2621c72007-04-29 19:47:21 +01002799 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002800 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002801 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002802 break;
2803
Simon Kelley25cf5e32015-01-09 15:53:03 +00002804 case 'q': /* --log-queries */
2805 set_option_bool(OPT_LOG);
2806 if (arg && strcmp(arg, "extra") == 0)
2807 set_option_bool(OPT_EXTRALOG);
2808 break;
2809
Simon Kelleyf2621c72007-04-29 19:47:21 +01002810 case LOPT_MAX_LOGS: /* --log-async */
2811 daemon->max_logs = LOG_MAX; /* default */
2812 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002813 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002814 else if (daemon->max_logs > 100)
2815 daemon->max_logs = 100;
2816 break;
2817
2818 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002819 {
2820 int i;
2821 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002822 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002823 daemon->edns_pktsz = (unsigned short)i;
2824 break;
2825 }
2826
Simon Kelleyf2621c72007-04-29 19:47:21 +01002827 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002828 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002829 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002830 /* if explicitly set to zero, use single OS ephemeral port
2831 and disable random ports */
2832 if (daemon->query_port == 0)
2833 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002834 break;
2835
Simon Kelley824af852008-02-12 20:43:05 +00002836 case 'T': /* --local-ttl */
2837 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002838 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002839 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002840 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002841 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002842 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002843 {
2844 int ttl;
2845 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002846 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002847 else if (option == LOPT_NEGTTL)
2848 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002849 else if (option == LOPT_MAXTTL)
2850 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002851 else if (option == LOPT_MINCTTL)
2852 {
2853 if (ttl > TTL_FLOOR_LIMIT)
2854 ttl = TTL_FLOOR_LIMIT;
2855 daemon->min_cache_ttl = (unsigned long)ttl;
2856 }
Simon Kelley1d860412012-09-20 20:48:04 +01002857 else if (option == LOPT_MAXCTTL)
2858 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002859 else if (option == LOPT_AUTHTTL)
2860 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002861 else if (option == LOPT_DHCPTTL)
2862 {
2863 daemon->dhcp_ttl = (unsigned long)ttl;
2864 daemon->use_dhcp_ttl = 1;
2865 }
Simon Kelley849a8352006-06-09 21:02:31 +01002866 else
2867 daemon->local_ttl = (unsigned long)ttl;
2868 break;
2869 }
2870
Simon Kelley7622fc02009-06-04 20:32:05 +01002871#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002872 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002873 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002874 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002875 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002876#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002877
Simon Kelley7622fc02009-06-04 20:32:05 +01002878#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002879 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002880 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002881 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002882 break;
2883
Simon Kelleybec366b2016-02-24 22:03:26 +00002884 case LOPT_TFTP_MTU: /* --tftp-mtu */
2885 if (!atoi_check(arg, &daemon->tftp_mtu))
2886 ret_err(gen_err);
2887 break;
2888
Simon Kelley824af852008-02-12 20:43:05 +00002889 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002890 comma = split(arg);
2891 if (comma)
2892 {
2893 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2894 new->interface = opt_string_alloc(comma);
2895 new->prefix = opt_string_alloc(arg);
2896 new->next = daemon->if_prefix;
2897 daemon->if_prefix = new;
2898 }
2899 else
2900 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002901 break;
2902
Simon Kelley824af852008-02-12 20:43:05 +00002903 case LOPT_TFTPPORTS: /* --tftp-port-range */
2904 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002905 !atoi_check16(arg, &daemon->start_tftp_port) ||
2906 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002907 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002908
2909 if (daemon->start_tftp_port > daemon->end_tftp_port)
2910 {
2911 int tmp = daemon->start_tftp_port;
2912 daemon->start_tftp_port = daemon->end_tftp_port;
2913 daemon->end_tftp_port = tmp;
2914 }
2915
2916 break;
Floris Bos60704f52017-04-09 22:22:49 +01002917
2918 case LOPT_APREF: /* --tftp-unique-root */
2919 if (!arg || strcasecmp(arg, "ip") == 0)
2920 set_option_bool(OPT_TFTP_APREF_IP);
2921 else if (strcasecmp(arg, "mac") == 0)
2922 set_option_bool(OPT_TFTP_APREF_MAC);
2923 else
2924 ret_err(gen_err);
2925 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002926#endif
Simon Kelley824af852008-02-12 20:43:05 +00002927
Simon Kelleyf2621c72007-04-29 19:47:21 +01002928 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002929 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002930 struct dhcp_bridge *new;
2931
Simon Kelley316e2732010-01-22 20:16:09 +00002932 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002933 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002934
Simon Kelley22cd8602018-01-14 22:57:14 +00002935 for (new = daemon->bridges; new; new = new->next)
2936 if (strcmp(new->iface, arg) == 0)
2937 break;
2938
2939 if (!new)
2940 {
2941 new = opt_malloc(sizeof(struct dhcp_bridge));
2942 strcpy(new->iface, arg);
2943 new->alias = NULL;
2944 new->next = daemon->bridges;
2945 daemon->bridges = new;
2946 }
2947
Simon Kelley832af0b2007-01-21 20:01:28 +00002948 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002949 arg = comma;
2950 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002951 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002952 {
Simon Kelley824af852008-02-12 20:43:05 +00002953 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002954 b->next = new->alias;
2955 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002956 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002957 }
2958 } while (comma);
2959
2960 break;
2961 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002962
Simon Kelley7622fc02009-06-04 20:32:05 +01002963#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002964 case LOPT_SHARED_NET: /* --shared-network */
2965 {
2966 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2967
2968#ifdef HAVE_DHCP6
2969 new->shared_addr.s_addr = 0;
2970#endif
2971 new->if_index = 0;
2972
2973 if (!(comma = split(arg)))
2974 {
2975 snerr:
2976 free(new);
2977 ret_err(_("bad shared-network"));
2978 }
2979
2980 if (inet_pton(AF_INET, comma, &new->shared_addr))
2981 {
2982 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2983 !(new->if_index = if_nametoindex(arg)))
2984 goto snerr;
2985 }
2986#ifdef HAVE_DHCP6
2987 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2988 {
2989 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
2990 !(new->if_index = if_nametoindex(arg)))
2991 goto snerr;
2992 }
2993#endif
2994 else
2995 goto snerr;
2996
2997 new->next = daemon->shared_networks;
2998 daemon->shared_networks = new;
2999 break;
3000 }
3001
Simon Kelleyf2621c72007-04-29 19:47:21 +01003002 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01003003 {
3004 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00003005 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00003006 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01003007
Simon Kelley52b92f42012-01-22 16:05:15 +00003008 memset (new, 0, sizeof(*new));
Simon Kelley52b92f42012-01-22 16:05:15 +00003009
Simon Kelley849a8352006-06-09 21:02:31 +01003010 while(1)
3011 {
3012 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003013 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3014 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3015 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003016 break;
3017
Simon Kelleyf2621c72007-04-29 19:47:21 +01003018 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003019 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003020 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003021 {
Simon Kelley0c387192013-09-05 10:21:12 +01003022 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003023 if (arg[4])
3024 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003025 }
3026 else
3027 {
3028 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003029 {
3030 dhcp_context_free(new);
3031 ret_err(_("only one tag allowed"));
3032 }
Simon Kelley849a8352006-06-09 21:02:31 +01003033 else
Petr Menšík59e47032018-11-02 22:39:39 +00003034 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003035 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003036 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003037 }
3038 else
3039 {
3040 a[0] = arg;
3041 break;
3042 }
3043 }
3044
Simon Kelley1f776932012-12-16 19:46:08 +00003045 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003046 if (!(a[k] = split(a[k-1])))
3047 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003048
Simon Kelley52b92f42012-01-22 16:05:15 +00003049 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003050 {
3051 dhcp_context_free(new);
3052 ret_err(_("bad dhcp-range"));
3053 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003054
3055 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003056 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003057 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003058 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003059 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003060 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003061 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003062 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003063 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003064 new->flags |= CONTEXT_PROXY;
3065 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003066 {
3067 dhcp_context_free(new);
3068 ret_err(_("bad dhcp-range"));
3069 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003070
3071 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3072 {
3073 struct in_addr tmp = new->start;
3074 new->start = new->end;
3075 new->end = tmp;
3076 }
3077
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003078 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003079 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003080 {
3081 new->flags |= CONTEXT_NETMASK;
3082 leasepos = 3;
3083 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003084 {
3085 dhcp_context_free(new);
3086 ret_err(_("inconsistent DHCP range"));
3087 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003088
Simon Kelley52b92f42012-01-22 16:05:15 +00003089
Simon Kelleyfa794662016-03-03 20:33:54 +00003090 if (k >= 4 && strchr(a[3], '.') &&
3091 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3092 {
3093 new->flags |= CONTEXT_BRDCAST;
3094 leasepos = 4;
3095 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003096 }
Simon Kelley849a8352006-06-09 21:02:31 +01003097 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003098#ifdef HAVE_DHCP6
3099 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003100 {
Petr Menšík59e47032018-11-02 22:39:39 +00003101 const char *err = NULL;
3102
Simon Kelley89500e32013-09-20 16:29:20 +01003103 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003104 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003105 new->end6 = new->start6;
Simon Kelley4d85e402020-07-12 22:45:46 +01003106 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003107 new->next = daemon->dhcp6;
3108 daemon->dhcp6 = new;
3109
Simon Kelley30cd9662012-03-25 20:44:38 +01003110 for (leasepos = 1; leasepos < k; leasepos++)
3111 {
3112 if (strcmp(a[leasepos], "static") == 0)
3113 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3114 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003115 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003116 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003117 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003118 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3119 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003120 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003121 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003122 else if (strcmp(a[leasepos], "off-link") == 0)
3123 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003124 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3125 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003126 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3127 {
3128 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3129 new->flags |= CONTEXT_TEMPLATE;
3130 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003131 else
3132 break;
3133 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003134
Simon Kelley52b92f42012-01-22 16:05:15 +00003135 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003136 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003137 {
3138 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003139 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003140 if (!(*cp >= '0' && *cp <= '9'))
3141 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003142 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003143 {
3144 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003145 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003146 }
3147 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003148
Petr Menšík59e47032018-11-02 22:39:39 +00003149 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003150 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003151 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003152 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003153 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003154 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003155 }
Petr Menšík59e47032018-11-02 22:39:39 +00003156 else if (new->prefix < 64)
3157 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003158
Petr Menšík59e47032018-11-02 22:39:39 +00003159 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3160 err=(_("inconsistent DHCPv6 range"));
3161
3162 if (err)
3163 {
3164 dhcp_context_free(new);
3165 ret_err(err);
3166 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003167
3168 /* dhcp-range=:: enables DHCP stateless on any interface */
3169 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3170 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003171
3172 if (new->flags & CONTEXT_TEMPLATE)
3173 {
3174 struct in6_addr zero;
3175 memset(&zero, 0, sizeof(zero));
3176 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003177 {
3178 dhcp_context_free(new);
3179 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3180 }
Simon Kelley66409192013-08-01 20:19:32 +01003181 }
3182
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003183 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003184 {
3185 struct in6_addr tmp = new->start6;
3186 new->start6 = new->end6;
3187 new->end6 = tmp;
3188 }
Simon Kelley849a8352006-06-09 21:02:31 +01003189 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003190#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003191 else
Petr Menšík59e47032018-11-02 22:39:39 +00003192 {
3193 dhcp_context_free(new);
3194 ret_err(_("bad dhcp-range"));
3195 }
Simon Kelley849a8352006-06-09 21:02:31 +01003196
Simon Kelley30cd9662012-03-25 20:44:38 +01003197 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003198 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003199 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003200 {
3201 dhcp_context_free(new);
3202 ret_err(_("bad dhcp-range"));
3203 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003204
Simon Kelley849a8352006-06-09 21:02:31 +01003205 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003206 {
3207 new->lease_time = 0xffffffff;
3208 new->flags |= CONTEXT_SETLEASE;
3209 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003210 else if (strcmp(a[leasepos], "deprecated") == 0)
3211 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003212 else
3213 {
3214 int fac = 1;
3215 if (strlen(a[leasepos]) > 0)
3216 {
3217 switch (a[leasepos][strlen(a[leasepos]) - 1])
3218 {
Simon Kelley42243212012-07-20 15:19:18 +01003219 case 'w':
3220 case 'W':
3221 fac *= 7;
3222 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003223 case 'd':
3224 case 'D':
3225 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003226 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003227 case 'h':
3228 case 'H':
3229 fac *= 60;
3230 /* fall through */
3231 case 'm':
3232 case 'M':
3233 fac *= 60;
3234 /* fall through */
3235 case 's':
3236 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003237 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003238 }
3239
Simon Kelleybe379862012-12-23 12:01:39 +00003240 for (cp = a[leasepos]; *cp; cp++)
3241 if (!(*cp >= '0' && *cp <= '9'))
3242 break;
3243
Simon Kelley54dae552013-02-05 17:55:10 +00003244 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003245 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003246
Simon Kelley849a8352006-06-09 21:02:31 +01003247 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003248 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003249 /* Leases of a minute or less confuse
3250 some clients, notably Apple's */
3251 if (new->lease_time < 120)
3252 new->lease_time = 120;
3253 }
3254 }
3255 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003256
Simon Kelley849a8352006-06-09 21:02:31 +01003257 break;
3258 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003259
Simon Kelley5aabfc72007-08-29 11:24:47 +01003260 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003261 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003262 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003263 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003264 struct in_addr in;
3265
Simon Kelley824af852008-02-12 20:43:05 +00003266 new = opt_malloc(sizeof(struct dhcp_config));
3267
Simon Kelley849a8352006-06-09 21:02:31 +01003268 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003269 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3270 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003271 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003272 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003273 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003274#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003275 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003276#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003277
Simon Kelley137286e2020-02-06 22:09:30 +00003278 while (arg)
3279 {
3280 comma = split(arg);
3281 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3282 {
3283 if ((arg[0] == 'i' || arg[0] == 'I') &&
3284 (arg[1] == 'd' || arg[1] == 'D') &&
3285 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003286 {
Simon Kelley137286e2020-02-06 22:09:30 +00003287 if (arg[3] == '*')
3288 new->flags |= CONFIG_NOCLID;
3289 else
3290 {
3291 int len;
3292 arg += 3; /* dump id: */
3293 if (strchr(arg, ':'))
3294 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3295 else
3296 {
3297 unhide_metas(arg);
3298 len = (int) strlen(arg);
3299 }
3300
3301 if (len == -1)
3302 {
3303 dhcp_config_free(new);
3304 ret_err(_("bad hex constant"));
3305 }
3306 else if ((new->clid = opt_malloc(len)))
3307 {
3308 new->flags |= CONFIG_CLID;
3309 new->clid_len = len;
3310 memcpy(new->clid, arg, len);
3311 }
3312 }
3313 }
3314 /* dhcp-host has strange backwards-compat needs. */
3315 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3316 {
3317 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3318 newlist->next = new->netid;
3319 new->netid = newlist;
3320 newlist->list = dhcp_netid_create(arg+4, NULL);
3321 }
3322 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003323 new->filter = dhcp_netid_create(arg+4, new->filter);
3324
Simon Kelley137286e2020-02-06 22:09:30 +00003325#ifdef HAVE_DHCP6
3326 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3327 {
3328 char *pref;
3329 struct in6_addr in6;
3330 struct addrlist *new_addr;
3331
3332 arg[strlen(arg)-1] = 0;
3333 arg++;
3334 pref = split_chr(arg, '/');
3335
3336 if (!inet_pton(AF_INET6, arg, &in6))
3337 {
3338 dhcp_config_free(new);
3339 ret_err(_("bad IPv6 address"));
3340 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003341
Simon Kelley137286e2020-02-06 22:09:30 +00003342 new_addr = opt_malloc(sizeof(struct addrlist));
3343 new_addr->next = new->addr6;
3344 new_addr->flags = 0;
3345 new_addr->addr.addr6 = in6;
3346 new->addr6 = new_addr;
3347
3348 if (pref)
3349 {
3350 u64 addrpart = addr6part(&in6);
3351
3352 if (!atoi_check(pref, &new_addr->prefixlen) ||
3353 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003354 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003355 {
3356 dhcp_config_free(new);
3357 ret_err(_("bad IPv6 prefix"));
3358 }
3359
3360 new_addr->flags |= ADDRLIST_PREFIX;
3361 }
3362
3363 for (i= 0; i < 8; i++)
3364 if (in6.s6_addr[i] != 0)
3365 break;
3366
3367 /* set WILDCARD if network part all zeros */
3368 if (i == 8)
3369 new_addr->flags |= ADDRLIST_WILDCARD;
3370
3371 new->flags |= CONFIG_ADDR6;
3372 }
3373#endif
3374 else
3375 {
3376 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3377 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3378 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3379 {
3380 free(newhw);
3381 dhcp_config_free(new);
3382 ret_err(_("bad hex constant"));
3383 }
3384 else
3385 {
3386 newhw->next = new->hwaddr;
3387 new->hwaddr = newhw;
3388 }
3389 }
3390 }
3391 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3392 {
3393 struct dhcp_config *configs;
3394
3395 new->addr = in;
3396 new->flags |= CONFIG_ADDR;
3397
3398 /* If the same IP appears in more than one host config, then DISCOVER
3399 for one of the hosts will get the address, but REQUEST will be NAKed,
3400 since the address is reserved by the other one -> protocol loop. */
3401 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3402 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003403 {
Simon Kelley137286e2020-02-06 22:09:30 +00003404 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3405 return 0;
3406 }
3407 }
3408 else
3409 {
3410 char *cp, *lastp = NULL, last = 0;
3411 int fac = 1, isdig = 0;
3412
3413 if (strlen(arg) > 1)
3414 {
3415 lastp = arg + strlen(arg) - 1;
3416 last = *lastp;
3417 switch (last)
3418 {
3419 case 'w':
3420 case 'W':
3421 fac *= 7;
3422 /* fall through */
3423 case 'd':
3424 case 'D':
3425 fac *= 24;
3426 /* fall through */
3427 case 'h':
3428 case 'H':
3429 fac *= 60;
3430 /* fall through */
3431 case 'm':
3432 case 'M':
3433 fac *= 60;
3434 /* fall through */
3435 case 's':
3436 case 'S':
3437 *lastp = 0;
3438 }
3439 }
3440
3441 for (cp = arg; *cp; cp++)
3442 if (isdigit((unsigned char)*cp))
3443 isdig = 1;
3444 else if (*cp != ' ')
3445 break;
3446
3447 if (*cp)
3448 {
3449 if (lastp)
3450 *lastp = last;
3451 if (strcmp(arg, "infinite") == 0)
3452 {
3453 new->lease_time = 0xffffffff;
3454 new->flags |= CONFIG_TIME;
3455 }
3456 else if (strcmp(arg, "ignore") == 0)
3457 new->flags |= CONFIG_DISABLE;
3458 else
3459 {
3460 if (!(new->hostname = canonicalise_opt(arg)) ||
3461 !legal_hostname(new->hostname))
3462 {
3463 dhcp_config_free(new);
3464 ret_err(_("bad DHCP host name"));
3465 }
3466
3467 new->flags |= CONFIG_NAME;
3468 new->domain = strip_hostname(new->hostname);
3469 }
3470 }
3471 else if (isdig)
3472 {
3473 new->lease_time = atoi(arg) * fac;
3474 /* Leases of a minute or less confuse
3475 some clients, notably Apple's */
3476 if (new->lease_time < 120)
3477 new->lease_time = 120;
3478 new->flags |= CONFIG_TIME;
3479 }
3480 }
3481
3482 arg = comma;
3483 }
3484
Simon Kelley5aabfc72007-08-29 11:24:47 +01003485 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003486 break;
3487 }
Simon Kelley137286e2020-02-06 22:09:30 +00003488
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003489 case LOPT_TAG_IF: /* --tag-if */
3490 {
3491 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3492
3493 new->tag = NULL;
3494 new->set = NULL;
3495 new->next = NULL;
3496
3497 /* preserve order */
3498 if (!daemon->tag_if)
3499 daemon->tag_if = new;
3500 else
3501 {
3502 struct tag_if *tmp;
3503 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3504 tmp->next = new;
3505 }
3506
3507 while (arg)
3508 {
3509 size_t len;
3510
3511 comma = split(arg);
3512 len = strlen(arg);
3513
3514 if (len < 5)
3515 {
3516 new->set = NULL;
3517 break;
3518 }
3519 else
3520 {
Petr Menšík59e47032018-11-02 22:39:39 +00003521 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003522
3523 if (strstr(arg, "set:") == arg)
3524 {
3525 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3526 newlist->next = new->set;
3527 new->set = newlist;
3528 newlist->list = newtag;
3529 }
3530 else if (strstr(arg, "tag:") == arg)
3531 {
3532 newtag->next = new->tag;
3533 new->tag = newtag;
3534 }
3535 else
3536 {
3537 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003538 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003539 break;
3540 }
3541 }
3542
3543 arg = comma;
3544 }
3545
3546 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003547 {
3548 dhcp_netid_free(new->tag);
3549 dhcp_netid_list_free(new->set);
3550 ret_err_free(_("bad tag-if"), new);
3551 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003552
3553 break;
3554 }
3555
Simon Kelley849a8352006-06-09 21:02:31 +01003556
Simon Kelley73a08a22009-02-05 20:28:08 +00003557 case 'O': /* --dhcp-option */
3558 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003559 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003560 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003561 return parse_dhcp_opt(errstr, arg,
3562 option == LOPT_FORCE ? DHOPT_FORCE :
3563 (option == LOPT_MATCH ? DHOPT_MATCH :
3564 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003565
3566 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3567 {
3568 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3569 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3570 ssize_t len;
3571
3572 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3573 ret_err(gen_err);
3574
3575 new->wildcard = 0;
3576 new->netid = id;
3577 id->net = opt_string_alloc(set_prefix(arg));
3578
3579 if (comma[len-1] == '*')
3580 {
3581 comma[len-1] = 0;
3582 new->wildcard = 1;
3583 }
3584 new->name = opt_string_alloc(comma);
3585
3586 new->next = daemon->dhcp_name_match;
3587 daemon->dhcp_name_match = new;
3588
3589 break;
3590 }
3591
Simon Kelleyf2621c72007-04-29 19:47:21 +01003592 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003593 {
Petr Menšík59e47032018-11-02 22:39:39 +00003594 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003595
Petr Menšík137e9f82018-12-16 21:25:29 +00003596 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003597 {
3598 ret_err(gen_err);
3599 }
Simon Kelley849a8352006-06-09 21:02:31 +01003600 else
3601 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003602 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003603 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003604 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003605 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003606 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003607 dhcp_next_server.s_addr = 0;
3608 if (comma)
3609 {
3610 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003611 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003612 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003613 if (comma)
3614 {
3615 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003616 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3617 {
3618 /*
3619 * The user may have specified the tftp hostname here.
3620 * save it so that it can be resolved/looked up during
3621 * actual dhcp_reply().
3622 */
3623
3624 tftp_sname = opt_string_alloc(comma);
3625 dhcp_next_server.s_addr = 0;
3626 }
Simon Kelley849a8352006-06-09 21:02:31 +01003627 }
3628 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003629
3630 new = opt_malloc(sizeof(struct dhcp_boot));
3631 new->file = dhcp_file;
3632 new->sname = dhcp_sname;
3633 new->tftp_sname = tftp_sname;
3634 new->next_server = dhcp_next_server;
3635 new->netid = id;
3636 new->next = daemon->boot_config;
3637 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003638 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003639
Simon Kelley849a8352006-06-09 21:02:31 +01003640 break;
3641 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003642
Floris Bos503c6092017-04-09 23:07:13 +01003643 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3644 {
Petr Menšík59e47032018-11-02 22:39:39 +00003645 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003646
Petr Menšík137e9f82018-12-16 21:25:29 +00003647 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003648 {
3649 ret_err(gen_err);
3650 }
Floris Bos503c6092017-04-09 23:07:13 +01003651 else
3652 {
3653 struct delay_config *new;
3654 int delay;
3655 if (!atoi_check(arg, &delay))
3656 ret_err(gen_err);
3657
3658 new = opt_malloc(sizeof(struct delay_config));
3659 new->delay = delay;
3660 new->netid = id;
3661 new->next = daemon->delay_conf;
3662 daemon->delay_conf = new;
3663 }
3664
3665 break;
3666 }
3667
Simon Kelley7622fc02009-06-04 20:32:05 +01003668 case LOPT_PXE_PROMT: /* --pxe-prompt */
3669 {
3670 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3671 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003672
Simon Kelley7622fc02009-06-04 20:32:05 +01003673 new->netid = NULL;
3674 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003675 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003676
Petr Menšík137e9f82018-12-16 21:25:29 +00003677 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003678 {
3679 dhcp_opt_free(new);
3680 ret_err(gen_err);
3681 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003682 else
3683 {
3684 comma = split(arg);
3685 unhide_metas(arg);
3686 new->len = strlen(arg) + 1;
3687 new->val = opt_malloc(new->len);
3688 memcpy(new->val + 1, arg, new->len - 1);
3689
Wang Shanker4ded9622020-12-04 10:17:35 +08003690 new->u.vendor_class = NULL;
3691 new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
Simon Kelley7622fc02009-06-04 20:32:05 +01003692
3693 if (comma && atoi_check(comma, &timeout))
3694 *(new->val) = timeout;
3695 else
3696 *(new->val) = 255;
3697
3698 new->next = daemon->dhcp_opts;
3699 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003700 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003701 }
3702
3703 break;
3704 }
3705
3706 case LOPT_PXE_SERV: /* --pxe-service */
3707 {
3708 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3709 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003710 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3711 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003712 static int boottype = 32768;
3713
3714 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003715 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003716 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003717 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003718
Simon Kelley7622fc02009-06-04 20:32:05 +01003719 if (arg && (comma = split(arg)))
3720 {
3721 for (i = 0; CSA[i]; i++)
3722 if (strcasecmp(CSA[i], arg) == 0)
3723 break;
3724
3725 if (CSA[i] || atoi_check(arg, &i))
3726 {
3727 arg = comma;
3728 comma = split(arg);
3729
3730 new->CSA = i;
3731 new->menu = opt_string_alloc(arg);
3732
Simon Kelley316e2732010-01-22 20:16:09 +00003733 if (!comma)
3734 {
3735 new->type = 0; /* local boot */
3736 new->basename = NULL;
3737 }
3738 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003739 {
3740 arg = comma;
3741 comma = split(arg);
3742 if (atoi_check(arg, &i))
3743 {
3744 new->type = i;
3745 new->basename = NULL;
3746 }
3747 else
3748 {
3749 new->type = boottype++;
3750 new->basename = opt_string_alloc(arg);
3751 }
3752
Simon Kelley751d6f42012-02-10 15:24:51 +00003753 if (comma)
3754 {
3755 if (!inet_pton(AF_INET, comma, &new->server))
3756 {
3757 new->server.s_addr = 0;
3758 new->sname = opt_string_alloc(comma);
3759 }
3760
3761 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003762 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003763
Simon Kelley316e2732010-01-22 20:16:09 +00003764 /* Order matters */
3765 new->next = NULL;
3766 if (!daemon->pxe_services)
3767 daemon->pxe_services = new;
3768 else
3769 {
3770 struct pxe_service *s;
3771 for (s = daemon->pxe_services; s->next; s = s->next);
3772 s->next = new;
3773 }
3774
3775 daemon->enable_pxe = 1;
3776 break;
3777
Simon Kelley7622fc02009-06-04 20:32:05 +01003778 }
3779 }
3780
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003781 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003782 }
3783
Simon Kelleyf2621c72007-04-29 19:47:21 +01003784 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003785 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003786 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003787 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003788 else
3789 {
Simon Kelley824af852008-02-12 20:43:05 +00003790 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003791 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003792 unhide_metas(comma);
3793 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003794 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003795 {
3796 free(new->netid.net);
3797 ret_err_free(gen_err, new);
3798 }
Simon Kelley28866e92011-02-14 20:19:14 +00003799 else
3800 {
3801 new->next = daemon->dhcp_macs;
3802 daemon->dhcp_macs = new;
3803 }
Simon Kelley849a8352006-06-09 21:02:31 +01003804 }
3805 }
3806 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003807
Simon Kelleyf2621c72007-04-29 19:47:21 +01003808 case 'U': /* --dhcp-vendorclass */
3809 case 'j': /* --dhcp-userclass */
3810 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3811 case LOPT_REMOTE: /* --dhcp-remoteid */
3812 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003813 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003814 unsigned char *p;
3815 int dig = 0;
3816 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3817
3818 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003819 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003820
3821 new->netid.net = opt_string_alloc(set_prefix(arg));
3822 /* check for hex string - must digits may include : must not have nothing else,
3823 only allowed for agent-options. */
3824
3825 arg = comma;
3826 if ((comma = split(arg)))
3827 {
3828 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003829 {
3830 free(new->netid.net);
3831 ret_err_free(gen_err, new);
3832 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003833 else
3834 new->enterprise = atoi(arg+11);
3835 }
3836 else
3837 comma = arg;
3838
3839 for (p = (unsigned char *)comma; *p; p++)
3840 if (isxdigit(*p))
3841 dig = 1;
3842 else if (*p != ':')
3843 break;
3844 unhide_metas(comma);
3845 if (option == 'U' || option == 'j' || *p || !dig)
3846 {
3847 new->len = strlen(comma);
3848 new->data = opt_malloc(new->len);
3849 memcpy(new->data, comma, new->len);
3850 }
3851 else
3852 {
3853 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3854 new->data = opt_malloc(new->len);
3855 memcpy(new->data, comma, new->len);
3856 }
3857
3858 switch (option)
3859 {
3860 case 'j':
3861 new->match_type = MATCH_USER;
3862 break;
3863 case 'U':
3864 new->match_type = MATCH_VENDOR;
3865 break;
3866 case LOPT_CIRCUIT:
3867 new->match_type = MATCH_CIRCUIT;
3868 break;
3869 case LOPT_REMOTE:
3870 new->match_type = MATCH_REMOTE;
3871 break;
3872 case LOPT_SUBSCR:
3873 new->match_type = MATCH_SUBSCRIBER;
3874 break;
3875 }
3876 new->next = daemon->dhcp_vendors;
3877 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003878
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003879 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003880 }
3881
Simon Kelley9e038942008-05-30 20:06:34 +01003882 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3883 if (!arg)
3884 {
3885 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3886 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3887 }
3888 else
3889 {
3890 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003891 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3892 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003893 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003894 if (!comma)
3895 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3896 }
3897 break;
3898
Simon Kelley824af852008-02-12 20:43:05 +00003899 case 'J': /* --dhcp-ignore */
3900 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3901 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003902 case '3': /* --bootp-dynamic */
3903 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003904 {
Simon Kelley824af852008-02-12 20:43:05 +00003905 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003906 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003907 if (option == 'J')
3908 {
3909 new->next = daemon->dhcp_ignore;
3910 daemon->dhcp_ignore = new;
3911 }
Simon Kelley824af852008-02-12 20:43:05 +00003912 else if (option == LOPT_BROADCAST)
3913 {
3914 new->next = daemon->force_broadcast;
3915 daemon->force_broadcast = new;
3916 }
Simon Kelley9009d742008-11-14 20:04:27 +00003917 else if (option == '3')
3918 {
3919 new->next = daemon->bootp_dynamic;
3920 daemon->bootp_dynamic = new;
3921 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003922 else if (option == LOPT_GEN_NAMES)
3923 {
3924 new->next = daemon->dhcp_gen_names;
3925 daemon->dhcp_gen_names = new;
3926 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003927 else
3928 {
3929 new->next = daemon->dhcp_ignore_names;
3930 daemon->dhcp_ignore_names = new;
3931 }
3932
3933 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003934 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003935 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003936 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003937 }
Simon Kelley849a8352006-06-09 21:02:31 +01003938
3939 new->list = list;
3940 break;
3941 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003942
3943 case LOPT_PROXY: /* --dhcp-proxy */
3944 daemon->override = 1;
3945 while (arg) {
3946 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3947 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003948 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003949 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003950 new->next = daemon->override_relays;
3951 daemon->override_relays = new;
3952 arg = comma;
Wang Shanker4ded9622020-12-04 10:17:35 +08003953 }
3954 break;
3955
3956 case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
3957 {
3958 while (arg) {
3959 struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct dhcp_pxe_vendor));
3960 comma = split(arg);
3961 new->data = opt_string_alloc(arg);
3962 new->next = daemon->dhcp_pxe_vendors;
3963 daemon->dhcp_pxe_vendors = new;
3964 arg = comma;
3965 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003966 }
3967 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003968
3969 case LOPT_RELAY: /* --dhcp-relay */
3970 {
3971 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3972 comma = split(arg);
3973 new->interface = opt_string_alloc(split(comma));
3974 new->iface_index = 0;
3975 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3976 {
3977 new->next = daemon->relay4;
3978 daemon->relay4 = new;
3979 }
3980#ifdef HAVE_DHCP6
3981 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3982 {
3983 new->next = daemon->relay6;
3984 daemon->relay6 = new;
3985 }
3986#endif
3987 else
Petr Menšík59e47032018-11-02 22:39:39 +00003988 {
3989 free(new->interface);
3990 ret_err_free(_("Bad dhcp-relay"), new);
3991 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003992
3993 break;
3994 }
3995
Simon Kelley7622fc02009-06-04 20:32:05 +01003996#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003997
Simon Kelley8b372702012-03-09 17:45:10 +00003998#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003999 case LOPT_RA_PARAM: /* --ra-param */
4000 if ((comma = split(arg)))
4001 {
4002 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
4003 new->lifetime = -1;
4004 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01004005 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004006 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004007 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01004008 if (strcasestr(comma, "mtu:") == comma)
4009 {
4010 arg = comma + 4;
4011 if (!(comma = split(comma)))
4012 goto err;
4013 if (!strcasecmp(arg, "off"))
4014 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01004015 else if (!atoi_check(arg, &new->mtu))
4016 new->mtu_name = opt_string_alloc(arg);
4017 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01004018 goto err;
4019 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004020 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
4021 {
4022 if (*comma == 'l' || *comma == 'L')
4023 new->prio = 0x18;
4024 else
4025 new->prio = 0x08;
4026 comma = split(comma);
4027 }
4028 arg = split(comma);
4029 if (!atoi_check(comma, &new->interval) ||
4030 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004031 {
David Flamand005c46d2017-04-11 11:49:54 +01004032err:
Petr Menšík59e47032018-11-02 22:39:39 +00004033 free(new->name);
4034 ret_err_free(_("bad RA-params"), new);
4035 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004036
4037 new->next = daemon->ra_interfaces;
4038 daemon->ra_interfaces = new;
4039 }
4040 break;
4041
Simon Kelley8b372702012-03-09 17:45:10 +00004042 case LOPT_DUID: /* --dhcp-duid */
4043 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004044 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004045 else
4046 {
4047 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4048 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4049 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4050 }
4051 break;
4052#endif
4053
Simon Kelleyf2621c72007-04-29 19:47:21 +01004054 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004055 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004056 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004057 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004058 struct doctor *new = opt_malloc(sizeof(struct doctor));
4059 new->next = daemon->doctors;
4060 daemon->doctors = new;
4061 new->mask.s_addr = 0xffffffff;
4062 new->end.s_addr = 0;
4063
Simon Kelley849a8352006-06-09 21:02:31 +01004064 if ((a[0] = arg))
4065 for (k = 1; k < 3; k++)
4066 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004067 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004068 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004069 unhide_metas(a[k]);
4070 }
Simon Kelley849a8352006-06-09 21:02:31 +01004071
Simon Kelley73a08a22009-02-05 20:28:08 +00004072 dash = split_chr(a[0], '-');
4073
Simon Kelley849a8352006-06-09 21:02:31 +01004074 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004075 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004076 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4077 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4078 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004079
Simon Kelley73a08a22009-02-05 20:28:08 +00004080 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004081 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004082 !is_same_net(new->in, new->end, new->mask) ||
4083 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004084 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004085
4086 break;
4087 }
4088
Simon Kelleyf2621c72007-04-29 19:47:21 +01004089 case LOPT_INTNAME: /* --interface-name */
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004090 case LOPT_DYNHOST: /* --dynamic-host */
Simon Kelleyf2621c72007-04-29 19:47:21 +01004091 {
4092 struct interface_name *new, **up;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004093 char *domain = arg;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004094
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004095 arg = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01004096
Simon Kelley824af852008-02-12 20:43:05 +00004097 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004098 memset(new, 0, sizeof(struct interface_name));
4099 new->flags = IN4 | IN6;
Simon Kelley376d48c2013-11-13 13:04:30 +00004100
Simon Kelleyf2621c72007-04-29 19:47:21 +01004101 /* Add to the end of the list, so that first name
4102 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004103 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004104 *up = new;
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004105
4106 while ((comma = split(arg)))
Simon Kelleyf7029f52013-11-21 15:09:09 +00004107 {
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004108 if (inet_pton(AF_INET, arg, &new->proto4))
4109 new->flags |= INP4;
4110 else if (inet_pton(AF_INET6, arg, &new->proto6))
4111 new->flags |= INP6;
4112 else
4113 break;
4114
4115 arg = comma;
4116 }
4117
4118 if ((comma = split_chr(arg, '/')))
4119 {
4120 if (strcmp(comma, "4") == 0)
4121 new->flags &= ~IN6;
4122 else if (strcmp(comma, "6") == 0)
4123 new->flags &= ~IN4;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004124 else
Petr Menšík59e47032018-11-02 22:39:39 +00004125 ret_err_free(gen_err, new);
Simon Kelleyb7cf7542021-03-04 16:54:14 +00004126 }
4127
4128 new->intr = opt_string_alloc(arg);
4129
4130 if (option == LOPT_DYNHOST)
4131 {
4132 if (!(new->flags & (INP4 | INP6)))
4133 ret_err(_("missing address in dynamic host"));
4134
4135 if (!(new->flags & IN4) || !(new->flags & IN6))
4136 arg = NULL; /* provoke error below */
4137
4138 new->flags &= ~(IN4 | IN6);
4139 }
4140 else
4141 {
4142 if (new->flags & (INP4 | INP6))
4143 arg = NULL; /* provoke error below */
4144 }
4145
4146 if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
4147 ret_err(option == LOPT_DYNHOST ?
4148 _("bad dynamic host") : _("bad interface name"));
4149
Simon Kelleyf2621c72007-04-29 19:47:21 +01004150 break;
4151 }
Simon Kelley9009d742008-11-14 20:04:27 +00004152
4153 case LOPT_CNAME: /* --cname */
4154 {
4155 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004156 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004157 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004158
Simon Kelleya1d973f2016-12-22 22:09:50 +00004159 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004160 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004161 pen = last;
4162 last = comma;
4163 }
4164
4165 if (!pen)
4166 ret_err(_("bad CNAME"));
4167
4168 if (pen != arg && atoi_check(last, &ttl))
4169 last = pen;
4170
4171 target = canonicalise_opt(last);
4172
4173 while (arg != last)
4174 {
Petr Menšík56f06232018-03-06 23:13:32 +00004175 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004176 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004177
4178 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004179 {
4180 free(target);
4181 free(alias);
4182 ret_err(_("bad CNAME"));
4183 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004184
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004185 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004186 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004187 {
4188 free(target);
4189 free(alias);
4190 ret_err(_("duplicate CNAME"));
4191 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004192 new = opt_malloc(sizeof(struct cname));
4193 new->next = daemon->cnames;
4194 daemon->cnames = new;
4195 new->alias = alias;
4196 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004197 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004198
Petr Menšík56f06232018-03-06 23:13:32 +00004199 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004200 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004201
Simon Kelley9009d742008-11-14 20:04:27 +00004202 break;
4203 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004204
4205 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004206 {
4207 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004208 char *dom, *target = NULL;
4209
Simon Kelleyf2621c72007-04-29 19:47:21 +01004210 comma = split(arg);
4211
Simon Kelley1f15b812009-10-13 17:49:32 +01004212 if (!(dom = canonicalise_opt(arg)) ||
4213 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004214 {
4215 free(dom);
4216 free(target);
4217 ret_err(_("bad PTR record"));
4218 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004219 else
4220 {
4221 new = opt_malloc(sizeof(struct ptr_record));
4222 new->next = daemon->ptr;
4223 daemon->ptr = new;
4224 new->name = dom;
4225 new->ptr = target;
4226 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004227 break;
4228 }
4229
Simon Kelley1a6bca82008-07-11 11:11:42 +01004230 case LOPT_NAPTR: /* --naptr-record */
4231 {
4232 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4233 int k = 0;
4234 struct naptr *new;
4235 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004236 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004237
4238 if ((a[0] = arg))
4239 for (k = 1; k < 7; k++)
4240 if (!(a[k] = split(a[k-1])))
4241 break;
4242
4243
4244 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004245 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004246 !atoi_check16(a[1], &order) ||
4247 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004248 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004249 {
4250 free(name);
4251 free(replace);
4252 ret_err(_("bad NAPTR record"));
4253 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004254 else
4255 {
4256 new = opt_malloc(sizeof(struct naptr));
4257 new->next = daemon->naptr;
4258 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004259 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004260 new->flags = opt_string_alloc(a[3]);
4261 new->services = opt_string_alloc(a[4]);
4262 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004263 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004264 new->order = order;
4265 new->pref = pref;
4266 }
4267 break;
4268 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004269
4270 case LOPT_RR: /* dns-rr */
4271 {
4272 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004273 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004274 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004275 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004276
4277 comma = split(arg);
4278 data = split(comma);
4279
4280 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004281 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004282
Petr Menšík59e47032018-11-02 22:39:39 +00004283 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004284 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004285 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004286 {
4287 free(new->name);
4288 ret_err_free(_("bad RR record"), new);
4289 }
4290
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004291 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004292 new->class = class;
4293 new->next = daemon->rr;
4294 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004295
4296 if (data)
4297 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004298 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004299 new->len = len;
4300 memcpy(new->txt, data, len);
4301 }
4302
4303 break;
4304 }
4305
Simon Kelley974a6d02018-08-23 23:01:16 +01004306 case LOPT_CAA: /* --caa-record */
4307 {
4308 struct txt_record *new;
4309 char *tag, *value;
4310 int flags;
4311
4312 comma = split(arg);
4313 tag = split(comma);
4314 value = split(tag);
4315
4316 new = opt_malloc(sizeof(struct txt_record));
4317 new->next = daemon->rr;
4318 daemon->rr = new;
4319
4320 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4321 ret_err(_("bad CAA record"));
4322
4323 unhide_metas(tag);
4324 unhide_metas(value);
4325
4326 new->len = strlen(tag) + strlen(value) + 2;
4327 new->txt = opt_malloc(new->len);
4328 new->txt[0] = flags;
4329 new->txt[1] = strlen(tag);
4330 memcpy(&new->txt[2], tag, strlen(tag));
4331 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4332 new->class = T_CAA;
4333
4334 break;
4335 }
4336
Simon Kelleyf2621c72007-04-29 19:47:21 +01004337 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004338 {
4339 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004340 unsigned char *p, *cnt;
4341 size_t len;
4342
4343 comma = split(arg);
4344
Simon Kelley824af852008-02-12 20:43:05 +00004345 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004346 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004347 new->stat = 0;
4348
Simon Kelley1f15b812009-10-13 17:49:32 +01004349 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004350 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004351
Petr Menšík59e47032018-11-02 22:39:39 +00004352 new->next = daemon->txt;
4353 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004354 len = comma ? strlen(comma) : 0;
4355 len += (len/255) + 1; /* room for extra counts */
4356 new->txt = p = opt_malloc(len);
4357
4358 cnt = p++;
4359 *cnt = 0;
4360
4361 while (comma && *comma)
4362 {
4363 unsigned char c = (unsigned char)*comma++;
4364
4365 if (c == ',' || *cnt == 255)
4366 {
4367 if (c != ',')
4368 comma--;
4369 cnt = p++;
4370 *cnt = 0;
4371 }
4372 else
4373 {
4374 *p++ = unhide_meta(c);
4375 (*cnt)++;
4376 }
4377 }
4378
4379 new->len = p - new->txt;
4380
Simon Kelley849a8352006-06-09 21:02:31 +01004381 break;
4382 }
4383
Simon Kelleyf2621c72007-04-29 19:47:21 +01004384 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004385 {
4386 int port = 1, priority = 0, weight = 0;
4387 char *name, *target = NULL;
4388 struct mx_srv_record *new;
4389
Simon Kelleyf2621c72007-04-29 19:47:21 +01004390 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004391
Simon Kelley1f15b812009-10-13 17:49:32 +01004392 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004393 ret_err(_("bad SRV record"));
4394
Simon Kelley849a8352006-06-09 21:02:31 +01004395 if (comma)
4396 {
4397 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004398 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004399 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004400 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004401
Simon Kelley849a8352006-06-09 21:02:31 +01004402 if (comma)
4403 {
4404 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004405 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004406 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004407 {
4408 free(name);
4409 ret_err_free(_("invalid port number"), target);
4410 }
Simon Kelley824af852008-02-12 20:43:05 +00004411
Simon Kelley849a8352006-06-09 21:02:31 +01004412 if (comma)
4413 {
4414 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004415 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004416 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004417 {
4418 free(name);
4419 ret_err_free(_("invalid priority"), target);
4420 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004421 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004422 {
4423 free(name);
4424 ret_err_free(_("invalid weight"), target);
4425 }
Simon Kelley849a8352006-06-09 21:02:31 +01004426 }
4427 }
4428 }
4429
Simon Kelley824af852008-02-12 20:43:05 +00004430 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004431 new->next = daemon->mxnames;
4432 daemon->mxnames = new;
4433 new->issrv = 1;
4434 new->name = name;
4435 new->target = target;
4436 new->srvport = port;
4437 new->priority = priority;
4438 new->weight = weight;
4439 break;
4440 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004441
Simon Kelleye759d422012-03-16 13:18:57 +00004442 case LOPT_HOST_REC: /* --host-record */
4443 {
Petr Menšík59e47032018-11-02 22:39:39 +00004444 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004445
Simon Kelleye759d422012-03-16 13:18:57 +00004446 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004447 ret_err(_("Bad host-record"));
4448
Petr Menšík59e47032018-11-02 22:39:39 +00004449 new = opt_malloc(sizeof(struct host_record));
4450 memset(new, 0, sizeof(struct host_record));
4451 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004452 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004453
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004454 while (arg)
4455 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004456 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004457 char *dig;
4458
4459 for (dig = arg; *dig != 0; dig++)
4460 if (*dig < '0' || *dig > '9')
4461 break;
4462 if (*dig == 0)
4463 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004464 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004465 {
4466 new->addr = addr.addr4;
4467 new->flags |= HR_4;
4468 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004469 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004470 {
4471 new->addr6 = addr.addr6;
4472 new->flags |= HR_6;
4473 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004474 else
4475 {
4476 int nomem;
4477 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004478 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004479 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004480 {
4481 struct name_list *tmp = new->names, *next;
4482 for (tmp = new->names; tmp; tmp = next)
4483 {
4484 next = tmp->next;
4485 free(tmp);
4486 }
4487 ret_err_free(_("Bad name in host-record"), new);
4488 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004489
Petr Menšík59e47032018-11-02 22:39:39 +00004490 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004491 nl->name = canon;
4492 /* keep order, so that PTR record goes to first name */
4493 nl->next = NULL;
4494 if (!new->names)
4495 new->names = nl;
4496 else
4497 {
4498 struct name_list *tmp;
4499 for (tmp = new->names; tmp->next; tmp = tmp->next);
4500 tmp->next = nl;
4501 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004502 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004503
4504 arg = comma;
4505 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004506 }
Simon Kelleye759d422012-03-16 13:18:57 +00004507
4508 /* Keep list order */
4509 if (!daemon->host_records_tail)
4510 daemon->host_records = new;
4511 else
4512 daemon->host_records_tail->next = new;
4513 new->next = NULL;
4514 daemon->host_records_tail = new;
4515 break;
4516 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004517
4518#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004519 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004520 daemon->timestamp_file = opt_string_alloc(arg);
4521 break;
4522
Simon Kelleyf3e57872018-07-20 21:10:48 +01004523 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004524 if (arg)
4525 {
4526 if (strcmp(arg, "no") == 0)
4527 set_option_bool(OPT_DNSSEC_IGN_NS);
4528 else
4529 ret_err(_("bad value for dnssec-check-unsigned"));
4530 }
4531 break;
4532
Simon Kelleyf3e57872018-07-20 21:10:48 +01004533 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004534 {
Simon Kelleyee415862014-02-11 11:07:22 +00004535 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4536 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4537 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004538
4539 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004540 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004541
Simon Kelleycbf13a22014-01-25 17:59:14 +00004542 if ((comma = split(arg)) && (algo = split(comma)))
4543 {
4544 int class = 0;
4545 if (strcmp(comma, "IN") == 0)
4546 class = C_IN;
4547 else if (strcmp(comma, "CH") == 0)
4548 class = C_CHAOS;
4549 else if (strcmp(comma, "HS") == 0)
4550 class = C_HESIOD;
4551
4552 if (class != 0)
4553 {
4554 new->class = class;
4555 comma = algo;
4556 algo = split(comma);
4557 }
4558 }
4559
Simon Kelleyee415862014-02-11 11:07:22 +00004560 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4561 !atoi_check16(comma, &new->keytag) ||
4562 !atoi_check8(algo, &new->algo) ||
4563 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004564 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004565 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004566
Simon Kelley0fc2f312014-01-08 10:26:58 +00004567 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004568 len = (2*strlen(keyhex))+1;
4569 new->digest = opt_malloc(len);
4570 unhide_metas(keyhex);
4571 /* 4034: "Whitespace is allowed within digits" */
4572 for (cp = keyhex; *cp; )
4573 if (isspace(*cp))
4574 for (cp1 = cp; *cp1; cp1++)
4575 *cp1 = *(cp1+1);
4576 else
4577 cp++;
4578 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004579 {
4580 free(new->name);
4581 ret_err_free(_("bad HEX in trust anchor"), new);
4582 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004583
Simon Kelleyee415862014-02-11 11:07:22 +00004584 new->next = daemon->ds;
4585 daemon->ds = new;
4586
Simon Kelley0fc2f312014-01-08 10:26:58 +00004587 break;
4588 }
4589#endif
4590
Simon Kelley7622fc02009-06-04 20:32:05 +01004591 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004592 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004593
Simon Kelley849a8352006-06-09 21:02:31 +01004594 }
Simon Kelley824af852008-02-12 20:43:05 +00004595
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004596 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004597}
4598
Simon Kelley28866e92011-02-14 20:19:14 +00004599static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004600{
Simon Kelley824af852008-02-12 20:43:05 +00004601 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004602 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004603
4604 while (fgets(buff, MAXDNAME, f))
4605 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004606 int white, i;
4607 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004608 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004609 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004610
Simon Kelley824af852008-02-12 20:43:05 +00004611 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004612 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004613 {
4614 if (setjmp(mem_jmp))
4615 continue;
4616 mem_recover = 1;
4617 }
4618
Simon Kelley13dee6f2017-02-28 16:51:58 +00004619 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004620 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004621 errmess = NULL;
4622
Simon Kelley849a8352006-06-09 21:02:31 +01004623 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4624 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004625 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004626 {
4627 if (*p == '"')
4628 {
4629 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004630
Simon Kelley849a8352006-06-09 21:02:31 +01004631 for(; *p && *p != '"'; p++)
4632 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004633 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004634 {
4635 if (p[1] == 't')
4636 p[1] = '\t';
4637 else if (p[1] == 'n')
4638 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004639 else if (p[1] == 'b')
4640 p[1] = '\b';
4641 else if (p[1] == 'r')
4642 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004643 else if (p[1] == 'e') /* escape */
4644 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004645 memmove(p, p+1, strlen(p+1)+1);
4646 }
4647 *p = hide_meta(*p);
4648 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004649
4650 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004651 {
4652 errmess = _("missing \"");
4653 goto oops;
4654 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004655
4656 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004657 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004658
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004659 if (isspace(*p))
4660 {
4661 *p = ' ';
4662 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004663 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004664 else
4665 {
4666 if (white && *p == '#')
4667 {
4668 *p = 0;
4669 break;
4670 }
4671 white = 0;
4672 }
Simon Kelley849a8352006-06-09 21:02:31 +01004673 }
4674
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004675
4676 /* strip leading spaces */
4677 for (start = buff; *start && *start == ' '; start++);
4678
4679 /* strip trailing spaces */
4680 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4681
4682 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004683 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004684 else
4685 start[len] = 0;
4686
Simon Kelley611ebc52012-07-16 16:23:46 +01004687 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004688 arg = start;
4689 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004690 {
4691 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004692 for (arg = p+1; *arg == ' '; arg++);
4693 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004694 *p = 0;
4695 }
4696 else
4697 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004698
Simon Kelley611ebc52012-07-16 16:23:46 +01004699 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004700 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004701 for (option = 0, i = 0; opts[i].name; i++)
4702 if (strcmp(opts[i].name, start) == 0)
4703 {
4704 option = opts[i].val;
4705 break;
4706 }
4707
4708 if (!option)
4709 errmess = _("bad option");
4710 else if (opts[i].has_arg == 0 && arg)
4711 errmess = _("extraneous parameter");
4712 else if (opts[i].has_arg == 1 && !arg)
4713 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004714 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4715 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004716 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004717
4718 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004719 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004720 strcpy(daemon->namebuff, errmess);
4721
Simon Kelley9bafdc62018-08-21 22:53:38 +01004722 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004723 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004724 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004725 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004726 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004727 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004728 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004729 }
Simon Kelley849a8352006-06-09 21:02:31 +01004730 }
4731
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004732 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004733 fclose(f);
4734}
4735
Simon Kelley4f7bb572018-03-08 18:47:08 +00004736#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004737int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004738{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004739 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4740
Simon Kelley70d18732015-01-31 19:59:29 +00004741 if (flags & AH_DHCP_HST)
4742 return one_file(file, LOPT_BANK);
4743 else if (flags & AH_DHCP_OPT)
4744 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004745
Simon Kelley70d18732015-01-31 19:59:29 +00004746 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004747}
4748#endif
4749
Simon Kelley395eb712012-07-06 22:07:05 +01004750static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004751{
4752 FILE *f;
4753 int nofile_ok = 0;
4754 static int read_stdin = 0;
4755 static struct fileread {
4756 dev_t dev;
4757 ino_t ino;
4758 struct fileread *next;
4759 } *filesread = NULL;
4760
4761 if (hard_opt == '7')
4762 {
4763 /* default conf-file reading */
4764 hard_opt = 0;
4765 nofile_ok = 1;
4766 }
4767
4768 if (hard_opt == 0 && strcmp(file, "-") == 0)
4769 {
4770 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004771 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004772 read_stdin = 1;
4773 file = "stdin";
4774 f = stdin;
4775 }
4776 else
4777 {
4778 /* ignore repeated files. */
4779 struct stat statbuf;
4780
4781 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4782 {
4783 struct fileread *r;
4784
4785 for (r = filesread; r; r = r->next)
4786 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004787 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004788
4789 r = safe_malloc(sizeof(struct fileread));
4790 r->next = filesread;
4791 filesread = r;
4792 r->dev = statbuf.st_dev;
4793 r->ino = statbuf.st_ino;
4794 }
4795
4796 if (!(f = fopen(file, "r")))
4797 {
4798 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004799 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004800 else
4801 {
4802 char *str = _("cannot read %s: %s");
4803 if (hard_opt != 0)
4804 {
4805 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004806 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004807 }
4808 else
4809 die(str, file, EC_FILE);
4810 }
4811 }
4812 }
4813
4814 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004815 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004816}
4817
4818/* expand any name which is a directory */
4819struct hostsfile *expand_filelist(struct hostsfile *list)
4820{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004821 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004822 struct hostsfile *ah;
4823
Simon Kelley19c51cf2014-03-18 22:38:30 +00004824 /* find largest used index */
4825 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004826 {
4827 if (i <= ah->index)
4828 i = ah->index + 1;
4829
4830 if (ah->flags & AH_DIR)
4831 ah->flags |= AH_INACTIVE;
4832 else
4833 ah->flags &= ~AH_INACTIVE;
4834 }
4835
4836 for (ah = list; ah; ah = ah->next)
4837 if (!(ah->flags & AH_INACTIVE))
4838 {
4839 struct stat buf;
4840 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4841 {
4842 DIR *dir_stream;
4843 struct dirent *ent;
4844
4845 /* don't read this as a file */
4846 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004847
Simon Kelley28866e92011-02-14 20:19:14 +00004848 if (!(dir_stream = opendir(ah->fname)))
4849 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4850 ah->fname, strerror(errno));
4851 else
4852 {
4853 while ((ent = readdir(dir_stream)))
4854 {
4855 size_t lendir = strlen(ah->fname);
4856 size_t lenfile = strlen(ent->d_name);
4857 struct hostsfile *ah1;
4858 char *path;
4859
4860 /* ignore emacs backups and dotfiles */
4861 if (lenfile == 0 ||
4862 ent->d_name[lenfile - 1] == '~' ||
4863 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4864 ent->d_name[0] == '.')
4865 continue;
4866
4867 /* see if we have an existing record.
4868 dir is ah->fname
4869 file is ent->d_name
4870 path to match is ah1->fname */
4871
4872 for (ah1 = list; ah1; ah1 = ah1->next)
4873 {
4874 if (lendir < strlen(ah1->fname) &&
4875 strstr(ah1->fname, ah->fname) == ah1->fname &&
4876 ah1->fname[lendir] == '/' &&
4877 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4878 {
4879 ah1->flags &= ~AH_INACTIVE;
4880 break;
4881 }
4882 }
4883
4884 /* make new record */
4885 if (!ah1)
4886 {
4887 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4888 continue;
4889
4890 if (!(path = whine_malloc(lendir + lenfile + 2)))
4891 {
4892 free(ah1);
4893 continue;
4894 }
4895
4896 strcpy(path, ah->fname);
4897 strcat(path, "/");
4898 strcat(path, ent->d_name);
4899 ah1->fname = path;
4900 ah1->index = i++;
4901 ah1->flags = AH_DIR;
4902 ah1->next = list;
4903 list = ah1;
4904 }
4905
4906 /* inactivate record if not regular file */
4907 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4908 ah1->flags |= AH_INACTIVE;
4909
4910 }
4911 closedir(dir_stream);
4912 }
4913 }
4914 }
4915
4916 return list;
4917}
4918
Simon Kelley7b1eae42014-02-20 13:43:28 +00004919void read_servers_file(void)
4920{
4921 FILE *f;
4922
4923 if (!(f = fopen(daemon->servers_file, "r")))
4924 {
4925 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4926 return;
4927 }
4928
4929 mark_servers(SERV_FROM_FILE);
4930 cleanup_servers();
4931
4932 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4933}
4934
Simon Kelley28866e92011-02-14 20:19:14 +00004935
Simon Kelley7622fc02009-06-04 20:32:05 +01004936#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004937static void clear_dynamic_conf(void)
4938{
4939 struct dhcp_config *configs, *cp, **up;
4940
4941 /* remove existing... */
4942 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4943 {
4944 cp = configs->next;
4945
4946 if (configs->flags & CONFIG_BANK)
4947 {
4948 struct hwaddr_config *mac, *tmp;
4949 struct dhcp_netid_list *list, *tmplist;
4950
4951 for (mac = configs->hwaddr; mac; mac = tmp)
4952 {
4953 tmp = mac->next;
4954 free(mac);
4955 }
4956
4957 if (configs->flags & CONFIG_CLID)
4958 free(configs->clid);
4959
4960 for (list = configs->netid; list; list = tmplist)
4961 {
4962 free(list->list);
4963 tmplist = list->next;
4964 free(list);
4965 }
4966
4967 if (configs->flags & CONFIG_NAME)
4968 free(configs->hostname);
4969
4970 *up = configs->next;
4971 free(configs);
4972 }
4973 else
4974 up = &configs->next;
4975 }
4976}
4977
4978static void clear_dynamic_opt(void)
4979{
4980 struct dhcp_opt *opts, *cp, **up;
4981 struct dhcp_netid *id, *next;
4982
4983 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4984 {
4985 cp = opts->next;
4986
4987 if (opts->flags & DHOPT_BANK)
4988 {
4989 if ((opts->flags & DHOPT_VENDOR))
4990 free(opts->u.vendor_class);
4991 free(opts->val);
4992 for (id = opts->netid; id; id = next)
4993 {
4994 next = id->next;
4995 free(id->net);
4996 free(id);
4997 }
4998 *up = opts->next;
4999 free(opts);
5000 }
5001 else
5002 up = &opts->next;
5003 }
5004}
5005
Simon Kelley824af852008-02-12 20:43:05 +00005006void reread_dhcp(void)
5007{
Simon Kelley4f7bb572018-03-08 18:47:08 +00005008 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00005009
Simon Kelley4f7bb572018-03-08 18:47:08 +00005010 /* Do these even if there is no daemon->dhcp_hosts_file or
5011 daemon->dhcp_opts_file since entries may have been created by the
5012 inotify dynamic file reading system. */
5013
5014 clear_dynamic_conf();
5015 clear_dynamic_opt();
5016
5017 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00005018 {
Simon Kelley28866e92011-02-14 20:19:14 +00005019 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
5020 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00005021 if (!(hf->flags & AH_INACTIVE))
5022 {
5023 if (one_file(hf->fname, LOPT_BANK))
5024 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
5025 }
Simon Kelley824af852008-02-12 20:43:05 +00005026 }
5027
5028 if (daemon->dhcp_opts_file)
5029 {
Simon Kelley28866e92011-02-14 20:19:14 +00005030 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
5031 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
5032 if (!(hf->flags & AH_INACTIVE))
5033 {
Simon Kelley395eb712012-07-06 22:07:05 +01005034 if (one_file(hf->fname, LOPT_OPTS))
5035 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00005036 }
Simon Kelley824af852008-02-12 20:43:05 +00005037 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00005038
5039# ifdef HAVE_INOTIFY
5040 /* Setup notify and read pre-existing files. */
5041 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
5042# endif
Simon Kelley824af852008-02-12 20:43:05 +00005043}
Simon Kelley7622fc02009-06-04 20:32:05 +01005044#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00005045
Simon Kelley5aabfc72007-08-29 11:24:47 +01005046void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005047{
Neil Jerram3bd4c472018-01-18 22:49:38 +00005048 size_t argbuf_size = MAXDNAME;
5049 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00005050 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00005051 int option, testmode = 0;
5052 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01005053
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005054 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01005055
Simon Kelley824af852008-02-12 20:43:05 +00005056 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01005057 memset(daemon, 0, sizeof(struct daemon));
5058 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005059
Simon Kelley3be34542004-09-11 19:12:13 +01005060 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005061 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005062 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005063 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005064 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5065 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005066 daemon->default_resolv.is_default = 1;
5067 daemon->default_resolv.name = RESOLVFILE;
5068 daemon->resolv_files = &daemon->default_resolv;
5069 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005070 daemon->runfile = RUNFILE;
5071 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005072 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005073 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005074 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005075 daemon->auth_ttl = AUTH_TTL;
5076 daemon->soa_refresh = SOA_REFRESH;
5077 daemon->soa_retry = SOA_RETRY;
5078 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley4a8c0982021-03-26 21:19:39 +00005079
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005080#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005081 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5082 add_txt("authors.bind", "Simon Kelley", 0);
5083 add_txt("copyright.bind", COPYRIGHT, 0);
5084 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5085 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5086 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5087 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5088 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5089#ifdef HAVE_AUTH
5090 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5091#endif
5092 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005093#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005094
Simon Kelley849a8352006-06-09 21:02:31 +01005095 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005096 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005097#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005098 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005099#else
Simon Kelley849a8352006-06-09 21:02:31 +01005100 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005101#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005102
5103 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005104 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005105 for (; optind < argc; optind++)
5106 {
5107 unsigned char *c = (unsigned char *)argv[optind];
5108 for (; *c != 0; c++)
5109 if (!isspace(*c))
5110 die(_("junk found in command line"), NULL, EC_BADCONF);
5111 }
Simon Kelley28866e92011-02-14 20:19:14 +00005112 break;
5113 }
5114
Simon Kelley849a8352006-06-09 21:02:31 +01005115 /* Copy optarg so that argv doesn't get changed */
5116 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005117 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005118 if (strlen(optarg) >= argbuf_size)
5119 {
5120 free(argbuf);
5121 argbuf_size = strlen(optarg) + 1;
5122 argbuf = opt_malloc(argbuf_size);
5123 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005124 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005125 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005126 }
5127 else
5128 arg = NULL;
5129
5130 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005131 if (option == LOPT_TEST)
5132 testmode = 1;
5133 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005134 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005135#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005136 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005137 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005138#ifdef HAVE_DHCP6
5139 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5140 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005141#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005142 else
5143#endif
5144 do_usage();
5145
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005146 exit(0);
5147 }
Simon Kelley849a8352006-06-09 21:02:31 +01005148 else if (option == 'v')
5149 {
5150 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005151 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005152 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5153 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005154 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005155 exit(0);
5156 }
Simon Kelley849a8352006-06-09 21:02:31 +01005157 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005158 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005159 if (!conffile)
5160 conffile = opt_string_alloc(arg);
5161 else
5162 {
5163 char *extra = opt_string_alloc(arg);
5164 one_file(extra, 0);
5165 free(extra);
5166 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005167 }
Simon Kelley849a8352006-06-09 21:02:31 +01005168 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005169 {
Simon Kelley26128d22004-11-14 16:43:54 +00005170#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005171 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005172#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005173 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005174#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005175 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005176 }
5177 }
Simon Kelley849a8352006-06-09 21:02:31 +01005178
Neil Jerram3bd4c472018-01-18 22:49:38 +00005179 free(argbuf);
5180
Simon Kelley849a8352006-06-09 21:02:31 +01005181 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005182 {
Petr Menšík59e47032018-11-02 22:39:39 +00005183 one_file(conffile, 0);
5184 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005185 }
Petr Menšík59e47032018-11-02 22:39:39 +00005186 else
5187 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005188
Simon Kelley1a6bca82008-07-11 11:11:42 +01005189 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005190 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005191 {
5192 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005193 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005194 if (!(tmp->flags & SERV_HAS_SOURCE))
5195 {
5196 if (tmp->source_addr.sa.sa_family == AF_INET)
5197 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005198 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5199 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005200 }
5201 }
5202
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005203 if (daemon->host_records)
5204 {
5205 struct host_record *hr;
5206
5207 for (hr = daemon->host_records; hr; hr = hr->next)
5208 if (hr->ttl == -1)
5209 hr->ttl = daemon->local_ttl;
5210 }
5211
5212 if (daemon->cnames)
5213 {
Simon Kelley903df072017-01-19 17:22:00 +00005214 struct cname *cn, *cn2, *cn3;
5215
5216#define NOLOOP 1
5217#define TESTLOOP 2
5218
Simon Kelley157d8cf2019-10-25 17:46:49 +01005219 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005220 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005221 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005222 {
5223 if (cn->ttl == -1)
5224 cn->ttl = daemon->local_ttl;
5225 cn->flag = 0;
5226 cn->targetp = NULL;
5227 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5228 if (hostname_isequal(cn->target, cn2->alias))
5229 {
5230 cn->targetp = cn2;
5231 break;
5232 }
5233 }
5234
5235 /* Find any CNAME loops.*/
5236 for (cn = daemon->cnames; cn; cn = cn->next)
5237 {
5238 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5239 {
5240 if (cn2->flag == NOLOOP)
5241 break;
5242
5243 if (cn2->flag == TESTLOOP)
5244 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5245
5246 cn2->flag = TESTLOOP;
5247 }
5248
5249 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5250 cn3->flag = NOLOOP;
5251 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005252 }
5253
Simon Kelley3be34542004-09-11 19:12:13 +01005254 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005255 {
5256 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005257 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005258 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005259 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005260 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005261 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005262 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005263
5264 /* create default, if not specified */
5265 if (daemon->authserver && !daemon->hostmaster)
5266 {
5267 strcpy(buff, "hostmaster.");
5268 strcat(buff, daemon->authserver);
5269 daemon->hostmaster = opt_string_alloc(buff);
5270 }
Wang Shanker4ded9622020-12-04 10:17:35 +08005271
5272 if (!daemon->dhcp_pxe_vendors)
5273 {
5274 daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
5275 daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
5276 daemon->dhcp_pxe_vendors->next = NULL;
5277 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005278
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005279 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005280 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005281 {
Simon Kelley0a852542005-03-23 20:28:59 +00005282 struct mx_srv_record *mx;
5283
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005284 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005285 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005286
Simon Kelley0a852542005-03-23 20:28:59 +00005287 for (mx = daemon->mxnames; mx; mx = mx->next)
5288 if (!mx->issrv && hostname_isequal(mx->name, buff))
5289 break;
5290
Simon Kelley28866e92011-02-14 20:19:14 +00005291 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005292 {
Simon Kelley824af852008-02-12 20:43:05 +00005293 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005294 mx->next = daemon->mxnames;
5295 mx->issrv = 0;
5296 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005297 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005298 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005299 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005300
Simon Kelley3be34542004-09-11 19:12:13 +01005301 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005302 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005303
5304 for (mx = daemon->mxnames; mx; mx = mx->next)
5305 if (!mx->issrv && !mx->target)
5306 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005307 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005308
Simon Kelley28866e92011-02-14 20:19:14 +00005309 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005310 daemon->resolv_files &&
5311 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005312 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005313 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005314
Simon Kelley28866e92011-02-14 20:19:14 +00005315 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005316 {
5317 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005318 FILE *f;
5319
Simon Kelley28866e92011-02-14 20:19:14 +00005320 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005321 !daemon->resolv_files ||
5322 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005323 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005324
Simon Kelley3be34542004-09-11 19:12:13 +01005325 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005326 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005327
5328 while ((line = fgets(buff, MAXDNAME, f)))
5329 {
5330 char *token = strtok(line, " \t\n\r");
5331
5332 if (!token || strcmp(token, "search") != 0)
5333 continue;
5334
5335 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005336 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005337 break;
5338 }
Simon Kelley3be34542004-09-11 19:12:13 +01005339
Simon Kelleyde379512004-06-22 20:23:33 +01005340 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005341
Simon Kelley3be34542004-09-11 19:12:13 +01005342 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005343 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005344 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005345
5346 if (daemon->domain_suffix)
5347 {
5348 /* add domain for any srv record without one. */
5349 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005350
Simon Kelley3d8df262005-08-29 12:19:27 +01005351 for (srv = daemon->mxnames; srv; srv = srv->next)
5352 if (srv->issrv &&
5353 strchr(srv->name, '.') &&
5354 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5355 {
5356 strcpy(buff, srv->name);
5357 strcat(buff, ".");
5358 strcat(buff, daemon->domain_suffix);
5359 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005360 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005361 }
5362 }
Simon Kelley28866e92011-02-14 20:19:14 +00005363 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005364 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005365
Simon Kelleyc8a80482014-03-05 14:29:54 +00005366 /* If there's access-control config, then ignore --local-service, it's intended
5367 as a system default to keep otherwise unconfigured installations safe. */
5368 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5369 reset_option_bool(OPT_LOCAL_SERVICE);
5370
Simon Kelley7622fc02009-06-04 20:32:05 +01005371 if (testmode)
5372 {
5373 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5374 exit(0);
5375 }
Simon Kelley849a8352006-06-09 21:02:31 +01005376}