blob: fed17a9ac8d3d2de5b0cfb2a646d5151b47d4ff6 [file] [log] [blame]
Simon Kelley2a8710a2020-01-05 16:40:06 +00001/* dnsmasq is Copyright (c) 2000-2020 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
Simon Kelleybec366b2016-02-24 22:03:26 +0000170
Simon Kelley849a8352006-06-09 21:02:31 +0100171#ifdef HAVE_GETOPT_LONG
172static const struct option opts[] =
173#else
174static const struct myoption opts[] =
175#endif
176 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100177 { "version", 0, 0, 'v' },
178 { "no-hosts", 0, 0, 'h' },
179 { "no-poll", 0, 0, 'n' },
180 { "help", 0, 0, 'w' },
181 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000182 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100183 { "user", 2, 0, 'u' },
184 { "group", 2, 0, 'g' },
185 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000186 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100187 { "mx-host", 1, 0, 'm' },
188 { "mx-target", 1, 0, 't' },
189 { "cache-size", 2, 0, 'c' },
190 { "port", 1, 0, 'p' },
191 { "dhcp-leasefile", 2, 0, 'l' },
192 { "dhcp-lease", 1, 0, 'l' },
193 { "dhcp-host", 1, 0, 'G' },
194 { "dhcp-range", 1, 0, 'F' },
195 { "dhcp-option", 1, 0, 'O' },
196 { "dhcp-boot", 1, 0, 'M' },
197 { "domain", 1, 0, 's' },
198 { "domain-suffix", 1, 0, 's' },
199 { "interface", 1, 0, 'i' },
200 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000201 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100202 { "bogus-priv", 0, 0, 'b' },
203 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000204 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100205 { "selfmx", 0, 0, 'e' },
206 { "filterwin2k", 0, 0, 'f' },
207 { "pid-file", 2, 0, 'x' },
208 { "strict-order", 0, 0, 'o' },
209 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000210 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100211 { "local", 1, 0, LOPT_LOCAL },
212 { "address", 1, 0, 'A' },
213 { "conf-file", 2, 0, 'C' },
214 { "no-resolv", 0, 0, 'R' },
215 { "expand-hosts", 0, 0, 'E' },
216 { "localmx", 0, 0, 'L' },
217 { "local-ttl", 1, 0, 'T' },
218 { "no-negcache", 0, 0, 'N' },
219 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000220 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100221 { "query-port", 1, 0, 'Q' },
222 { "except-interface", 1, 0, 'I' },
223 { "no-dhcp-interface", 1, 0, '2' },
224 { "domain-needed", 0, 0, 'D' },
225 { "dhcp-lease-max", 1, 0, 'X' },
226 { "bind-interfaces", 0, 0, 'z' },
227 { "read-ethers", 0, 0, 'Z' },
228 { "alias", 1, 0, 'V' },
229 { "dhcp-vendorclass", 1, 0, 'U' },
230 { "dhcp-userclass", 1, 0, 'j' },
231 { "dhcp-ignore", 1, 0, 'J' },
232 { "edns-packet-max", 1, 0, 'P' },
233 { "keep-in-foreground", 0, 0, 'k' },
234 { "dhcp-authoritative", 0, 0, 'K' },
235 { "srv-host", 1, 0, 'W' },
236 { "localise-queries", 0, 0, 'y' },
237 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100238 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100239 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100240 { "enable-dbus", 2, 0, '1' },
Oldřich Jedličkad162bee2020-03-20 22:18:57 +0100241 { "enable-ubus", 2, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100242 { "bootp-dynamic", 2, 0, '3' },
243 { "dhcp-mac", 1, 0, '4' },
244 { "no-ping", 0, 0, '5' },
245 { "dhcp-script", 1, 0, '6' },
246 { "conf-dir", 1, 0, '7' },
247 { "log-facility", 1, 0 ,'8' },
248 { "leasefile-ro", 0, 0, '9' },
Simon Kelleyee645822020-02-27 16:34:14 +0000249 { "script-on-renewal", 0, 0, LOPT_SCRIPT_TIME},
Simon Kelley7622fc02009-06-04 20:32:05 +0100250 { "dns-forward-max", 1, 0, '0' },
251 { "clear-on-reload", 0, 0, LOPT_RELOAD },
252 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100253 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100254 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100255 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100256 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100257 { "tftp-root", 1, 0, LOPT_PREFIX },
258 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000259 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100260 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley66f62652020-01-05 16:21:24 +0000261 { "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100262 { "ptr-record", 1, 0, LOPT_PTR },
263 { "naptr-record", 1, 0, LOPT_NAPTR },
264 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000265 { "shared-network", 1, 0, LOPT_SHARED_NET },
Simon Kelley7622fc02009-06-04 20:32:05 +0100266 { "dhcp-option-force", 1, 0, LOPT_FORCE },
267 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
268 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
269 { "log-async", 2, 0, LOPT_MAX_LOGS },
270 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
271 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
272 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
273 { "interface-name", 1, 0, LOPT_INTNAME },
274 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
275 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000276 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000277 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100278 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
279 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
280 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100281 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100282 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100283 { "dhcp-match", 1, 0, LOPT_MATCH },
284 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100285 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100286 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100287 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000288 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100289 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100290 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
291 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
292 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000293 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100294 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
295 { "cname", 1, 0, LOPT_CNAME },
296 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
297 { "pxe-service", 1, 0, LOPT_PXE_SERV },
298 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100299 { "tag-if", 1, 0, LOPT_TAG_IF },
300 { "dhcp-proxy", 2, 0, LOPT_PROXY },
301 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
302 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000303 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100304 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000305 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000306 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100307 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
308 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000309 { "dhcp-client-update", 0, 0, LOPT_FQDN },
310 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000311 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000312 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000313 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100314 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000315 { "auth-zone", 1, 0, LOPT_AUTHZONE },
316 { "auth-server", 1, 0, LOPT_AUTHSERV },
317 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
318 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000319 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000320 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000321 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100322 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200323 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000324 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000325 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100326 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000327 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000328 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100329 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100330 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100331 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
332 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
333 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100334 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000335 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000336 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100337 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000338 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100339 { "dumpfile", 1, 0, LOPT_DUMPFILE },
340 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Florent Fourcot13a58f92019-06-20 10:26:40 +0200341 { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
Simon Kelley849a8352006-06-09 21:02:31 +0100342 { NULL, 0, 0, 0 }
343 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000344
Simon Kelley28866e92011-02-14 20:19:14 +0000345
346#define ARG_DUP OPT_LAST
347#define ARG_ONE OPT_LAST + 1
348#define ARG_USED_CL OPT_LAST + 2
349#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000350
Simon Kelley1a6bca82008-07-11 11:11:42 +0100351static struct {
352 int opt;
353 unsigned int rept;
354 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000355 char * const desc;
356 char * const arg;
357} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000358 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
359 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100360 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000361 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
362 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
363 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100364 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
365 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
366 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
367 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
368 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000369 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
370 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100371 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000372 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
373 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000374 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000375 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100376 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { '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 +0000379 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000380 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
381 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100382 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
383 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
384 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
385 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
386 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
387 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100388 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
389 { '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 +0000390 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100391 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000392 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100393 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
394 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
395 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
396 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
397 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
398 { 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 +0000399 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
400 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000401 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000402 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100403 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000404 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000405 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000406 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000407 { 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 +0000408 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000409 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000410 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
411 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
412 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
413 { 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 +0000414 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
415 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000416 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100417 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100418 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000419 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
420 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100421 { '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 +0000422 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
423 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100424 { '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 +0000425 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
426 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
427 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100428 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
429 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100430 { '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 +0100431 { LOPT_UBUS, ARG_ONE, "[=<busname>]", gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000432 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100433 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
434 { '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 +0000435 { 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 +0000436 { 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 +0100437 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000438 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
439 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
440 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000441 { 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 +0000442 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000443 { '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 +0100444 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000445 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100446 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100447 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100448 { 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 +0100449 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100450 { 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 +0100451 { 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 +0100452 { 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 +0100453 { 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 +0000454 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000455 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100456 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100457 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100458 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
Simon Kelley66f62652020-01-05 16:21:24 +0000459 { LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100460 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000461 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100462 { 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 +0100463 { 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 +0000464 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100465 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100466 { 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 +0100467 { 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 +0100468 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100469 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
470 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000471 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000472 { 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 +0000473 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
474 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100475 { 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 +0000476 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100477 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
478 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
479 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000480 { 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 +0100481 { 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 +0000482 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100483 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100484 { 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 +0200485 { 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 +0100486 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000487 { 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 +0000488 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000489 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000490 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley974a6d02018-08-23 23:01:16 +0100491 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100492 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000493 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000494 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000495 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000496 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000497 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000498 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
499 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100500 { 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 +0100501 { 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 +0000502 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000503 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000504 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100505 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000506 { 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 +0000507 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100508 { 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 +0100509 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
510 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
511 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000512 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
513 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000514 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000515 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100516 { 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 +0000517 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100518 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
519 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelleyee645822020-02-27 16:34:14 +0000520 { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100521 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000522};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000523
Josh Soref730c6742017-02-06 16:14:04 +0000524/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100525 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 +0100526 following sequence so that they map to themselves: it is therefore possible to call
527 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000528 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100529 couple of other places.
530 Note that space is included here so that
531 --dhcp-option=3, string
532 has five characters, whilst
533 --dhcp-option=3," string"
534 has six.
535*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100536
Simon Kelleyf2621c72007-04-29 19:47:21 +0100537static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100538
539static char hide_meta(char c)
540{
541 unsigned int i;
542
543 for (i = 0; i < (sizeof(meta) - 1); i++)
544 if (c == meta[i])
545 return (char)i;
546
547 return c;
548}
549
550static char unhide_meta(char cr)
551{
552 unsigned int c = cr;
553
554 if (c < (sizeof(meta) - 1))
555 cr = meta[c];
556
557 return cr;
558}
559
560static void unhide_metas(char *cp)
561{
562 if (cp)
563 for(; *cp; cp++)
564 *cp = unhide_meta(*cp);
565}
566
Simon Kelley824af852008-02-12 20:43:05 +0000567static void *opt_malloc(size_t size)
568{
569 void *ret;
570
571 if (mem_recover)
572 {
573 ret = whine_malloc(size);
574 if (!ret)
575 longjmp(mem_jmp, 1);
576 }
577 else
578 ret = safe_malloc(size);
579
580 return ret;
581}
582
Petr Menšík59e47032018-11-02 22:39:39 +0000583static char *opt_string_alloc(const char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100584{
585 char *ret = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +0000586 size_t len;
Simon Kelley3d8df262005-08-29 12:19:27 +0100587
Petr Menšík59e47032018-11-02 22:39:39 +0000588 if (cp && (len = strlen(cp)) != 0)
Simon Kelley3d8df262005-08-29 12:19:27 +0100589 {
Petr Menšík59e47032018-11-02 22:39:39 +0000590 ret = opt_malloc(len+1);
591 memcpy(ret, cp, len+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100592
593 /* restore hidden metachars */
594 unhide_metas(ret);
595 }
596
597 return ret;
598}
599
Simon Kelley3d8df262005-08-29 12:19:27 +0100600
Simon Kelleyf2621c72007-04-29 19:47:21 +0100601/* find next comma, split string with zero and eliminate spaces.
602 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000603
604static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100605{
606 char *comma, *p;
607
Simon Kelley73a08a22009-02-05 20:28:08 +0000608 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100609 return NULL;
610
611 p = comma;
612 *comma = ' ';
613
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100614 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100615
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100616 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100617 *p = 0;
618
619 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100620}
621
Simon Kelley73a08a22009-02-05 20:28:08 +0000622static char *split(char *s)
623{
624 return split_chr(s, ',');
625}
626
Simon Kelley1f15b812009-10-13 17:49:32 +0100627static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100628{
Simon Kelley1f15b812009-10-13 17:49:32 +0100629 char *ret;
630 int nomem;
631
Simon Kelley3d8df262005-08-29 12:19:27 +0100632 if (!s)
633 return 0;
634
635 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100636 if (!(ret = canonicalise(s, &nomem)) && nomem)
637 {
638 if (mem_recover)
639 longjmp(mem_jmp, 1);
640 else
641 die(_("could not get memory"), NULL, EC_NOMEM);
642 }
643
644 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100645}
646
647static int atoi_check(char *a, int *res)
648{
649 char *p;
650
651 if (!a)
652 return 0;
653
654 unhide_metas(a);
655
656 for (p = a; *p; p++)
657 if (*p < '0' || *p > '9')
658 return 0;
659
660 *res = atoi(a);
661 return 1;
662}
663
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100664static int atoi_check16(char *a, int *res)
665{
666 if (!(atoi_check(a, res)) ||
667 *res < 0 ||
668 *res > 0xffff)
669 return 0;
670
671 return 1;
672}
Simon Kelleyee415862014-02-11 11:07:22 +0000673
Simon Kelleyde73a492014-02-17 21:43:27 +0000674#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000675static int atoi_check8(char *a, int *res)
676{
677 if (!(atoi_check(a, res)) ||
678 *res < 0 ||
679 *res > 0xff)
680 return 0;
681
682 return 1;
683}
Simon Kelleyde73a492014-02-17 21:43:27 +0000684#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100685
686#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000687static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000688{
Simon Kelley824af852008-02-12 20:43:05 +0000689 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000690
691 if (txt)
692 {
693 size_t len = strlen(txt);
694 r->txt = opt_malloc(len+1);
695 r->len = len+1;
696 *(r->txt) = len;
697 memcpy((r->txt)+1, txt, len);
698 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100699
Simon Kelleyfec216d2014-03-27 20:54:34 +0000700 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000701 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000702 r->next = daemon->txt;
703 daemon->txt = r;
704 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000705}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100706#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000707
Simon Kelley849a8352006-06-09 21:02:31 +0100708static void do_usage(void)
709{
710 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000711 int i, j;
712
713 struct {
714 char handle;
715 int val;
716 } tab[] = {
717 { '$', CACHESIZ },
718 { '*', EDNS_PKTSZ },
719 { '&', MAXLEASES },
720 { '!', FTABSIZ },
721 { '#', TFTP_MAX_CONNECTIONS },
722 { '\0', 0 }
723 };
Simon Kelley849a8352006-06-09 21:02:31 +0100724
725 printf(_("Usage: dnsmasq [options]\n\n"));
726#ifndef HAVE_GETOPT_LONG
727 printf(_("Use short options only on the command line.\n"));
728#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100729 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100730
Simon Kelley1a6bca82008-07-11 11:11:42 +0100731 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100732 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100733 char *desc = usage[i].flagdesc;
734 char *eq = "=";
735
736 if (!desc || *desc == '[')
737 eq = "";
738
739 if (!desc)
740 desc = "";
741
742 for ( j = 0; opts[j].name; j++)
743 if (opts[j].val == usage[i].opt)
744 break;
745 if (usage[i].opt < 256)
746 sprintf(buff, "-%c, ", usage[i].opt);
747 else
748 sprintf(buff, " ");
749
750 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100751 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100752
Simon Kelley849a8352006-06-09 21:02:31 +0100753 if (usage[i].arg)
754 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000755 strcpy(buff, usage[i].arg);
756 for (j = 0; tab[j].handle; j++)
757 if (tab[j].handle == *(usage[i].arg))
758 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100759 }
Simon Kelley849a8352006-06-09 21:02:31 +0100760 printf(_(usage[i].desc), buff);
761 printf("\n");
762 }
763}
764
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100765#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
Petr Menšík59e47032018-11-02 22:39:39 +0000766#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
767#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100768
Ed Bardsleya7369be2015-08-05 21:17:18 +0100769static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
770{
771 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
772 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100773 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
774 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100775 else
776 return _("bad address");
777
778 return NULL;
779}
780
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100781char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
782{
783 int source_port = 0, serv_port = NAMESERVER_PORT;
784 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000785 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100786 int scope_index = 0;
787 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100788
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000789 if (!arg || strlen(arg) == 0)
790 {
791 *flags |= SERV_NO_ADDR;
792 *interface = 0;
793 return NULL;
794 }
795
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100796 if ((source = split_chr(arg, '@')) && /* is there a source. */
797 (portno = split_chr(source, '#')) &&
798 !atoi_check16(portno, &source_port))
799 return _("bad port");
800
801 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
802 !atoi_check16(portno, &serv_port))
803 return _("bad port");
804
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100805 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100806
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000807 if (source) {
808 interface_opt = split_chr(source, '@');
809
810 if (interface_opt)
811 {
812#if defined(SO_BINDTODEVICE)
Petr Menšík47b45b22018-08-15 18:17:00 +0200813 safe_strncpy(interface, interface_opt, IF_NAMESIZE);
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000814#else
815 return _("interface binding not supported");
816#endif
817 }
818 }
819
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100820 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100821 {
822 addr->in.sin_port = htons(serv_port);
823 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
824#ifdef HAVE_SOCKADDR_SA_LEN
825 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
826#endif
827 source_addr->in.sin_addr.s_addr = INADDR_ANY;
828 source_addr->in.sin_port = htons(daemon->query_port);
829
830 if (source)
831 {
832 if (flags)
833 *flags |= SERV_HAS_SOURCE;
834 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100835 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100836 {
837#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000838 if (interface_opt)
839 return _("interface can only be specified once");
840
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100841 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200842 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100843#else
844 return _("interface binding not supported");
845#endif
846 }
847 }
848 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100849 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
850 {
851 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
852 return _("bad interface name");
853
854 addr->in6.sin6_port = htons(serv_port);
855 addr->in6.sin6_scope_id = scope_index;
856 source_addr->in6.sin6_addr = in6addr_any;
857 source_addr->in6.sin6_port = htons(daemon->query_port);
858 source_addr->in6.sin6_scope_id = 0;
859 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
860 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
861#ifdef HAVE_SOCKADDR_SA_LEN
862 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
863#endif
864 if (source)
865 {
866 if (flags)
867 *flags |= SERV_HAS_SOURCE;
868 source_addr->in6.sin6_port = htons(source_port);
869 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
870 {
871#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000872 if (interface_opt)
873 return _("interface can only be specified once");
874
875 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200876 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100877#else
878 return _("interface binding not supported");
879#endif
880 }
881 }
882 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100883 else
884 return _("bad address");
885
886 return NULL;
887}
888
Simon Kelleyde73a492014-02-17 21:43:27 +0000889static struct server *add_rev4(struct in_addr addr, int msize)
890{
891 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000892 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000893 char *p;
894
895 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000896 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
897
898 switch (msize)
899 {
900 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100901 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000902 /* fall through */
903 case 24:
904 p += sprintf(p, "%d.", (a >> 8) & 0xff);
905 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000906 case 16:
907 p += sprintf(p, "%d.", (a >> 16) & 0xff);
908 /* fall through */
909 case 8:
910 p += sprintf(p, "%d.", (a >> 24) & 0xff);
911 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000912 default:
Petr Menšík59e47032018-11-02 22:39:39 +0000913 free(serv->domain);
914 free(serv);
Olivier Gayotdc990582017-03-06 22:17:21 +0000915 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000916 }
917
918 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000919
920 serv->flags = SERV_HAS_DOMAIN;
921 serv->next = daemon->servers;
922 daemon->servers = serv;
923
924 return serv;
925
926}
927
928static struct server *add_rev6(struct in6_addr *addr, int msize)
929{
930 struct server *serv = opt_malloc(sizeof(struct server));
931 char *p;
932 int i;
933
934 memset(serv, 0, sizeof(struct server));
935 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
936
937 for (i = msize-1; i >= 0; i -= 4)
938 {
939 int dig = ((unsigned char *)addr)[i>>3];
940 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
941 }
942 p += sprintf(p, "ip6.arpa");
943
944 serv->flags = SERV_HAS_DOMAIN;
945 serv->next = daemon->servers;
946 daemon->servers = serv;
947
948 return serv;
949}
950
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000951#ifdef HAVE_DHCP
952
953static int is_tag_prefix(char *arg)
954{
955 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
956 return 1;
957
958 return 0;
959}
960
961static char *set_prefix(char *arg)
962{
963 if (strstr(arg, "set:") == arg)
964 return arg+4;
965
966 return arg;
967}
968
Simon Kelley52ec7832020-02-07 21:05:54 +0000969static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
Petr Menšík59e47032018-11-02 22:39:39 +0000970{
971 struct dhcp_netid *tt;
972 tt = opt_malloc(sizeof (struct dhcp_netid));
973 tt->net = opt_string_alloc(net);
974 tt->next = next;
975 return tt;
976}
977
978static void dhcp_netid_free(struct dhcp_netid *nid)
979{
980 while (nid)
981 {
982 struct dhcp_netid *tmp = nid;
983 nid = nid->next;
984 free(tmp->net);
985 free(tmp);
986 }
987}
988
989/* Parse one or more tag:s before parameters.
990 * Moves arg to the end of tags. */
991static struct dhcp_netid * dhcp_tags(char **arg)
992{
993 struct dhcp_netid *id = NULL;
994
995 while (is_tag_prefix(*arg))
996 {
997 char *comma = split(*arg);
998 id = dhcp_netid_create((*arg)+4, id);
999 *arg = comma;
1000 };
1001 if (!*arg)
1002 {
1003 dhcp_netid_free(id);
1004 id = NULL;
1005 }
1006 return id;
1007}
1008
1009static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
1010{
1011 while (netid)
1012 {
1013 struct dhcp_netid_list *tmplist = netid;
1014 netid = netid->next;
1015 dhcp_netid_free(tmplist->list);
1016 free(tmplist);
1017 }
1018}
1019
1020static void dhcp_config_free(struct dhcp_config *config)
1021{
1022 if (config)
1023 {
1024 struct hwaddr_config *hwaddr = config->hwaddr;
Simon Kelley137286e2020-02-06 22:09:30 +00001025
Petr Menšík59e47032018-11-02 22:39:39 +00001026 while (hwaddr)
1027 {
1028 struct hwaddr_config *tmp = hwaddr;
1029 hwaddr = hwaddr->next;
1030 free(tmp);
1031 }
Simon Kelley137286e2020-02-06 22:09:30 +00001032
Petr Menšík59e47032018-11-02 22:39:39 +00001033 dhcp_netid_list_free(config->netid);
Simon Kelley52ec7832020-02-07 21:05:54 +00001034 dhcp_netid_free(config->filter);
1035
Petr Menšík59e47032018-11-02 22:39:39 +00001036 if (config->flags & CONFIG_CLID)
1037 free(config->clid);
Simon Kelley137286e2020-02-06 22:09:30 +00001038
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001039#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00001040 if (config->flags & CONFIG_ADDR6)
1041 {
1042 struct addrlist *addr, *tmp;
1043
1044 for (addr = config->addr6; addr; addr = tmp)
1045 {
1046 tmp = addr->next;
1047 free(addr);
1048 }
1049 }
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00001050#endif
Simon Kelley137286e2020-02-06 22:09:30 +00001051
Petr Menšík59e47032018-11-02 22:39:39 +00001052 free(config);
1053 }
1054}
1055
1056static void dhcp_context_free(struct dhcp_context *ctx)
1057{
1058 if (ctx)
1059 {
1060 dhcp_netid_free(ctx->filter);
1061 free(ctx->netid.net);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001062#ifdef HAVE_DHCP6
Petr Menšík59e47032018-11-02 22:39:39 +00001063 free(ctx->template_interface);
Kevin Darbyshire-Bryantb683cf32018-12-10 10:34:35 +00001064#endif
Petr Menšík59e47032018-11-02 22:39:39 +00001065 free(ctx);
1066 }
1067}
1068
1069static void dhcp_opt_free(struct dhcp_opt *opt)
1070{
1071 if (opt->flags & DHOPT_VENDOR)
1072 free(opt->u.vendor_class);
1073 dhcp_netid_free(opt->netid);
1074 free(opt->val);
1075 free(opt);
1076}
1077
1078
Simon Kelley832af0b2007-01-21 20:01:28 +00001079/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001080static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +00001081{
Simon Kelley824af852008-02-12 20:43:05 +00001082 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +00001083 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +00001084 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001085 char *comma = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001086 u16 opt_len = 0;
1087 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001088 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001089
1090 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001091 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +00001092 new->netid = NULL;
1093 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001094 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001095
Simon Kelleyf2621c72007-04-29 19:47:21 +01001096 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +00001097 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001098 comma = split(arg);
1099
1100 for (cp = arg; *cp; cp++)
1101 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +00001102 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001103
1104 if (!*cp)
1105 {
1106 new->opt = atoi(arg);
1107 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001108 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001109 break;
1110 }
1111
1112 if (strstr(arg, "option:") == arg)
1113 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001114 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1115 {
1116 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1117 /* option:<optname> must follow tag and vendor string. */
1118 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1119 option_ok = 1;
1120 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001121 break;
1122 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001123#ifdef HAVE_DHCP6
1124 else if (strstr(arg, "option6:") == arg)
1125 {
1126 for (cp = arg+8; *cp; cp++)
1127 if (*cp < '0' || *cp > '9')
1128 break;
1129
1130 if (!*cp)
1131 {
1132 new->opt = atoi(arg+8);
1133 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001134 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001135 }
1136 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001137 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001138 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1139 {
1140 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1141 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1142 option_ok = 1;
1143 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001144 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001145 /* option6:<opt>|<optname> must follow tag and vendor string. */
1146 is6 = 1;
1147 break;
1148 }
1149#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001150 else if (strstr(arg, "vendor:") == arg)
1151 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001152 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1153 new->flags |= DHOPT_VENDOR;
1154 }
1155 else if (strstr(arg, "encap:") == arg)
1156 {
1157 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001158 new->flags |= DHOPT_ENCAPSULATE;
1159 }
Simon Kelley316e2732010-01-22 20:16:09 +00001160 else if (strstr(arg, "vi-encap:") == arg)
1161 {
1162 new->u.encap = atoi(arg+9);
1163 new->flags |= DHOPT_RFC3925;
1164 if (flags == DHOPT_MATCH)
1165 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001166 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001167 break;
1168 }
1169 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001170 else
1171 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001172 /* allow optional "net:" or "tag:" for consistency */
Petr Menšík59e47032018-11-02 22:39:39 +00001173 const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
1174 new->netid = dhcp_netid_create(name, new->netid);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001175 }
1176
1177 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001178 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001179
1180#ifdef HAVE_DHCP6
1181 if (is6)
1182 {
1183 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Petr Menšík59e47032018-11-02 22:39:39 +00001184 goto_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001185
1186 if (opt_len == 0 &&
1187 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001188 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001189 }
1190 else
1191#endif
1192 if (opt_len == 0 &&
1193 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001194 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001195
Simon Kelley316e2732010-01-22 20:16:09 +00001196 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001197 if (!option_ok)
Petr Menšík59e47032018-11-02 22:39:39 +00001198 goto_err(_("bad dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001199
1200 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001201 {
1202 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001203 char c;
Simon Kelley67993202019-03-04 22:59:42 +00001204 int found_dig = 0, found_colon = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001205 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001206 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001207 dots = 0;
1208 for (cp = comma; (c = *cp); cp++)
1209 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001210 {
1211 addrs++;
1212 is_dec = is_hex = 0;
1213 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001214 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001215 {
1216 digs++;
1217 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001218 found_colon = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001219 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001220 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001221 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001222 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001223 if (cp == comma) /* leading / means a pathname */
1224 is_addr = 0;
1225 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001226 else if (c == '.')
1227 {
Simon Kelley8baf5832020-04-23 23:14:45 +01001228 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001229 dots++;
1230 }
1231 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001232 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001233 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001234 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001235 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001236 {
1237 is_addr = 0;
1238 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001239 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001240 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001241 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001242 *cp = 0;
1243 }
1244 else
1245 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001246 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001247 (c >='a' && c <= 'f') ||
1248 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001249 {
1250 is_hex = 0;
1251 if (c != '[' && c != ']')
1252 is_addr6 = 0;
1253 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001254 }
Simon Kelley28866e92011-02-14 20:19:14 +00001255 else
1256 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001257
Simon Kelley28866e92011-02-14 20:19:14 +00001258 if (!found_dig)
1259 is_dec = is_addr = 0;
Simon Kelley67993202019-03-04 22:59:42 +00001260
1261 if (!found_colon)
1262 is_addr6 = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001263
1264#ifdef HAVE_DHCP6
1265 /* NTP server option takes hex, addresses or FQDN */
1266 if (is6 && new->opt == OPTION6_NTP_SERVER && !is_hex)
1267 opt_len |= is_addr6 ? OT_ADDR_LIST : OT_RFC1035_NAME;
1268#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00001269
Simon Kelleyf2621c72007-04-29 19:47:21 +01001270 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001271 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001272 {
1273 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001274
1275 if (!is6 && (!is_addr || dots == 0))
Petr Menšík59e47032018-11-02 22:39:39 +00001276 goto_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001277
1278 if (is6 && !is_addr6)
Petr Menšík59e47032018-11-02 22:39:39 +00001279 goto_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001280 }
Simon Kelley28866e92011-02-14 20:19:14 +00001281 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001282 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1283 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001284
1285 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1286 {
1287 int val, fac = 1;
1288
1289 switch (comma[strlen(comma) - 1])
1290 {
Simon Kelley42243212012-07-20 15:19:18 +01001291 case 'w':
1292 case 'W':
1293 fac *= 7;
1294 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001295 case 'd':
1296 case 'D':
1297 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001298 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001299 case 'h':
1300 case 'H':
1301 fac *= 60;
1302 /* fall through */
1303 case 'm':
1304 case 'M':
1305 fac *= 60;
1306 /* fall through */
1307 case 's':
1308 case 'S':
1309 comma[strlen(comma) - 1] = 0;
1310 }
1311
1312 new->len = 4;
1313 new->val = opt_malloc(4);
1314 val = atoi(comma);
1315 *((int *)new->val) = htonl(val * fac);
1316 }
1317 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001318 {
1319 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001320 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001321 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1322 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001323 }
1324 else if (is_dec)
1325 {
1326 int i, val = atoi(comma);
1327 /* assume numeric arg is 1 byte except for
1328 options where it is known otherwise.
1329 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001330 if (opt_len != 0)
1331 new->len = opt_len;
1332 else if (val & 0xffff0000)
1333 new->len = 4;
1334 else if (val & 0xff00)
1335 new->len = 2;
1336 else
1337 new->len = 1;
1338
Simon Kelley832af0b2007-01-21 20:01:28 +00001339 if (lenchar == 'b')
1340 new->len = 1;
1341 else if (lenchar == 's')
1342 new->len = 2;
1343 else if (lenchar == 'i')
1344 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001345
Simon Kelley824af852008-02-12 20:43:05 +00001346 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001347 for (i=0; i<new->len; i++)
1348 new->val[i] = val>>((new->len - i - 1)*8);
1349 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001350 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001351 {
1352 struct in_addr in;
1353 unsigned char *op;
1354 char *slash;
1355 /* max length of address/subnet descriptor is five bytes,
1356 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001357 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001358 new->flags |= DHOPT_ADDR;
1359
Simon Kelley572b41e2011-02-18 18:11:18 +00001360 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1361 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001362 {
Simon Kelley6b010842007-02-12 20:32:07 +00001363 *(op++) = 1; /* RFC 3361 "enc byte" */
1364 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001365 }
1366 while (addrs--)
1367 {
1368 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001369 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001370 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001371 if (!inet_pton(AF_INET, cp, &in))
Petr Menšík59e47032018-11-02 22:39:39 +00001372 goto_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001373 if (!slash)
1374 {
1375 memcpy(op, &in, INADDRSZ);
1376 op += INADDRSZ;
1377 }
1378 else
1379 {
1380 unsigned char *p = (unsigned char *)&in;
1381 int netsize = atoi(slash);
1382 *op++ = netsize;
1383 if (netsize > 0)
1384 *op++ = *p++;
1385 if (netsize > 8)
1386 *op++ = *p++;
1387 if (netsize > 16)
1388 *op++ = *p++;
1389 if (netsize > 24)
1390 *op++ = *p++;
1391 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1392 }
1393 }
1394 new->len = op - new->val;
1395 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001396 else if (is_addr6 && is6)
1397 {
1398 unsigned char *op;
1399 new->val = op = opt_malloc(16 * addrs);
1400 new->flags |= DHOPT_ADDR6;
1401 while (addrs--)
1402 {
1403 cp = comma;
1404 comma = split(cp);
1405
1406 /* check for [1234::7] */
1407 if (*cp == '[')
1408 cp++;
1409 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1410 cp[strlen(cp)-1] = 0;
1411
1412 if (inet_pton(AF_INET6, cp, op))
1413 {
1414 op += IN6ADDRSZ;
1415 continue;
1416 }
Petr Menšík59e47032018-11-02 22:39:39 +00001417
1418 goto_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001419 }
1420 new->len = op - new->val;
1421 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001422 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001423 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001424 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001425 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001426 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001427 {
1428 /* dns search, RFC 3397, or SIP, RFC 3361 */
1429 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001430 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001431 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001432 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001433
1434 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001435 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001436
1437 while (arg && *arg)
1438 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001439 char *in, *dom = NULL;
1440 size_t domlen = 1;
1441 /* Allow "." as an empty domain */
1442 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001443 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001444 if (!(dom = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00001445 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001446
Simon Kelleyc52e1892010-06-07 22:01:39 +01001447 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001448 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001449
1450 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001451 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001452 {
1453 memcpy(newp, m, header_size + len);
1454 free(m);
1455 }
Simon Kelley824af852008-02-12 20:43:05 +00001456 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001457 p = m + header_size;
1458 q = p + len;
1459
1460 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001461 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001462 {
1463 unsigned char *cp = q++;
1464 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001465 for (j = 0; *in && (*in != '.'); in++, j++)
1466 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001467 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001468 if (*in)
1469 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001470 }
1471 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001472 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001473
Simon Kelley832af0b2007-01-21 20:01:28 +00001474 /* Now tail-compress using earlier names. */
1475 newlen = q - p;
1476 for (tail = p + len; *tail; tail += (*tail) + 1)
1477 for (r = p; r - p < (int)len; r += (*r) + 1)
1478 if (strcmp((char *)r, (char *)tail) == 0)
1479 {
1480 PUTSHORT((r - p) | 0xc000, tail);
1481 newlen = tail - p;
1482 goto end;
1483 }
1484 end:
1485 len = newlen;
1486
1487 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001488 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001489 }
1490
1491 /* RFC 3361, enc byte is zero for names */
Simon Kelley9e732442019-12-12 20:56:08 +00001492 if (new->opt == OPTION_SIP_SERVER && m)
Simon Kelley832af0b2007-01-21 20:01:28 +00001493 m[0] = 0;
1494 new->len = (int) len + header_size;
1495 new->val = m;
1496 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001497#ifdef HAVE_DHCP6
1498 else if (comma && (opt_len & OT_CSTRING))
1499 {
1500 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001501 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001502 unsigned char *p, *newp;
1503
Simon Kelley40ef23b2012-03-13 21:59:28 +00001504 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001505 if (comma[i] == ',')
1506 commas++;
1507
1508 newp = opt_malloc(strlen(comma)+(2*commas));
1509 p = newp;
1510 arg = comma;
1511 comma = split(arg);
1512
1513 while (arg && *arg)
1514 {
1515 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001516 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001517 PUTSHORT(len, p);
1518 memcpy(p, arg, len);
1519 p += len;
1520
1521 arg = comma;
1522 comma = split(arg);
1523 }
1524
1525 new->val = newp;
1526 new->len = p - newp;
1527 }
1528 else if (comma && (opt_len & OT_RFC1035_NAME))
1529 {
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001530 unsigned char *p = NULL, *q, *newp, *end;
Simon Kelley18f0fb02012-03-31 21:18:55 +01001531 int len = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001532 int header_size = (is6 && new->opt == OPTION6_NTP_SERVER) ? 4 : 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001533 arg = comma;
1534 comma = split(arg);
1535
1536 while (arg && *arg)
1537 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001538 char *dom = canonicalise_opt(arg);
1539 if (!dom)
Petr Menšík59e47032018-11-02 22:39:39 +00001540 goto_err(_("bad domain in dhcp-option"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001541
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001542 newp = opt_malloc(len + header_size + strlen(dom) + 2);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001543
1544 if (p)
1545 {
1546 memcpy(newp, p, len);
1547 free(p);
1548 }
1549
1550 p = newp;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001551 q = p + len;
1552 end = do_rfc1035_name(q + header_size, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001553 *end++ = 0;
Vladislav Grishenkodded78b2020-03-08 15:34:34 +00001554 if (is6 && new->opt == OPTION6_NTP_SERVER)
1555 {
1556 PUTSHORT(NTP_SUBOPTION_SRV_FQDN, q);
1557 PUTSHORT(end - q - 2, q);
1558 }
Simon Kelley18f0fb02012-03-31 21:18:55 +01001559 len = end - p;
1560 free(dom);
1561
Simon Kelley4cb1b322012-02-06 14:30:41 +00001562 arg = comma;
1563 comma = split(arg);
1564 }
1565
Simon Kelley18f0fb02012-03-31 21:18:55 +01001566 new->val = p;
1567 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001568 }
1569#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001570 else
1571 {
1572 new->len = strlen(comma);
1573 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001574 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001575 new->flags |= DHOPT_STRING;
1576 }
1577 }
1578 }
1579
Simon Kelley4cb1b322012-02-06 14:30:41 +00001580 if (!is6 &&
1581 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001582 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001583 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Petr Menšík59e47032018-11-02 22:39:39 +00001584 goto_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001585
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001586 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001587 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001588 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1589 !new->netid ||
1590 new->netid->next)
Petr Menšík59e47032018-11-02 22:39:39 +00001591 goto_err(_("illegal dhcp-match"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001592
1593 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001594 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001595 new->next = daemon->dhcp_match6;
1596 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001597 }
1598 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001599 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001600 new->next = daemon->dhcp_match;
1601 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001602 }
Simon Kelley824af852008-02-12 20:43:05 +00001603 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001604 else if (is6)
1605 {
1606 new->next = daemon->dhcp_opts6;
1607 daemon->dhcp_opts6 = new;
1608 }
1609 else
1610 {
1611 new->next = daemon->dhcp_opts;
1612 daemon->dhcp_opts = new;
1613 }
1614
1615 return 1;
Petr Menšík59e47032018-11-02 22:39:39 +00001616on_error:
1617 dhcp_opt_free(new);
1618 return 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001619}
1620
Simon Kelley7622fc02009-06-04 20:32:05 +01001621#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001622
Simon Kelley28866e92011-02-14 20:19:14 +00001623void set_option_bool(unsigned int opt)
1624{
Petr Menšík24b87602018-10-24 22:30:18 +01001625 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001626}
1627
Simon Kelley2b5bae92012-06-26 16:55:23 +01001628void reset_option_bool(unsigned int opt)
1629{
Petr Menšík24b87602018-10-24 22:30:18 +01001630 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001631}
1632
Petr Menšík59e47032018-11-02 22:39:39 +00001633static void server_list_free(struct server *list)
1634{
1635 while (list)
1636 {
1637 struct server *tmp = list;
1638 list = list->next;
1639 free(tmp);
1640 }
1641}
1642
Simon Kelley7b1eae42014-02-20 13:43:28 +00001643static 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 +01001644{
1645 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001646 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001647
Simon Kelley832af0b2007-01-21 20:01:28 +00001648 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001649 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001650
Simon Kelley1a6bca82008-07-11 11:11:42 +01001651 for (i=0; usage[i].opt != 0; i++)
1652 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001653 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001654 int rept = usage[i].rept;
1655
Simon Kelley28866e92011-02-14 20:19:14 +00001656 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001657 {
1658 /* command line */
1659 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001660 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001661 if (rept == ARG_ONE)
1662 usage[i].rept = ARG_USED_CL;
1663 }
1664 else
1665 {
1666 /* allow file to override command line */
1667 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001668 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001669 if (rept == ARG_USED_CL || rept == ARG_ONE)
1670 usage[i].rept = ARG_USED_FILE;
1671 }
1672
1673 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1674 {
Simon Kelley28866e92011-02-14 20:19:14 +00001675 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001676 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001677 }
1678
1679 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001680 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001681
Simon Kelley849a8352006-06-09 21:02:31 +01001682 switch (option)
1683 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001684 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001685 {
Simon Kelley824af852008-02-12 20:43:05 +00001686 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001687 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001688 {
Simon Kelley28866e92011-02-14 20:19:14 +00001689 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001690 free(file);
1691 }
Simon Kelley849a8352006-06-09 21:02:31 +01001692 break;
1693 }
1694
Simon Kelleyf2621c72007-04-29 19:47:21 +01001695 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001696 {
1697 DIR *dir_stream;
1698 struct dirent *ent;
1699 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001700 struct list {
Simon Kelleyab538832020-01-10 20:44:48 +00001701 char *name;
Simon Kelley1f15b812009-10-13 17:49:32 +01001702 struct list *next;
Simon Kelleyab538832020-01-10 20:44:48 +00001703 } *ignore_suffix = NULL, *match_suffix = NULL, *files = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001704
Simon Kelley1f15b812009-10-13 17:49:32 +01001705 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001706 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001707 break;
1708
Simon Kelley1f15b812009-10-13 17:49:32 +01001709 for (arg = comma; arg; arg = comma)
1710 {
1711 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001712 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001713 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001714 li = opt_malloc(sizeof(struct list));
1715 if (*arg == '*')
1716 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001717 /* "*" with no suffix is a no-op */
1718 if (arg[1] == 0)
1719 free(li);
1720 else
1721 {
1722 li->next = match_suffix;
1723 match_suffix = li;
1724 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001725 li->name = opt_string_alloc(arg+1);
Simon Kelley0007ee92015-11-21 21:47:41 +00001726 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001727 }
1728 else
1729 {
1730 li->next = ignore_suffix;
1731 ignore_suffix = li;
1732 /* Have to copy: buffer is overwritten */
Simon Kelleyab538832020-01-10 20:44:48 +00001733 li->name = opt_string_alloc(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001734 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001735 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001736 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001737
Simon Kelley849a8352006-06-09 21:02:31 +01001738 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001739 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001740
Simon Kelley849a8352006-06-09 21:02:31 +01001741 while ((ent = readdir(dir_stream)))
1742 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001743 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001744 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001745
1746 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001747 if (len == 0 ||
1748 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001749 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1750 ent->d_name[0] == '.')
1751 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001752
Simon Kelley3e1551a2014-09-09 21:46:07 +01001753 if (match_suffix)
1754 {
1755 for (li = match_suffix; li; li = li->next)
1756 {
1757 /* check for required suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001758 size_t ls = strlen(li->name);
Simon Kelley3e1551a2014-09-09 21:46:07 +01001759 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001760 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001761 break;
1762 }
1763 if (!li)
1764 continue;
1765 }
1766
Simon Kelley1f15b812009-10-13 17:49:32 +01001767 for (li = ignore_suffix; li; li = li->next)
1768 {
1769 /* check for proscribed suffices */
Simon Kelleyab538832020-01-10 20:44:48 +00001770 size_t ls = strlen(li->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001771 if (len > ls &&
Simon Kelleyab538832020-01-10 20:44:48 +00001772 strcmp(li->name, &ent->d_name[len - ls]) == 0)
Simon Kelley1f15b812009-10-13 17:49:32 +01001773 break;
1774 }
1775 if (li)
1776 continue;
1777
Simon Kelley824af852008-02-12 20:43:05 +00001778 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001779 strcpy(path, directory);
1780 strcat(path, "/");
1781 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001782
Simon Kelley39595cf2013-02-04 21:40:07 +00001783 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001784 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001785 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001786
Simon Kelley39595cf2013-02-04 21:40:07 +00001787 /* only reg files allowed. */
1788 if (S_ISREG(buf.st_mode))
Simon Kelleyab538832020-01-10 20:44:48 +00001789 {
1790 /* sort files into order. */
1791 struct list **up, *new = opt_malloc(sizeof(struct list));
1792 new->name = path;
1793
1794 for (up = &files, li = files; li; up = &li->next, li = li->next)
1795 if (strcmp(li->name, path) >=0)
1796 break;
1797
1798 new->next = li;
1799 *up = new;
1800 }
1801
Simon Kelley849a8352006-06-09 21:02:31 +01001802 }
Simon Kelleyab538832020-01-10 20:44:48 +00001803
1804 for (li = files; li; li = li->next)
1805 one_file(li->name, 0);
1806
Simon Kelley849a8352006-06-09 21:02:31 +01001807 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001808 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001809 for(; ignore_suffix; ignore_suffix = li)
1810 {
1811 li = ignore_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001812 free(ignore_suffix->name);
Simon Kelley1f15b812009-10-13 17:49:32 +01001813 free(ignore_suffix);
1814 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001815 for(; match_suffix; match_suffix = li)
1816 {
1817 li = match_suffix->next;
Simon Kelleyab538832020-01-10 20:44:48 +00001818 free(match_suffix->name);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001819 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001820 }
Simon Kelleyab538832020-01-10 20:44:48 +00001821 for(; files; files = li)
1822 {
1823 li = files->next;
1824 free(files->name);
1825 free(files);
1826 }
Simon Kelley849a8352006-06-09 21:02:31 +01001827 break;
1828 }
1829
Simon Kelleyed4c0762013-10-08 20:46:34 +01001830 case LOPT_ADD_SBNET: /* --add-subnet */
1831 set_option_bool(OPT_CLIENT_SUBNET);
1832 if (arg)
1833 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001834 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001835 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001836
1837 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1838 if ((end = split_chr(arg, '/')))
1839 {
1840 /* has subnet+len */
1841 err = parse_mysockaddr(arg, &new->addr);
1842 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001843 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001844 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001845 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001846 new->addr_used = 1;
1847 }
1848 else if (!atoi_check(arg, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001849 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001850
1851 daemon->add_subnet4 = new;
1852
Ed Bardsleya7369be2015-08-05 21:17:18 +01001853 if (comma)
1854 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001855 new = opt_malloc(sizeof(struct mysubnet));
1856 if ((end = split_chr(comma, '/')))
1857 {
1858 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001859 err = parse_mysockaddr(comma, &new->addr);
1860 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00001861 ret_err_free(err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001862 if (!atoi_check(end, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001863 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001864 new->addr_used = 1;
1865 }
1866 else
1867 {
1868 if (!atoi_check(comma, &new->mask))
Petr Menšík59e47032018-11-02 22:39:39 +00001869 ret_err_free(gen_err, new);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001870 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001871
1872 daemon->add_subnet6 = new;
1873 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001874 }
1875 break;
1876
Simon Kelleyad094272012-08-10 17:10:54 +01001877 case '1': /* --enable-dbus */
1878 set_option_bool(OPT_DBUS);
1879 if (arg)
1880 daemon->dbus_name = opt_string_alloc(arg);
1881 else
1882 daemon->dbus_name = DNSMASQ_SERVICE;
1883 break;
Oldřich Jedličkad162bee2020-03-20 22:18:57 +01001884
1885 case LOPT_UBUS: /* --enable-ubus */
1886 set_option_bool(OPT_UBUS);
1887 if (arg)
1888 daemon->ubus_name = opt_string_alloc(arg);
1889 else
1890 daemon->ubus_name = DNSMASQ_UBUS_NAME;
1891 break;
1892
Simon Kelleyf2621c72007-04-29 19:47:21 +01001893 case '8': /* --log-facility */
1894 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001895 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001896 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001897 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001898 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001899#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001900 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001901#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001902 for (i = 0; facilitynames[i].c_name; i++)
1903 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1904 break;
1905
1906 if (facilitynames[i].c_name)
1907 daemon->log_fac = facilitynames[i].c_val;
1908 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001909 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001910#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001911 }
1912 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001913
Simon Kelleyf2621c72007-04-29 19:47:21 +01001914 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001915 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001916 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001917
Simon Kelleyf2621c72007-04-29 19:47:21 +01001918 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001919 {
Simon Kelley824af852008-02-12 20:43:05 +00001920 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001921 struct resolvc *new, *list = daemon->resolv_files;
1922
1923 if (list && list->is_default)
1924 {
1925 /* replace default resolv file - possibly with nothing */
1926 if (name)
1927 {
1928 list->is_default = 0;
1929 list->name = name;
1930 }
1931 else
1932 list = NULL;
1933 }
1934 else if (name)
1935 {
Simon Kelley824af852008-02-12 20:43:05 +00001936 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001937 new->next = list;
1938 new->name = name;
1939 new->is_default = 0;
1940 new->mtime = 0;
1941 new->logged = 0;
1942 list = new;
1943 }
1944 daemon->resolv_files = list;
1945 break;
1946 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001947
1948 case LOPT_SERVERS_FILE:
1949 daemon->servers_file = opt_string_alloc(arg);
1950 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001951
Simon Kelleyf2621c72007-04-29 19:47:21 +01001952 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001953 {
1954 int pref = 1;
1955 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001956 char *name, *target = NULL;
1957
Simon Kelleyf2621c72007-04-29 19:47:21 +01001958 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001959 {
1960 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001961 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001962 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001963 }
1964
Simon Kelley1f15b812009-10-13 17:49:32 +01001965 if (!(name = canonicalise_opt(arg)) ||
1966 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001967 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001968
Simon Kelley824af852008-02-12 20:43:05 +00001969 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001970 new->next = daemon->mxnames;
1971 daemon->mxnames = new;
1972 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001973 new->name = name;
1974 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001975 new->weight = pref;
1976 break;
1977 }
1978
Simon Kelleyf2621c72007-04-29 19:47:21 +01001979 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001980 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001981 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001982 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001983
Simon Kelley6b173352018-05-08 18:32:14 +01001984 case LOPT_DUMPFILE: /* --dumpfile */
1985 daemon->dump_file = opt_string_alloc(arg);
1986 break;
1987
1988 case LOPT_DUMPMASK: /* --dumpmask */
1989 daemon->dump_mask = strtol(arg, NULL, 0);
1990 break;
1991
Simon Kelley7622fc02009-06-04 20:32:05 +01001992#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001993 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001994 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001995 break;
1996
Simon Kelleyc72daea2012-01-05 21:33:27 +00001997 /* Sorry about the gross pre-processor abuse */
1998 case '6': /* --dhcp-script */
1999 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00002000# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002001 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01002002# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00002003 if (option == LOPT_LUASCRIPT)
2004# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002005 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00002006# else
2007 daemon->luascript = opt_string_alloc(arg);
2008# endif
2009 else
2010 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01002011# endif
Simon Kelley849a8352006-06-09 21:02:31 +01002012 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00002013#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01002014
Simon Kelley70d18732015-01-31 19:59:29 +00002015 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
2016 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
2017 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
2018 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
2019 case LOPT_HOST_INOTIFY: /* --hostsdir */
2020 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01002021 {
Simon Kelley824af852008-02-12 20:43:05 +00002022 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00002023 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00002024 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002025 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01002026 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00002027 if (option == 'H')
2028 {
2029 new->next = daemon->addn_hosts;
2030 daemon->addn_hosts = new;
2031 }
2032 else if (option == LOPT_DHCP_HOST)
2033 {
2034 new->next = daemon->dhcp_hosts_file;
2035 daemon->dhcp_hosts_file = new;
2036 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00002037 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00002038 {
2039 new->next = daemon->dhcp_opts_file;
2040 daemon->dhcp_opts_file = new;
2041 }
Simon Kelley70d18732015-01-31 19:59:29 +00002042 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002043 {
Simon Kelley70d18732015-01-31 19:59:29 +00002044 new->next = daemon->dynamic_dirs;
2045 daemon->dynamic_dirs = new;
2046 if (option == LOPT_DHCP_INOTIFY)
2047 new->flags |= AH_DHCP_HST;
2048 else if (option == LOPT_DHOPT_INOTIFY)
2049 new->flags |= AH_DHCP_OPT;
2050 else if (option == LOPT_HOST_INOTIFY)
2051 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00002052 }
2053
Simon Kelley849a8352006-06-09 21:02:31 +01002054 break;
2055 }
2056
Simon Kelley4f7b3042012-11-28 21:27:02 +00002057 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01002058 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002059
Simon Kelley4f7b3042012-11-28 21:27:02 +00002060 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01002061
2062 while ((arg = comma))
2063 {
2064 struct iname *new = opt_malloc(sizeof(struct iname));
2065 comma = split(arg);
2066 new->name = NULL;
2067 unhide_metas(arg);
2068 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
2069 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002070 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2071 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002072 else
2073 {
2074 char *fam = split_chr(arg, '/');
2075 new->name = opt_string_alloc(arg);
2076 new->addr.sa.sa_family = 0;
2077 if (fam)
2078 {
2079 if (strcmp(fam, "4") == 0)
2080 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01002081 else if (strcmp(fam, "6") == 0)
2082 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01002083 else
Petr Menšík59e47032018-11-02 22:39:39 +00002084 {
2085 free(new->name);
2086 ret_err_free(gen_err, new);
2087 }
Simon Kelley08933472018-10-05 16:34:35 +01002088 }
2089 }
2090 new->next = daemon->authinterface;
2091 daemon->authinterface = new;
2092 };
Simon Kelley429798f2012-12-10 20:45:53 +00002093
Simon Kelley4f7b3042012-11-28 21:27:02 +00002094 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00002095
2096 case LOPT_AUTHSFS: /* --auth-sec-servers */
2097 {
2098 struct name_list *new;
2099
2100 do {
2101 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00002102 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00002103 new->name = opt_string_alloc(arg);
2104 new->next = daemon->secondary_forward_server;
2105 daemon->secondary_forward_server = new;
2106 arg = comma;
2107 } while (arg);
2108 break;
2109 }
2110
Simon Kelley4f7b3042012-11-28 21:27:02 +00002111 case LOPT_AUTHZONE: /* --auth-zone */
2112 {
2113 struct auth_zone *new;
2114
2115 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00002116
Simon Kelley429798f2012-12-10 20:45:53 +00002117 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002118 new->domain = opt_string_alloc(arg);
2119 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002120 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00002121 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002122 new->next = daemon->auth_zones;
2123 daemon->auth_zones = new;
2124
2125 while ((arg = comma))
2126 {
2127 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002128 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002129 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00002130 struct addrlist *subnet = NULL;
Simon Kelleycc921df2019-01-02 22:48:59 +00002131 union all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002132
2133 comma = split(arg);
2134 prefix = split_chr(arg, '/');
2135
2136 if (prefix && !atoi_check(prefix, &prefixlen))
2137 ret_err(gen_err);
2138
Mathias Kresin094bfae2016-07-24 14:15:22 +01002139 if (strstr(arg, "exclude:") == arg)
2140 {
2141 is_exclude = 1;
2142 arg = arg+8;
2143 }
2144
Simon Kelleycc921df2019-01-02 22:48:59 +00002145 if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002146 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002147 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002148 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002149 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002150 }
Simon Kelleycc921df2019-01-02 22:48:59 +00002151 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002152 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002153 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002154 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002155 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002156 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002157 else
2158 {
2159 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2160 name->name = opt_string_alloc(arg);
2161 name->flags = AUTH4 | AUTH6;
2162 name->next = new->interface_names;
2163 new->interface_names = name;
2164 if (prefix)
2165 {
2166 if (prefixlen == 4)
2167 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00002168 else if (prefixlen == 6)
2169 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00002170 else
2171 ret_err(gen_err);
2172 }
2173 }
2174
2175 if (subnet)
2176 {
2177 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002178
2179 if (is_exclude)
2180 {
2181 subnet->next = new->exclude;
2182 new->exclude = subnet;
2183 }
2184 else
2185 {
2186 subnet->next = new->subnet;
2187 new->subnet = subnet;
2188 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002189 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002190 }
2191 break;
2192 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002193
Simon Kelley4f7b3042012-11-28 21:27:02 +00002194 case LOPT_AUTHSOA: /* --auth-soa */
2195 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002196 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002197 if (comma)
2198 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002199 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002200 arg = comma;
2201 comma = split(arg);
2202 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002203 for (cp = daemon->hostmaster; *cp; cp++)
2204 if (*cp == '@')
2205 *cp = '.';
2206
Simon Kelley4f7b3042012-11-28 21:27:02 +00002207 if (comma)
2208 {
2209 arg = comma;
2210 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002211 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002212 if (comma)
2213 {
2214 arg = comma;
2215 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002216 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002217 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002218 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002219 }
2220 }
2221 }
2222
2223 break;
2224
Simon Kelley2bb73af2013-04-24 17:38:19 +01002225 case 's': /* --domain */
2226 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002227 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002228 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002229 else
Simon Kelley9009d742008-11-14 20:04:27 +00002230 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002231 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002232 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002233 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002234 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002235 else
2236 {
Simon Kelley9009d742008-11-14 20:04:27 +00002237 if (comma)
2238 {
Simon Kelley429798f2012-12-10 20:45:53 +00002239 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002240 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002241
Simon Kelley48fd1c42013-04-25 09:49:38 +01002242 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002243 new->indexed = 0;
2244
Simon Kelley9009d742008-11-14 20:04:27 +00002245 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002246 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002247 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002248 int msize;
2249
Simon Kelley28866e92011-02-14 20:19:14 +00002250 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002251 if (!atoi_check(netpart, &msize))
Petr Menšík59e47032018-11-02 22:39:39 +00002252 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002253 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002254 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002255 int mask = (1 << (32 - msize)) - 1;
2256 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002257 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2258 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002259 if (arg)
2260 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002261 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002262 {
2263 if (!(new->prefix = canonicalise_opt(arg)) ||
2264 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002265 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002266 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002267 else if (strcmp(arg, "local") != 0 ||
2268 (msize != 8 && msize != 16 && msize != 24))
Petr Menšík59e47032018-11-02 22:39:39 +00002269 ret_err_free(gen_err, new);
Simon Kelley28866e92011-02-14 20:19:14 +00002270 else
2271 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002272 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002273 local=/xxx.yyy.zzz.in-addr.arpa/ */
2274 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002275 if (!serv)
Petr Menšík59e47032018-11-02 22:39:39 +00002276 ret_err_free(_("bad prefix"), new);
Olivier Gayotdc990582017-03-06 22:17:21 +00002277
Simon Kelleyde73a492014-02-17 21:43:27 +00002278 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002279
2280 /* local=/<domain>/ */
2281 serv = opt_malloc(sizeof(struct server));
2282 memset(serv, 0, sizeof(struct server));
2283 serv->domain = d;
2284 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2285 serv->next = daemon->servers;
2286 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002287 }
2288 }
Simon Kelley9009d742008-11-14 20:04:27 +00002289 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002290 else if (inet_pton(AF_INET6, comma, &new->start6))
2291 {
2292 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2293 u64 addrpart = addr6part(&new->start6);
2294 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002295
Simon Kelleyd74942a2012-02-07 20:51:56 +00002296 /* prefix==64 overflows the mask calculation above */
2297 if (msize == 64)
2298 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002299
Simon Kelleyd74942a2012-02-07 20:51:56 +00002300 new->end6 = new->start6;
2301 setaddr6part(&new->start6, addrpart & ~mask);
2302 setaddr6part(&new->end6, addrpart | mask);
2303
2304 if (msize < 64)
Petr Menšík59e47032018-11-02 22:39:39 +00002305 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002306 else if (arg)
2307 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002308 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002309 {
2310 if (!(new->prefix = canonicalise_opt(arg)) ||
2311 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002312 ret_err_free(_("bad prefix"), new);
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002313 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002314 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Petr Menšík59e47032018-11-02 22:39:39 +00002315 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002316 else
2317 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002318 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002319 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002320 struct server *serv = add_rev6(&new->start6, msize);
2321 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002322
2323 /* local=/<domain>/ */
2324 serv = opt_malloc(sizeof(struct server));
2325 memset(serv, 0, sizeof(struct server));
2326 serv->domain = d;
2327 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2328 serv->next = daemon->servers;
2329 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002330 }
2331 }
2332 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002333 else
Petr Menšík59e47032018-11-02 22:39:39 +00002334 ret_err_free(gen_err, new);
Simon Kelley9009d742008-11-14 20:04:27 +00002335 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002336 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002337 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002338 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002339 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002340 prefstr = split(arg);
2341
Simon Kelleyd74942a2012-02-07 20:51:56 +00002342 if (inet_pton(AF_INET, comma, &new->start))
2343 {
2344 new->is6 = 0;
2345 if (!arg)
2346 new->end.s_addr = new->start.s_addr;
2347 else if (!inet_pton(AF_INET, arg, &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00002348 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002349 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002350 else if (inet_pton(AF_INET6, comma, &new->start6))
2351 {
2352 new->is6 = 1;
2353 if (!arg)
2354 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2355 else if (!inet_pton(AF_INET6, arg, &new->end6))
Petr Menšík59e47032018-11-02 22:39:39 +00002356 ret_err_free(gen_err, new);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002357 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002358 else
Petr Menšík59e47032018-11-02 22:39:39 +00002359 ret_err_free(gen_err, new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002360
2361 if (option != 's' && prefstr)
2362 {
2363 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2364 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
Petr Menšík59e47032018-11-02 22:39:39 +00002365 ret_err_free(_("bad prefix"), new);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002366 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002367 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002368
2369 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002370 if (option == 's')
2371 {
2372 new->next = daemon->cond_domain;
2373 daemon->cond_domain = new;
2374 }
2375 else
2376 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002377 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002378 new->next = daemon->synth_domains;
2379 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002380 if (new->prefix &&
2381 (star = strrchr(new->prefix, '*'))
2382 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002383 {
2384 *star = 0;
2385 new->indexed = 1;
2386 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002387 }
Simon Kelley9009d742008-11-14 20:04:27 +00002388 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002389 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002390 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002391 else
2392 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002393 }
2394 }
Simon Kelley849a8352006-06-09 21:02:31 +01002395 break;
2396
Simon Kelley1e505122016-01-25 21:29:23 +00002397 case LOPT_CPE_ID: /* --add-dns-client */
2398 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002399 daemon->dns_client_id = opt_string_alloc(arg);
2400 break;
2401
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002402 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002403 if (!arg)
2404 set_option_bool(OPT_ADD_MAC);
2405 else
2406 {
2407 unhide_metas(arg);
2408 if (strcmp(arg, "base64") == 0)
2409 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002410 else if (strcmp(arg, "text") == 0)
2411 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002412 else
2413 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002414 }
2415 break;
2416
Simon Kelleyf2621c72007-04-29 19:47:21 +01002417 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002418 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002419 break;
2420
Simon Kelleyf2621c72007-04-29 19:47:21 +01002421 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002422 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002423 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002424 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002425
Simon Kelley7622fc02009-06-04 20:32:05 +01002426#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002427 case LOPT_SCRIPTUSR: /* --scriptuser */
2428 daemon->scriptuser = opt_string_alloc(arg);
2429 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002430#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002431
Simon Kelleyf2621c72007-04-29 19:47:21 +01002432 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002433 do {
Simon Kelley824af852008-02-12 20:43:05 +00002434 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002435 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002436 new->next = daemon->if_names;
2437 daemon->if_names = new;
2438 /* new->name may be NULL if someone does
2439 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002440 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002441 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002442 arg = comma;
2443 } while (arg);
2444 break;
2445
Simon Kelley2937f8a2013-07-29 19:49:07 +01002446 case LOPT_TFTP: /* --enable-tftp */
2447 set_option_bool(OPT_TFTP);
2448 if (!arg)
2449 break;
2450 /* fall through */
2451
Simon Kelleyf2621c72007-04-29 19:47:21 +01002452 case 'I': /* --except-interface */
2453 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002454 do {
Simon Kelley824af852008-02-12 20:43:05 +00002455 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002456 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002457 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002458 if (option == 'I')
2459 {
2460 new->next = daemon->if_except;
2461 daemon->if_except = new;
2462 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002463 else if (option == LOPT_TFTP)
2464 {
2465 new->next = daemon->tftp_interfaces;
2466 daemon->tftp_interfaces = new;
2467 }
Simon Kelley849a8352006-06-09 21:02:31 +01002468 else
2469 {
2470 new->next = daemon->dhcp_except;
2471 daemon->dhcp_except = new;
2472 }
2473 arg = comma;
2474 } while (arg);
2475 break;
2476
Simon Kelleyf2621c72007-04-29 19:47:21 +01002477 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002478 case LOPT_IGNORE_ADDR: /* --ignore-address */
2479 {
Simon Kelley849a8352006-06-09 21:02:31 +01002480 struct in_addr addr;
2481 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002482 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002483 {
Simon Kelley824af852008-02-12 20:43:05 +00002484 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002485 if (option == 'B')
2486 {
2487 baddr->next = daemon->bogus_addr;
2488 daemon->bogus_addr = baddr;
2489 }
2490 else
2491 {
2492 baddr->next = daemon->ignore_addr;
2493 daemon->ignore_addr = baddr;
2494 }
Simon Kelley849a8352006-06-09 21:02:31 +01002495 baddr->addr = addr;
2496 }
2497 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002498 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002499 break;
2500 }
2501
Simon Kelleyf2621c72007-04-29 19:47:21 +01002502 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002503 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002504 do {
Simon Kelley824af852008-02-12 20:43:05 +00002505 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002506 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002507 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002508 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002509 {
2510 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002511 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002512#ifdef HAVE_SOCKADDR_SA_LEN
2513 new->addr.in.sin_len = sizeof(new->addr.in);
2514#endif
2515 }
Simon Kelley849a8352006-06-09 21:02:31 +01002516 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2517 {
2518 new->addr.sa.sa_family = AF_INET6;
2519 new->addr.in6.sin6_flowinfo = 0;
2520 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002521 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002522#ifdef HAVE_SOCKADDR_SA_LEN
2523 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2524#endif
2525 }
Simon Kelley849a8352006-06-09 21:02:31 +01002526 else
Petr Menšík59e47032018-11-02 22:39:39 +00002527 ret_err_free(gen_err, new);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002528
2529 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002530 if (option == 'a')
2531 {
2532 new->next = daemon->if_addrs;
2533 daemon->if_addrs = new;
2534 }
2535 else
2536 {
2537 new->next = daemon->auth_peers;
2538 daemon->auth_peers = new;
2539 }
Simon Kelley849a8352006-06-09 21:02:31 +01002540 arg = comma;
2541 } while (arg);
2542 break;
2543
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002544 case 'S': /* --server */
2545 case LOPT_LOCAL: /* --local */
2546 case 'A': /* --address */
2547 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002548 {
2549 struct server *serv, *newlist = NULL;
2550
2551 unhide_metas(arg);
2552
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002553 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002554 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002555 int rebind = !(*arg == '/');
2556 char *end = NULL;
2557 if (!rebind)
2558 arg++;
2559 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002560 {
2561 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002562 /* elide leading dots - they are implied in the search algorithm */
2563 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002564 /* # matches everything and becomes a zero length domain string */
2565 if (strcmp(arg, "#") == 0)
2566 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002567 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002568 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002569 serv = opt_malloc(sizeof(struct server));
2570 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002571 serv->next = newlist;
2572 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002573 serv->domain = domain;
2574 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002575 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002576 if (rebind)
2577 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002578 }
2579 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002580 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002581 }
2582 else
2583 {
Simon Kelley824af852008-02-12 20:43:05 +00002584 newlist = opt_malloc(sizeof(struct server));
2585 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002586#ifdef HAVE_LOOP
2587 newlist->uid = rand32();
2588#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002589 }
2590
Simon Kelley7b1eae42014-02-20 13:43:28 +00002591 if (servers_only && option == 'S')
2592 newlist->flags |= SERV_FROM_FILE;
2593
Simon Kelley849a8352006-06-09 21:02:31 +01002594 if (option == 'A')
2595 {
2596 newlist->flags |= SERV_LITERAL_ADDRESS;
2597 if (!(newlist->flags & SERV_TYPE))
Petr Menšík59e47032018-11-02 22:39:39 +00002598 {
2599 server_list_free(newlist);
2600 ret_err(gen_err);
2601 }
Simon Kelley849a8352006-06-09 21:02:31 +01002602 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002603 else if (option == LOPT_NO_REBIND)
2604 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002605
2606 if (!arg || !*arg)
2607 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002608 if (!(newlist->flags & SERV_NO_REBIND))
2609 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002610 }
2611
2612 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002613 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002614 else
2615 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002616 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2617 if (err)
Petr Menšík59e47032018-11-02 22:39:39 +00002618 {
2619 server_list_free(newlist);
2620 ret_err(err);
2621 }
Simon Kelley849a8352006-06-09 21:02:31 +01002622 }
2623
Simon Kelleyf2621c72007-04-29 19:47:21 +01002624 serv = newlist;
2625 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002626 {
Simon Kelleyd9603ef2020-01-26 18:13:35 +00002627 serv->next->flags |= serv->flags & ~(SERV_HAS_DOMAIN | SERV_FOR_NODOTS);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002628 serv->next->addr = serv->addr;
2629 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002630 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002631 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002632 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002633 serv->next = daemon->servers;
2634 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002635 break;
2636 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002637
Simon Kelleyde73a492014-02-17 21:43:27 +00002638 case LOPT_REV_SERV: /* --rev-server */
2639 {
2640 char *string;
2641 int size;
2642 struct server *serv;
2643 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002644 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002645
2646 unhide_metas(arg);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002647 if (!arg)
Simon Kelleyde73a492014-02-17 21:43:27 +00002648 ret_err(gen_err);
Simon Kelleya9b022a2020-02-11 21:58:59 +00002649
2650 comma=split(arg);
Simon Kelleyde73a492014-02-17 21:43:27 +00002651
Simon Kelleya9b022a2020-02-11 21:58:59 +00002652 if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2653 ret_err(gen_err);
2654
Simon Kelleyde73a492014-02-17 21:43:27 +00002655 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002656 {
2657 serv = add_rev4(addr4, size);
2658 if (!serv)
2659 ret_err(_("bad prefix"));
2660 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002661 else if (inet_pton(AF_INET6, arg, &addr6))
2662 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002663 else
2664 ret_err(gen_err);
2665
2666 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2667
2668 if (string)
2669 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002670
2671 if (servers_only)
2672 serv->flags |= SERV_FROM_FILE;
2673
Simon Kelleyde73a492014-02-17 21:43:27 +00002674 break;
2675 }
2676
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002677 case LOPT_IPSET: /* --ipset */
2678#ifndef HAVE_IPSET
2679 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2680 break;
2681#else
2682 {
2683 struct ipsets ipsets_head;
2684 struct ipsets *ipsets = &ipsets_head;
2685 int size;
2686 char *end;
2687 char **sets, **sets_pos;
2688 memset(ipsets, 0, sizeof(struct ipsets));
2689 unhide_metas(arg);
2690 if (arg && *arg == '/')
2691 {
2692 arg++;
2693 while ((end = split_chr(arg, '/')))
2694 {
2695 char *domain = NULL;
2696 /* elide leading dots - they are implied in the search algorithm */
2697 while (*arg == '.')
2698 arg++;
2699 /* # matches everything and becomes a zero length domain string */
2700 if (strcmp(arg, "#") == 0 || !*arg)
2701 domain = "";
2702 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002703 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002704 ipsets->next = opt_malloc(sizeof(struct ipsets));
2705 ipsets = ipsets->next;
2706 memset(ipsets, 0, sizeof(struct ipsets));
2707 ipsets->domain = domain;
2708 arg = end;
2709 }
2710 }
2711 else
2712 {
2713 ipsets->next = opt_malloc(sizeof(struct ipsets));
2714 ipsets = ipsets->next;
2715 memset(ipsets, 0, sizeof(struct ipsets));
2716 ipsets->domain = "";
2717 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002718
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002719 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002720 ret_err(gen_err);
2721
2722 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002723 if (*end == ',')
2724 ++size;
2725
2726 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2727
2728 do {
2729 end = split(arg);
2730 *sets_pos++ = opt_string_alloc(arg);
2731 arg = end;
2732 } while (end);
2733 *sets_pos = 0;
2734 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2735 ipsets->next->sets = sets;
2736 ipsets->next = daemon->ipsets;
2737 daemon->ipsets = ipsets_head.next;
2738
2739 break;
2740 }
2741#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002742
Simon Kelleyf2621c72007-04-29 19:47:21 +01002743 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002744 {
2745 int size;
2746
2747 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002748 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002749 else
2750 {
2751 /* zero is OK, and means no caching. */
2752
2753 if (size < 0)
2754 size = 0;
Simon Kelley248efe82019-08-20 23:36:49 +01002755
2756 /* Note that for very large cache sizes, the malloc()
2757 will overflow. For the size of the cache record
2758 at the time this was noted, the value of "very large"
2759 was 46684428. Limit to an order of magnitude less than
2760 that to be safe from changes to the cache record. */
2761 if (size > 5000000)
2762 size = 5000000;
Simon Kelley849a8352006-06-09 21:02:31 +01002763
2764 daemon->cachesize = size;
2765 }
2766 break;
2767 }
2768
Simon Kelleyf2621c72007-04-29 19:47:21 +01002769 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002770 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002771 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002772 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002773
Simon Kelley1a6bca82008-07-11 11:11:42 +01002774 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002775 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002776 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002777 break;
2778
Hans Dedecker926332a2016-01-23 10:48:12 +00002779 case LOPT_MAXPORT: /* --max-port */
2780 if (!atoi_check16(arg, &daemon->max_port))
2781 ret_err(gen_err);
2782 break;
2783
Simon Kelleyf2621c72007-04-29 19:47:21 +01002784 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002785 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002786 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002787 break;
2788
Simon Kelley25cf5e32015-01-09 15:53:03 +00002789 case 'q': /* --log-queries */
2790 set_option_bool(OPT_LOG);
2791 if (arg && strcmp(arg, "extra") == 0)
2792 set_option_bool(OPT_EXTRALOG);
2793 break;
2794
Simon Kelleyf2621c72007-04-29 19:47:21 +01002795 case LOPT_MAX_LOGS: /* --log-async */
2796 daemon->max_logs = LOG_MAX; /* default */
2797 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002798 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002799 else if (daemon->max_logs > 100)
2800 daemon->max_logs = 100;
2801 break;
2802
2803 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002804 {
2805 int i;
2806 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002807 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002808 daemon->edns_pktsz = (unsigned short)i;
2809 break;
2810 }
2811
Simon Kelleyf2621c72007-04-29 19:47:21 +01002812 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002813 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002814 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002815 /* if explicitly set to zero, use single OS ephemeral port
2816 and disable random ports */
2817 if (daemon->query_port == 0)
2818 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002819 break;
2820
Simon Kelley824af852008-02-12 20:43:05 +00002821 case 'T': /* --local-ttl */
2822 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002823 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002824 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002825 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002826 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002827 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002828 {
2829 int ttl;
2830 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002831 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002832 else if (option == LOPT_NEGTTL)
2833 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002834 else if (option == LOPT_MAXTTL)
2835 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002836 else if (option == LOPT_MINCTTL)
2837 {
2838 if (ttl > TTL_FLOOR_LIMIT)
2839 ttl = TTL_FLOOR_LIMIT;
2840 daemon->min_cache_ttl = (unsigned long)ttl;
2841 }
Simon Kelley1d860412012-09-20 20:48:04 +01002842 else if (option == LOPT_MAXCTTL)
2843 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002844 else if (option == LOPT_AUTHTTL)
2845 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002846 else if (option == LOPT_DHCPTTL)
2847 {
2848 daemon->dhcp_ttl = (unsigned long)ttl;
2849 daemon->use_dhcp_ttl = 1;
2850 }
Simon Kelley849a8352006-06-09 21:02:31 +01002851 else
2852 daemon->local_ttl = (unsigned long)ttl;
2853 break;
2854 }
2855
Simon Kelley7622fc02009-06-04 20:32:05 +01002856#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002857 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002858 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002859 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002860 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002861#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002862
Simon Kelley7622fc02009-06-04 20:32:05 +01002863#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002864 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002865 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002866 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002867 break;
2868
Simon Kelleybec366b2016-02-24 22:03:26 +00002869 case LOPT_TFTP_MTU: /* --tftp-mtu */
2870 if (!atoi_check(arg, &daemon->tftp_mtu))
2871 ret_err(gen_err);
2872 break;
2873
Simon Kelley824af852008-02-12 20:43:05 +00002874 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002875 comma = split(arg);
2876 if (comma)
2877 {
2878 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2879 new->interface = opt_string_alloc(comma);
2880 new->prefix = opt_string_alloc(arg);
2881 new->next = daemon->if_prefix;
2882 daemon->if_prefix = new;
2883 }
2884 else
2885 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002886 break;
2887
Simon Kelley824af852008-02-12 20:43:05 +00002888 case LOPT_TFTPPORTS: /* --tftp-port-range */
2889 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002890 !atoi_check16(arg, &daemon->start_tftp_port) ||
2891 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002892 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002893
2894 if (daemon->start_tftp_port > daemon->end_tftp_port)
2895 {
2896 int tmp = daemon->start_tftp_port;
2897 daemon->start_tftp_port = daemon->end_tftp_port;
2898 daemon->end_tftp_port = tmp;
2899 }
2900
2901 break;
Floris Bos60704f52017-04-09 22:22:49 +01002902
2903 case LOPT_APREF: /* --tftp-unique-root */
2904 if (!arg || strcasecmp(arg, "ip") == 0)
2905 set_option_bool(OPT_TFTP_APREF_IP);
2906 else if (strcasecmp(arg, "mac") == 0)
2907 set_option_bool(OPT_TFTP_APREF_MAC);
2908 else
2909 ret_err(gen_err);
2910 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002911#endif
Simon Kelley824af852008-02-12 20:43:05 +00002912
Simon Kelleyf2621c72007-04-29 19:47:21 +01002913 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002914 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002915 struct dhcp_bridge *new;
2916
Simon Kelley316e2732010-01-22 20:16:09 +00002917 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002918 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002919
Simon Kelley22cd8602018-01-14 22:57:14 +00002920 for (new = daemon->bridges; new; new = new->next)
2921 if (strcmp(new->iface, arg) == 0)
2922 break;
2923
2924 if (!new)
2925 {
2926 new = opt_malloc(sizeof(struct dhcp_bridge));
2927 strcpy(new->iface, arg);
2928 new->alias = NULL;
2929 new->next = daemon->bridges;
2930 daemon->bridges = new;
2931 }
2932
Simon Kelley832af0b2007-01-21 20:01:28 +00002933 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002934 arg = comma;
2935 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002936 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002937 {
Simon Kelley824af852008-02-12 20:43:05 +00002938 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002939 b->next = new->alias;
2940 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002941 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002942 }
2943 } while (comma);
2944
2945 break;
2946 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002947
Simon Kelley7622fc02009-06-04 20:32:05 +01002948#ifdef HAVE_DHCP
Simon Kelleyae5b7e02019-03-27 22:33:28 +00002949 case LOPT_SHARED_NET: /* --shared-network */
2950 {
2951 struct shared_network *new = opt_malloc(sizeof(struct shared_network));
2952
2953#ifdef HAVE_DHCP6
2954 new->shared_addr.s_addr = 0;
2955#endif
2956 new->if_index = 0;
2957
2958 if (!(comma = split(arg)))
2959 {
2960 snerr:
2961 free(new);
2962 ret_err(_("bad shared-network"));
2963 }
2964
2965 if (inet_pton(AF_INET, comma, &new->shared_addr))
2966 {
2967 if (!inet_pton(AF_INET, arg, &new->match_addr) &&
2968 !(new->if_index = if_nametoindex(arg)))
2969 goto snerr;
2970 }
2971#ifdef HAVE_DHCP6
2972 else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
2973 {
2974 if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
2975 !(new->if_index = if_nametoindex(arg)))
2976 goto snerr;
2977 }
2978#endif
2979 else
2980 goto snerr;
2981
2982 new->next = daemon->shared_networks;
2983 daemon->shared_networks = new;
2984 break;
2985 }
2986
Simon Kelleyf2621c72007-04-29 19:47:21 +01002987 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002988 {
2989 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002990 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002991 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002992
Simon Kelley52b92f42012-01-22 16:05:15 +00002993 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002994 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002995
Simon Kelley849a8352006-06-09 21:02:31 +01002996 while(1)
2997 {
2998 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002999 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
3000 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3001 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003002 break;
3003
Simon Kelleyf2621c72007-04-29 19:47:21 +01003004 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003005 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003006 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003007 {
Simon Kelley0c387192013-09-05 10:21:12 +01003008 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003009 if (arg[4])
3010 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003011 }
3012 else
3013 {
3014 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003015 {
3016 dhcp_context_free(new);
3017 ret_err(_("only one tag allowed"));
3018 }
Simon Kelley849a8352006-06-09 21:02:31 +01003019 else
Petr Menšík59e47032018-11-02 22:39:39 +00003020 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003021 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003022 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003023 }
3024 else
3025 {
3026 a[0] = arg;
3027 break;
3028 }
3029 }
3030
Simon Kelley1f776932012-12-16 19:46:08 +00003031 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003032 if (!(a[k] = split(a[k-1])))
3033 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003034
Simon Kelley52b92f42012-01-22 16:05:15 +00003035 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003036 {
3037 dhcp_context_free(new);
3038 ret_err(_("bad dhcp-range"));
3039 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003040
3041 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003042 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003043 new->next = daemon->dhcp;
3044 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01003045 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00003046 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003047 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00003048 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01003049 new->flags |= CONTEXT_PROXY;
3050 else if (!inet_pton(AF_INET, a[1], &new->end))
Petr Menšík59e47032018-11-02 22:39:39 +00003051 {
3052 dhcp_context_free(new);
3053 ret_err(_("bad dhcp-range"));
3054 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003055
3056 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
3057 {
3058 struct in_addr tmp = new->start;
3059 new->start = new->end;
3060 new->end = tmp;
3061 }
3062
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003063 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003064 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00003065 {
3066 new->flags |= CONTEXT_NETMASK;
3067 leasepos = 3;
3068 if (!is_same_net(new->start, new->end, new->netmask))
Petr Menšík59e47032018-11-02 22:39:39 +00003069 {
3070 dhcp_context_free(new);
3071 ret_err(_("inconsistent DHCP range"));
3072 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003073
Simon Kelley52b92f42012-01-22 16:05:15 +00003074
Simon Kelleyfa794662016-03-03 20:33:54 +00003075 if (k >= 4 && strchr(a[3], '.') &&
3076 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
3077 {
3078 new->flags |= CONTEXT_BRDCAST;
3079 leasepos = 4;
3080 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003081 }
Simon Kelley849a8352006-06-09 21:02:31 +01003082 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003083#ifdef HAVE_DHCP6
3084 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01003085 {
Petr Menšík59e47032018-11-02 22:39:39 +00003086 const char *err = NULL;
3087
Simon Kelley89500e32013-09-20 16:29:20 +01003088 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00003089 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01003090 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003091 new->next = daemon->dhcp6;
3092 daemon->dhcp6 = new;
3093
Simon Kelley30cd9662012-03-25 20:44:38 +01003094 for (leasepos = 1; leasepos < k; leasepos++)
3095 {
3096 if (strcmp(a[leasepos], "static") == 0)
3097 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3098 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003099 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003100 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003101 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003102 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3103 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003104 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003105 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003106 else if (strcmp(a[leasepos], "off-link") == 0)
3107 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003108 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3109 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003110 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3111 {
3112 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3113 new->flags |= CONTEXT_TEMPLATE;
3114 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003115 else
3116 break;
3117 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003118
Simon Kelley52b92f42012-01-22 16:05:15 +00003119 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003120 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003121 {
3122 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003123 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003124 if (!(*cp >= '0' && *cp <= '9'))
3125 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003126 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003127 {
3128 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003129 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003130 }
3131 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003132
Petr Menšík59e47032018-11-02 22:39:39 +00003133 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003134 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003135 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003136 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003137 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003138 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003139 }
Petr Menšík59e47032018-11-02 22:39:39 +00003140 else if (new->prefix < 64)
3141 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003142
Petr Menšík59e47032018-11-02 22:39:39 +00003143 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3144 err=(_("inconsistent DHCPv6 range"));
3145
3146 if (err)
3147 {
3148 dhcp_context_free(new);
3149 ret_err(err);
3150 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003151
3152 /* dhcp-range=:: enables DHCP stateless on any interface */
3153 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3154 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003155
3156 if (new->flags & CONTEXT_TEMPLATE)
3157 {
3158 struct in6_addr zero;
3159 memset(&zero, 0, sizeof(zero));
3160 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003161 {
3162 dhcp_context_free(new);
3163 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3164 }
Simon Kelley66409192013-08-01 20:19:32 +01003165 }
3166
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003167 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003168 {
3169 struct in6_addr tmp = new->start6;
3170 new->start6 = new->end6;
3171 new->end6 = tmp;
3172 }
Simon Kelley849a8352006-06-09 21:02:31 +01003173 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003174#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003175 else
Petr Menšík59e47032018-11-02 22:39:39 +00003176 {
3177 dhcp_context_free(new);
3178 ret_err(_("bad dhcp-range"));
3179 }
Simon Kelley849a8352006-06-09 21:02:31 +01003180
Simon Kelley30cd9662012-03-25 20:44:38 +01003181 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003182 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003183 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003184 {
3185 dhcp_context_free(new);
3186 ret_err(_("bad dhcp-range"));
3187 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003188
Simon Kelley849a8352006-06-09 21:02:31 +01003189 if (strcmp(a[leasepos], "infinite") == 0)
3190 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01003191 else if (strcmp(a[leasepos], "deprecated") == 0)
3192 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003193 else
3194 {
3195 int fac = 1;
3196 if (strlen(a[leasepos]) > 0)
3197 {
3198 switch (a[leasepos][strlen(a[leasepos]) - 1])
3199 {
Simon Kelley42243212012-07-20 15:19:18 +01003200 case 'w':
3201 case 'W':
3202 fac *= 7;
3203 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003204 case 'd':
3205 case 'D':
3206 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003207 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003208 case 'h':
3209 case 'H':
3210 fac *= 60;
3211 /* fall through */
3212 case 'm':
3213 case 'M':
3214 fac *= 60;
3215 /* fall through */
3216 case 's':
3217 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003218 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003219 }
3220
Simon Kelleybe379862012-12-23 12:01:39 +00003221 for (cp = a[leasepos]; *cp; cp++)
3222 if (!(*cp >= '0' && *cp <= '9'))
3223 break;
3224
Simon Kelley54dae552013-02-05 17:55:10 +00003225 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003226 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003227
Simon Kelley849a8352006-06-09 21:02:31 +01003228 new->lease_time = atoi(a[leasepos]) * fac;
3229 /* Leases of a minute or less confuse
3230 some clients, notably Apple's */
3231 if (new->lease_time < 120)
3232 new->lease_time = 120;
3233 }
3234 }
3235 }
3236 break;
3237 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003238
Simon Kelley5aabfc72007-08-29 11:24:47 +01003239 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003240 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003241 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003242 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003243 struct in_addr in;
3244
Simon Kelley824af852008-02-12 20:43:05 +00003245 new = opt_malloc(sizeof(struct dhcp_config));
3246
Simon Kelley849a8352006-06-09 21:02:31 +01003247 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003248 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3249 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003250 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003251 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003252 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003253#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003254 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003255#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003256
Simon Kelley137286e2020-02-06 22:09:30 +00003257 while (arg)
3258 {
3259 comma = split(arg);
3260 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3261 {
3262 if ((arg[0] == 'i' || arg[0] == 'I') &&
3263 (arg[1] == 'd' || arg[1] == 'D') &&
3264 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003265 {
Simon Kelley137286e2020-02-06 22:09:30 +00003266 if (arg[3] == '*')
3267 new->flags |= CONFIG_NOCLID;
3268 else
3269 {
3270 int len;
3271 arg += 3; /* dump id: */
3272 if (strchr(arg, ':'))
3273 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3274 else
3275 {
3276 unhide_metas(arg);
3277 len = (int) strlen(arg);
3278 }
3279
3280 if (len == -1)
3281 {
3282 dhcp_config_free(new);
3283 ret_err(_("bad hex constant"));
3284 }
3285 else if ((new->clid = opt_malloc(len)))
3286 {
3287 new->flags |= CONFIG_CLID;
3288 new->clid_len = len;
3289 memcpy(new->clid, arg, len);
3290 }
3291 }
3292 }
3293 /* dhcp-host has strange backwards-compat needs. */
3294 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3295 {
3296 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3297 newlist->next = new->netid;
3298 new->netid = newlist;
3299 newlist->list = dhcp_netid_create(arg+4, NULL);
3300 }
3301 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003302 new->filter = dhcp_netid_create(arg+4, new->filter);
3303
Simon Kelley137286e2020-02-06 22:09:30 +00003304#ifdef HAVE_DHCP6
3305 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3306 {
3307 char *pref;
3308 struct in6_addr in6;
3309 struct addrlist *new_addr;
3310
3311 arg[strlen(arg)-1] = 0;
3312 arg++;
3313 pref = split_chr(arg, '/');
3314
3315 if (!inet_pton(AF_INET6, arg, &in6))
3316 {
3317 dhcp_config_free(new);
3318 ret_err(_("bad IPv6 address"));
3319 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003320
Simon Kelley137286e2020-02-06 22:09:30 +00003321 new_addr = opt_malloc(sizeof(struct addrlist));
3322 new_addr->next = new->addr6;
3323 new_addr->flags = 0;
3324 new_addr->addr.addr6 = in6;
3325 new->addr6 = new_addr;
3326
3327 if (pref)
3328 {
3329 u64 addrpart = addr6part(&in6);
3330
3331 if (!atoi_check(pref, &new_addr->prefixlen) ||
3332 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003333 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003334 {
3335 dhcp_config_free(new);
3336 ret_err(_("bad IPv6 prefix"));
3337 }
3338
3339 new_addr->flags |= ADDRLIST_PREFIX;
3340 }
3341
3342 for (i= 0; i < 8; i++)
3343 if (in6.s6_addr[i] != 0)
3344 break;
3345
3346 /* set WILDCARD if network part all zeros */
3347 if (i == 8)
3348 new_addr->flags |= ADDRLIST_WILDCARD;
3349
3350 new->flags |= CONFIG_ADDR6;
3351 }
3352#endif
3353 else
3354 {
3355 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3356 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3357 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3358 {
3359 free(newhw);
3360 dhcp_config_free(new);
3361 ret_err(_("bad hex constant"));
3362 }
3363 else
3364 {
3365 newhw->next = new->hwaddr;
3366 new->hwaddr = newhw;
3367 }
3368 }
3369 }
3370 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3371 {
3372 struct dhcp_config *configs;
3373
3374 new->addr = in;
3375 new->flags |= CONFIG_ADDR;
3376
3377 /* If the same IP appears in more than one host config, then DISCOVER
3378 for one of the hosts will get the address, but REQUEST will be NAKed,
3379 since the address is reserved by the other one -> protocol loop. */
3380 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3381 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003382 {
Simon Kelley137286e2020-02-06 22:09:30 +00003383 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3384 return 0;
3385 }
3386 }
3387 else
3388 {
3389 char *cp, *lastp = NULL, last = 0;
3390 int fac = 1, isdig = 0;
3391
3392 if (strlen(arg) > 1)
3393 {
3394 lastp = arg + strlen(arg) - 1;
3395 last = *lastp;
3396 switch (last)
3397 {
3398 case 'w':
3399 case 'W':
3400 fac *= 7;
3401 /* fall through */
3402 case 'd':
3403 case 'D':
3404 fac *= 24;
3405 /* fall through */
3406 case 'h':
3407 case 'H':
3408 fac *= 60;
3409 /* fall through */
3410 case 'm':
3411 case 'M':
3412 fac *= 60;
3413 /* fall through */
3414 case 's':
3415 case 'S':
3416 *lastp = 0;
3417 }
3418 }
3419
3420 for (cp = arg; *cp; cp++)
3421 if (isdigit((unsigned char)*cp))
3422 isdig = 1;
3423 else if (*cp != ' ')
3424 break;
3425
3426 if (*cp)
3427 {
3428 if (lastp)
3429 *lastp = last;
3430 if (strcmp(arg, "infinite") == 0)
3431 {
3432 new->lease_time = 0xffffffff;
3433 new->flags |= CONFIG_TIME;
3434 }
3435 else if (strcmp(arg, "ignore") == 0)
3436 new->flags |= CONFIG_DISABLE;
3437 else
3438 {
3439 if (!(new->hostname = canonicalise_opt(arg)) ||
3440 !legal_hostname(new->hostname))
3441 {
3442 dhcp_config_free(new);
3443 ret_err(_("bad DHCP host name"));
3444 }
3445
3446 new->flags |= CONFIG_NAME;
3447 new->domain = strip_hostname(new->hostname);
3448 }
3449 }
3450 else if (isdig)
3451 {
3452 new->lease_time = atoi(arg) * fac;
3453 /* Leases of a minute or less confuse
3454 some clients, notably Apple's */
3455 if (new->lease_time < 120)
3456 new->lease_time = 120;
3457 new->flags |= CONFIG_TIME;
3458 }
3459 }
3460
3461 arg = comma;
3462 }
3463
Simon Kelley5aabfc72007-08-29 11:24:47 +01003464 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003465 break;
3466 }
Simon Kelley137286e2020-02-06 22:09:30 +00003467
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003468 case LOPT_TAG_IF: /* --tag-if */
3469 {
3470 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3471
3472 new->tag = NULL;
3473 new->set = NULL;
3474 new->next = NULL;
3475
3476 /* preserve order */
3477 if (!daemon->tag_if)
3478 daemon->tag_if = new;
3479 else
3480 {
3481 struct tag_if *tmp;
3482 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3483 tmp->next = new;
3484 }
3485
3486 while (arg)
3487 {
3488 size_t len;
3489
3490 comma = split(arg);
3491 len = strlen(arg);
3492
3493 if (len < 5)
3494 {
3495 new->set = NULL;
3496 break;
3497 }
3498 else
3499 {
Petr Menšík59e47032018-11-02 22:39:39 +00003500 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003501
3502 if (strstr(arg, "set:") == arg)
3503 {
3504 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3505 newlist->next = new->set;
3506 new->set = newlist;
3507 newlist->list = newtag;
3508 }
3509 else if (strstr(arg, "tag:") == arg)
3510 {
3511 newtag->next = new->tag;
3512 new->tag = newtag;
3513 }
3514 else
3515 {
3516 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003517 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003518 break;
3519 }
3520 }
3521
3522 arg = comma;
3523 }
3524
3525 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003526 {
3527 dhcp_netid_free(new->tag);
3528 dhcp_netid_list_free(new->set);
3529 ret_err_free(_("bad tag-if"), new);
3530 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003531
3532 break;
3533 }
3534
Simon Kelley849a8352006-06-09 21:02:31 +01003535
Simon Kelley73a08a22009-02-05 20:28:08 +00003536 case 'O': /* --dhcp-option */
3537 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003538 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003539 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003540 return parse_dhcp_opt(errstr, arg,
3541 option == LOPT_FORCE ? DHOPT_FORCE :
3542 (option == LOPT_MATCH ? DHOPT_MATCH :
3543 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003544
3545 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3546 {
3547 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3548 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3549 ssize_t len;
3550
3551 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3552 ret_err(gen_err);
3553
3554 new->wildcard = 0;
3555 new->netid = id;
3556 id->net = opt_string_alloc(set_prefix(arg));
3557
3558 if (comma[len-1] == '*')
3559 {
3560 comma[len-1] = 0;
3561 new->wildcard = 1;
3562 }
3563 new->name = opt_string_alloc(comma);
3564
3565 new->next = daemon->dhcp_name_match;
3566 daemon->dhcp_name_match = new;
3567
3568 break;
3569 }
3570
Simon Kelleyf2621c72007-04-29 19:47:21 +01003571 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003572 {
Petr Menšík59e47032018-11-02 22:39:39 +00003573 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003574
Petr Menšík137e9f82018-12-16 21:25:29 +00003575 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003576 {
3577 ret_err(gen_err);
3578 }
Simon Kelley849a8352006-06-09 21:02:31 +01003579 else
3580 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003581 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003582 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003583 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003584 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003585 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003586 dhcp_next_server.s_addr = 0;
3587 if (comma)
3588 {
3589 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003590 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003591 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003592 if (comma)
3593 {
3594 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003595 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3596 {
3597 /*
3598 * The user may have specified the tftp hostname here.
3599 * save it so that it can be resolved/looked up during
3600 * actual dhcp_reply().
3601 */
3602
3603 tftp_sname = opt_string_alloc(comma);
3604 dhcp_next_server.s_addr = 0;
3605 }
Simon Kelley849a8352006-06-09 21:02:31 +01003606 }
3607 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003608
3609 new = opt_malloc(sizeof(struct dhcp_boot));
3610 new->file = dhcp_file;
3611 new->sname = dhcp_sname;
3612 new->tftp_sname = tftp_sname;
3613 new->next_server = dhcp_next_server;
3614 new->netid = id;
3615 new->next = daemon->boot_config;
3616 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003617 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003618
Simon Kelley849a8352006-06-09 21:02:31 +01003619 break;
3620 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003621
Floris Bos503c6092017-04-09 23:07:13 +01003622 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3623 {
Petr Menšík59e47032018-11-02 22:39:39 +00003624 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003625
Petr Menšík137e9f82018-12-16 21:25:29 +00003626 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003627 {
3628 ret_err(gen_err);
3629 }
Floris Bos503c6092017-04-09 23:07:13 +01003630 else
3631 {
3632 struct delay_config *new;
3633 int delay;
3634 if (!atoi_check(arg, &delay))
3635 ret_err(gen_err);
3636
3637 new = opt_malloc(sizeof(struct delay_config));
3638 new->delay = delay;
3639 new->netid = id;
3640 new->next = daemon->delay_conf;
3641 daemon->delay_conf = new;
3642 }
3643
3644 break;
3645 }
3646
Simon Kelley7622fc02009-06-04 20:32:05 +01003647 case LOPT_PXE_PROMT: /* --pxe-prompt */
3648 {
3649 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3650 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003651
Simon Kelley7622fc02009-06-04 20:32:05 +01003652 new->netid = NULL;
3653 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003654 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003655
Petr Menšík137e9f82018-12-16 21:25:29 +00003656 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003657 {
3658 dhcp_opt_free(new);
3659 ret_err(gen_err);
3660 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003661 else
3662 {
3663 comma = split(arg);
3664 unhide_metas(arg);
3665 new->len = strlen(arg) + 1;
3666 new->val = opt_malloc(new->len);
3667 memcpy(new->val + 1, arg, new->len - 1);
3668
3669 new->u.vendor_class = (unsigned char *)"PXEClient";
3670 new->flags = DHOPT_VENDOR;
3671
3672 if (comma && atoi_check(comma, &timeout))
3673 *(new->val) = timeout;
3674 else
3675 *(new->val) = 255;
3676
3677 new->next = daemon->dhcp_opts;
3678 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003679 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003680 }
3681
3682 break;
3683 }
3684
3685 case LOPT_PXE_SERV: /* --pxe-service */
3686 {
3687 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3688 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003689 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3690 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003691 static int boottype = 32768;
3692
3693 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003694 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003695 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003696 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003697
Simon Kelley7622fc02009-06-04 20:32:05 +01003698 if (arg && (comma = split(arg)))
3699 {
3700 for (i = 0; CSA[i]; i++)
3701 if (strcasecmp(CSA[i], arg) == 0)
3702 break;
3703
3704 if (CSA[i] || atoi_check(arg, &i))
3705 {
3706 arg = comma;
3707 comma = split(arg);
3708
3709 new->CSA = i;
3710 new->menu = opt_string_alloc(arg);
3711
Simon Kelley316e2732010-01-22 20:16:09 +00003712 if (!comma)
3713 {
3714 new->type = 0; /* local boot */
3715 new->basename = NULL;
3716 }
3717 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003718 {
3719 arg = comma;
3720 comma = split(arg);
3721 if (atoi_check(arg, &i))
3722 {
3723 new->type = i;
3724 new->basename = NULL;
3725 }
3726 else
3727 {
3728 new->type = boottype++;
3729 new->basename = opt_string_alloc(arg);
3730 }
3731
Simon Kelley751d6f42012-02-10 15:24:51 +00003732 if (comma)
3733 {
3734 if (!inet_pton(AF_INET, comma, &new->server))
3735 {
3736 new->server.s_addr = 0;
3737 new->sname = opt_string_alloc(comma);
3738 }
3739
3740 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003741 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003742
Simon Kelley316e2732010-01-22 20:16:09 +00003743 /* Order matters */
3744 new->next = NULL;
3745 if (!daemon->pxe_services)
3746 daemon->pxe_services = new;
3747 else
3748 {
3749 struct pxe_service *s;
3750 for (s = daemon->pxe_services; s->next; s = s->next);
3751 s->next = new;
3752 }
3753
3754 daemon->enable_pxe = 1;
3755 break;
3756
Simon Kelley7622fc02009-06-04 20:32:05 +01003757 }
3758 }
3759
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003760 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003761 }
3762
Simon Kelleyf2621c72007-04-29 19:47:21 +01003763 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003764 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003765 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003766 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003767 else
3768 {
Simon Kelley824af852008-02-12 20:43:05 +00003769 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003770 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003771 unhide_metas(comma);
3772 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003773 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003774 {
3775 free(new->netid.net);
3776 ret_err_free(gen_err, new);
3777 }
Simon Kelley28866e92011-02-14 20:19:14 +00003778 else
3779 {
3780 new->next = daemon->dhcp_macs;
3781 daemon->dhcp_macs = new;
3782 }
Simon Kelley849a8352006-06-09 21:02:31 +01003783 }
3784 }
3785 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003786
Simon Kelleyf2621c72007-04-29 19:47:21 +01003787 case 'U': /* --dhcp-vendorclass */
3788 case 'j': /* --dhcp-userclass */
3789 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3790 case LOPT_REMOTE: /* --dhcp-remoteid */
3791 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003792 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003793 unsigned char *p;
3794 int dig = 0;
3795 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3796
3797 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003798 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003799
3800 new->netid.net = opt_string_alloc(set_prefix(arg));
3801 /* check for hex string - must digits may include : must not have nothing else,
3802 only allowed for agent-options. */
3803
3804 arg = comma;
3805 if ((comma = split(arg)))
3806 {
3807 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003808 {
3809 free(new->netid.net);
3810 ret_err_free(gen_err, new);
3811 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003812 else
3813 new->enterprise = atoi(arg+11);
3814 }
3815 else
3816 comma = arg;
3817
3818 for (p = (unsigned char *)comma; *p; p++)
3819 if (isxdigit(*p))
3820 dig = 1;
3821 else if (*p != ':')
3822 break;
3823 unhide_metas(comma);
3824 if (option == 'U' || option == 'j' || *p || !dig)
3825 {
3826 new->len = strlen(comma);
3827 new->data = opt_malloc(new->len);
3828 memcpy(new->data, comma, new->len);
3829 }
3830 else
3831 {
3832 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3833 new->data = opt_malloc(new->len);
3834 memcpy(new->data, comma, new->len);
3835 }
3836
3837 switch (option)
3838 {
3839 case 'j':
3840 new->match_type = MATCH_USER;
3841 break;
3842 case 'U':
3843 new->match_type = MATCH_VENDOR;
3844 break;
3845 case LOPT_CIRCUIT:
3846 new->match_type = MATCH_CIRCUIT;
3847 break;
3848 case LOPT_REMOTE:
3849 new->match_type = MATCH_REMOTE;
3850 break;
3851 case LOPT_SUBSCR:
3852 new->match_type = MATCH_SUBSCRIBER;
3853 break;
3854 }
3855 new->next = daemon->dhcp_vendors;
3856 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003857
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003858 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003859 }
3860
Simon Kelley9e038942008-05-30 20:06:34 +01003861 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3862 if (!arg)
3863 {
3864 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3865 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3866 }
3867 else
3868 {
3869 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003870 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3871 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003872 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003873 if (!comma)
3874 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3875 }
3876 break;
3877
Simon Kelley824af852008-02-12 20:43:05 +00003878 case 'J': /* --dhcp-ignore */
3879 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3880 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003881 case '3': /* --bootp-dynamic */
3882 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003883 {
Simon Kelley824af852008-02-12 20:43:05 +00003884 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003885 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003886 if (option == 'J')
3887 {
3888 new->next = daemon->dhcp_ignore;
3889 daemon->dhcp_ignore = new;
3890 }
Simon Kelley824af852008-02-12 20:43:05 +00003891 else if (option == LOPT_BROADCAST)
3892 {
3893 new->next = daemon->force_broadcast;
3894 daemon->force_broadcast = new;
3895 }
Simon Kelley9009d742008-11-14 20:04:27 +00003896 else if (option == '3')
3897 {
3898 new->next = daemon->bootp_dynamic;
3899 daemon->bootp_dynamic = new;
3900 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003901 else if (option == LOPT_GEN_NAMES)
3902 {
3903 new->next = daemon->dhcp_gen_names;
3904 daemon->dhcp_gen_names = new;
3905 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003906 else
3907 {
3908 new->next = daemon->dhcp_ignore_names;
3909 daemon->dhcp_ignore_names = new;
3910 }
3911
3912 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003913 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003914 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003915 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003916 }
Simon Kelley849a8352006-06-09 21:02:31 +01003917
3918 new->list = list;
3919 break;
3920 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003921
3922 case LOPT_PROXY: /* --dhcp-proxy */
3923 daemon->override = 1;
3924 while (arg) {
3925 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3926 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003927 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003928 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003929 new->next = daemon->override_relays;
3930 daemon->override_relays = new;
3931 arg = comma;
3932 }
3933 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003934
3935 case LOPT_RELAY: /* --dhcp-relay */
3936 {
3937 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3938 comma = split(arg);
3939 new->interface = opt_string_alloc(split(comma));
3940 new->iface_index = 0;
3941 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3942 {
3943 new->next = daemon->relay4;
3944 daemon->relay4 = new;
3945 }
3946#ifdef HAVE_DHCP6
3947 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3948 {
3949 new->next = daemon->relay6;
3950 daemon->relay6 = new;
3951 }
3952#endif
3953 else
Petr Menšík59e47032018-11-02 22:39:39 +00003954 {
3955 free(new->interface);
3956 ret_err_free(_("Bad dhcp-relay"), new);
3957 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003958
3959 break;
3960 }
3961
Simon Kelley7622fc02009-06-04 20:32:05 +01003962#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003963
Simon Kelley8b372702012-03-09 17:45:10 +00003964#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003965 case LOPT_RA_PARAM: /* --ra-param */
3966 if ((comma = split(arg)))
3967 {
3968 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3969 new->lifetime = -1;
3970 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003971 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003972 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003973 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003974 if (strcasestr(comma, "mtu:") == comma)
3975 {
3976 arg = comma + 4;
3977 if (!(comma = split(comma)))
3978 goto err;
3979 if (!strcasecmp(arg, "off"))
3980 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003981 else if (!atoi_check(arg, &new->mtu))
3982 new->mtu_name = opt_string_alloc(arg);
3983 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003984 goto err;
3985 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003986 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3987 {
3988 if (*comma == 'l' || *comma == 'L')
3989 new->prio = 0x18;
3990 else
3991 new->prio = 0x08;
3992 comma = split(comma);
3993 }
3994 arg = split(comma);
3995 if (!atoi_check(comma, &new->interval) ||
3996 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00003997 {
David Flamand005c46d2017-04-11 11:49:54 +01003998err:
Petr Menšík59e47032018-11-02 22:39:39 +00003999 free(new->name);
4000 ret_err_free(_("bad RA-params"), new);
4001 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004002
4003 new->next = daemon->ra_interfaces;
4004 daemon->ra_interfaces = new;
4005 }
4006 break;
4007
Simon Kelley8b372702012-03-09 17:45:10 +00004008 case LOPT_DUID: /* --dhcp-duid */
4009 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004010 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004011 else
4012 {
4013 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4014 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4015 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4016 }
4017 break;
4018#endif
4019
Simon Kelleyf2621c72007-04-29 19:47:21 +01004020 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004021 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004022 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004023 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004024 struct doctor *new = opt_malloc(sizeof(struct doctor));
4025 new->next = daemon->doctors;
4026 daemon->doctors = new;
4027 new->mask.s_addr = 0xffffffff;
4028 new->end.s_addr = 0;
4029
Simon Kelley849a8352006-06-09 21:02:31 +01004030 if ((a[0] = arg))
4031 for (k = 1; k < 3; k++)
4032 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004033 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004034 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004035 unhide_metas(a[k]);
4036 }
Simon Kelley849a8352006-06-09 21:02:31 +01004037
Simon Kelley73a08a22009-02-05 20:28:08 +00004038 dash = split_chr(a[0], '-');
4039
Simon Kelley849a8352006-06-09 21:02:31 +01004040 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004041 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004042 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4043 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4044 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004045
Simon Kelley73a08a22009-02-05 20:28:08 +00004046 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004047 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004048 !is_same_net(new->in, new->end, new->mask) ||
4049 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004050 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004051
4052 break;
4053 }
4054
Simon Kelleyf2621c72007-04-29 19:47:21 +01004055 case LOPT_INTNAME: /* --interface-name */
4056 {
4057 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01004058 char *domain = NULL;
4059
Simon Kelleyf2621c72007-04-29 19:47:21 +01004060 comma = split(arg);
4061
Simon Kelley1f15b812009-10-13 17:49:32 +01004062 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004063 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01004064
Simon Kelley824af852008-02-12 20:43:05 +00004065 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004066 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00004067 new->addr = NULL;
4068
Simon Kelleyf2621c72007-04-29 19:47:21 +01004069 /* Add to the end of the list, so that first name
4070 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004071 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004072 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004073 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004074 new->family = 0;
4075 arg = split_chr(comma, '/');
4076 if (arg)
4077 {
4078 if (strcmp(arg, "4") == 0)
4079 new->family = AF_INET;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004080 else if (strcmp(arg, "6") == 0)
4081 new->family = AF_INET6;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004082 else
Petr Menšík59e47032018-11-02 22:39:39 +00004083 ret_err_free(gen_err, new);
Simon Kelleyf7029f52013-11-21 15:09:09 +00004084 }
Simon Kelley824af852008-02-12 20:43:05 +00004085 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004086 break;
4087 }
Simon Kelley9009d742008-11-14 20:04:27 +00004088
4089 case LOPT_CNAME: /* --cname */
4090 {
4091 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004092 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004093 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004094
Simon Kelleya1d973f2016-12-22 22:09:50 +00004095 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004096 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004097 pen = last;
4098 last = comma;
4099 }
4100
4101 if (!pen)
4102 ret_err(_("bad CNAME"));
4103
4104 if (pen != arg && atoi_check(last, &ttl))
4105 last = pen;
4106
4107 target = canonicalise_opt(last);
4108
4109 while (arg != last)
4110 {
Petr Menšík56f06232018-03-06 23:13:32 +00004111 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004112 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004113
4114 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004115 {
4116 free(target);
4117 free(alias);
4118 ret_err(_("bad CNAME"));
4119 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004120
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004121 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004122 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004123 {
4124 free(target);
4125 free(alias);
4126 ret_err(_("duplicate CNAME"));
4127 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004128 new = opt_malloc(sizeof(struct cname));
4129 new->next = daemon->cnames;
4130 daemon->cnames = new;
4131 new->alias = alias;
4132 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004133 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004134
Petr Menšík56f06232018-03-06 23:13:32 +00004135 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004136 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004137
Simon Kelley9009d742008-11-14 20:04:27 +00004138 break;
4139 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004140
4141 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004142 {
4143 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004144 char *dom, *target = NULL;
4145
Simon Kelleyf2621c72007-04-29 19:47:21 +01004146 comma = split(arg);
4147
Simon Kelley1f15b812009-10-13 17:49:32 +01004148 if (!(dom = canonicalise_opt(arg)) ||
4149 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004150 {
4151 free(dom);
4152 free(target);
4153 ret_err(_("bad PTR record"));
4154 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004155 else
4156 {
4157 new = opt_malloc(sizeof(struct ptr_record));
4158 new->next = daemon->ptr;
4159 daemon->ptr = new;
4160 new->name = dom;
4161 new->ptr = target;
4162 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004163 break;
4164 }
4165
Simon Kelley1a6bca82008-07-11 11:11:42 +01004166 case LOPT_NAPTR: /* --naptr-record */
4167 {
4168 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4169 int k = 0;
4170 struct naptr *new;
4171 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004172 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004173
4174 if ((a[0] = arg))
4175 for (k = 1; k < 7; k++)
4176 if (!(a[k] = split(a[k-1])))
4177 break;
4178
4179
4180 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004181 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004182 !atoi_check16(a[1], &order) ||
4183 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004184 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004185 {
4186 free(name);
4187 free(replace);
4188 ret_err(_("bad NAPTR record"));
4189 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004190 else
4191 {
4192 new = opt_malloc(sizeof(struct naptr));
4193 new->next = daemon->naptr;
4194 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004195 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004196 new->flags = opt_string_alloc(a[3]);
4197 new->services = opt_string_alloc(a[4]);
4198 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004199 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004200 new->order = order;
4201 new->pref = pref;
4202 }
4203 break;
4204 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004205
4206 case LOPT_RR: /* dns-rr */
4207 {
4208 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004209 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004210 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004211 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004212
4213 comma = split(arg);
4214 data = split(comma);
4215
4216 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004217 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004218
Petr Menšík59e47032018-11-02 22:39:39 +00004219 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004220 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004221 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004222 {
4223 free(new->name);
4224 ret_err_free(_("bad RR record"), new);
4225 }
4226
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004227 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004228 new->class = class;
4229 new->next = daemon->rr;
4230 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004231
4232 if (data)
4233 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004234 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004235 new->len = len;
4236 memcpy(new->txt, data, len);
4237 }
4238
4239 break;
4240 }
4241
Simon Kelley974a6d02018-08-23 23:01:16 +01004242 case LOPT_CAA: /* --caa-record */
4243 {
4244 struct txt_record *new;
4245 char *tag, *value;
4246 int flags;
4247
4248 comma = split(arg);
4249 tag = split(comma);
4250 value = split(tag);
4251
4252 new = opt_malloc(sizeof(struct txt_record));
4253 new->next = daemon->rr;
4254 daemon->rr = new;
4255
4256 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4257 ret_err(_("bad CAA record"));
4258
4259 unhide_metas(tag);
4260 unhide_metas(value);
4261
4262 new->len = strlen(tag) + strlen(value) + 2;
4263 new->txt = opt_malloc(new->len);
4264 new->txt[0] = flags;
4265 new->txt[1] = strlen(tag);
4266 memcpy(&new->txt[2], tag, strlen(tag));
4267 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4268 new->class = T_CAA;
4269
4270 break;
4271 }
4272
Simon Kelleyf2621c72007-04-29 19:47:21 +01004273 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004274 {
4275 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004276 unsigned char *p, *cnt;
4277 size_t len;
4278
4279 comma = split(arg);
4280
Simon Kelley824af852008-02-12 20:43:05 +00004281 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004282 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004283 new->stat = 0;
4284
Simon Kelley1f15b812009-10-13 17:49:32 +01004285 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004286 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004287
Petr Menšík59e47032018-11-02 22:39:39 +00004288 new->next = daemon->txt;
4289 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004290 len = comma ? strlen(comma) : 0;
4291 len += (len/255) + 1; /* room for extra counts */
4292 new->txt = p = opt_malloc(len);
4293
4294 cnt = p++;
4295 *cnt = 0;
4296
4297 while (comma && *comma)
4298 {
4299 unsigned char c = (unsigned char)*comma++;
4300
4301 if (c == ',' || *cnt == 255)
4302 {
4303 if (c != ',')
4304 comma--;
4305 cnt = p++;
4306 *cnt = 0;
4307 }
4308 else
4309 {
4310 *p++ = unhide_meta(c);
4311 (*cnt)++;
4312 }
4313 }
4314
4315 new->len = p - new->txt;
4316
Simon Kelley849a8352006-06-09 21:02:31 +01004317 break;
4318 }
4319
Simon Kelleyf2621c72007-04-29 19:47:21 +01004320 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004321 {
4322 int port = 1, priority = 0, weight = 0;
4323 char *name, *target = NULL;
4324 struct mx_srv_record *new;
4325
Simon Kelleyf2621c72007-04-29 19:47:21 +01004326 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004327
Simon Kelley1f15b812009-10-13 17:49:32 +01004328 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004329 ret_err(_("bad SRV record"));
4330
Simon Kelley849a8352006-06-09 21:02:31 +01004331 if (comma)
4332 {
4333 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004334 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004335 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004336 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004337
Simon Kelley849a8352006-06-09 21:02:31 +01004338 if (comma)
4339 {
4340 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004341 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004342 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004343 {
4344 free(name);
4345 ret_err_free(_("invalid port number"), target);
4346 }
Simon Kelley824af852008-02-12 20:43:05 +00004347
Simon Kelley849a8352006-06-09 21:02:31 +01004348 if (comma)
4349 {
4350 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004351 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004352 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004353 {
4354 free(name);
4355 ret_err_free(_("invalid priority"), target);
4356 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004357 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004358 {
4359 free(name);
4360 ret_err_free(_("invalid weight"), target);
4361 }
Simon Kelley849a8352006-06-09 21:02:31 +01004362 }
4363 }
4364 }
4365
Simon Kelley824af852008-02-12 20:43:05 +00004366 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004367 new->next = daemon->mxnames;
4368 daemon->mxnames = new;
4369 new->issrv = 1;
4370 new->name = name;
4371 new->target = target;
4372 new->srvport = port;
4373 new->priority = priority;
4374 new->weight = weight;
4375 break;
4376 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004377
Simon Kelleye759d422012-03-16 13:18:57 +00004378 case LOPT_HOST_REC: /* --host-record */
4379 {
Petr Menšík59e47032018-11-02 22:39:39 +00004380 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004381
Simon Kelleye759d422012-03-16 13:18:57 +00004382 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004383 ret_err(_("Bad host-record"));
4384
Petr Menšík59e47032018-11-02 22:39:39 +00004385 new = opt_malloc(sizeof(struct host_record));
4386 memset(new, 0, sizeof(struct host_record));
4387 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004388 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004389
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004390 while (arg)
4391 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004392 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004393 char *dig;
4394
4395 for (dig = arg; *dig != 0; dig++)
4396 if (*dig < '0' || *dig > '9')
4397 break;
4398 if (*dig == 0)
4399 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004400 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004401 {
4402 new->addr = addr.addr4;
4403 new->flags |= HR_4;
4404 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004405 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004406 {
4407 new->addr6 = addr.addr6;
4408 new->flags |= HR_6;
4409 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004410 else
4411 {
4412 int nomem;
4413 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004414 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004415 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004416 {
4417 struct name_list *tmp = new->names, *next;
4418 for (tmp = new->names; tmp; tmp = next)
4419 {
4420 next = tmp->next;
4421 free(tmp);
4422 }
4423 ret_err_free(_("Bad name in host-record"), new);
4424 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004425
Petr Menšík59e47032018-11-02 22:39:39 +00004426 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004427 nl->name = canon;
4428 /* keep order, so that PTR record goes to first name */
4429 nl->next = NULL;
4430 if (!new->names)
4431 new->names = nl;
4432 else
4433 {
4434 struct name_list *tmp;
4435 for (tmp = new->names; tmp->next; tmp = tmp->next);
4436 tmp->next = nl;
4437 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004438 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004439
4440 arg = comma;
4441 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004442 }
Simon Kelleye759d422012-03-16 13:18:57 +00004443
4444 /* Keep list order */
4445 if (!daemon->host_records_tail)
4446 daemon->host_records = new;
4447 else
4448 daemon->host_records_tail->next = new;
4449 new->next = NULL;
4450 daemon->host_records_tail = new;
4451 break;
4452 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004453
4454#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004455 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004456 daemon->timestamp_file = opt_string_alloc(arg);
4457 break;
4458
Simon Kelleyf3e57872018-07-20 21:10:48 +01004459 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004460 if (arg)
4461 {
4462 if (strcmp(arg, "no") == 0)
4463 set_option_bool(OPT_DNSSEC_IGN_NS);
4464 else
4465 ret_err(_("bad value for dnssec-check-unsigned"));
4466 }
4467 break;
4468
Simon Kelleyf3e57872018-07-20 21:10:48 +01004469 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004470 {
Simon Kelleyee415862014-02-11 11:07:22 +00004471 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4472 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4473 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004474
4475 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004476 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004477
Simon Kelleycbf13a22014-01-25 17:59:14 +00004478 if ((comma = split(arg)) && (algo = split(comma)))
4479 {
4480 int class = 0;
4481 if (strcmp(comma, "IN") == 0)
4482 class = C_IN;
4483 else if (strcmp(comma, "CH") == 0)
4484 class = C_CHAOS;
4485 else if (strcmp(comma, "HS") == 0)
4486 class = C_HESIOD;
4487
4488 if (class != 0)
4489 {
4490 new->class = class;
4491 comma = algo;
4492 algo = split(comma);
4493 }
4494 }
4495
Simon Kelleyee415862014-02-11 11:07:22 +00004496 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4497 !atoi_check16(comma, &new->keytag) ||
4498 !atoi_check8(algo, &new->algo) ||
4499 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004500 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004501 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004502
Simon Kelley0fc2f312014-01-08 10:26:58 +00004503 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004504 len = (2*strlen(keyhex))+1;
4505 new->digest = opt_malloc(len);
4506 unhide_metas(keyhex);
4507 /* 4034: "Whitespace is allowed within digits" */
4508 for (cp = keyhex; *cp; )
4509 if (isspace(*cp))
4510 for (cp1 = cp; *cp1; cp1++)
4511 *cp1 = *(cp1+1);
4512 else
4513 cp++;
4514 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004515 {
4516 free(new->name);
4517 ret_err_free(_("bad HEX in trust anchor"), new);
4518 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004519
Simon Kelleyee415862014-02-11 11:07:22 +00004520 new->next = daemon->ds;
4521 daemon->ds = new;
4522
Simon Kelley0fc2f312014-01-08 10:26:58 +00004523 break;
4524 }
4525#endif
4526
Simon Kelley7622fc02009-06-04 20:32:05 +01004527 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004528 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004529
Simon Kelley849a8352006-06-09 21:02:31 +01004530 }
Simon Kelley824af852008-02-12 20:43:05 +00004531
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004532 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004533}
4534
Simon Kelley28866e92011-02-14 20:19:14 +00004535static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004536{
Simon Kelley824af852008-02-12 20:43:05 +00004537 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004538 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004539
4540 while (fgets(buff, MAXDNAME, f))
4541 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004542 int white, i;
4543 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004544 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004545 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004546
Simon Kelley824af852008-02-12 20:43:05 +00004547 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004548 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004549 {
4550 if (setjmp(mem_jmp))
4551 continue;
4552 mem_recover = 1;
4553 }
4554
Simon Kelley13dee6f2017-02-28 16:51:58 +00004555 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004556 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004557 errmess = NULL;
4558
Simon Kelley849a8352006-06-09 21:02:31 +01004559 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4560 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004561 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004562 {
4563 if (*p == '"')
4564 {
4565 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004566
Simon Kelley849a8352006-06-09 21:02:31 +01004567 for(; *p && *p != '"'; p++)
4568 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004569 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004570 {
4571 if (p[1] == 't')
4572 p[1] = '\t';
4573 else if (p[1] == 'n')
4574 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004575 else if (p[1] == 'b')
4576 p[1] = '\b';
4577 else if (p[1] == 'r')
4578 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004579 else if (p[1] == 'e') /* escape */
4580 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004581 memmove(p, p+1, strlen(p+1)+1);
4582 }
4583 *p = hide_meta(*p);
4584 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004585
4586 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004587 {
4588 errmess = _("missing \"");
4589 goto oops;
4590 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004591
4592 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004593 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004594
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004595 if (isspace(*p))
4596 {
4597 *p = ' ';
4598 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004599 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004600 else
4601 {
4602 if (white && *p == '#')
4603 {
4604 *p = 0;
4605 break;
4606 }
4607 white = 0;
4608 }
Simon Kelley849a8352006-06-09 21:02:31 +01004609 }
4610
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004611
4612 /* strip leading spaces */
4613 for (start = buff; *start && *start == ' '; start++);
4614
4615 /* strip trailing spaces */
4616 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4617
4618 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004619 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004620 else
4621 start[len] = 0;
4622
Simon Kelley611ebc52012-07-16 16:23:46 +01004623 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004624 arg = start;
4625 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004626 {
4627 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004628 for (arg = p+1; *arg == ' '; arg++);
4629 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004630 *p = 0;
4631 }
4632 else
4633 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004634
Simon Kelley611ebc52012-07-16 16:23:46 +01004635 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004636 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004637 for (option = 0, i = 0; opts[i].name; i++)
4638 if (strcmp(opts[i].name, start) == 0)
4639 {
4640 option = opts[i].val;
4641 break;
4642 }
4643
4644 if (!option)
4645 errmess = _("bad option");
4646 else if (opts[i].has_arg == 0 && arg)
4647 errmess = _("extraneous parameter");
4648 else if (opts[i].has_arg == 1 && !arg)
4649 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004650 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4651 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004652 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004653
4654 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004655 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004656 strcpy(daemon->namebuff, errmess);
4657
Simon Kelley9bafdc62018-08-21 22:53:38 +01004658 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004659 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004660 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004661 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004662 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004663 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004664 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004665 }
Simon Kelley849a8352006-06-09 21:02:31 +01004666 }
4667
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004668 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004669 fclose(f);
4670}
4671
Simon Kelley4f7bb572018-03-08 18:47:08 +00004672#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004673int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004674{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004675 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4676
Simon Kelley70d18732015-01-31 19:59:29 +00004677 if (flags & AH_DHCP_HST)
4678 return one_file(file, LOPT_BANK);
4679 else if (flags & AH_DHCP_OPT)
4680 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004681
Simon Kelley70d18732015-01-31 19:59:29 +00004682 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004683}
4684#endif
4685
Simon Kelley395eb712012-07-06 22:07:05 +01004686static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004687{
4688 FILE *f;
4689 int nofile_ok = 0;
4690 static int read_stdin = 0;
4691 static struct fileread {
4692 dev_t dev;
4693 ino_t ino;
4694 struct fileread *next;
4695 } *filesread = NULL;
4696
4697 if (hard_opt == '7')
4698 {
4699 /* default conf-file reading */
4700 hard_opt = 0;
4701 nofile_ok = 1;
4702 }
4703
4704 if (hard_opt == 0 && strcmp(file, "-") == 0)
4705 {
4706 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004707 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004708 read_stdin = 1;
4709 file = "stdin";
4710 f = stdin;
4711 }
4712 else
4713 {
4714 /* ignore repeated files. */
4715 struct stat statbuf;
4716
4717 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4718 {
4719 struct fileread *r;
4720
4721 for (r = filesread; r; r = r->next)
4722 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004723 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004724
4725 r = safe_malloc(sizeof(struct fileread));
4726 r->next = filesread;
4727 filesread = r;
4728 r->dev = statbuf.st_dev;
4729 r->ino = statbuf.st_ino;
4730 }
4731
4732 if (!(f = fopen(file, "r")))
4733 {
4734 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004735 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004736 else
4737 {
4738 char *str = _("cannot read %s: %s");
4739 if (hard_opt != 0)
4740 {
4741 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004742 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004743 }
4744 else
4745 die(str, file, EC_FILE);
4746 }
4747 }
4748 }
4749
4750 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004751 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004752}
4753
4754/* expand any name which is a directory */
4755struct hostsfile *expand_filelist(struct hostsfile *list)
4756{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004757 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004758 struct hostsfile *ah;
4759
Simon Kelley19c51cf2014-03-18 22:38:30 +00004760 /* find largest used index */
4761 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004762 {
4763 if (i <= ah->index)
4764 i = ah->index + 1;
4765
4766 if (ah->flags & AH_DIR)
4767 ah->flags |= AH_INACTIVE;
4768 else
4769 ah->flags &= ~AH_INACTIVE;
4770 }
4771
4772 for (ah = list; ah; ah = ah->next)
4773 if (!(ah->flags & AH_INACTIVE))
4774 {
4775 struct stat buf;
4776 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4777 {
4778 DIR *dir_stream;
4779 struct dirent *ent;
4780
4781 /* don't read this as a file */
4782 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004783
Simon Kelley28866e92011-02-14 20:19:14 +00004784 if (!(dir_stream = opendir(ah->fname)))
4785 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4786 ah->fname, strerror(errno));
4787 else
4788 {
4789 while ((ent = readdir(dir_stream)))
4790 {
4791 size_t lendir = strlen(ah->fname);
4792 size_t lenfile = strlen(ent->d_name);
4793 struct hostsfile *ah1;
4794 char *path;
4795
4796 /* ignore emacs backups and dotfiles */
4797 if (lenfile == 0 ||
4798 ent->d_name[lenfile - 1] == '~' ||
4799 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4800 ent->d_name[0] == '.')
4801 continue;
4802
4803 /* see if we have an existing record.
4804 dir is ah->fname
4805 file is ent->d_name
4806 path to match is ah1->fname */
4807
4808 for (ah1 = list; ah1; ah1 = ah1->next)
4809 {
4810 if (lendir < strlen(ah1->fname) &&
4811 strstr(ah1->fname, ah->fname) == ah1->fname &&
4812 ah1->fname[lendir] == '/' &&
4813 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4814 {
4815 ah1->flags &= ~AH_INACTIVE;
4816 break;
4817 }
4818 }
4819
4820 /* make new record */
4821 if (!ah1)
4822 {
4823 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4824 continue;
4825
4826 if (!(path = whine_malloc(lendir + lenfile + 2)))
4827 {
4828 free(ah1);
4829 continue;
4830 }
4831
4832 strcpy(path, ah->fname);
4833 strcat(path, "/");
4834 strcat(path, ent->d_name);
4835 ah1->fname = path;
4836 ah1->index = i++;
4837 ah1->flags = AH_DIR;
4838 ah1->next = list;
4839 list = ah1;
4840 }
4841
4842 /* inactivate record if not regular file */
4843 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4844 ah1->flags |= AH_INACTIVE;
4845
4846 }
4847 closedir(dir_stream);
4848 }
4849 }
4850 }
4851
4852 return list;
4853}
4854
Simon Kelley7b1eae42014-02-20 13:43:28 +00004855void read_servers_file(void)
4856{
4857 FILE *f;
4858
4859 if (!(f = fopen(daemon->servers_file, "r")))
4860 {
4861 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4862 return;
4863 }
4864
4865 mark_servers(SERV_FROM_FILE);
4866 cleanup_servers();
4867
4868 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4869}
4870
Simon Kelley28866e92011-02-14 20:19:14 +00004871
Simon Kelley7622fc02009-06-04 20:32:05 +01004872#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004873static void clear_dynamic_conf(void)
4874{
4875 struct dhcp_config *configs, *cp, **up;
4876
4877 /* remove existing... */
4878 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4879 {
4880 cp = configs->next;
4881
4882 if (configs->flags & CONFIG_BANK)
4883 {
4884 struct hwaddr_config *mac, *tmp;
4885 struct dhcp_netid_list *list, *tmplist;
4886
4887 for (mac = configs->hwaddr; mac; mac = tmp)
4888 {
4889 tmp = mac->next;
4890 free(mac);
4891 }
4892
4893 if (configs->flags & CONFIG_CLID)
4894 free(configs->clid);
4895
4896 for (list = configs->netid; list; list = tmplist)
4897 {
4898 free(list->list);
4899 tmplist = list->next;
4900 free(list);
4901 }
4902
4903 if (configs->flags & CONFIG_NAME)
4904 free(configs->hostname);
4905
4906 *up = configs->next;
4907 free(configs);
4908 }
4909 else
4910 up = &configs->next;
4911 }
4912}
4913
4914static void clear_dynamic_opt(void)
4915{
4916 struct dhcp_opt *opts, *cp, **up;
4917 struct dhcp_netid *id, *next;
4918
4919 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4920 {
4921 cp = opts->next;
4922
4923 if (opts->flags & DHOPT_BANK)
4924 {
4925 if ((opts->flags & DHOPT_VENDOR))
4926 free(opts->u.vendor_class);
4927 free(opts->val);
4928 for (id = opts->netid; id; id = next)
4929 {
4930 next = id->next;
4931 free(id->net);
4932 free(id);
4933 }
4934 *up = opts->next;
4935 free(opts);
4936 }
4937 else
4938 up = &opts->next;
4939 }
4940}
4941
Simon Kelley824af852008-02-12 20:43:05 +00004942void reread_dhcp(void)
4943{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004944 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004945
Simon Kelley4f7bb572018-03-08 18:47:08 +00004946 /* Do these even if there is no daemon->dhcp_hosts_file or
4947 daemon->dhcp_opts_file since entries may have been created by the
4948 inotify dynamic file reading system. */
4949
4950 clear_dynamic_conf();
4951 clear_dynamic_opt();
4952
4953 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004954 {
Simon Kelley28866e92011-02-14 20:19:14 +00004955 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4956 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004957 if (!(hf->flags & AH_INACTIVE))
4958 {
4959 if (one_file(hf->fname, LOPT_BANK))
4960 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4961 }
Simon Kelley824af852008-02-12 20:43:05 +00004962 }
4963
4964 if (daemon->dhcp_opts_file)
4965 {
Simon Kelley28866e92011-02-14 20:19:14 +00004966 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4967 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4968 if (!(hf->flags & AH_INACTIVE))
4969 {
Simon Kelley395eb712012-07-06 22:07:05 +01004970 if (one_file(hf->fname, LOPT_OPTS))
4971 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004972 }
Simon Kelley824af852008-02-12 20:43:05 +00004973 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004974
4975# ifdef HAVE_INOTIFY
4976 /* Setup notify and read pre-existing files. */
4977 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
4978# endif
Simon Kelley824af852008-02-12 20:43:05 +00004979}
Simon Kelley7622fc02009-06-04 20:32:05 +01004980#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00004981
Simon Kelley5aabfc72007-08-29 11:24:47 +01004982void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004983{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004984 size_t argbuf_size = MAXDNAME;
4985 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004986 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00004987 int option, testmode = 0;
4988 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004989
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004990 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004991
Simon Kelley824af852008-02-12 20:43:05 +00004992 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004993 memset(daemon, 0, sizeof(struct daemon));
4994 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004995
Simon Kelley3be34542004-09-11 19:12:13 +01004996 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004997 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004998 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004999 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005000 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5001 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005002 daemon->default_resolv.is_default = 1;
5003 daemon->default_resolv.name = RESOLVFILE;
5004 daemon->resolv_files = &daemon->default_resolv;
5005 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005006 daemon->runfile = RUNFILE;
5007 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005008 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005009 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005010 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005011 daemon->auth_ttl = AUTH_TTL;
5012 daemon->soa_refresh = SOA_REFRESH;
5013 daemon->soa_retry = SOA_RETRY;
5014 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00005015 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00005016 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01005017
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005018#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005019 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5020 add_txt("authors.bind", "Simon Kelley", 0);
5021 add_txt("copyright.bind", COPYRIGHT, 0);
5022 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5023 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5024 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5025 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5026 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5027#ifdef HAVE_AUTH
5028 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5029#endif
5030 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005031#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005032
Simon Kelley849a8352006-06-09 21:02:31 +01005033 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005034 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005035#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005036 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005037#else
Simon Kelley849a8352006-06-09 21:02:31 +01005038 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005039#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005040
5041 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005042 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005043 for (; optind < argc; optind++)
5044 {
5045 unsigned char *c = (unsigned char *)argv[optind];
5046 for (; *c != 0; c++)
5047 if (!isspace(*c))
5048 die(_("junk found in command line"), NULL, EC_BADCONF);
5049 }
Simon Kelley28866e92011-02-14 20:19:14 +00005050 break;
5051 }
5052
Simon Kelley849a8352006-06-09 21:02:31 +01005053 /* Copy optarg so that argv doesn't get changed */
5054 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005055 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005056 if (strlen(optarg) >= argbuf_size)
5057 {
5058 free(argbuf);
5059 argbuf_size = strlen(optarg) + 1;
5060 argbuf = opt_malloc(argbuf_size);
5061 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005062 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005063 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005064 }
5065 else
5066 arg = NULL;
5067
5068 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005069 if (option == LOPT_TEST)
5070 testmode = 1;
5071 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005072 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005073#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005074 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005075 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005076#ifdef HAVE_DHCP6
5077 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5078 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005079#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005080 else
5081#endif
5082 do_usage();
5083
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005084 exit(0);
5085 }
Simon Kelley849a8352006-06-09 21:02:31 +01005086 else if (option == 'v')
5087 {
5088 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005089 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005090 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5091 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005092 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005093 exit(0);
5094 }
Simon Kelley849a8352006-06-09 21:02:31 +01005095 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005096 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005097 if (!conffile)
5098 conffile = opt_string_alloc(arg);
5099 else
5100 {
5101 char *extra = opt_string_alloc(arg);
5102 one_file(extra, 0);
5103 free(extra);
5104 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005105 }
Simon Kelley849a8352006-06-09 21:02:31 +01005106 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005107 {
Simon Kelley26128d22004-11-14 16:43:54 +00005108#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005109 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005110#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005111 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005112#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005113 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005114 }
5115 }
Simon Kelley849a8352006-06-09 21:02:31 +01005116
Neil Jerram3bd4c472018-01-18 22:49:38 +00005117 free(argbuf);
5118
Simon Kelley849a8352006-06-09 21:02:31 +01005119 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005120 {
Petr Menšík59e47032018-11-02 22:39:39 +00005121 one_file(conffile, 0);
5122 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005123 }
Petr Menšík59e47032018-11-02 22:39:39 +00005124 else
5125 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005126
Simon Kelley1a6bca82008-07-11 11:11:42 +01005127 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005128 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005129 {
5130 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005131 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005132 if (!(tmp->flags & SERV_HAS_SOURCE))
5133 {
5134 if (tmp->source_addr.sa.sa_family == AF_INET)
5135 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005136 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5137 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005138 }
5139 }
5140
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005141 if (daemon->host_records)
5142 {
5143 struct host_record *hr;
5144
5145 for (hr = daemon->host_records; hr; hr = hr->next)
5146 if (hr->ttl == -1)
5147 hr->ttl = daemon->local_ttl;
5148 }
5149
5150 if (daemon->cnames)
5151 {
Simon Kelley903df072017-01-19 17:22:00 +00005152 struct cname *cn, *cn2, *cn3;
5153
5154#define NOLOOP 1
5155#define TESTLOOP 2
5156
Simon Kelley157d8cf2019-10-25 17:46:49 +01005157 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005158 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005159 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005160 {
5161 if (cn->ttl == -1)
5162 cn->ttl = daemon->local_ttl;
5163 cn->flag = 0;
5164 cn->targetp = NULL;
5165 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5166 if (hostname_isequal(cn->target, cn2->alias))
5167 {
5168 cn->targetp = cn2;
5169 break;
5170 }
5171 }
5172
5173 /* Find any CNAME loops.*/
5174 for (cn = daemon->cnames; cn; cn = cn->next)
5175 {
5176 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5177 {
5178 if (cn2->flag == NOLOOP)
5179 break;
5180
5181 if (cn2->flag == TESTLOOP)
5182 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5183
5184 cn2->flag = TESTLOOP;
5185 }
5186
5187 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5188 cn3->flag = NOLOOP;
5189 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005190 }
5191
Simon Kelley3be34542004-09-11 19:12:13 +01005192 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005193 {
5194 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005195 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005196 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005197 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005198 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005199 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005200 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005201
5202 /* create default, if not specified */
5203 if (daemon->authserver && !daemon->hostmaster)
5204 {
5205 strcpy(buff, "hostmaster.");
5206 strcat(buff, daemon->authserver);
5207 daemon->hostmaster = opt_string_alloc(buff);
5208 }
5209
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005210 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005211 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005212 {
Simon Kelley0a852542005-03-23 20:28:59 +00005213 struct mx_srv_record *mx;
5214
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005215 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005216 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005217
Simon Kelley0a852542005-03-23 20:28:59 +00005218 for (mx = daemon->mxnames; mx; mx = mx->next)
5219 if (!mx->issrv && hostname_isequal(mx->name, buff))
5220 break;
5221
Simon Kelley28866e92011-02-14 20:19:14 +00005222 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005223 {
Simon Kelley824af852008-02-12 20:43:05 +00005224 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005225 mx->next = daemon->mxnames;
5226 mx->issrv = 0;
5227 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005228 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005229 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005230 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005231
Simon Kelley3be34542004-09-11 19:12:13 +01005232 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005233 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005234
5235 for (mx = daemon->mxnames; mx; mx = mx->next)
5236 if (!mx->issrv && !mx->target)
5237 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005238 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005239
Simon Kelley28866e92011-02-14 20:19:14 +00005240 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005241 daemon->resolv_files &&
5242 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005243 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005244 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005245
Simon Kelley28866e92011-02-14 20:19:14 +00005246 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005247 {
5248 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005249 FILE *f;
5250
Simon Kelley28866e92011-02-14 20:19:14 +00005251 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005252 !daemon->resolv_files ||
5253 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005254 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005255
Simon Kelley3be34542004-09-11 19:12:13 +01005256 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005257 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005258
5259 while ((line = fgets(buff, MAXDNAME, f)))
5260 {
5261 char *token = strtok(line, " \t\n\r");
5262
5263 if (!token || strcmp(token, "search") != 0)
5264 continue;
5265
5266 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005267 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005268 break;
5269 }
Simon Kelley3be34542004-09-11 19:12:13 +01005270
Simon Kelleyde379512004-06-22 20:23:33 +01005271 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005272
Simon Kelley3be34542004-09-11 19:12:13 +01005273 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005274 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005275 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005276
5277 if (daemon->domain_suffix)
5278 {
5279 /* add domain for any srv record without one. */
5280 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005281
Simon Kelley3d8df262005-08-29 12:19:27 +01005282 for (srv = daemon->mxnames; srv; srv = srv->next)
5283 if (srv->issrv &&
5284 strchr(srv->name, '.') &&
5285 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5286 {
5287 strcpy(buff, srv->name);
5288 strcat(buff, ".");
5289 strcat(buff, daemon->domain_suffix);
5290 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005291 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005292 }
5293 }
Simon Kelley28866e92011-02-14 20:19:14 +00005294 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005295 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005296
Simon Kelleyc8a80482014-03-05 14:29:54 +00005297 /* If there's access-control config, then ignore --local-service, it's intended
5298 as a system default to keep otherwise unconfigured installations safe. */
5299 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5300 reset_option_bool(OPT_LOCAL_SERVICE);
5301
Simon Kelley7622fc02009-06-04 20:32:05 +01005302 if (testmode)
5303 {
5304 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5305 exit(0);
5306 }
Simon Kelley849a8352006-06-09 21:02:31 +01005307}