blob: dbe5f9054b8066ae6d62400e016d03ade127a980 [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 Kelley52b92f42012-01-22 16:05:15 +00002994
Simon Kelley849a8352006-06-09 21:02:31 +01002995 while(1)
2996 {
2997 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002998 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2999 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
3000 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01003001 break;
3002
Simon Kelleyf2621c72007-04-29 19:47:21 +01003003 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01003004 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01003005 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003006 {
Simon Kelley0c387192013-09-05 10:21:12 +01003007 /* ignore empty tag */
Petr Menšík59e47032018-11-02 22:39:39 +00003008 if (arg[4])
3009 new->filter = dhcp_netid_create(arg+4, new->filter);
Simon Kelley849a8352006-06-09 21:02:31 +01003010 }
3011 else
3012 {
3013 if (new->netid.net)
Petr Menšík59e47032018-11-02 22:39:39 +00003014 {
3015 dhcp_context_free(new);
3016 ret_err(_("only one tag allowed"));
3017 }
Simon Kelley849a8352006-06-09 21:02:31 +01003018 else
Petr Menšík59e47032018-11-02 22:39:39 +00003019 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelley849a8352006-06-09 21:02:31 +01003020 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003021 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01003022 }
3023 else
3024 {
3025 a[0] = arg;
3026 break;
3027 }
3028 }
3029
Simon Kelley1f776932012-12-16 19:46:08 +00003030 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003031 if (!(a[k] = split(a[k-1])))
3032 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003033
Simon Kelley52b92f42012-01-22 16:05:15 +00003034 if (k < 2)
Petr Menšík59e47032018-11-02 22:39:39 +00003035 {
3036 dhcp_context_free(new);
3037 ret_err(_("bad dhcp-range"));
3038 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003039
3040 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01003041 {
Simon Kelley52b92f42012-01-22 16:05:15 +00003042 new->next = daemon->dhcp;
Simon Kelley4d85e402020-07-12 22:45:46 +01003043 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00003044 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 Kelley4d85e402020-07-12 22:45:46 +01003091 new->lease_time = DEFLEASE6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01003092 new->next = daemon->dhcp6;
3093 daemon->dhcp6 = new;
3094
Simon Kelley30cd9662012-03-25 20:44:38 +01003095 for (leasepos = 1; leasepos < k; leasepos++)
3096 {
3097 if (strcmp(a[leasepos], "static") == 0)
3098 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
3099 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003100 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003101 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003102 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003103 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
3104 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01003105 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00003106 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01003107 else if (strcmp(a[leasepos], "off-link") == 0)
3108 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01003109 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
3110 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00003111 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
3112 {
3113 new->template_interface = opt_string_alloc(a[leasepos] + 12);
3114 new->flags |= CONTEXT_TEMPLATE;
3115 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003116 else
3117 break;
3118 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003119
Simon Kelley52b92f42012-01-22 16:05:15 +00003120 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003121 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00003122 {
3123 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003124 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00003125 if (!(*cp >= '0' && *cp <= '9'))
3126 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01003127 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00003128 {
3129 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01003130 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00003131 }
3132 }
Simon Kelley30cd9662012-03-25 20:44:38 +01003133
Petr Menšík59e47032018-11-02 22:39:39 +00003134 if (new->prefix > 64)
Simon Kelley6692a1a2013-08-20 14:41:31 +01003135 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01003136 if (new->flags & CONTEXT_RA)
Petr Menšík59e47032018-11-02 22:39:39 +00003137 err=(_("prefix length must be exactly 64 for RA subnets"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003138 else if (new->flags & CONTEXT_TEMPLATE)
Petr Menšík59e47032018-11-02 22:39:39 +00003139 err=(_("prefix length must be exactly 64 for subnet constructors"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003140 }
Petr Menšík59e47032018-11-02 22:39:39 +00003141 else if (new->prefix < 64)
3142 err=(_("prefix length must be at least 64"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01003143
Petr Menšík59e47032018-11-02 22:39:39 +00003144 if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
3145 err=(_("inconsistent DHCPv6 range"));
3146
3147 if (err)
3148 {
3149 dhcp_context_free(new);
3150 ret_err(err);
3151 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01003152
3153 /* dhcp-range=:: enables DHCP stateless on any interface */
3154 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
3155 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01003156
3157 if (new->flags & CONTEXT_TEMPLATE)
3158 {
3159 struct in6_addr zero;
3160 memset(&zero, 0, sizeof(zero));
3161 if (!is_same_net6(&zero, &new->start6, new->prefix))
Petr Menšík59e47032018-11-02 22:39:39 +00003162 {
3163 dhcp_context_free(new);
3164 ret_err(_("prefix must be zero with \"constructor:\" argument"));
3165 }
Simon Kelley66409192013-08-01 20:19:32 +01003166 }
3167
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003168 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00003169 {
3170 struct in6_addr tmp = new->start6;
3171 new->start6 = new->end6;
3172 new->end6 = tmp;
3173 }
Simon Kelley849a8352006-06-09 21:02:31 +01003174 }
Simon Kelley52b92f42012-01-22 16:05:15 +00003175#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01003176 else
Petr Menšík59e47032018-11-02 22:39:39 +00003177 {
3178 dhcp_context_free(new);
3179 ret_err(_("bad dhcp-range"));
3180 }
Simon Kelley849a8352006-06-09 21:02:31 +01003181
Simon Kelley30cd9662012-03-25 20:44:38 +01003182 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01003183 {
Simon Kelleyfa794662016-03-03 20:33:54 +00003184 if (leasepos != k-1)
Petr Menšík59e47032018-11-02 22:39:39 +00003185 {
3186 dhcp_context_free(new);
3187 ret_err(_("bad dhcp-range"));
3188 }
Simon Kelleyfa794662016-03-03 20:33:54 +00003189
Simon Kelley849a8352006-06-09 21:02:31 +01003190 if (strcmp(a[leasepos], "infinite") == 0)
Simon Kelley4d85e402020-07-12 22:45:46 +01003191 {
3192 new->lease_time = 0xffffffff;
3193 new->flags |= CONTEXT_SETLEASE;
3194 }
Simon Kelleyc8257542012-03-28 21:15:41 +01003195 else if (strcmp(a[leasepos], "deprecated") == 0)
3196 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01003197 else
3198 {
3199 int fac = 1;
3200 if (strlen(a[leasepos]) > 0)
3201 {
3202 switch (a[leasepos][strlen(a[leasepos]) - 1])
3203 {
Simon Kelley42243212012-07-20 15:19:18 +01003204 case 'w':
3205 case 'W':
3206 fac *= 7;
3207 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003208 case 'd':
3209 case 'D':
3210 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00003211 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003212 case 'h':
3213 case 'H':
3214 fac *= 60;
3215 /* fall through */
3216 case 'm':
3217 case 'M':
3218 fac *= 60;
3219 /* fall through */
3220 case 's':
3221 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003222 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003223 }
3224
Simon Kelleybe379862012-12-23 12:01:39 +00003225 for (cp = a[leasepos]; *cp; cp++)
3226 if (!(*cp >= '0' && *cp <= '9'))
3227 break;
3228
Simon Kelley54dae552013-02-05 17:55:10 +00003229 if (*cp || (leasepos+1 < k))
Petr Menšík59e47032018-11-02 22:39:39 +00003230 ret_err_free(_("bad dhcp-range"), new);
Simon Kelleybe379862012-12-23 12:01:39 +00003231
Simon Kelley849a8352006-06-09 21:02:31 +01003232 new->lease_time = atoi(a[leasepos]) * fac;
Simon Kelley4d85e402020-07-12 22:45:46 +01003233 new->flags |= CONTEXT_SETLEASE;
Simon Kelley849a8352006-06-09 21:02:31 +01003234 /* Leases of a minute or less confuse
3235 some clients, notably Apple's */
3236 if (new->lease_time < 120)
3237 new->lease_time = 120;
3238 }
3239 }
3240 }
Simon Kelley4d85e402020-07-12 22:45:46 +01003241
Simon Kelley849a8352006-06-09 21:02:31 +01003242 break;
3243 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003244
Simon Kelley5aabfc72007-08-29 11:24:47 +01003245 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003246 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003247 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003248 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003249 struct in_addr in;
3250
Simon Kelley824af852008-02-12 20:43:05 +00003251 new = opt_malloc(sizeof(struct dhcp_config));
3252
Simon Kelley849a8352006-06-09 21:02:31 +01003253 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003254 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3255 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003256 new->netid = NULL;
Simon Kelley52ec7832020-02-07 21:05:54 +00003257 new->filter = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003258 new->clid = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003259#ifdef HAVE_DHCP6
Simon Kelley137286e2020-02-06 22:09:30 +00003260 new->addr6 = NULL;
Kevin Darbyshire-Bryant8d6d5732020-03-02 10:00:21 +00003261#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003262
Simon Kelley137286e2020-02-06 22:09:30 +00003263 while (arg)
3264 {
3265 comma = split(arg);
3266 if (strchr(arg, ':')) /* ethernet address, netid or binary CLID */
3267 {
3268 if ((arg[0] == 'i' || arg[0] == 'I') &&
3269 (arg[1] == 'd' || arg[1] == 'D') &&
3270 arg[2] == ':')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003271 {
Simon Kelley137286e2020-02-06 22:09:30 +00003272 if (arg[3] == '*')
3273 new->flags |= CONFIG_NOCLID;
3274 else
3275 {
3276 int len;
3277 arg += 3; /* dump id: */
3278 if (strchr(arg, ':'))
3279 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3280 else
3281 {
3282 unhide_metas(arg);
3283 len = (int) strlen(arg);
3284 }
3285
3286 if (len == -1)
3287 {
3288 dhcp_config_free(new);
3289 ret_err(_("bad hex constant"));
3290 }
3291 else if ((new->clid = opt_malloc(len)))
3292 {
3293 new->flags |= CONFIG_CLID;
3294 new->clid_len = len;
3295 memcpy(new->clid, arg, len);
3296 }
3297 }
3298 }
3299 /* dhcp-host has strange backwards-compat needs. */
3300 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
3301 {
3302 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3303 newlist->next = new->netid;
3304 new->netid = newlist;
3305 newlist->list = dhcp_netid_create(arg+4, NULL);
3306 }
3307 else if (strstr(arg, "tag:") == arg)
Simon Kelley52ec7832020-02-07 21:05:54 +00003308 new->filter = dhcp_netid_create(arg+4, new->filter);
3309
Simon Kelley137286e2020-02-06 22:09:30 +00003310#ifdef HAVE_DHCP6
3311 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3312 {
3313 char *pref;
3314 struct in6_addr in6;
3315 struct addrlist *new_addr;
3316
3317 arg[strlen(arg)-1] = 0;
3318 arg++;
3319 pref = split_chr(arg, '/');
3320
3321 if (!inet_pton(AF_INET6, arg, &in6))
3322 {
3323 dhcp_config_free(new);
3324 ret_err(_("bad IPv6 address"));
3325 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003326
Simon Kelley137286e2020-02-06 22:09:30 +00003327 new_addr = opt_malloc(sizeof(struct addrlist));
3328 new_addr->next = new->addr6;
3329 new_addr->flags = 0;
3330 new_addr->addr.addr6 = in6;
3331 new->addr6 = new_addr;
3332
3333 if (pref)
3334 {
3335 u64 addrpart = addr6part(&in6);
3336
3337 if (!atoi_check(pref, &new_addr->prefixlen) ||
3338 new_addr->prefixlen > 128 ||
Petr Menšík46bdfe62020-03-08 15:56:19 +00003339 ((((u64)1<<(128-new_addr->prefixlen))-1) & addrpart) != 0)
Simon Kelley137286e2020-02-06 22:09:30 +00003340 {
3341 dhcp_config_free(new);
3342 ret_err(_("bad IPv6 prefix"));
3343 }
3344
3345 new_addr->flags |= ADDRLIST_PREFIX;
3346 }
3347
3348 for (i= 0; i < 8; i++)
3349 if (in6.s6_addr[i] != 0)
3350 break;
3351
3352 /* set WILDCARD if network part all zeros */
3353 if (i == 8)
3354 new_addr->flags |= ADDRLIST_WILDCARD;
3355
3356 new->flags |= CONFIG_ADDR6;
3357 }
3358#endif
3359 else
3360 {
3361 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
3362 if ((newhw->hwaddr_len = parse_hex(arg, newhw->hwaddr, DHCP_CHADDR_MAX,
3363 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
3364 {
3365 free(newhw);
3366 dhcp_config_free(new);
3367 ret_err(_("bad hex constant"));
3368 }
3369 else
3370 {
3371 newhw->next = new->hwaddr;
3372 new->hwaddr = newhw;
3373 }
3374 }
3375 }
3376 else if (strchr(arg, '.') && (inet_pton(AF_INET, arg, &in) > 0))
3377 {
3378 struct dhcp_config *configs;
3379
3380 new->addr = in;
3381 new->flags |= CONFIG_ADDR;
3382
3383 /* If the same IP appears in more than one host config, then DISCOVER
3384 for one of the hosts will get the address, but REQUEST will be NAKed,
3385 since the address is reserved by the other one -> protocol loop. */
3386 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3387 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
Simon Kelley849a8352006-06-09 21:02:31 +01003388 {
Simon Kelley137286e2020-02-06 22:09:30 +00003389 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3390 return 0;
3391 }
3392 }
3393 else
3394 {
3395 char *cp, *lastp = NULL, last = 0;
3396 int fac = 1, isdig = 0;
3397
3398 if (strlen(arg) > 1)
3399 {
3400 lastp = arg + strlen(arg) - 1;
3401 last = *lastp;
3402 switch (last)
3403 {
3404 case 'w':
3405 case 'W':
3406 fac *= 7;
3407 /* fall through */
3408 case 'd':
3409 case 'D':
3410 fac *= 24;
3411 /* fall through */
3412 case 'h':
3413 case 'H':
3414 fac *= 60;
3415 /* fall through */
3416 case 'm':
3417 case 'M':
3418 fac *= 60;
3419 /* fall through */
3420 case 's':
3421 case 'S':
3422 *lastp = 0;
3423 }
3424 }
3425
3426 for (cp = arg; *cp; cp++)
3427 if (isdigit((unsigned char)*cp))
3428 isdig = 1;
3429 else if (*cp != ' ')
3430 break;
3431
3432 if (*cp)
3433 {
3434 if (lastp)
3435 *lastp = last;
3436 if (strcmp(arg, "infinite") == 0)
3437 {
3438 new->lease_time = 0xffffffff;
3439 new->flags |= CONFIG_TIME;
3440 }
3441 else if (strcmp(arg, "ignore") == 0)
3442 new->flags |= CONFIG_DISABLE;
3443 else
3444 {
3445 if (!(new->hostname = canonicalise_opt(arg)) ||
3446 !legal_hostname(new->hostname))
3447 {
3448 dhcp_config_free(new);
3449 ret_err(_("bad DHCP host name"));
3450 }
3451
3452 new->flags |= CONFIG_NAME;
3453 new->domain = strip_hostname(new->hostname);
3454 }
3455 }
3456 else if (isdig)
3457 {
3458 new->lease_time = atoi(arg) * fac;
3459 /* Leases of a minute or less confuse
3460 some clients, notably Apple's */
3461 if (new->lease_time < 120)
3462 new->lease_time = 120;
3463 new->flags |= CONFIG_TIME;
3464 }
3465 }
3466
3467 arg = comma;
3468 }
3469
Simon Kelley5aabfc72007-08-29 11:24:47 +01003470 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003471 break;
3472 }
Simon Kelley137286e2020-02-06 22:09:30 +00003473
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003474 case LOPT_TAG_IF: /* --tag-if */
3475 {
3476 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3477
3478 new->tag = NULL;
3479 new->set = NULL;
3480 new->next = NULL;
3481
3482 /* preserve order */
3483 if (!daemon->tag_if)
3484 daemon->tag_if = new;
3485 else
3486 {
3487 struct tag_if *tmp;
3488 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3489 tmp->next = new;
3490 }
3491
3492 while (arg)
3493 {
3494 size_t len;
3495
3496 comma = split(arg);
3497 len = strlen(arg);
3498
3499 if (len < 5)
3500 {
3501 new->set = NULL;
3502 break;
3503 }
3504 else
3505 {
Petr Menšík59e47032018-11-02 22:39:39 +00003506 struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003507
3508 if (strstr(arg, "set:") == arg)
3509 {
3510 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3511 newlist->next = new->set;
3512 new->set = newlist;
3513 newlist->list = newtag;
3514 }
3515 else if (strstr(arg, "tag:") == arg)
3516 {
3517 newtag->next = new->tag;
3518 new->tag = newtag;
3519 }
3520 else
3521 {
3522 new->set = NULL;
Petr Menšík59e47032018-11-02 22:39:39 +00003523 dhcp_netid_free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003524 break;
3525 }
3526 }
3527
3528 arg = comma;
3529 }
3530
3531 if (!new->set)
Petr Menšík59e47032018-11-02 22:39:39 +00003532 {
3533 dhcp_netid_free(new->tag);
3534 dhcp_netid_list_free(new->set);
3535 ret_err_free(_("bad tag-if"), new);
3536 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003537
3538 break;
3539 }
3540
Simon Kelley849a8352006-06-09 21:02:31 +01003541
Simon Kelley73a08a22009-02-05 20:28:08 +00003542 case 'O': /* --dhcp-option */
3543 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003544 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003545 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003546 return parse_dhcp_opt(errstr, arg,
3547 option == LOPT_FORCE ? DHOPT_FORCE :
3548 (option == LOPT_MATCH ? DHOPT_MATCH :
3549 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003550
3551 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3552 {
3553 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3554 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3555 ssize_t len;
3556
3557 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3558 ret_err(gen_err);
3559
3560 new->wildcard = 0;
3561 new->netid = id;
3562 id->net = opt_string_alloc(set_prefix(arg));
3563
3564 if (comma[len-1] == '*')
3565 {
3566 comma[len-1] = 0;
3567 new->wildcard = 1;
3568 }
3569 new->name = opt_string_alloc(comma);
3570
3571 new->next = daemon->dhcp_name_match;
3572 daemon->dhcp_name_match = new;
3573
3574 break;
3575 }
3576
Simon Kelleyf2621c72007-04-29 19:47:21 +01003577 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003578 {
Petr Menšík59e47032018-11-02 22:39:39 +00003579 struct dhcp_netid *id = dhcp_tags(&arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003580
Petr Menšík137e9f82018-12-16 21:25:29 +00003581 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003582 {
3583 ret_err(gen_err);
3584 }
Simon Kelley849a8352006-06-09 21:02:31 +01003585 else
3586 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003587 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003588 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003589 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003590 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003591 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003592 dhcp_next_server.s_addr = 0;
3593 if (comma)
3594 {
3595 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003596 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003597 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003598 if (comma)
3599 {
3600 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003601 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3602 {
3603 /*
3604 * The user may have specified the tftp hostname here.
3605 * save it so that it can be resolved/looked up during
3606 * actual dhcp_reply().
3607 */
3608
3609 tftp_sname = opt_string_alloc(comma);
3610 dhcp_next_server.s_addr = 0;
3611 }
Simon Kelley849a8352006-06-09 21:02:31 +01003612 }
3613 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003614
3615 new = opt_malloc(sizeof(struct dhcp_boot));
3616 new->file = dhcp_file;
3617 new->sname = dhcp_sname;
3618 new->tftp_sname = tftp_sname;
3619 new->next_server = dhcp_next_server;
3620 new->netid = id;
3621 new->next = daemon->boot_config;
3622 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003623 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003624
Simon Kelley849a8352006-06-09 21:02:31 +01003625 break;
3626 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003627
Floris Bos503c6092017-04-09 23:07:13 +01003628 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3629 {
Petr Menšík59e47032018-11-02 22:39:39 +00003630 struct dhcp_netid *id = dhcp_tags(&arg);
Floris Bos503c6092017-04-09 23:07:13 +01003631
Petr Menšík137e9f82018-12-16 21:25:29 +00003632 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003633 {
3634 ret_err(gen_err);
3635 }
Floris Bos503c6092017-04-09 23:07:13 +01003636 else
3637 {
3638 struct delay_config *new;
3639 int delay;
3640 if (!atoi_check(arg, &delay))
3641 ret_err(gen_err);
3642
3643 new = opt_malloc(sizeof(struct delay_config));
3644 new->delay = delay;
3645 new->netid = id;
3646 new->next = daemon->delay_conf;
3647 daemon->delay_conf = new;
3648 }
3649
3650 break;
3651 }
3652
Simon Kelley7622fc02009-06-04 20:32:05 +01003653 case LOPT_PXE_PROMT: /* --pxe-prompt */
3654 {
3655 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3656 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003657
Simon Kelley7622fc02009-06-04 20:32:05 +01003658 new->netid = NULL;
3659 new->opt = 10; /* PXE_MENU_PROMPT */
Petr Menšík59e47032018-11-02 22:39:39 +00003660 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003661
Petr Menšík137e9f82018-12-16 21:25:29 +00003662 if (!arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003663 {
3664 dhcp_opt_free(new);
3665 ret_err(gen_err);
3666 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003667 else
3668 {
3669 comma = split(arg);
3670 unhide_metas(arg);
3671 new->len = strlen(arg) + 1;
3672 new->val = opt_malloc(new->len);
3673 memcpy(new->val + 1, arg, new->len - 1);
3674
3675 new->u.vendor_class = (unsigned char *)"PXEClient";
3676 new->flags = DHOPT_VENDOR;
3677
3678 if (comma && atoi_check(comma, &timeout))
3679 *(new->val) = timeout;
3680 else
3681 *(new->val) = 255;
3682
3683 new->next = daemon->dhcp_opts;
3684 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003685 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003686 }
3687
3688 break;
3689 }
3690
3691 case LOPT_PXE_SERV: /* --pxe-service */
3692 {
3693 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3694 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003695 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3696 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003697 static int boottype = 32768;
3698
3699 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003700 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003701 new->server.s_addr = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00003702 new->netid = dhcp_tags(&arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01003703
Simon Kelley7622fc02009-06-04 20:32:05 +01003704 if (arg && (comma = split(arg)))
3705 {
3706 for (i = 0; CSA[i]; i++)
3707 if (strcasecmp(CSA[i], arg) == 0)
3708 break;
3709
3710 if (CSA[i] || atoi_check(arg, &i))
3711 {
3712 arg = comma;
3713 comma = split(arg);
3714
3715 new->CSA = i;
3716 new->menu = opt_string_alloc(arg);
3717
Simon Kelley316e2732010-01-22 20:16:09 +00003718 if (!comma)
3719 {
3720 new->type = 0; /* local boot */
3721 new->basename = NULL;
3722 }
3723 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003724 {
3725 arg = comma;
3726 comma = split(arg);
3727 if (atoi_check(arg, &i))
3728 {
3729 new->type = i;
3730 new->basename = NULL;
3731 }
3732 else
3733 {
3734 new->type = boottype++;
3735 new->basename = opt_string_alloc(arg);
3736 }
3737
Simon Kelley751d6f42012-02-10 15:24:51 +00003738 if (comma)
3739 {
3740 if (!inet_pton(AF_INET, comma, &new->server))
3741 {
3742 new->server.s_addr = 0;
3743 new->sname = opt_string_alloc(comma);
3744 }
3745
3746 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003747 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003748
Simon Kelley316e2732010-01-22 20:16:09 +00003749 /* Order matters */
3750 new->next = NULL;
3751 if (!daemon->pxe_services)
3752 daemon->pxe_services = new;
3753 else
3754 {
3755 struct pxe_service *s;
3756 for (s = daemon->pxe_services; s->next; s = s->next);
3757 s->next = new;
3758 }
3759
3760 daemon->enable_pxe = 1;
3761 break;
3762
Simon Kelley7622fc02009-06-04 20:32:05 +01003763 }
3764 }
3765
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003766 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003767 }
3768
Simon Kelleyf2621c72007-04-29 19:47:21 +01003769 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003770 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003771 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003772 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003773 else
3774 {
Simon Kelley824af852008-02-12 20:43:05 +00003775 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003776 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003777 unhide_metas(comma);
3778 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003779 if (new->hwaddr_len == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00003780 {
3781 free(new->netid.net);
3782 ret_err_free(gen_err, new);
3783 }
Simon Kelley28866e92011-02-14 20:19:14 +00003784 else
3785 {
3786 new->next = daemon->dhcp_macs;
3787 daemon->dhcp_macs = new;
3788 }
Simon Kelley849a8352006-06-09 21:02:31 +01003789 }
3790 }
3791 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003792
Simon Kelleyf2621c72007-04-29 19:47:21 +01003793 case 'U': /* --dhcp-vendorclass */
3794 case 'j': /* --dhcp-userclass */
3795 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3796 case LOPT_REMOTE: /* --dhcp-remoteid */
3797 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003798 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003799 unsigned char *p;
3800 int dig = 0;
3801 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3802
3803 if (!(comma = split(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00003804 ret_err_free(gen_err, new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003805
3806 new->netid.net = opt_string_alloc(set_prefix(arg));
3807 /* check for hex string - must digits may include : must not have nothing else,
3808 only allowed for agent-options. */
3809
3810 arg = comma;
3811 if ((comma = split(arg)))
3812 {
3813 if (option != 'U' || strstr(arg, "enterprise:") != arg)
Petr Menšík59e47032018-11-02 22:39:39 +00003814 {
3815 free(new->netid.net);
3816 ret_err_free(gen_err, new);
3817 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003818 else
3819 new->enterprise = atoi(arg+11);
3820 }
3821 else
3822 comma = arg;
3823
3824 for (p = (unsigned char *)comma; *p; p++)
3825 if (isxdigit(*p))
3826 dig = 1;
3827 else if (*p != ':')
3828 break;
3829 unhide_metas(comma);
3830 if (option == 'U' || option == 'j' || *p || !dig)
3831 {
3832 new->len = strlen(comma);
3833 new->data = opt_malloc(new->len);
3834 memcpy(new->data, comma, new->len);
3835 }
3836 else
3837 {
3838 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3839 new->data = opt_malloc(new->len);
3840 memcpy(new->data, comma, new->len);
3841 }
3842
3843 switch (option)
3844 {
3845 case 'j':
3846 new->match_type = MATCH_USER;
3847 break;
3848 case 'U':
3849 new->match_type = MATCH_VENDOR;
3850 break;
3851 case LOPT_CIRCUIT:
3852 new->match_type = MATCH_CIRCUIT;
3853 break;
3854 case LOPT_REMOTE:
3855 new->match_type = MATCH_REMOTE;
3856 break;
3857 case LOPT_SUBSCR:
3858 new->match_type = MATCH_SUBSCRIBER;
3859 break;
3860 }
3861 new->next = daemon->dhcp_vendors;
3862 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003863
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003864 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003865 }
3866
Simon Kelley9e038942008-05-30 20:06:34 +01003867 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3868 if (!arg)
3869 {
3870 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3871 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3872 }
3873 else
3874 {
3875 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003876 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3877 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003878 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003879 if (!comma)
3880 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3881 }
3882 break;
3883
Simon Kelley824af852008-02-12 20:43:05 +00003884 case 'J': /* --dhcp-ignore */
3885 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3886 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003887 case '3': /* --bootp-dynamic */
3888 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003889 {
Simon Kelley824af852008-02-12 20:43:05 +00003890 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003891 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003892 if (option == 'J')
3893 {
3894 new->next = daemon->dhcp_ignore;
3895 daemon->dhcp_ignore = new;
3896 }
Simon Kelley824af852008-02-12 20:43:05 +00003897 else if (option == LOPT_BROADCAST)
3898 {
3899 new->next = daemon->force_broadcast;
3900 daemon->force_broadcast = new;
3901 }
Simon Kelley9009d742008-11-14 20:04:27 +00003902 else if (option == '3')
3903 {
3904 new->next = daemon->bootp_dynamic;
3905 daemon->bootp_dynamic = new;
3906 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003907 else if (option == LOPT_GEN_NAMES)
3908 {
3909 new->next = daemon->dhcp_gen_names;
3910 daemon->dhcp_gen_names = new;
3911 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003912 else
3913 {
3914 new->next = daemon->dhcp_ignore_names;
3915 daemon->dhcp_ignore_names = new;
3916 }
3917
3918 while (arg) {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003919 comma = split(arg);
Petr Menšík59e47032018-11-02 22:39:39 +00003920 list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
Simon Kelley849a8352006-06-09 21:02:31 +01003921 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003922 }
Simon Kelley849a8352006-06-09 21:02:31 +01003923
3924 new->list = list;
3925 break;
3926 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003927
3928 case LOPT_PROXY: /* --dhcp-proxy */
3929 daemon->override = 1;
3930 while (arg) {
3931 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3932 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003933 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Petr Menšík59e47032018-11-02 22:39:39 +00003934 ret_err_free(_("bad dhcp-proxy address"), new);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003935 new->next = daemon->override_relays;
3936 daemon->override_relays = new;
3937 arg = comma;
3938 }
3939 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003940
3941 case LOPT_RELAY: /* --dhcp-relay */
3942 {
3943 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3944 comma = split(arg);
3945 new->interface = opt_string_alloc(split(comma));
3946 new->iface_index = 0;
3947 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3948 {
3949 new->next = daemon->relay4;
3950 daemon->relay4 = new;
3951 }
3952#ifdef HAVE_DHCP6
3953 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3954 {
3955 new->next = daemon->relay6;
3956 daemon->relay6 = new;
3957 }
3958#endif
3959 else
Petr Menšík59e47032018-11-02 22:39:39 +00003960 {
3961 free(new->interface);
3962 ret_err_free(_("Bad dhcp-relay"), new);
3963 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01003964
3965 break;
3966 }
3967
Simon Kelley7622fc02009-06-04 20:32:05 +01003968#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003969
Simon Kelley8b372702012-03-09 17:45:10 +00003970#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003971 case LOPT_RA_PARAM: /* --ra-param */
3972 if ((comma = split(arg)))
3973 {
3974 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3975 new->lifetime = -1;
3976 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003977 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003978 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003979 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003980 if (strcasestr(comma, "mtu:") == comma)
3981 {
3982 arg = comma + 4;
3983 if (!(comma = split(comma)))
3984 goto err;
3985 if (!strcasecmp(arg, "off"))
3986 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003987 else if (!atoi_check(arg, &new->mtu))
3988 new->mtu_name = opt_string_alloc(arg);
3989 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003990 goto err;
3991 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003992 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3993 {
3994 if (*comma == 'l' || *comma == 'L')
3995 new->prio = 0x18;
3996 else
3997 new->prio = 0x08;
3998 comma = split(comma);
3999 }
4000 arg = split(comma);
4001 if (!atoi_check(comma, &new->interval) ||
4002 (arg && !atoi_check(arg, &new->lifetime)))
Petr Menšík59e47032018-11-02 22:39:39 +00004003 {
David Flamand005c46d2017-04-11 11:49:54 +01004004err:
Petr Menšík59e47032018-11-02 22:39:39 +00004005 free(new->name);
4006 ret_err_free(_("bad RA-params"), new);
4007 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01004008
4009 new->next = daemon->ra_interfaces;
4010 daemon->ra_interfaces = new;
4011 }
4012 break;
4013
Simon Kelley8b372702012-03-09 17:45:10 +00004014 case LOPT_DUID: /* --dhcp-duid */
4015 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004016 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00004017 else
4018 {
4019 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
4020 daemon->duid_config = opt_malloc(daemon->duid_config_len);
4021 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
4022 }
4023 break;
4024#endif
4025
Simon Kelleyf2621c72007-04-29 19:47:21 +01004026 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01004027 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004028 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01004029 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00004030 struct doctor *new = opt_malloc(sizeof(struct doctor));
4031 new->next = daemon->doctors;
4032 daemon->doctors = new;
4033 new->mask.s_addr = 0xffffffff;
4034 new->end.s_addr = 0;
4035
Simon Kelley849a8352006-06-09 21:02:31 +01004036 if ((a[0] = arg))
4037 for (k = 1; k < 3; k++)
4038 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01004039 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01004040 break;
Simon Kelley849a8352006-06-09 21:02:31 +01004041 unhide_metas(a[k]);
4042 }
Simon Kelley849a8352006-06-09 21:02:31 +01004043
Simon Kelley73a08a22009-02-05 20:28:08 +00004044 dash = split_chr(a[0], '-');
4045
Simon Kelley849a8352006-06-09 21:02:31 +01004046 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004047 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01004048 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
4049 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
4050 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01004051
Simon Kelley73a08a22009-02-05 20:28:08 +00004052 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01004053 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00004054 !is_same_net(new->in, new->end, new->mask) ||
4055 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Petr Menšík59e47032018-11-02 22:39:39 +00004056 ret_err_free(_("invalid alias range"), new);
Simon Kelley849a8352006-06-09 21:02:31 +01004057
4058 break;
4059 }
4060
Simon Kelleyf2621c72007-04-29 19:47:21 +01004061 case LOPT_INTNAME: /* --interface-name */
4062 {
4063 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01004064 char *domain = NULL;
4065
Simon Kelleyf2621c72007-04-29 19:47:21 +01004066 comma = split(arg);
4067
Simon Kelley1f15b812009-10-13 17:49:32 +01004068 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004069 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01004070
Simon Kelley824af852008-02-12 20:43:05 +00004071 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004072 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00004073 new->addr = NULL;
4074
Simon Kelleyf2621c72007-04-29 19:47:21 +01004075 /* Add to the end of the list, so that first name
4076 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00004077 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01004078 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004079 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004080 new->family = 0;
4081 arg = split_chr(comma, '/');
4082 if (arg)
4083 {
4084 if (strcmp(arg, "4") == 0)
4085 new->family = AF_INET;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004086 else if (strcmp(arg, "6") == 0)
4087 new->family = AF_INET6;
Simon Kelleyf7029f52013-11-21 15:09:09 +00004088 else
Petr Menšík59e47032018-11-02 22:39:39 +00004089 ret_err_free(gen_err, new);
Simon Kelleyf7029f52013-11-21 15:09:09 +00004090 }
Simon Kelley824af852008-02-12 20:43:05 +00004091 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004092 break;
4093 }
Simon Kelley9009d742008-11-14 20:04:27 +00004094
4095 case LOPT_CNAME: /* --cname */
4096 {
4097 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004098 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004099 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004100
Simon Kelleya1d973f2016-12-22 22:09:50 +00004101 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00004102 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00004103 pen = last;
4104 last = comma;
4105 }
4106
4107 if (!pen)
4108 ret_err(_("bad CNAME"));
4109
4110 if (pen != arg && atoi_check(last, &ttl))
4111 last = pen;
4112
4113 target = canonicalise_opt(last);
4114
4115 while (arg != last)
4116 {
Petr Menšík56f06232018-03-06 23:13:32 +00004117 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00004118 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01004119
4120 if (!alias || !target)
Petr Menšík59e47032018-11-02 22:39:39 +00004121 {
4122 free(target);
4123 free(alias);
4124 ret_err(_("bad CNAME"));
4125 }
Simon Kelleya1d973f2016-12-22 22:09:50 +00004126
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004127 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01004128 if (hostname_isequal(new->alias, alias))
Petr Menšík59e47032018-11-02 22:39:39 +00004129 {
4130 free(target);
4131 free(alias);
4132 ret_err(_("duplicate CNAME"));
4133 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004134 new = opt_malloc(sizeof(struct cname));
4135 new->next = daemon->cnames;
4136 daemon->cnames = new;
4137 new->alias = alias;
4138 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004139 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00004140
Petr Menšík56f06232018-03-06 23:13:32 +00004141 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00004142 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004143
Simon Kelley9009d742008-11-14 20:04:27 +00004144 break;
4145 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004146
4147 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00004148 {
4149 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004150 char *dom, *target = NULL;
4151
Simon Kelleyf2621c72007-04-29 19:47:21 +01004152 comma = split(arg);
4153
Simon Kelley1f15b812009-10-13 17:49:32 +01004154 if (!(dom = canonicalise_opt(arg)) ||
4155 (comma && !(target = canonicalise_opt(comma))))
Petr Menšík59e47032018-11-02 22:39:39 +00004156 {
4157 free(dom);
4158 free(target);
4159 ret_err(_("bad PTR record"));
4160 }
Simon Kelley1f15b812009-10-13 17:49:32 +01004161 else
4162 {
4163 new = opt_malloc(sizeof(struct ptr_record));
4164 new->next = daemon->ptr;
4165 daemon->ptr = new;
4166 new->name = dom;
4167 new->ptr = target;
4168 }
Simon Kelley832af0b2007-01-21 20:01:28 +00004169 break;
4170 }
4171
Simon Kelley1a6bca82008-07-11 11:11:42 +01004172 case LOPT_NAPTR: /* --naptr-record */
4173 {
4174 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
4175 int k = 0;
4176 struct naptr *new;
4177 int order, pref;
Petr Menšík59e47032018-11-02 22:39:39 +00004178 char *name=NULL, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004179
4180 if ((a[0] = arg))
4181 for (k = 1; k < 7; k++)
4182 if (!(a[k] = split(a[k-1])))
4183 break;
4184
4185
4186 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004187 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004188 !atoi_check16(a[1], &order) ||
4189 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01004190 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Petr Menšík59e47032018-11-02 22:39:39 +00004191 {
4192 free(name);
4193 free(replace);
4194 ret_err(_("bad NAPTR record"));
4195 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01004196 else
4197 {
4198 new = opt_malloc(sizeof(struct naptr));
4199 new->next = daemon->naptr;
4200 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01004201 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004202 new->flags = opt_string_alloc(a[3]);
4203 new->services = opt_string_alloc(a[4]);
4204 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01004205 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01004206 new->order = order;
4207 new->pref = pref;
4208 }
4209 break;
4210 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004211
4212 case LOPT_RR: /* dns-rr */
4213 {
4214 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00004215 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004216 char *data;
Petr Menšík59e47032018-11-02 22:39:39 +00004217 int class;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004218
4219 comma = split(arg);
4220 data = split(comma);
4221
4222 new = opt_malloc(sizeof(struct txt_record));
Petr Menšík59e47032018-11-02 22:39:39 +00004223 new->name = NULL;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004224
Petr Menšík59e47032018-11-02 22:39:39 +00004225 if (!atoi_check(comma, &class) ||
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004226 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01004227 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Petr Menšík59e47032018-11-02 22:39:39 +00004228 {
4229 free(new->name);
4230 ret_err_free(_("bad RR record"), new);
4231 }
4232
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004233 new->len = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004234 new->class = class;
4235 new->next = daemon->rr;
4236 daemon->rr = new;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004237
4238 if (data)
4239 {
Simon Kelley974a6d02018-08-23 23:01:16 +01004240 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004241 new->len = len;
4242 memcpy(new->txt, data, len);
4243 }
4244
4245 break;
4246 }
4247
Simon Kelley974a6d02018-08-23 23:01:16 +01004248 case LOPT_CAA: /* --caa-record */
4249 {
4250 struct txt_record *new;
4251 char *tag, *value;
4252 int flags;
4253
4254 comma = split(arg);
4255 tag = split(comma);
4256 value = split(tag);
4257
4258 new = opt_malloc(sizeof(struct txt_record));
4259 new->next = daemon->rr;
4260 daemon->rr = new;
4261
4262 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
4263 ret_err(_("bad CAA record"));
4264
4265 unhide_metas(tag);
4266 unhide_metas(value);
4267
4268 new->len = strlen(tag) + strlen(value) + 2;
4269 new->txt = opt_malloc(new->len);
4270 new->txt[0] = flags;
4271 new->txt[1] = strlen(tag);
4272 memcpy(&new->txt[2], tag, strlen(tag));
4273 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4274 new->class = T_CAA;
4275
4276 break;
4277 }
4278
Simon Kelleyf2621c72007-04-29 19:47:21 +01004279 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004280 {
4281 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004282 unsigned char *p, *cnt;
4283 size_t len;
4284
4285 comma = split(arg);
4286
Simon Kelley824af852008-02-12 20:43:05 +00004287 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004288 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004289 new->stat = 0;
4290
Simon Kelley1f15b812009-10-13 17:49:32 +01004291 if (!(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004292 ret_err_free(_("bad TXT record"), new);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004293
Petr Menšík59e47032018-11-02 22:39:39 +00004294 new->next = daemon->txt;
4295 daemon->txt = new;
Simon Kelley28866e92011-02-14 20:19:14 +00004296 len = comma ? strlen(comma) : 0;
4297 len += (len/255) + 1; /* room for extra counts */
4298 new->txt = p = opt_malloc(len);
4299
4300 cnt = p++;
4301 *cnt = 0;
4302
4303 while (comma && *comma)
4304 {
4305 unsigned char c = (unsigned char)*comma++;
4306
4307 if (c == ',' || *cnt == 255)
4308 {
4309 if (c != ',')
4310 comma--;
4311 cnt = p++;
4312 *cnt = 0;
4313 }
4314 else
4315 {
4316 *p++ = unhide_meta(c);
4317 (*cnt)++;
4318 }
4319 }
4320
4321 new->len = p - new->txt;
4322
Simon Kelley849a8352006-06-09 21:02:31 +01004323 break;
4324 }
4325
Simon Kelleyf2621c72007-04-29 19:47:21 +01004326 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004327 {
4328 int port = 1, priority = 0, weight = 0;
4329 char *name, *target = NULL;
4330 struct mx_srv_record *new;
4331
Simon Kelleyf2621c72007-04-29 19:47:21 +01004332 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004333
Simon Kelley1f15b812009-10-13 17:49:32 +01004334 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004335 ret_err(_("bad SRV record"));
4336
Simon Kelley849a8352006-06-09 21:02:31 +01004337 if (comma)
4338 {
4339 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004340 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004341 if (!(target = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004342 ret_err_free(_("bad SRV target"), name);
Simon Kelley824af852008-02-12 20:43:05 +00004343
Simon Kelley849a8352006-06-09 21:02:31 +01004344 if (comma)
4345 {
4346 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004347 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004348 if (!atoi_check16(arg, &port))
Petr Menšík59e47032018-11-02 22:39:39 +00004349 {
4350 free(name);
4351 ret_err_free(_("invalid port number"), target);
4352 }
Simon Kelley824af852008-02-12 20:43:05 +00004353
Simon Kelley849a8352006-06-09 21:02:31 +01004354 if (comma)
4355 {
4356 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004357 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004358 if (!atoi_check16(arg, &priority))
Petr Menšík59e47032018-11-02 22:39:39 +00004359 {
4360 free(name);
4361 ret_err_free(_("invalid priority"), target);
4362 }
Simon Kelley407a1f32016-03-01 17:06:07 +00004363 if (comma && !atoi_check16(comma, &weight))
Petr Menšík59e47032018-11-02 22:39:39 +00004364 {
4365 free(name);
4366 ret_err_free(_("invalid weight"), target);
4367 }
Simon Kelley849a8352006-06-09 21:02:31 +01004368 }
4369 }
4370 }
4371
Simon Kelley824af852008-02-12 20:43:05 +00004372 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004373 new->next = daemon->mxnames;
4374 daemon->mxnames = new;
4375 new->issrv = 1;
4376 new->name = name;
4377 new->target = target;
4378 new->srvport = port;
4379 new->priority = priority;
4380 new->weight = weight;
4381 break;
4382 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004383
Simon Kelleye759d422012-03-16 13:18:57 +00004384 case LOPT_HOST_REC: /* --host-record */
4385 {
Petr Menšík59e47032018-11-02 22:39:39 +00004386 struct host_record *new;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004387
Simon Kelleye759d422012-03-16 13:18:57 +00004388 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004389 ret_err(_("Bad host-record"));
4390
Petr Menšík59e47032018-11-02 22:39:39 +00004391 new = opt_malloc(sizeof(struct host_record));
4392 memset(new, 0, sizeof(struct host_record));
4393 new->ttl = -1;
Simon Kelley157d8cf2019-10-25 17:46:49 +01004394 new->flags = 0;
Petr Menšík59e47032018-11-02 22:39:39 +00004395
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004396 while (arg)
4397 {
Simon Kelleycc921df2019-01-02 22:48:59 +00004398 union all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004399 char *dig;
4400
4401 for (dig = arg; *dig != 0; dig++)
4402 if (*dig < '0' || *dig > '9')
4403 break;
4404 if (*dig == 0)
4405 new->ttl = atoi(arg);
Simon Kelleycc921df2019-01-02 22:48:59 +00004406 else if (inet_pton(AF_INET, arg, &addr.addr4))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004407 {
4408 new->addr = addr.addr4;
4409 new->flags |= HR_4;
4410 }
Simon Kelleycc921df2019-01-02 22:48:59 +00004411 else if (inet_pton(AF_INET6, arg, &addr.addr6))
Simon Kelley157d8cf2019-10-25 17:46:49 +01004412 {
4413 new->addr6 = addr.addr6;
4414 new->flags |= HR_6;
4415 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004416 else
4417 {
4418 int nomem;
4419 char *canon = canonicalise(arg, &nomem);
Petr Menšík59e47032018-11-02 22:39:39 +00004420 struct name_list *nl;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004421 if (!canon)
Petr Menšík59e47032018-11-02 22:39:39 +00004422 {
4423 struct name_list *tmp = new->names, *next;
4424 for (tmp = new->names; tmp; tmp = next)
4425 {
4426 next = tmp->next;
4427 free(tmp);
4428 }
4429 ret_err_free(_("Bad name in host-record"), new);
4430 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004431
Petr Menšík59e47032018-11-02 22:39:39 +00004432 nl = opt_malloc(sizeof(struct name_list));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004433 nl->name = canon;
4434 /* keep order, so that PTR record goes to first name */
4435 nl->next = NULL;
4436 if (!new->names)
4437 new->names = nl;
4438 else
4439 {
4440 struct name_list *tmp;
4441 for (tmp = new->names; tmp->next; tmp = tmp->next);
4442 tmp->next = nl;
4443 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004444 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004445
4446 arg = comma;
4447 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004448 }
Simon Kelleye759d422012-03-16 13:18:57 +00004449
4450 /* Keep list order */
4451 if (!daemon->host_records_tail)
4452 daemon->host_records = new;
4453 else
4454 daemon->host_records_tail->next = new;
4455 new->next = NULL;
4456 daemon->host_records_tail = new;
4457 break;
4458 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004459
4460#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004461 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004462 daemon->timestamp_file = opt_string_alloc(arg);
4463 break;
4464
Simon Kelleyf3e57872018-07-20 21:10:48 +01004465 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004466 if (arg)
4467 {
4468 if (strcmp(arg, "no") == 0)
4469 set_option_bool(OPT_DNSSEC_IGN_NS);
4470 else
4471 ret_err(_("bad value for dnssec-check-unsigned"));
4472 }
4473 break;
4474
Simon Kelleyf3e57872018-07-20 21:10:48 +01004475 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004476 {
Simon Kelleyee415862014-02-11 11:07:22 +00004477 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4478 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4479 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004480
4481 new->class = C_IN;
Petr Menšík59e47032018-11-02 22:39:39 +00004482 new->name = NULL;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004483
Simon Kelleycbf13a22014-01-25 17:59:14 +00004484 if ((comma = split(arg)) && (algo = split(comma)))
4485 {
4486 int class = 0;
4487 if (strcmp(comma, "IN") == 0)
4488 class = C_IN;
4489 else if (strcmp(comma, "CH") == 0)
4490 class = C_CHAOS;
4491 else if (strcmp(comma, "HS") == 0)
4492 class = C_HESIOD;
4493
4494 if (class != 0)
4495 {
4496 new->class = class;
4497 comma = algo;
4498 algo = split(comma);
4499 }
4500 }
4501
Simon Kelleyee415862014-02-11 11:07:22 +00004502 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4503 !atoi_check16(comma, &new->keytag) ||
4504 !atoi_check8(algo, &new->algo) ||
4505 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004506 !(new->name = canonicalise_opt(arg)))
Petr Menšík59e47032018-11-02 22:39:39 +00004507 ret_err_free(_("bad trust anchor"), new);
Simon Kelleycbf13a22014-01-25 17:59:14 +00004508
Simon Kelley0fc2f312014-01-08 10:26:58 +00004509 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004510 len = (2*strlen(keyhex))+1;
4511 new->digest = opt_malloc(len);
4512 unhide_metas(keyhex);
4513 /* 4034: "Whitespace is allowed within digits" */
4514 for (cp = keyhex; *cp; )
4515 if (isspace(*cp))
4516 for (cp1 = cp; *cp1; cp1++)
4517 *cp1 = *(cp1+1);
4518 else
4519 cp++;
4520 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
Petr Menšík59e47032018-11-02 22:39:39 +00004521 {
4522 free(new->name);
4523 ret_err_free(_("bad HEX in trust anchor"), new);
4524 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004525
Simon Kelleyee415862014-02-11 11:07:22 +00004526 new->next = daemon->ds;
4527 daemon->ds = new;
4528
Simon Kelley0fc2f312014-01-08 10:26:58 +00004529 break;
4530 }
4531#endif
4532
Simon Kelley7622fc02009-06-04 20:32:05 +01004533 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004534 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004535
Simon Kelley849a8352006-06-09 21:02:31 +01004536 }
Simon Kelley824af852008-02-12 20:43:05 +00004537
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004538 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004539}
4540
Simon Kelley28866e92011-02-14 20:19:14 +00004541static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004542{
Simon Kelley824af852008-02-12 20:43:05 +00004543 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004544 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004545
4546 while (fgets(buff, MAXDNAME, f))
4547 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004548 int white, i;
4549 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004550 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004551 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004552
Simon Kelley824af852008-02-12 20:43:05 +00004553 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004554 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004555 {
4556 if (setjmp(mem_jmp))
4557 continue;
4558 mem_recover = 1;
4559 }
4560
Simon Kelley13dee6f2017-02-28 16:51:58 +00004561 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004562 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004563 errmess = NULL;
4564
Simon Kelley849a8352006-06-09 21:02:31 +01004565 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4566 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004567 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004568 {
4569 if (*p == '"')
4570 {
4571 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004572
Simon Kelley849a8352006-06-09 21:02:31 +01004573 for(; *p && *p != '"'; p++)
4574 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004575 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004576 {
4577 if (p[1] == 't')
4578 p[1] = '\t';
4579 else if (p[1] == 'n')
4580 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004581 else if (p[1] == 'b')
4582 p[1] = '\b';
4583 else if (p[1] == 'r')
4584 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004585 else if (p[1] == 'e') /* escape */
4586 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004587 memmove(p, p+1, strlen(p+1)+1);
4588 }
4589 *p = hide_meta(*p);
4590 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004591
4592 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004593 {
4594 errmess = _("missing \"");
4595 goto oops;
4596 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004597
4598 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004599 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004600
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004601 if (isspace(*p))
4602 {
4603 *p = ' ';
4604 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004605 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004606 else
4607 {
4608 if (white && *p == '#')
4609 {
4610 *p = 0;
4611 break;
4612 }
4613 white = 0;
4614 }
Simon Kelley849a8352006-06-09 21:02:31 +01004615 }
4616
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004617
4618 /* strip leading spaces */
4619 for (start = buff; *start && *start == ' '; start++);
4620
4621 /* strip trailing spaces */
4622 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4623
4624 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004625 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004626 else
4627 start[len] = 0;
4628
Simon Kelley611ebc52012-07-16 16:23:46 +01004629 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004630 arg = start;
4631 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004632 {
4633 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004634 for (arg = p+1; *arg == ' '; arg++);
4635 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004636 *p = 0;
4637 }
4638 else
4639 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004640
Simon Kelley611ebc52012-07-16 16:23:46 +01004641 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004642 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004643 for (option = 0, i = 0; opts[i].name; i++)
4644 if (strcmp(opts[i].name, start) == 0)
4645 {
4646 option = opts[i].val;
4647 break;
4648 }
4649
4650 if (!option)
4651 errmess = _("bad option");
4652 else if (opts[i].has_arg == 0 && arg)
4653 errmess = _("extraneous parameter");
4654 else if (opts[i].has_arg == 1 && !arg)
4655 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004656 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4657 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004658 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004659
4660 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004661 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004662 strcpy(daemon->namebuff, errmess);
4663
Simon Kelley9bafdc62018-08-21 22:53:38 +01004664 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004665 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004666 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004667 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004668 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004669 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004670 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004671 }
Simon Kelley849a8352006-06-09 21:02:31 +01004672 }
4673
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004674 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004675 fclose(f);
4676}
4677
Simon Kelley4f7bb572018-03-08 18:47:08 +00004678#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004679int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004680{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004681 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4682
Simon Kelley70d18732015-01-31 19:59:29 +00004683 if (flags & AH_DHCP_HST)
4684 return one_file(file, LOPT_BANK);
4685 else if (flags & AH_DHCP_OPT)
4686 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004687
Simon Kelley70d18732015-01-31 19:59:29 +00004688 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004689}
4690#endif
4691
Simon Kelley395eb712012-07-06 22:07:05 +01004692static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004693{
4694 FILE *f;
4695 int nofile_ok = 0;
4696 static int read_stdin = 0;
4697 static struct fileread {
4698 dev_t dev;
4699 ino_t ino;
4700 struct fileread *next;
4701 } *filesread = NULL;
4702
4703 if (hard_opt == '7')
4704 {
4705 /* default conf-file reading */
4706 hard_opt = 0;
4707 nofile_ok = 1;
4708 }
4709
4710 if (hard_opt == 0 && strcmp(file, "-") == 0)
4711 {
4712 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004713 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004714 read_stdin = 1;
4715 file = "stdin";
4716 f = stdin;
4717 }
4718 else
4719 {
4720 /* ignore repeated files. */
4721 struct stat statbuf;
4722
4723 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4724 {
4725 struct fileread *r;
4726
4727 for (r = filesread; r; r = r->next)
4728 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004729 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004730
4731 r = safe_malloc(sizeof(struct fileread));
4732 r->next = filesread;
4733 filesread = r;
4734 r->dev = statbuf.st_dev;
4735 r->ino = statbuf.st_ino;
4736 }
4737
4738 if (!(f = fopen(file, "r")))
4739 {
4740 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004741 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004742 else
4743 {
4744 char *str = _("cannot read %s: %s");
4745 if (hard_opt != 0)
4746 {
4747 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004748 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004749 }
4750 else
4751 die(str, file, EC_FILE);
4752 }
4753 }
4754 }
4755
4756 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004757 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004758}
4759
4760/* expand any name which is a directory */
4761struct hostsfile *expand_filelist(struct hostsfile *list)
4762{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004763 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004764 struct hostsfile *ah;
4765
Simon Kelley19c51cf2014-03-18 22:38:30 +00004766 /* find largest used index */
4767 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004768 {
4769 if (i <= ah->index)
4770 i = ah->index + 1;
4771
4772 if (ah->flags & AH_DIR)
4773 ah->flags |= AH_INACTIVE;
4774 else
4775 ah->flags &= ~AH_INACTIVE;
4776 }
4777
4778 for (ah = list; ah; ah = ah->next)
4779 if (!(ah->flags & AH_INACTIVE))
4780 {
4781 struct stat buf;
4782 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4783 {
4784 DIR *dir_stream;
4785 struct dirent *ent;
4786
4787 /* don't read this as a file */
4788 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004789
Simon Kelley28866e92011-02-14 20:19:14 +00004790 if (!(dir_stream = opendir(ah->fname)))
4791 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4792 ah->fname, strerror(errno));
4793 else
4794 {
4795 while ((ent = readdir(dir_stream)))
4796 {
4797 size_t lendir = strlen(ah->fname);
4798 size_t lenfile = strlen(ent->d_name);
4799 struct hostsfile *ah1;
4800 char *path;
4801
4802 /* ignore emacs backups and dotfiles */
4803 if (lenfile == 0 ||
4804 ent->d_name[lenfile - 1] == '~' ||
4805 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4806 ent->d_name[0] == '.')
4807 continue;
4808
4809 /* see if we have an existing record.
4810 dir is ah->fname
4811 file is ent->d_name
4812 path to match is ah1->fname */
4813
4814 for (ah1 = list; ah1; ah1 = ah1->next)
4815 {
4816 if (lendir < strlen(ah1->fname) &&
4817 strstr(ah1->fname, ah->fname) == ah1->fname &&
4818 ah1->fname[lendir] == '/' &&
4819 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4820 {
4821 ah1->flags &= ~AH_INACTIVE;
4822 break;
4823 }
4824 }
4825
4826 /* make new record */
4827 if (!ah1)
4828 {
4829 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4830 continue;
4831
4832 if (!(path = whine_malloc(lendir + lenfile + 2)))
4833 {
4834 free(ah1);
4835 continue;
4836 }
4837
4838 strcpy(path, ah->fname);
4839 strcat(path, "/");
4840 strcat(path, ent->d_name);
4841 ah1->fname = path;
4842 ah1->index = i++;
4843 ah1->flags = AH_DIR;
4844 ah1->next = list;
4845 list = ah1;
4846 }
4847
4848 /* inactivate record if not regular file */
4849 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4850 ah1->flags |= AH_INACTIVE;
4851
4852 }
4853 closedir(dir_stream);
4854 }
4855 }
4856 }
4857
4858 return list;
4859}
4860
Simon Kelley7b1eae42014-02-20 13:43:28 +00004861void read_servers_file(void)
4862{
4863 FILE *f;
4864
4865 if (!(f = fopen(daemon->servers_file, "r")))
4866 {
4867 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4868 return;
4869 }
4870
4871 mark_servers(SERV_FROM_FILE);
4872 cleanup_servers();
4873
4874 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4875}
4876
Simon Kelley28866e92011-02-14 20:19:14 +00004877
Simon Kelley7622fc02009-06-04 20:32:05 +01004878#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004879static void clear_dynamic_conf(void)
4880{
4881 struct dhcp_config *configs, *cp, **up;
4882
4883 /* remove existing... */
4884 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4885 {
4886 cp = configs->next;
4887
4888 if (configs->flags & CONFIG_BANK)
4889 {
4890 struct hwaddr_config *mac, *tmp;
4891 struct dhcp_netid_list *list, *tmplist;
4892
4893 for (mac = configs->hwaddr; mac; mac = tmp)
4894 {
4895 tmp = mac->next;
4896 free(mac);
4897 }
4898
4899 if (configs->flags & CONFIG_CLID)
4900 free(configs->clid);
4901
4902 for (list = configs->netid; list; list = tmplist)
4903 {
4904 free(list->list);
4905 tmplist = list->next;
4906 free(list);
4907 }
4908
4909 if (configs->flags & CONFIG_NAME)
4910 free(configs->hostname);
4911
4912 *up = configs->next;
4913 free(configs);
4914 }
4915 else
4916 up = &configs->next;
4917 }
4918}
4919
4920static void clear_dynamic_opt(void)
4921{
4922 struct dhcp_opt *opts, *cp, **up;
4923 struct dhcp_netid *id, *next;
4924
4925 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4926 {
4927 cp = opts->next;
4928
4929 if (opts->flags & DHOPT_BANK)
4930 {
4931 if ((opts->flags & DHOPT_VENDOR))
4932 free(opts->u.vendor_class);
4933 free(opts->val);
4934 for (id = opts->netid; id; id = next)
4935 {
4936 next = id->next;
4937 free(id->net);
4938 free(id);
4939 }
4940 *up = opts->next;
4941 free(opts);
4942 }
4943 else
4944 up = &opts->next;
4945 }
4946}
4947
Simon Kelley824af852008-02-12 20:43:05 +00004948void reread_dhcp(void)
4949{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004950 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004951
Simon Kelley4f7bb572018-03-08 18:47:08 +00004952 /* Do these even if there is no daemon->dhcp_hosts_file or
4953 daemon->dhcp_opts_file since entries may have been created by the
4954 inotify dynamic file reading system. */
4955
4956 clear_dynamic_conf();
4957 clear_dynamic_opt();
4958
4959 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004960 {
Simon Kelley28866e92011-02-14 20:19:14 +00004961 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4962 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004963 if (!(hf->flags & AH_INACTIVE))
4964 {
4965 if (one_file(hf->fname, LOPT_BANK))
4966 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4967 }
Simon Kelley824af852008-02-12 20:43:05 +00004968 }
4969
4970 if (daemon->dhcp_opts_file)
4971 {
Simon Kelley28866e92011-02-14 20:19:14 +00004972 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4973 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4974 if (!(hf->flags & AH_INACTIVE))
4975 {
Simon Kelley395eb712012-07-06 22:07:05 +01004976 if (one_file(hf->fname, LOPT_OPTS))
4977 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004978 }
Simon Kelley824af852008-02-12 20:43:05 +00004979 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004980
4981# ifdef HAVE_INOTIFY
4982 /* Setup notify and read pre-existing files. */
4983 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
4984# endif
Simon Kelley824af852008-02-12 20:43:05 +00004985}
Simon Kelley7622fc02009-06-04 20:32:05 +01004986#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00004987
Simon Kelley5aabfc72007-08-29 11:24:47 +01004988void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004989{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004990 size_t argbuf_size = MAXDNAME;
4991 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004992 char *buff = opt_malloc(MAXDNAME);
Petr Menšík59e47032018-11-02 22:39:39 +00004993 int option, testmode = 0;
4994 char *arg, *conffile = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004995
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004996 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004997
Simon Kelley824af852008-02-12 20:43:05 +00004998 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004999 memset(daemon, 0, sizeof(struct daemon));
5000 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005001
Simon Kelley3be34542004-09-11 19:12:13 +01005002 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01005003 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01005004 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01005005 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01005006 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
5007 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01005008 daemon->default_resolv.is_default = 1;
5009 daemon->default_resolv.name = RESOLVFILE;
5010 daemon->resolv_files = &daemon->default_resolv;
5011 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01005012 daemon->runfile = RUNFILE;
5013 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00005014 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01005015 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01005016 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00005017 daemon->auth_ttl = AUTH_TTL;
5018 daemon->soa_refresh = SOA_REFRESH;
5019 daemon->soa_retry = SOA_RETRY;
5020 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00005021 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00005022 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01005023
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005024#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00005025 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
5026 add_txt("authors.bind", "Simon Kelley", 0);
5027 add_txt("copyright.bind", COPYRIGHT, 0);
5028 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
5029 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
5030 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
5031 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
5032 add_txt("hits.bind", NULL, TXT_STAT_HITS);
5033#ifdef HAVE_AUTH
5034 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
5035#endif
5036 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01005037#endif
Simon Kelley0a852542005-03-23 20:28:59 +00005038
Simon Kelley849a8352006-06-09 21:02:31 +01005039 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005040 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005041#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01005042 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005043#else
Simon Kelley849a8352006-06-09 21:02:31 +01005044 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005045#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005046
5047 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00005048 {
Simon Kelley572b41e2011-02-18 18:11:18 +00005049 for (; optind < argc; optind++)
5050 {
5051 unsigned char *c = (unsigned char *)argv[optind];
5052 for (; *c != 0; c++)
5053 if (!isspace(*c))
5054 die(_("junk found in command line"), NULL, EC_BADCONF);
5055 }
Simon Kelley28866e92011-02-14 20:19:14 +00005056 break;
5057 }
5058
Simon Kelley849a8352006-06-09 21:02:31 +01005059 /* Copy optarg so that argv doesn't get changed */
5060 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005061 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00005062 if (strlen(optarg) >= argbuf_size)
5063 {
5064 free(argbuf);
5065 argbuf_size = strlen(optarg) + 1;
5066 argbuf = opt_malloc(argbuf_size);
5067 }
Petr Menšík47b45b22018-08-15 18:17:00 +02005068 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00005069 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01005070 }
5071 else
5072 arg = NULL;
5073
5074 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01005075 if (option == LOPT_TEST)
5076 testmode = 1;
5077 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01005078 {
Simon Kelley7622fc02009-06-04 20:32:05 +01005079#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00005080 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01005081 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00005082#ifdef HAVE_DHCP6
5083 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
5084 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01005085#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00005086 else
5087#endif
5088 do_usage();
5089
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005090 exit(0);
5091 }
Simon Kelley849a8352006-06-09 21:02:31 +01005092 else if (option == 'v')
5093 {
5094 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00005095 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00005096 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
5097 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00005098 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005099 exit(0);
5100 }
Simon Kelley849a8352006-06-09 21:02:31 +01005101 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005102 {
Simon Kelley5c464ef2019-03-29 23:11:05 +00005103 if (!conffile)
5104 conffile = opt_string_alloc(arg);
5105 else
5106 {
5107 char *extra = opt_string_alloc(arg);
5108 one_file(extra, 0);
5109 free(extra);
5110 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005111 }
Simon Kelley849a8352006-06-09 21:02:31 +01005112 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005113 {
Simon Kelley26128d22004-11-14 16:43:54 +00005114#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00005115 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005116#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00005117 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01005118#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01005119 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005120 }
5121 }
Simon Kelley849a8352006-06-09 21:02:31 +01005122
Neil Jerram3bd4c472018-01-18 22:49:38 +00005123 free(argbuf);
5124
Simon Kelley849a8352006-06-09 21:02:31 +01005125 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00005126 {
Petr Menšík59e47032018-11-02 22:39:39 +00005127 one_file(conffile, 0);
5128 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00005129 }
Petr Menšík59e47032018-11-02 22:39:39 +00005130 else
5131 one_file(CONFFILE, '7');
Simon Kelley849a8352006-06-09 21:02:31 +01005132
Simon Kelley1a6bca82008-07-11 11:11:42 +01005133 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01005134 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005135 {
5136 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005137 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01005138 if (!(tmp->flags & SERV_HAS_SOURCE))
5139 {
5140 if (tmp->source_addr.sa.sa_family == AF_INET)
5141 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005142 else if (tmp->source_addr.sa.sa_family == AF_INET6)
5143 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01005144 }
5145 }
5146
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005147 if (daemon->host_records)
5148 {
5149 struct host_record *hr;
5150
5151 for (hr = daemon->host_records; hr; hr = hr->next)
5152 if (hr->ttl == -1)
5153 hr->ttl = daemon->local_ttl;
5154 }
5155
5156 if (daemon->cnames)
5157 {
Simon Kelley903df072017-01-19 17:22:00 +00005158 struct cname *cn, *cn2, *cn3;
5159
5160#define NOLOOP 1
5161#define TESTLOOP 2
5162
Simon Kelley157d8cf2019-10-25 17:46:49 +01005163 /* Fill in TTL for CNAMES now we have local_ttl.
Simon Kelley903df072017-01-19 17:22:00 +00005164 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005165 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00005166 {
5167 if (cn->ttl == -1)
5168 cn->ttl = daemon->local_ttl;
5169 cn->flag = 0;
5170 cn->targetp = NULL;
5171 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
5172 if (hostname_isequal(cn->target, cn2->alias))
5173 {
5174 cn->targetp = cn2;
5175 break;
5176 }
5177 }
5178
5179 /* Find any CNAME loops.*/
5180 for (cn = daemon->cnames; cn; cn = cn->next)
5181 {
5182 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
5183 {
5184 if (cn2->flag == NOLOOP)
5185 break;
5186
5187 if (cn2->flag == TESTLOOP)
5188 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
5189
5190 cn2->flag = TESTLOOP;
5191 }
5192
5193 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
5194 cn3->flag = NOLOOP;
5195 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00005196 }
5197
Simon Kelley3be34542004-09-11 19:12:13 +01005198 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005199 {
5200 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01005201 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005202 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01005203 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005204 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01005205 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005206 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00005207
5208 /* create default, if not specified */
5209 if (daemon->authserver && !daemon->hostmaster)
5210 {
5211 strcpy(buff, "hostmaster.");
5212 strcat(buff, daemon->authserver);
5213 daemon->hostmaster = opt_string_alloc(buff);
5214 }
5215
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005216 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00005217 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005218 {
Simon Kelley0a852542005-03-23 20:28:59 +00005219 struct mx_srv_record *mx;
5220
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005221 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005222 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005223
Simon Kelley0a852542005-03-23 20:28:59 +00005224 for (mx = daemon->mxnames; mx; mx = mx->next)
5225 if (!mx->issrv && hostname_isequal(mx->name, buff))
5226 break;
5227
Simon Kelley28866e92011-02-14 20:19:14 +00005228 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01005229 {
Simon Kelley824af852008-02-12 20:43:05 +00005230 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01005231 mx->next = daemon->mxnames;
5232 mx->issrv = 0;
5233 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00005234 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01005235 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005236 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005237
Simon Kelley3be34542004-09-11 19:12:13 +01005238 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00005239 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00005240
5241 for (mx = daemon->mxnames; mx; mx = mx->next)
5242 if (!mx->issrv && !mx->target)
5243 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00005244 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00005245
Simon Kelley28866e92011-02-14 20:19:14 +00005246 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01005247 daemon->resolv_files &&
5248 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00005249 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005250 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005251
Simon Kelley28866e92011-02-14 20:19:14 +00005252 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01005253 {
5254 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01005255 FILE *f;
5256
Simon Kelley28866e92011-02-14 20:19:14 +00005257 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01005258 !daemon->resolv_files ||
5259 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005260 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01005261
Simon Kelley3be34542004-09-11 19:12:13 +01005262 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01005263 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01005264
5265 while ((line = fgets(buff, MAXDNAME, f)))
5266 {
5267 char *token = strtok(line, " \t\n\r");
5268
5269 if (!token || strcmp(token, "search") != 0)
5270 continue;
5271
5272 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01005273 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01005274 break;
5275 }
Simon Kelley3be34542004-09-11 19:12:13 +01005276
Simon Kelleyde379512004-06-22 20:23:33 +01005277 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00005278
Simon Kelley3be34542004-09-11 19:12:13 +01005279 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01005280 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01005281 }
Simon Kelley3d8df262005-08-29 12:19:27 +01005282
5283 if (daemon->domain_suffix)
5284 {
5285 /* add domain for any srv record without one. */
5286 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01005287
Simon Kelley3d8df262005-08-29 12:19:27 +01005288 for (srv = daemon->mxnames; srv; srv = srv->next)
5289 if (srv->issrv &&
5290 strchr(srv->name, '.') &&
5291 strchr(srv->name, '.') == strrchr(srv->name, '.'))
5292 {
5293 strcpy(buff, srv->name);
5294 strcat(buff, ".");
5295 strcat(buff, daemon->domain_suffix);
5296 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005297 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005298 }
5299 }
Simon Kelley28866e92011-02-14 20:19:14 +00005300 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005301 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005302
Simon Kelleyc8a80482014-03-05 14:29:54 +00005303 /* If there's access-control config, then ignore --local-service, it's intended
5304 as a system default to keep otherwise unconfigured installations safe. */
5305 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5306 reset_option_bool(OPT_LOCAL_SERVICE);
5307
Simon Kelley7622fc02009-06-04 20:32:05 +01005308 if (testmode)
5309 {
5310 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5311 exit(0);
5312 }
Simon Kelley849a8352006-06-09 21:02:31 +01005313}