blob: e36734a6af2ec346a949c5967fb7ee79785c9a9e [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 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 Kelleyc6309242013-03-07 20:59:28 +0000132#ifdef OPTION6_PREFIX_CLASS
Simon Kelleye98bd522014-03-28 20:41:23 +0000133#define LOPT_PREF_CLSS 321
Simon Kelleyc6309242013-03-07 20:59:28 +0000134#endif
Simon Kelleye98bd522014-03-28 20:41:23 +0000135#define LOPT_RELAY 323
136#define LOPT_RA_PARAM 324
137#define LOPT_ADD_SBNET 325
138#define LOPT_QUIET_DHCP 326
139#define LOPT_QUIET_DHCP6 327
140#define LOPT_QUIET_RA 328
141#define LOPT_SEC_VALID 329
142#define LOPT_TRUST_ANCHOR 330
143#define LOPT_DNSSEC_DEBUG 331
144#define LOPT_REV_SERV 332
145#define LOPT_SERVERS_FILE 333
146#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000147#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000148#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100149#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000150#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000151#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000152#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000153#define LOPT_DHOPT_INOTIFY 341
154#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000155#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100156#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000157#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000158#define LOPT_CPE_ID 346
159#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000160#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000161#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100162#define LOPT_REPLY_DELAY 350
Simon Kelleybec366b2016-02-24 22:03:26 +0000163
Simon Kelley849a8352006-06-09 21:02:31 +0100164#ifdef HAVE_GETOPT_LONG
165static const struct option opts[] =
166#else
167static const struct myoption opts[] =
168#endif
169 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100170 { "version", 0, 0, 'v' },
171 { "no-hosts", 0, 0, 'h' },
172 { "no-poll", 0, 0, 'n' },
173 { "help", 0, 0, 'w' },
174 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000175 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100176 { "user", 2, 0, 'u' },
177 { "group", 2, 0, 'g' },
178 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000179 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100180 { "mx-host", 1, 0, 'm' },
181 { "mx-target", 1, 0, 't' },
182 { "cache-size", 2, 0, 'c' },
183 { "port", 1, 0, 'p' },
184 { "dhcp-leasefile", 2, 0, 'l' },
185 { "dhcp-lease", 1, 0, 'l' },
186 { "dhcp-host", 1, 0, 'G' },
187 { "dhcp-range", 1, 0, 'F' },
188 { "dhcp-option", 1, 0, 'O' },
189 { "dhcp-boot", 1, 0, 'M' },
190 { "domain", 1, 0, 's' },
191 { "domain-suffix", 1, 0, 's' },
192 { "interface", 1, 0, 'i' },
193 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000194 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100195 { "bogus-priv", 0, 0, 'b' },
196 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000197 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100198 { "selfmx", 0, 0, 'e' },
199 { "filterwin2k", 0, 0, 'f' },
200 { "pid-file", 2, 0, 'x' },
201 { "strict-order", 0, 0, 'o' },
202 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000203 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100204 { "local", 1, 0, LOPT_LOCAL },
205 { "address", 1, 0, 'A' },
206 { "conf-file", 2, 0, 'C' },
207 { "no-resolv", 0, 0, 'R' },
208 { "expand-hosts", 0, 0, 'E' },
209 { "localmx", 0, 0, 'L' },
210 { "local-ttl", 1, 0, 'T' },
211 { "no-negcache", 0, 0, 'N' },
212 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000213 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100214 { "query-port", 1, 0, 'Q' },
215 { "except-interface", 1, 0, 'I' },
216 { "no-dhcp-interface", 1, 0, '2' },
217 { "domain-needed", 0, 0, 'D' },
218 { "dhcp-lease-max", 1, 0, 'X' },
219 { "bind-interfaces", 0, 0, 'z' },
220 { "read-ethers", 0, 0, 'Z' },
221 { "alias", 1, 0, 'V' },
222 { "dhcp-vendorclass", 1, 0, 'U' },
223 { "dhcp-userclass", 1, 0, 'j' },
224 { "dhcp-ignore", 1, 0, 'J' },
225 { "edns-packet-max", 1, 0, 'P' },
226 { "keep-in-foreground", 0, 0, 'k' },
227 { "dhcp-authoritative", 0, 0, 'K' },
228 { "srv-host", 1, 0, 'W' },
229 { "localise-queries", 0, 0, 'y' },
230 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100231 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100232 { "enable-dbus", 2, 0, '1' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100233 { "bootp-dynamic", 2, 0, '3' },
234 { "dhcp-mac", 1, 0, '4' },
235 { "no-ping", 0, 0, '5' },
236 { "dhcp-script", 1, 0, '6' },
237 { "conf-dir", 1, 0, '7' },
238 { "log-facility", 1, 0 ,'8' },
239 { "leasefile-ro", 0, 0, '9' },
240 { "dns-forward-max", 1, 0, '0' },
241 { "clear-on-reload", 0, 0, LOPT_RELOAD },
242 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100243 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100244 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100245 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100246 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100247 { "tftp-root", 1, 0, LOPT_PREFIX },
248 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000249 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100250 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100251 { "ptr-record", 1, 0, LOPT_PTR },
252 { "naptr-record", 1, 0, LOPT_NAPTR },
253 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
254 { "dhcp-option-force", 1, 0, LOPT_FORCE },
255 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
256 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
257 { "log-async", 2, 0, LOPT_MAX_LOGS },
258 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
259 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
260 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
261 { "interface-name", 1, 0, LOPT_INTNAME },
262 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
263 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000264 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000265 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100266 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
267 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
268 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100269 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100270 { "all-servers", 0, 0, LOPT_NOLAST },
271 { "dhcp-match", 1, 0, LOPT_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100272 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100273 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100274 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000275 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100276 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100277 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
278 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
279 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000280 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100281 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
282 { "cname", 1, 0, LOPT_CNAME },
283 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
284 { "pxe-service", 1, 0, LOPT_PXE_SERV },
285 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100286 { "tag-if", 1, 0, LOPT_TAG_IF },
287 { "dhcp-proxy", 2, 0, LOPT_PROXY },
288 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
289 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000290 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100291 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000292 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000293 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100294 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
295 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000296 { "dhcp-client-update", 0, 0, LOPT_FQDN },
297 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000298 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000299 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000300 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100301 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000302 { "auth-zone", 1, 0, LOPT_AUTHZONE },
303 { "auth-server", 1, 0, LOPT_AUTHSERV },
304 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
305 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000306 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000307 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000308 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100309 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200310 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000311 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000312 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelley00a5b5d2014-02-28 18:10:55 +0000313 { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000314 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000315 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyc6309242013-03-07 20:59:28 +0000316#ifdef OPTION6_PREFIX_CLASS
317 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
318#endif
Simon Kelleyff7eea22013-09-04 18:01:38 +0100319 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100320 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100321 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
322 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
323 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100324 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000325 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000326 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100327 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley849a8352006-06-09 21:02:31 +0100328 { NULL, 0, 0, 0 }
329 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000330
Simon Kelley28866e92011-02-14 20:19:14 +0000331
332#define ARG_DUP OPT_LAST
333#define ARG_ONE OPT_LAST + 1
334#define ARG_USED_CL OPT_LAST + 2
335#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000336
Simon Kelley1a6bca82008-07-11 11:11:42 +0100337static struct {
338 int opt;
339 unsigned int rept;
340 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000341 char * const desc;
342 char * const arg;
343} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000344 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
345 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100346 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000347 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
348 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
349 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100350 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
351 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
352 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
353 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
354 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000355 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
356 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100357 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000358 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
359 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000360 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000361 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100362 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100363 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000364 { '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 +0000365 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000366 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
367 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100368 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
369 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
370 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
371 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
372 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
373 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100374 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
375 { '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 +0000376 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100377 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100379 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
380 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
381 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
382 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
383 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
384 { 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 +0000385 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
386 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000387 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000388 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100389 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000390 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000391 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000392 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000393 { 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 +0000394 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000395 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000396 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
397 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
398 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
399 { 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 +0000400 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
401 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000402 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100403 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100404 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000405 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
406 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100407 { '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 +0000408 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
409 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { '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 +0000411 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
412 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
413 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100414 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
415 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100416 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000417 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100418 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
419 { '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 +0000420 { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100421 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000422 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
423 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
424 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000425 { 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 +0000426 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000427 { '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 +0100428 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000429 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100430 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100431 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100432 { 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 +0100433 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100434 { 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 +0100435 { 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 +0100436 { 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 +0100437 { 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 +0000438 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000439 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100440 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100441 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100442 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
443 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000444 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100445 { 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 +0100446 { 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 +0000447 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100448 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100449 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100450 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
452 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000453 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000454 { 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 +0000455 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
456 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100457 { 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 +0000458 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100459 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
460 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
461 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000462 { 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 +0100463 { 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 +0000464 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100465 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100466 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
467 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000468 { 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 +0000469 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000470 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000471 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100472 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000473 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000474 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000475 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000476 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000477 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000478 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
479 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100480 { 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 +0100481 { 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 +0000482 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000483 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000484 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelley00a5b5d2014-02-28 18:10:55 +0000485 { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000486 { 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 +0000487 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000488#ifdef OPTION6_PREFIX_CLASS
489 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
490#endif
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100491 { 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 +0100492 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
493 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
494 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000495 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
496 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000497 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000498 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100499 { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100500 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000501};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000502
Josh Soref730c6742017-02-06 16:14:04 +0000503/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100504 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 +0100505 following sequence so that they map to themselves: it is therefore possible to call
506 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000507 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100508 couple of other places.
509 Note that space is included here so that
510 --dhcp-option=3, string
511 has five characters, whilst
512 --dhcp-option=3," string"
513 has six.
514*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100515
Simon Kelleyf2621c72007-04-29 19:47:21 +0100516static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100517
518static char hide_meta(char c)
519{
520 unsigned int i;
521
522 for (i = 0; i < (sizeof(meta) - 1); i++)
523 if (c == meta[i])
524 return (char)i;
525
526 return c;
527}
528
529static char unhide_meta(char cr)
530{
531 unsigned int c = cr;
532
533 if (c < (sizeof(meta) - 1))
534 cr = meta[c];
535
536 return cr;
537}
538
539static void unhide_metas(char *cp)
540{
541 if (cp)
542 for(; *cp; cp++)
543 *cp = unhide_meta(*cp);
544}
545
Simon Kelley824af852008-02-12 20:43:05 +0000546static void *opt_malloc(size_t size)
547{
548 void *ret;
549
550 if (mem_recover)
551 {
552 ret = whine_malloc(size);
553 if (!ret)
554 longjmp(mem_jmp, 1);
555 }
556 else
557 ret = safe_malloc(size);
558
559 return ret;
560}
561
562static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100563{
564 char *ret = NULL;
565
566 if (cp && strlen(cp) != 0)
567 {
Simon Kelley824af852008-02-12 20:43:05 +0000568 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100569 strcpy(ret, cp);
570
571 /* restore hidden metachars */
572 unhide_metas(ret);
573 }
574
575 return ret;
576}
577
Simon Kelley3d8df262005-08-29 12:19:27 +0100578
Simon Kelleyf2621c72007-04-29 19:47:21 +0100579/* find next comma, split string with zero and eliminate spaces.
580 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000581
582static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100583{
584 char *comma, *p;
585
Simon Kelley73a08a22009-02-05 20:28:08 +0000586 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100587 return NULL;
588
589 p = comma;
590 *comma = ' ';
591
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100592 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100593
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100594 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100595 *p = 0;
596
597 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100598}
599
Simon Kelley73a08a22009-02-05 20:28:08 +0000600static char *split(char *s)
601{
602 return split_chr(s, ',');
603}
604
Simon Kelley1f15b812009-10-13 17:49:32 +0100605static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100606{
Simon Kelley1f15b812009-10-13 17:49:32 +0100607 char *ret;
608 int nomem;
609
Simon Kelley3d8df262005-08-29 12:19:27 +0100610 if (!s)
611 return 0;
612
613 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100614 if (!(ret = canonicalise(s, &nomem)) && nomem)
615 {
616 if (mem_recover)
617 longjmp(mem_jmp, 1);
618 else
619 die(_("could not get memory"), NULL, EC_NOMEM);
620 }
621
622 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100623}
624
625static int atoi_check(char *a, int *res)
626{
627 char *p;
628
629 if (!a)
630 return 0;
631
632 unhide_metas(a);
633
634 for (p = a; *p; p++)
635 if (*p < '0' || *p > '9')
636 return 0;
637
638 *res = atoi(a);
639 return 1;
640}
641
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100642static int atoi_check16(char *a, int *res)
643{
644 if (!(atoi_check(a, res)) ||
645 *res < 0 ||
646 *res > 0xffff)
647 return 0;
648
649 return 1;
650}
Simon Kelleyee415862014-02-11 11:07:22 +0000651
Simon Kelleyde73a492014-02-17 21:43:27 +0000652#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000653static int atoi_check8(char *a, int *res)
654{
655 if (!(atoi_check(a, res)) ||
656 *res < 0 ||
657 *res > 0xff)
658 return 0;
659
660 return 1;
661}
Simon Kelleyde73a492014-02-17 21:43:27 +0000662#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100663
664#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000665static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000666{
Simon Kelley824af852008-02-12 20:43:05 +0000667 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000668
669 if (txt)
670 {
671 size_t len = strlen(txt);
672 r->txt = opt_malloc(len+1);
673 r->len = len+1;
674 *(r->txt) = len;
675 memcpy((r->txt)+1, txt, len);
676 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100677
Simon Kelleyfec216d2014-03-27 20:54:34 +0000678 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000679 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000680 r->next = daemon->txt;
681 daemon->txt = r;
682 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000683}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100684#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000685
Simon Kelley849a8352006-06-09 21:02:31 +0100686static void do_usage(void)
687{
688 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000689 int i, j;
690
691 struct {
692 char handle;
693 int val;
694 } tab[] = {
695 { '$', CACHESIZ },
696 { '*', EDNS_PKTSZ },
697 { '&', MAXLEASES },
698 { '!', FTABSIZ },
699 { '#', TFTP_MAX_CONNECTIONS },
700 { '\0', 0 }
701 };
Simon Kelley849a8352006-06-09 21:02:31 +0100702
703 printf(_("Usage: dnsmasq [options]\n\n"));
704#ifndef HAVE_GETOPT_LONG
705 printf(_("Use short options only on the command line.\n"));
706#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100707 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100708
Simon Kelley1a6bca82008-07-11 11:11:42 +0100709 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100710 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100711 char *desc = usage[i].flagdesc;
712 char *eq = "=";
713
714 if (!desc || *desc == '[')
715 eq = "";
716
717 if (!desc)
718 desc = "";
719
720 for ( j = 0; opts[j].name; j++)
721 if (opts[j].val == usage[i].opt)
722 break;
723 if (usage[i].opt < 256)
724 sprintf(buff, "-%c, ", usage[i].opt);
725 else
726 sprintf(buff, " ");
727
728 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100729 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100730
Simon Kelley849a8352006-06-09 21:02:31 +0100731 if (usage[i].arg)
732 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000733 strcpy(buff, usage[i].arg);
734 for (j = 0; tab[j].handle; j++)
735 if (tab[j].handle == *(usage[i].arg))
736 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100737 }
Simon Kelley849a8352006-06-09 21:02:31 +0100738 printf(_(usage[i].desc), buff);
739 printf("\n");
740 }
741}
742
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100743#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
744
Ed Bardsleya7369be2015-08-05 21:17:18 +0100745static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
746{
747 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
748 addr->sa.sa_family = AF_INET;
749#ifdef HAVE_IPV6
750 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
751 addr->sa.sa_family = AF_INET6;
752#endif
753 else
754 return _("bad address");
755
756 return NULL;
757}
758
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100759char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
760{
761 int source_port = 0, serv_port = NAMESERVER_PORT;
762 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000763 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100764#ifdef HAVE_IPV6
765 int scope_index = 0;
766 char *scope_id;
767#endif
768
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000769 if (!arg || strlen(arg) == 0)
770 {
771 *flags |= SERV_NO_ADDR;
772 *interface = 0;
773 return NULL;
774 }
775
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100776 if ((source = split_chr(arg, '@')) && /* is there a source. */
777 (portno = split_chr(source, '#')) &&
778 !atoi_check16(portno, &source_port))
779 return _("bad port");
780
781 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
782 !atoi_check16(portno, &serv_port))
783 return _("bad port");
784
785#ifdef HAVE_IPV6
786 scope_id = split_chr(arg, '%');
787#endif
788
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000789 if (source) {
790 interface_opt = split_chr(source, '@');
791
792 if (interface_opt)
793 {
794#if defined(SO_BINDTODEVICE)
795 strncpy(interface, interface_opt, IF_NAMESIZE - 1);
796#else
797 return _("interface binding not supported");
798#endif
799 }
800 }
801
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100802 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100803 {
804 addr->in.sin_port = htons(serv_port);
805 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
806#ifdef HAVE_SOCKADDR_SA_LEN
807 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
808#endif
809 source_addr->in.sin_addr.s_addr = INADDR_ANY;
810 source_addr->in.sin_port = htons(daemon->query_port);
811
812 if (source)
813 {
814 if (flags)
815 *flags |= SERV_HAS_SOURCE;
816 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100817 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100818 {
819#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000820 if (interface_opt)
821 return _("interface can only be specified once");
822
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100823 source_addr->in.sin_addr.s_addr = INADDR_ANY;
824 strncpy(interface, source, IF_NAMESIZE - 1);
825#else
826 return _("interface binding not supported");
827#endif
828 }
829 }
830 }
831#ifdef HAVE_IPV6
832 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
833 {
834 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
835 return _("bad interface name");
836
837 addr->in6.sin6_port = htons(serv_port);
838 addr->in6.sin6_scope_id = scope_index;
839 source_addr->in6.sin6_addr = in6addr_any;
840 source_addr->in6.sin6_port = htons(daemon->query_port);
841 source_addr->in6.sin6_scope_id = 0;
842 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
843 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
844#ifdef HAVE_SOCKADDR_SA_LEN
845 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
846#endif
847 if (source)
848 {
849 if (flags)
850 *flags |= SERV_HAS_SOURCE;
851 source_addr->in6.sin6_port = htons(source_port);
852 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
853 {
854#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000855 if (interface_opt)
856 return _("interface can only be specified once");
857
858 source_addr->in6.sin6_addr = in6addr_any;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100859 strncpy(interface, source, IF_NAMESIZE - 1);
860#else
861 return _("interface binding not supported");
862#endif
863 }
864 }
865 }
866#endif
867 else
868 return _("bad address");
869
870 return NULL;
871}
872
Simon Kelleyde73a492014-02-17 21:43:27 +0000873static struct server *add_rev4(struct in_addr addr, int msize)
874{
875 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000876 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000877 char *p;
878
879 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000880 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
881
882 switch (msize)
883 {
884 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100885 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000886 /* fall through */
887 case 24:
888 p += sprintf(p, "%d.", (a >> 8) & 0xff);
889 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000890 case 16:
891 p += sprintf(p, "%d.", (a >> 16) & 0xff);
892 /* fall through */
893 case 8:
894 p += sprintf(p, "%d.", (a >> 24) & 0xff);
895 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000896 default:
897 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000898 }
899
900 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000901
902 serv->flags = SERV_HAS_DOMAIN;
903 serv->next = daemon->servers;
904 daemon->servers = serv;
905
906 return serv;
907
908}
909
910static struct server *add_rev6(struct in6_addr *addr, int msize)
911{
912 struct server *serv = opt_malloc(sizeof(struct server));
913 char *p;
914 int i;
915
916 memset(serv, 0, sizeof(struct server));
917 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
918
919 for (i = msize-1; i >= 0; i -= 4)
920 {
921 int dig = ((unsigned char *)addr)[i>>3];
922 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
923 }
924 p += sprintf(p, "ip6.arpa");
925
926 serv->flags = SERV_HAS_DOMAIN;
927 serv->next = daemon->servers;
928 daemon->servers = serv;
929
930 return serv;
931}
932
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000933#ifdef HAVE_DHCP
934
935static int is_tag_prefix(char *arg)
936{
937 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
938 return 1;
939
940 return 0;
941}
942
943static char *set_prefix(char *arg)
944{
945 if (strstr(arg, "set:") == arg)
946 return arg+4;
947
948 return arg;
949}
950
Simon Kelley832af0b2007-01-21 20:01:28 +0000951/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100952static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000953{
Simon Kelley824af852008-02-12 20:43:05 +0000954 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000955 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000956 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100957 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100958 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000959 u16 opt_len = 0;
960 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100961 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000962
963 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000964 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000965 new->netid = NULL;
966 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100967 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000968
Simon Kelleyf2621c72007-04-29 19:47:21 +0100969 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000970 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100971 comma = split(arg);
972
973 for (cp = arg; *cp; cp++)
974 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000975 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100976
977 if (!*cp)
978 {
979 new->opt = atoi(arg);
980 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100981 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100982 break;
983 }
984
985 if (strstr(arg, "option:") == arg)
986 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100987 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
988 {
989 opt_len = lookup_dhcp_len(AF_INET, new->opt);
990 /* option:<optname> must follow tag and vendor string. */
991 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
992 option_ok = 1;
993 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100994 break;
995 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000996#ifdef HAVE_DHCP6
997 else if (strstr(arg, "option6:") == arg)
998 {
999 for (cp = arg+8; *cp; cp++)
1000 if (*cp < '0' || *cp > '9')
1001 break;
1002
1003 if (!*cp)
1004 {
1005 new->opt = atoi(arg+8);
1006 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001007 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001008 }
1009 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001010 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001011 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1012 {
1013 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1014 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1015 option_ok = 1;
1016 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001017 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001018 /* option6:<opt>|<optname> must follow tag and vendor string. */
1019 is6 = 1;
1020 break;
1021 }
1022#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001023 else if (strstr(arg, "vendor:") == arg)
1024 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001025 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1026 new->flags |= DHOPT_VENDOR;
1027 }
1028 else if (strstr(arg, "encap:") == arg)
1029 {
1030 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001031 new->flags |= DHOPT_ENCAPSULATE;
1032 }
Simon Kelley316e2732010-01-22 20:16:09 +00001033 else if (strstr(arg, "vi-encap:") == arg)
1034 {
1035 new->u.encap = atoi(arg+9);
1036 new->flags |= DHOPT_RFC3925;
1037 if (flags == DHOPT_MATCH)
1038 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001039 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001040 break;
1041 }
1042 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001043 else
1044 {
Simon Kelley824af852008-02-12 20:43:05 +00001045 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001046 /* allow optional "net:" or "tag:" for consistency */
1047 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +00001048 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001049 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001050 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001051 new->netid->next = np;
1052 np = new->netid;
1053 }
1054
1055 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001056 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001057
1058#ifdef HAVE_DHCP6
1059 if (is6)
1060 {
1061 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001062 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001063
1064 if (opt_len == 0 &&
1065 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001066 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001067 }
1068 else
1069#endif
1070 if (opt_len == 0 &&
1071 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001072 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001073
Simon Kelley316e2732010-01-22 20:16:09 +00001074 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001075 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001076 ret_err(_("bad dhcp-option"));
1077
1078 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001079 {
1080 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001081 char c;
Simon Kelley28866e92011-02-14 20:19:14 +00001082 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001083 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001084 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001085 dots = 0;
1086 for (cp = comma; (c = *cp); cp++)
1087 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001088 {
1089 addrs++;
1090 is_dec = is_hex = 0;
1091 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001092 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001093 {
1094 digs++;
1095 is_dec = is_addr = 0;
1096 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001097 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001098 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001099 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001100 if (cp == comma) /* leading / means a pathname */
1101 is_addr = 0;
1102 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001103 else if (c == '.')
1104 {
Simon Kelley23245c02012-07-18 16:21:11 +01001105 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001106 dots++;
1107 }
1108 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001109 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001110 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001111 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001113 {
1114 is_addr = 0;
1115 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001116 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001117 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001118 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001119 *cp = 0;
1120 }
1121 else
1122 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001123 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001124 (c >='a' && c <= 'f') ||
1125 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001126 {
1127 is_hex = 0;
1128 if (c != '[' && c != ']')
1129 is_addr6 = 0;
1130 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001131 }
Simon Kelley28866e92011-02-14 20:19:14 +00001132 else
1133 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001134
Simon Kelley28866e92011-02-14 20:19:14 +00001135 if (!found_dig)
1136 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001137
Simon Kelleyf2621c72007-04-29 19:47:21 +01001138 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001139 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001140 {
1141 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001142
1143 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001144 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001145
1146 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001147 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001148 }
Simon Kelley28866e92011-02-14 20:19:14 +00001149 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001150 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1151 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001152
1153 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1154 {
1155 int val, fac = 1;
1156
1157 switch (comma[strlen(comma) - 1])
1158 {
Simon Kelley42243212012-07-20 15:19:18 +01001159 case 'w':
1160 case 'W':
1161 fac *= 7;
1162 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001163 case 'd':
1164 case 'D':
1165 fac *= 24;
1166 /* fall though */
1167 case 'h':
1168 case 'H':
1169 fac *= 60;
1170 /* fall through */
1171 case 'm':
1172 case 'M':
1173 fac *= 60;
1174 /* fall through */
1175 case 's':
1176 case 'S':
1177 comma[strlen(comma) - 1] = 0;
1178 }
1179
1180 new->len = 4;
1181 new->val = opt_malloc(4);
1182 val = atoi(comma);
1183 *((int *)new->val) = htonl(val * fac);
1184 }
1185 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001186 {
1187 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001188 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001189 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1190 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001191 }
1192 else if (is_dec)
1193 {
1194 int i, val = atoi(comma);
1195 /* assume numeric arg is 1 byte except for
1196 options where it is known otherwise.
1197 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001198 if (opt_len != 0)
1199 new->len = opt_len;
1200 else if (val & 0xffff0000)
1201 new->len = 4;
1202 else if (val & 0xff00)
1203 new->len = 2;
1204 else
1205 new->len = 1;
1206
Simon Kelley832af0b2007-01-21 20:01:28 +00001207 if (lenchar == 'b')
1208 new->len = 1;
1209 else if (lenchar == 's')
1210 new->len = 2;
1211 else if (lenchar == 'i')
1212 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001213
Simon Kelley824af852008-02-12 20:43:05 +00001214 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001215 for (i=0; i<new->len; i++)
1216 new->val[i] = val>>((new->len - i - 1)*8);
1217 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001218 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001219 {
1220 struct in_addr in;
1221 unsigned char *op;
1222 char *slash;
1223 /* max length of address/subnet descriptor is five bytes,
1224 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001225 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001226 new->flags |= DHOPT_ADDR;
1227
Simon Kelley572b41e2011-02-18 18:11:18 +00001228 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1229 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001230 {
Simon Kelley6b010842007-02-12 20:32:07 +00001231 *(op++) = 1; /* RFC 3361 "enc byte" */
1232 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001233 }
1234 while (addrs--)
1235 {
1236 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001237 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001238 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001239 if (!inet_pton(AF_INET, cp, &in))
1240 ret_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001241 if (!slash)
1242 {
1243 memcpy(op, &in, INADDRSZ);
1244 op += INADDRSZ;
1245 }
1246 else
1247 {
1248 unsigned char *p = (unsigned char *)&in;
1249 int netsize = atoi(slash);
1250 *op++ = netsize;
1251 if (netsize > 0)
1252 *op++ = *p++;
1253 if (netsize > 8)
1254 *op++ = *p++;
1255 if (netsize > 16)
1256 *op++ = *p++;
1257 if (netsize > 24)
1258 *op++ = *p++;
1259 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1260 }
1261 }
1262 new->len = op - new->val;
1263 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001264 else if (is_addr6 && is6)
1265 {
1266 unsigned char *op;
1267 new->val = op = opt_malloc(16 * addrs);
1268 new->flags |= DHOPT_ADDR6;
1269 while (addrs--)
1270 {
1271 cp = comma;
1272 comma = split(cp);
1273
1274 /* check for [1234::7] */
1275 if (*cp == '[')
1276 cp++;
1277 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1278 cp[strlen(cp)-1] = 0;
1279
1280 if (inet_pton(AF_INET6, cp, op))
1281 {
1282 op += IN6ADDRSZ;
1283 continue;
1284 }
1285
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001286 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001287 }
1288 new->len = op - new->val;
1289 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001290 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001291 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001292 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001293 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001294 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001295 {
1296 /* dns search, RFC 3397, or SIP, RFC 3361 */
1297 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001298 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001299 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001300 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001301
1302 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001303 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001304
1305 while (arg && *arg)
1306 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001307 char *in, *dom = NULL;
1308 size_t domlen = 1;
1309 /* Allow "." as an empty domain */
1310 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001311 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001312 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001313 ret_err(_("bad domain in dhcp-option"));
1314
Simon Kelleyc52e1892010-06-07 22:01:39 +01001315 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001316 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001317
1318 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001319 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001320 {
1321 memcpy(newp, m, header_size + len);
1322 free(m);
1323 }
Simon Kelley824af852008-02-12 20:43:05 +00001324 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001325 p = m + header_size;
1326 q = p + len;
1327
1328 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001329 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001330 {
1331 unsigned char *cp = q++;
1332 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001333 for (j = 0; *in && (*in != '.'); in++, j++)
1334 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001335 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001336 if (*in)
1337 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001338 }
1339 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001340 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001341
Simon Kelley832af0b2007-01-21 20:01:28 +00001342 /* Now tail-compress using earlier names. */
1343 newlen = q - p;
1344 for (tail = p + len; *tail; tail += (*tail) + 1)
1345 for (r = p; r - p < (int)len; r += (*r) + 1)
1346 if (strcmp((char *)r, (char *)tail) == 0)
1347 {
1348 PUTSHORT((r - p) | 0xc000, tail);
1349 newlen = tail - p;
1350 goto end;
1351 }
1352 end:
1353 len = newlen;
1354
1355 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001356 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001357 }
1358
1359 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001360 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001361 m[0] = 0;
1362 new->len = (int) len + header_size;
1363 new->val = m;
1364 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001365#ifdef HAVE_DHCP6
1366 else if (comma && (opt_len & OT_CSTRING))
1367 {
1368 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001369 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001370 unsigned char *p, *newp;
1371
Simon Kelley40ef23b2012-03-13 21:59:28 +00001372 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001373 if (comma[i] == ',')
1374 commas++;
1375
1376 newp = opt_malloc(strlen(comma)+(2*commas));
1377 p = newp;
1378 arg = comma;
1379 comma = split(arg);
1380
1381 while (arg && *arg)
1382 {
1383 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001384 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001385 PUTSHORT(len, p);
1386 memcpy(p, arg, len);
1387 p += len;
1388
1389 arg = comma;
1390 comma = split(arg);
1391 }
1392
1393 new->val = newp;
1394 new->len = p - newp;
1395 }
1396 else if (comma && (opt_len & OT_RFC1035_NAME))
1397 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001398 unsigned char *p = NULL, *newp, *end;
1399 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001400 arg = comma;
1401 comma = split(arg);
1402
1403 while (arg && *arg)
1404 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001405 char *dom = canonicalise_opt(arg);
1406 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001407 ret_err(_("bad domain in dhcp-option"));
1408
Simon Kelley18f0fb02012-03-31 21:18:55 +01001409 newp = opt_malloc(len + strlen(dom) + 2);
1410
1411 if (p)
1412 {
1413 memcpy(newp, p, len);
1414 free(p);
1415 }
1416
1417 p = newp;
Simon Kelley0549c732017-09-25 18:17:11 +01001418 end = do_rfc1035_name(p + len, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001419 *end++ = 0;
1420 len = end - p;
1421 free(dom);
1422
Simon Kelley4cb1b322012-02-06 14:30:41 +00001423 arg = comma;
1424 comma = split(arg);
1425 }
1426
Simon Kelley18f0fb02012-03-31 21:18:55 +01001427 new->val = p;
1428 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001429 }
1430#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001431 else
1432 {
1433 new->len = strlen(comma);
1434 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001435 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001436 new->flags |= DHOPT_STRING;
1437 }
1438 }
1439 }
1440
Simon Kelley4cb1b322012-02-06 14:30:41 +00001441 if (!is6 &&
1442 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001443 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001444 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001445 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001446
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001447 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001448 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001449 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1450 !new->netid ||
1451 new->netid->next)
1452 ret_err(_("illegal dhcp-match"));
1453
1454 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001455 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001456 new->next = daemon->dhcp_match6;
1457 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001458 }
1459 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001460 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001461 new->next = daemon->dhcp_match;
1462 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001463 }
Simon Kelley824af852008-02-12 20:43:05 +00001464 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001465 else if (is6)
1466 {
1467 new->next = daemon->dhcp_opts6;
1468 daemon->dhcp_opts6 = new;
1469 }
1470 else
1471 {
1472 new->next = daemon->dhcp_opts;
1473 daemon->dhcp_opts = new;
1474 }
1475
1476 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001477}
1478
Simon Kelley7622fc02009-06-04 20:32:05 +01001479#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001480
Simon Kelley28866e92011-02-14 20:19:14 +00001481void set_option_bool(unsigned int opt)
1482{
1483 if (opt < 32)
1484 daemon->options |= 1u << opt;
1485 else
1486 daemon->options2 |= 1u << (opt - 32);
1487}
1488
Simon Kelley2b5bae92012-06-26 16:55:23 +01001489void reset_option_bool(unsigned int opt)
1490{
1491 if (opt < 32)
1492 daemon->options &= ~(1u << opt);
1493 else
1494 daemon->options2 &= ~(1u << (opt - 32));
1495}
1496
Simon Kelley7b1eae42014-02-20 13:43:28 +00001497static 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 +01001498{
1499 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001500 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001501
Simon Kelley832af0b2007-01-21 20:01:28 +00001502 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001503 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001504
Simon Kelley1a6bca82008-07-11 11:11:42 +01001505 for (i=0; usage[i].opt != 0; i++)
1506 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001507 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001508 int rept = usage[i].rept;
1509
Simon Kelley28866e92011-02-14 20:19:14 +00001510 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001511 {
1512 /* command line */
1513 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001514 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001515 if (rept == ARG_ONE)
1516 usage[i].rept = ARG_USED_CL;
1517 }
1518 else
1519 {
1520 /* allow file to override command line */
1521 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001522 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001523 if (rept == ARG_USED_CL || rept == ARG_ONE)
1524 usage[i].rept = ARG_USED_FILE;
1525 }
1526
1527 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1528 {
Simon Kelley28866e92011-02-14 20:19:14 +00001529 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001530 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001531 }
1532
1533 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001534 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001535
Simon Kelley849a8352006-06-09 21:02:31 +01001536 switch (option)
1537 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001538 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001539 {
Simon Kelley824af852008-02-12 20:43:05 +00001540 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001541 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001542 {
Simon Kelley28866e92011-02-14 20:19:14 +00001543 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001544 free(file);
1545 }
Simon Kelley849a8352006-06-09 21:02:31 +01001546 break;
1547 }
1548
Simon Kelleyf2621c72007-04-29 19:47:21 +01001549 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001550 {
1551 DIR *dir_stream;
1552 struct dirent *ent;
1553 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001554 struct list {
1555 char *suffix;
1556 struct list *next;
Simon Kelley3e1551a2014-09-09 21:46:07 +01001557 } *ignore_suffix = NULL, *match_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001558
Simon Kelley1f15b812009-10-13 17:49:32 +01001559 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001560 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001561 break;
1562
Simon Kelley1f15b812009-10-13 17:49:32 +01001563 for (arg = comma; arg; arg = comma)
1564 {
1565 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001566 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001567 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001568 li = opt_malloc(sizeof(struct list));
1569 if (*arg == '*')
1570 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001571 /* "*" with no suffix is a no-op */
1572 if (arg[1] == 0)
1573 free(li);
1574 else
1575 {
1576 li->next = match_suffix;
1577 match_suffix = li;
1578 /* Have to copy: buffer is overwritten */
1579 li->suffix = opt_string_alloc(arg+1);
1580 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001581 }
1582 else
1583 {
1584 li->next = ignore_suffix;
1585 ignore_suffix = li;
1586 /* Have to copy: buffer is overwritten */
1587 li->suffix = opt_string_alloc(arg);
1588 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001589 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001590 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001591
Simon Kelley849a8352006-06-09 21:02:31 +01001592 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001593 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001594
Simon Kelley849a8352006-06-09 21:02:31 +01001595 while ((ent = readdir(dir_stream)))
1596 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001597 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001598 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001599
1600 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001601 if (len == 0 ||
1602 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001603 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1604 ent->d_name[0] == '.')
1605 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001606
Simon Kelley3e1551a2014-09-09 21:46:07 +01001607 if (match_suffix)
1608 {
1609 for (li = match_suffix; li; li = li->next)
1610 {
1611 /* check for required suffices */
1612 size_t ls = strlen(li->suffix);
1613 if (len > ls &&
1614 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1615 break;
1616 }
1617 if (!li)
1618 continue;
1619 }
1620
Simon Kelley1f15b812009-10-13 17:49:32 +01001621 for (li = ignore_suffix; li; li = li->next)
1622 {
1623 /* check for proscribed suffices */
1624 size_t ls = strlen(li->suffix);
1625 if (len > ls &&
1626 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1627 break;
1628 }
1629 if (li)
1630 continue;
1631
Simon Kelley824af852008-02-12 20:43:05 +00001632 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001633 strcpy(path, directory);
1634 strcat(path, "/");
1635 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001636
Simon Kelley39595cf2013-02-04 21:40:07 +00001637 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001638 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001639 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001640
Simon Kelley39595cf2013-02-04 21:40:07 +00001641 /* only reg files allowed. */
1642 if (S_ISREG(buf.st_mode))
1643 one_file(path, 0);
1644
Simon Kelley849a8352006-06-09 21:02:31 +01001645 free(path);
1646 }
1647
1648 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001649 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001650 for(; ignore_suffix; ignore_suffix = li)
1651 {
1652 li = ignore_suffix->next;
1653 free(ignore_suffix->suffix);
1654 free(ignore_suffix);
1655 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001656 for(; match_suffix; match_suffix = li)
1657 {
1658 li = match_suffix->next;
1659 free(match_suffix->suffix);
1660 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001661 }
Simon Kelley849a8352006-06-09 21:02:31 +01001662 break;
1663 }
1664
Simon Kelleyed4c0762013-10-08 20:46:34 +01001665 case LOPT_ADD_SBNET: /* --add-subnet */
1666 set_option_bool(OPT_CLIENT_SUBNET);
1667 if (arg)
1668 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001669 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001670 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001671
1672 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1673 if ((end = split_chr(arg, '/')))
1674 {
1675 /* has subnet+len */
1676 err = parse_mysockaddr(arg, &new->addr);
1677 if (err)
1678 ret_err(err);
1679 if (!atoi_check(end, &new->mask))
1680 ret_err(gen_err);
1681 new->addr_used = 1;
1682 }
1683 else if (!atoi_check(arg, &new->mask))
1684 ret_err(gen_err);
1685
1686 daemon->add_subnet4 = new;
1687
Ed Bardsleya7369be2015-08-05 21:17:18 +01001688 if (comma)
1689 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001690 new = opt_malloc(sizeof(struct mysubnet));
1691 if ((end = split_chr(comma, '/')))
1692 {
1693 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001694 err = parse_mysockaddr(comma, &new->addr);
1695 if (err)
1696 ret_err(err);
1697 if (!atoi_check(end, &new->mask))
1698 ret_err(gen_err);
1699 new->addr_used = 1;
1700 }
1701 else
1702 {
1703 if (!atoi_check(comma, &new->mask))
1704 ret_err(gen_err);
1705 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001706
1707 daemon->add_subnet6 = new;
1708 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001709 }
1710 break;
1711
Simon Kelleyad094272012-08-10 17:10:54 +01001712 case '1': /* --enable-dbus */
1713 set_option_bool(OPT_DBUS);
1714 if (arg)
1715 daemon->dbus_name = opt_string_alloc(arg);
1716 else
1717 daemon->dbus_name = DNSMASQ_SERVICE;
1718 break;
1719
Simon Kelleyf2621c72007-04-29 19:47:21 +01001720 case '8': /* --log-facility */
1721 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001722 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001723 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001724 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001725 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001726#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001727 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001728#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001729 for (i = 0; facilitynames[i].c_name; i++)
1730 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1731 break;
1732
1733 if (facilitynames[i].c_name)
1734 daemon->log_fac = facilitynames[i].c_val;
1735 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001736 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001737#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001738 }
1739 break;
1740
Simon Kelleyf2621c72007-04-29 19:47:21 +01001741 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001742 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001743 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001744
Simon Kelleyf2621c72007-04-29 19:47:21 +01001745 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001746 {
Simon Kelley824af852008-02-12 20:43:05 +00001747 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001748 struct resolvc *new, *list = daemon->resolv_files;
1749
1750 if (list && list->is_default)
1751 {
1752 /* replace default resolv file - possibly with nothing */
1753 if (name)
1754 {
1755 list->is_default = 0;
1756 list->name = name;
1757 }
1758 else
1759 list = NULL;
1760 }
1761 else if (name)
1762 {
Simon Kelley824af852008-02-12 20:43:05 +00001763 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001764 new->next = list;
1765 new->name = name;
1766 new->is_default = 0;
1767 new->mtime = 0;
1768 new->logged = 0;
1769 list = new;
1770 }
1771 daemon->resolv_files = list;
1772 break;
1773 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001774
1775 case LOPT_SERVERS_FILE:
1776 daemon->servers_file = opt_string_alloc(arg);
1777 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001778
Simon Kelleyf2621c72007-04-29 19:47:21 +01001779 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001780 {
1781 int pref = 1;
1782 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001783 char *name, *target = NULL;
1784
Simon Kelleyf2621c72007-04-29 19:47:21 +01001785 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001786 {
1787 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001788 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001789 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001790 }
1791
Simon Kelley1f15b812009-10-13 17:49:32 +01001792 if (!(name = canonicalise_opt(arg)) ||
1793 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001794 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001795
Simon Kelley824af852008-02-12 20:43:05 +00001796 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001797 new->next = daemon->mxnames;
1798 daemon->mxnames = new;
1799 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001800 new->name = name;
1801 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001802 new->weight = pref;
1803 break;
1804 }
1805
Simon Kelleyf2621c72007-04-29 19:47:21 +01001806 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001807 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001808 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001809 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001810
1811#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001812 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001813 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001814 break;
1815
Simon Kelleyc72daea2012-01-05 21:33:27 +00001816 /* Sorry about the gross pre-processor abuse */
1817 case '6': /* --dhcp-script */
1818 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001819# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001820 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001821# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001822 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001823# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001824 if (option == LOPT_LUASCRIPT)
1825# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001826 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001827# else
1828 daemon->luascript = opt_string_alloc(arg);
1829# endif
1830 else
1831 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001832# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001833 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001834#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001835
Simon Kelley70d18732015-01-31 19:59:29 +00001836 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
1837 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
1838 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
1839 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
1840 case LOPT_HOST_INOTIFY: /* --hostsdir */
1841 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001842 {
Simon Kelley824af852008-02-12 20:43:05 +00001843 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00001844 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00001845 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001846 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001847 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001848 if (option == 'H')
1849 {
1850 new->next = daemon->addn_hosts;
1851 daemon->addn_hosts = new;
1852 }
1853 else if (option == LOPT_DHCP_HOST)
1854 {
1855 new->next = daemon->dhcp_hosts_file;
1856 daemon->dhcp_hosts_file = new;
1857 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001858 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001859 {
1860 new->next = daemon->dhcp_opts_file;
1861 daemon->dhcp_opts_file = new;
1862 }
Simon Kelley70d18732015-01-31 19:59:29 +00001863 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001864 {
Simon Kelley70d18732015-01-31 19:59:29 +00001865 new->next = daemon->dynamic_dirs;
1866 daemon->dynamic_dirs = new;
1867 if (option == LOPT_DHCP_INOTIFY)
1868 new->flags |= AH_DHCP_HST;
1869 else if (option == LOPT_DHOPT_INOTIFY)
1870 new->flags |= AH_DHCP_OPT;
1871 else if (option == LOPT_HOST_INOTIFY)
1872 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001873 }
1874
Simon Kelley849a8352006-06-09 21:02:31 +01001875 break;
1876 }
1877
Simon Kelleyf373a152013-09-23 12:47:47 +01001878
1879#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +00001880 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001881 if (!(comma = split(arg)))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001882 ret_err(gen_err);
1883
Simon Kelley4f7b3042012-11-28 21:27:02 +00001884 daemon->authserver = opt_string_alloc(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001885 arg = comma;
1886 do {
1887 struct iname *new = opt_malloc(sizeof(struct iname));
1888 comma = split(arg);
1889 new->name = NULL;
1890 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001891 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
Simon Kelley429798f2012-12-10 20:45:53 +00001892 new->addr.sa.sa_family = AF_INET;
1893#ifdef HAVE_IPV6
1894 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1895 new->addr.sa.sa_family = AF_INET6;
1896#endif
1897 else
Simon Kelleyf25e6c62013-11-17 12:23:42 +00001898 {
1899 char *fam = split_chr(arg, '/');
1900 new->name = opt_string_alloc(arg);
1901 new->addr.sa.sa_family = 0;
1902 if (fam)
1903 {
1904 if (strcmp(fam, "4") == 0)
1905 new->addr.sa.sa_family = AF_INET;
1906#ifdef HAVE_IPV6
1907 else if (strcmp(fam, "6") == 0)
1908 new->addr.sa.sa_family = AF_INET6;
1909#endif
1910 else
1911 ret_err(gen_err);
1912 }
1913 }
Simon Kelley429798f2012-12-10 20:45:53 +00001914 new->next = daemon->authinterface;
1915 daemon->authinterface = new;
1916
1917 arg = comma;
1918 } while (arg);
1919
Simon Kelley4f7b3042012-11-28 21:27:02 +00001920 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001921
1922 case LOPT_AUTHSFS: /* --auth-sec-servers */
1923 {
1924 struct name_list *new;
1925
1926 do {
1927 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001928 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001929 new->name = opt_string_alloc(arg);
1930 new->next = daemon->secondary_forward_server;
1931 daemon->secondary_forward_server = new;
1932 arg = comma;
1933 } while (arg);
1934 break;
1935 }
1936
Simon Kelley4f7b3042012-11-28 21:27:02 +00001937 case LOPT_AUTHZONE: /* --auth-zone */
1938 {
1939 struct auth_zone *new;
1940
1941 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001942
Simon Kelley429798f2012-12-10 20:45:53 +00001943 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001944 new->domain = opt_string_alloc(arg);
1945 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001946 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00001947 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001948 new->next = daemon->auth_zones;
1949 daemon->auth_zones = new;
1950
1951 while ((arg = comma))
1952 {
1953 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001954 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001955 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00001956 struct addrlist *subnet = NULL;
1957 struct all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001958
1959 comma = split(arg);
1960 prefix = split_chr(arg, '/');
1961
1962 if (prefix && !atoi_check(prefix, &prefixlen))
1963 ret_err(gen_err);
1964
Mathias Kresin094bfae2016-07-24 14:15:22 +01001965 if (strstr(arg, "exclude:") == arg)
1966 {
1967 is_exclude = 1;
1968 arg = arg+8;
1969 }
1970
Simon Kelley376d48c2013-11-13 13:04:30 +00001971 if (inet_pton(AF_INET, arg, &addr.addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001972 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001973 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001974 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001975 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001976 }
1977#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +00001978 else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001979 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001980 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001981 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001982 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001983 }
1984#endif
Simon Kelley376d48c2013-11-13 13:04:30 +00001985 else
1986 {
1987 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
1988 name->name = opt_string_alloc(arg);
1989 name->flags = AUTH4 | AUTH6;
1990 name->next = new->interface_names;
1991 new->interface_names = name;
1992 if (prefix)
1993 {
1994 if (prefixlen == 4)
1995 name->flags &= ~AUTH6;
1996#ifdef HAVE_IPV6
1997 else if (prefixlen == 6)
1998 name->flags &= ~AUTH4;
1999#endif
2000 else
2001 ret_err(gen_err);
2002 }
2003 }
2004
2005 if (subnet)
2006 {
2007 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002008
2009 if (is_exclude)
2010 {
2011 subnet->next = new->exclude;
2012 new->exclude = subnet;
2013 }
2014 else
2015 {
2016 subnet->next = new->subnet;
2017 new->subnet = subnet;
2018 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002019 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002020 }
2021 break;
2022 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002023
Simon Kelley4f7b3042012-11-28 21:27:02 +00002024 case LOPT_AUTHSOA: /* --auth-soa */
2025 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002026 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002027 if (comma)
2028 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002029 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002030 arg = comma;
2031 comma = split(arg);
2032 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002033 for (cp = daemon->hostmaster; *cp; cp++)
2034 if (*cp == '@')
2035 *cp = '.';
2036
Simon Kelley4f7b3042012-11-28 21:27:02 +00002037 if (comma)
2038 {
2039 arg = comma;
2040 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002041 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002042 if (comma)
2043 {
2044 arg = comma;
2045 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002046 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002047 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002048 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002049 }
2050 }
2051 }
2052
2053 break;
Simon Kelleyf373a152013-09-23 12:47:47 +01002054#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +00002055
Simon Kelley2bb73af2013-04-24 17:38:19 +01002056 case 's': /* --domain */
2057 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002058 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002059 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002060 else
Simon Kelley9009d742008-11-14 20:04:27 +00002061 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002062 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002063 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002064 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002065 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002066 else
2067 {
Simon Kelley9009d742008-11-14 20:04:27 +00002068 if (comma)
2069 {
Simon Kelley429798f2012-12-10 20:45:53 +00002070 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002071 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002072
Simon Kelley48fd1c42013-04-25 09:49:38 +01002073 new->prefix = NULL;
2074
Simon Kelley9009d742008-11-14 20:04:27 +00002075 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002076 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002077 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002078 int msize;
2079
Simon Kelley28866e92011-02-14 20:19:14 +00002080 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002081 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002082 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002083 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002084 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002085 int mask = (1 << (32 - msize)) - 1;
2086 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002087 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2088 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002089 if (arg)
2090 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002091 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002092 {
2093 if (!(new->prefix = canonicalise_opt(arg)) ||
2094 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2095 ret_err(_("bad prefix"));
2096 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002097 else if (strcmp(arg, "local") != 0 ||
2098 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002099 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00002100 else
2101 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002102 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002103 local=/xxx.yyy.zzz.in-addr.arpa/ */
2104 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002105 if (!serv)
2106 ret_err(_("bad prefix"));
2107
Simon Kelleyde73a492014-02-17 21:43:27 +00002108 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002109
2110 /* local=/<domain>/ */
2111 serv = opt_malloc(sizeof(struct server));
2112 memset(serv, 0, sizeof(struct server));
2113 serv->domain = d;
2114 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2115 serv->next = daemon->servers;
2116 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002117 }
2118 }
Simon Kelley9009d742008-11-14 20:04:27 +00002119 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002120#ifdef HAVE_IPV6
2121 else if (inet_pton(AF_INET6, comma, &new->start6))
2122 {
2123 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2124 u64 addrpart = addr6part(&new->start6);
2125 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002126
Simon Kelleyd74942a2012-02-07 20:51:56 +00002127 /* prefix==64 overflows the mask calculation above */
2128 if (msize == 64)
2129 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002130
Simon Kelleyd74942a2012-02-07 20:51:56 +00002131 new->end6 = new->start6;
2132 setaddr6part(&new->start6, addrpart & ~mask);
2133 setaddr6part(&new->end6, addrpart | mask);
2134
2135 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002136 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002137 else if (arg)
2138 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002139 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002140 {
2141 if (!(new->prefix = canonicalise_opt(arg)) ||
2142 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
2143 ret_err(_("bad prefix"));
2144 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002145 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002146 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002147 else
2148 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002149 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002150 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002151 struct server *serv = add_rev6(&new->start6, msize);
2152 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002153
2154 /* local=/<domain>/ */
2155 serv = opt_malloc(sizeof(struct server));
2156 memset(serv, 0, sizeof(struct server));
2157 serv->domain = d;
2158 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2159 serv->next = daemon->servers;
2160 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002161 }
2162 }
2163 }
2164#endif
2165 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002166 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002167 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002168 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002169 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002170 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002171 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002172 prefstr = split(arg);
2173
Simon Kelleyd74942a2012-02-07 20:51:56 +00002174 if (inet_pton(AF_INET, comma, &new->start))
2175 {
2176 new->is6 = 0;
2177 if (!arg)
2178 new->end.s_addr = new->start.s_addr;
2179 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002180 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002181 }
2182#ifdef HAVE_IPV6
2183 else if (inet_pton(AF_INET6, comma, &new->start6))
2184 {
2185 new->is6 = 1;
2186 if (!arg)
2187 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2188 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002189 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002190 }
2191#endif
2192 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002193 ret_err(gen_err);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002194
2195 if (option != 's' && prefstr)
2196 {
2197 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2198 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2199 ret_err(_("bad prefix"));
2200 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002201 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002202
2203 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002204 if (option == 's')
2205 {
2206 new->next = daemon->cond_domain;
2207 daemon->cond_domain = new;
2208 }
2209 else
2210 {
2211 new->next = daemon->synth_domains;
2212 daemon->synth_domains = new;
2213 }
Simon Kelley9009d742008-11-14 20:04:27 +00002214 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002215 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002216 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002217 else
2218 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002219 }
2220 }
Simon Kelley849a8352006-06-09 21:02:31 +01002221 break;
2222
Simon Kelley1e505122016-01-25 21:29:23 +00002223 case LOPT_CPE_ID: /* --add-dns-client */
2224 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002225 daemon->dns_client_id = opt_string_alloc(arg);
2226 break;
2227
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002228 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002229 if (!arg)
2230 set_option_bool(OPT_ADD_MAC);
2231 else
2232 {
2233 unhide_metas(arg);
2234 if (strcmp(arg, "base64") == 0)
2235 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002236 else if (strcmp(arg, "text") == 0)
2237 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002238 else
2239 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002240 }
2241 break;
2242
Simon Kelleyf2621c72007-04-29 19:47:21 +01002243 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002244 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002245 break;
2246
Simon Kelleyf2621c72007-04-29 19:47:21 +01002247 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002248 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002249 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002250 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002251
Simon Kelley7622fc02009-06-04 20:32:05 +01002252#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002253 case LOPT_SCRIPTUSR: /* --scriptuser */
2254 daemon->scriptuser = opt_string_alloc(arg);
2255 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002256#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002257
Simon Kelleyf2621c72007-04-29 19:47:21 +01002258 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002259 do {
Simon Kelley824af852008-02-12 20:43:05 +00002260 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002261 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002262 new->next = daemon->if_names;
2263 daemon->if_names = new;
2264 /* new->name may be NULL if someone does
2265 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002266 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002267 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002268 arg = comma;
2269 } while (arg);
2270 break;
2271
Simon Kelley2937f8a2013-07-29 19:49:07 +01002272 case LOPT_TFTP: /* --enable-tftp */
2273 set_option_bool(OPT_TFTP);
2274 if (!arg)
2275 break;
2276 /* fall through */
2277
Simon Kelleyf2621c72007-04-29 19:47:21 +01002278 case 'I': /* --except-interface */
2279 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002280 do {
Simon Kelley824af852008-02-12 20:43:05 +00002281 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002282 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002283 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002284 if (option == 'I')
2285 {
2286 new->next = daemon->if_except;
2287 daemon->if_except = new;
2288 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002289 else if (option == LOPT_TFTP)
2290 {
2291 new->next = daemon->tftp_interfaces;
2292 daemon->tftp_interfaces = new;
2293 }
Simon Kelley849a8352006-06-09 21:02:31 +01002294 else
2295 {
2296 new->next = daemon->dhcp_except;
2297 daemon->dhcp_except = new;
2298 }
2299 arg = comma;
2300 } while (arg);
2301 break;
2302
Simon Kelleyf2621c72007-04-29 19:47:21 +01002303 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002304 case LOPT_IGNORE_ADDR: /* --ignore-address */
2305 {
Simon Kelley849a8352006-06-09 21:02:31 +01002306 struct in_addr addr;
2307 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002308 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002309 {
Simon Kelley824af852008-02-12 20:43:05 +00002310 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002311 if (option == 'B')
2312 {
2313 baddr->next = daemon->bogus_addr;
2314 daemon->bogus_addr = baddr;
2315 }
2316 else
2317 {
2318 baddr->next = daemon->ignore_addr;
2319 daemon->ignore_addr = baddr;
2320 }
Simon Kelley849a8352006-06-09 21:02:31 +01002321 baddr->addr = addr;
2322 }
2323 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002324 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002325 break;
2326 }
2327
Simon Kelleyf2621c72007-04-29 19:47:21 +01002328 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002329 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002330 do {
Simon Kelley824af852008-02-12 20:43:05 +00002331 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002332 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002333 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002334 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002335 {
2336 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002337 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002338#ifdef HAVE_SOCKADDR_SA_LEN
2339 new->addr.in.sin_len = sizeof(new->addr.in);
2340#endif
2341 }
2342#ifdef HAVE_IPV6
2343 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2344 {
2345 new->addr.sa.sa_family = AF_INET6;
2346 new->addr.in6.sin6_flowinfo = 0;
2347 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002348 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002349#ifdef HAVE_SOCKADDR_SA_LEN
2350 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2351#endif
2352 }
2353#endif
2354 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002355 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002356
2357 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002358 if (option == 'a')
2359 {
2360 new->next = daemon->if_addrs;
2361 daemon->if_addrs = new;
2362 }
2363 else
2364 {
2365 new->next = daemon->auth_peers;
2366 daemon->auth_peers = new;
2367 }
Simon Kelley849a8352006-06-09 21:02:31 +01002368 arg = comma;
2369 } while (arg);
2370 break;
2371
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002372 case 'S': /* --server */
2373 case LOPT_LOCAL: /* --local */
2374 case 'A': /* --address */
2375 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002376 {
2377 struct server *serv, *newlist = NULL;
2378
2379 unhide_metas(arg);
2380
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002381 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002382 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002383 int rebind = !(*arg == '/');
2384 char *end = NULL;
2385 if (!rebind)
2386 arg++;
2387 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002388 {
2389 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002390 /* elide leading dots - they are implied in the search algorithm */
2391 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002392 /* # matches everything and becomes a zero length domain string */
2393 if (strcmp(arg, "#") == 0)
2394 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002395 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002396 option = '?';
Simon Kelley824af852008-02-12 20:43:05 +00002397 serv = opt_malloc(sizeof(struct server));
2398 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002399 serv->next = newlist;
2400 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002401 serv->domain = domain;
2402 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002403 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002404 if (rebind)
2405 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002406 }
2407 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002408 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002409 }
2410 else
2411 {
Simon Kelley824af852008-02-12 20:43:05 +00002412 newlist = opt_malloc(sizeof(struct server));
2413 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002414#ifdef HAVE_LOOP
2415 newlist->uid = rand32();
2416#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002417 }
2418
Simon Kelley7b1eae42014-02-20 13:43:28 +00002419 if (servers_only && option == 'S')
2420 newlist->flags |= SERV_FROM_FILE;
2421
Simon Kelley849a8352006-06-09 21:02:31 +01002422 if (option == 'A')
2423 {
2424 newlist->flags |= SERV_LITERAL_ADDRESS;
2425 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002426 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002427 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002428 else if (option == LOPT_NO_REBIND)
2429 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002430
2431 if (!arg || !*arg)
2432 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002433 if (!(newlist->flags & SERV_NO_REBIND))
2434 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002435 }
2436
2437 else if (strcmp(arg, "#") == 0)
2438 {
2439 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002440 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002441 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002442 }
2443 else
2444 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002445 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2446 if (err)
2447 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002448 }
2449
Simon Kelleyf2621c72007-04-29 19:47:21 +01002450 serv = newlist;
2451 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002452 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002453 serv->next->flags = serv->flags;
2454 serv->next->addr = serv->addr;
2455 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002456 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002457 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002458 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002459 serv->next = daemon->servers;
2460 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002461 break;
2462 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002463
Simon Kelleyde73a492014-02-17 21:43:27 +00002464 case LOPT_REV_SERV: /* --rev-server */
2465 {
2466 char *string;
2467 int size;
2468 struct server *serv;
2469 struct in_addr addr4;
2470#ifdef HAVE_IPV6
2471 struct in6_addr addr6;
2472#endif
2473
2474 unhide_metas(arg);
2475 if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2476 ret_err(gen_err);
2477
2478 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002479 {
2480 serv = add_rev4(addr4, size);
2481 if (!serv)
2482 ret_err(_("bad prefix"));
2483 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002484#ifdef HAVE_IPV6
2485 else if (inet_pton(AF_INET6, arg, &addr6))
2486 serv = add_rev6(&addr6, size);
2487#endif
2488 else
2489 ret_err(gen_err);
2490
2491 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2492
2493 if (string)
2494 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002495
2496 if (servers_only)
2497 serv->flags |= SERV_FROM_FILE;
2498
Simon Kelleyde73a492014-02-17 21:43:27 +00002499 break;
2500 }
2501
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002502 case LOPT_IPSET: /* --ipset */
2503#ifndef HAVE_IPSET
2504 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2505 break;
2506#else
2507 {
2508 struct ipsets ipsets_head;
2509 struct ipsets *ipsets = &ipsets_head;
2510 int size;
2511 char *end;
2512 char **sets, **sets_pos;
2513 memset(ipsets, 0, sizeof(struct ipsets));
2514 unhide_metas(arg);
2515 if (arg && *arg == '/')
2516 {
2517 arg++;
2518 while ((end = split_chr(arg, '/')))
2519 {
2520 char *domain = NULL;
2521 /* elide leading dots - they are implied in the search algorithm */
2522 while (*arg == '.')
2523 arg++;
2524 /* # matches everything and becomes a zero length domain string */
2525 if (strcmp(arg, "#") == 0 || !*arg)
2526 domain = "";
2527 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
2528 option = '?';
2529 ipsets->next = opt_malloc(sizeof(struct ipsets));
2530 ipsets = ipsets->next;
2531 memset(ipsets, 0, sizeof(struct ipsets));
2532 ipsets->domain = domain;
2533 arg = end;
2534 }
2535 }
2536 else
2537 {
2538 ipsets->next = opt_malloc(sizeof(struct ipsets));
2539 ipsets = ipsets->next;
2540 memset(ipsets, 0, sizeof(struct ipsets));
2541 ipsets->domain = "";
2542 }
2543 if (!arg || !*arg)
2544 {
2545 option = '?';
2546 break;
2547 }
2548 size = 2;
2549 for (end = arg; *end; ++end)
2550 if (*end == ',')
2551 ++size;
2552
2553 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2554
2555 do {
2556 end = split(arg);
2557 *sets_pos++ = opt_string_alloc(arg);
2558 arg = end;
2559 } while (end);
2560 *sets_pos = 0;
2561 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2562 ipsets->next->sets = sets;
2563 ipsets->next = daemon->ipsets;
2564 daemon->ipsets = ipsets_head.next;
2565
2566 break;
2567 }
2568#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002569
Simon Kelleyf2621c72007-04-29 19:47:21 +01002570 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002571 {
2572 int size;
2573
2574 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002575 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002576 else
2577 {
2578 /* zero is OK, and means no caching. */
2579
2580 if (size < 0)
2581 size = 0;
2582 else if (size > 10000)
2583 size = 10000;
2584
2585 daemon->cachesize = size;
2586 }
2587 break;
2588 }
2589
Simon Kelleyf2621c72007-04-29 19:47:21 +01002590 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002591 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002592 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002593 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002594
Simon Kelley1a6bca82008-07-11 11:11:42 +01002595 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002596 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002597 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002598 break;
2599
Hans Dedecker926332a2016-01-23 10:48:12 +00002600 case LOPT_MAXPORT: /* --max-port */
2601 if (!atoi_check16(arg, &daemon->max_port))
2602 ret_err(gen_err);
2603 break;
2604
Simon Kelleyf2621c72007-04-29 19:47:21 +01002605 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002606 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002607 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002608 break;
2609
Simon Kelley25cf5e32015-01-09 15:53:03 +00002610 case 'q': /* --log-queries */
2611 set_option_bool(OPT_LOG);
2612 if (arg && strcmp(arg, "extra") == 0)
2613 set_option_bool(OPT_EXTRALOG);
2614 break;
2615
Simon Kelleyf2621c72007-04-29 19:47:21 +01002616 case LOPT_MAX_LOGS: /* --log-async */
2617 daemon->max_logs = LOG_MAX; /* default */
2618 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002619 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002620 else if (daemon->max_logs > 100)
2621 daemon->max_logs = 100;
2622 break;
2623
2624 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002625 {
2626 int i;
2627 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002628 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002629 daemon->edns_pktsz = (unsigned short)i;
2630 break;
2631 }
2632
Simon Kelleyf2621c72007-04-29 19:47:21 +01002633 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002634 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002635 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002636 /* if explicitly set to zero, use single OS ephemeral port
2637 and disable random ports */
2638 if (daemon->query_port == 0)
2639 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002640 break;
2641
Simon Kelley824af852008-02-12 20:43:05 +00002642 case 'T': /* --local-ttl */
2643 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002644 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002645 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002646 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002647 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002648 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002649 {
2650 int ttl;
2651 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002652 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002653 else if (option == LOPT_NEGTTL)
2654 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002655 else if (option == LOPT_MAXTTL)
2656 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002657 else if (option == LOPT_MINCTTL)
2658 {
2659 if (ttl > TTL_FLOOR_LIMIT)
2660 ttl = TTL_FLOOR_LIMIT;
2661 daemon->min_cache_ttl = (unsigned long)ttl;
2662 }
Simon Kelley1d860412012-09-20 20:48:04 +01002663 else if (option == LOPT_MAXCTTL)
2664 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002665 else if (option == LOPT_AUTHTTL)
2666 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002667 else if (option == LOPT_DHCPTTL)
2668 {
2669 daemon->dhcp_ttl = (unsigned long)ttl;
2670 daemon->use_dhcp_ttl = 1;
2671 }
Simon Kelley849a8352006-06-09 21:02:31 +01002672 else
2673 daemon->local_ttl = (unsigned long)ttl;
2674 break;
2675 }
2676
Simon Kelley7622fc02009-06-04 20:32:05 +01002677#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002678 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002679 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002680 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002681 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002682#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002683
Simon Kelley7622fc02009-06-04 20:32:05 +01002684#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002685 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002686 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002687 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002688 break;
2689
Simon Kelleybec366b2016-02-24 22:03:26 +00002690 case LOPT_TFTP_MTU: /* --tftp-mtu */
2691 if (!atoi_check(arg, &daemon->tftp_mtu))
2692 ret_err(gen_err);
2693 break;
2694
Simon Kelley824af852008-02-12 20:43:05 +00002695 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002696 comma = split(arg);
2697 if (comma)
2698 {
2699 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2700 new->interface = opt_string_alloc(comma);
2701 new->prefix = opt_string_alloc(arg);
2702 new->next = daemon->if_prefix;
2703 daemon->if_prefix = new;
2704 }
2705 else
2706 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002707 break;
2708
Simon Kelley824af852008-02-12 20:43:05 +00002709 case LOPT_TFTPPORTS: /* --tftp-port-range */
2710 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002711 !atoi_check16(arg, &daemon->start_tftp_port) ||
2712 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002713 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002714
2715 if (daemon->start_tftp_port > daemon->end_tftp_port)
2716 {
2717 int tmp = daemon->start_tftp_port;
2718 daemon->start_tftp_port = daemon->end_tftp_port;
2719 daemon->end_tftp_port = tmp;
2720 }
2721
2722 break;
Floris Bos60704f52017-04-09 22:22:49 +01002723
2724 case LOPT_APREF: /* --tftp-unique-root */
2725 if (!arg || strcasecmp(arg, "ip") == 0)
2726 set_option_bool(OPT_TFTP_APREF_IP);
2727 else if (strcasecmp(arg, "mac") == 0)
2728 set_option_bool(OPT_TFTP_APREF_MAC);
2729 else
2730 ret_err(gen_err);
2731 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002732#endif
Simon Kelley824af852008-02-12 20:43:05 +00002733
Simon Kelleyf2621c72007-04-29 19:47:21 +01002734 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002735 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002736 struct dhcp_bridge *new;
2737
Simon Kelley316e2732010-01-22 20:16:09 +00002738 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002739 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002740
Simon Kelley22cd8602018-01-14 22:57:14 +00002741 for (new = daemon->bridges; new; new = new->next)
2742 if (strcmp(new->iface, arg) == 0)
2743 break;
2744
2745 if (!new)
2746 {
2747 new = opt_malloc(sizeof(struct dhcp_bridge));
2748 strcpy(new->iface, arg);
2749 new->alias = NULL;
2750 new->next = daemon->bridges;
2751 daemon->bridges = new;
2752 }
2753
Simon Kelley832af0b2007-01-21 20:01:28 +00002754 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002755 arg = comma;
2756 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002757 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002758 {
Simon Kelley824af852008-02-12 20:43:05 +00002759 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002760 b->next = new->alias;
2761 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002762 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002763 }
2764 } while (comma);
2765
2766 break;
2767 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002768
Simon Kelley7622fc02009-06-04 20:32:05 +01002769#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002770 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002771 {
2772 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002773 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002774 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002775
Simon Kelley52b92f42012-01-22 16:05:15 +00002776 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002777 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002778
Simon Kelley849a8352006-06-09 21:02:31 +01002779 if (!arg)
2780 {
2781 option = '?';
2782 break;
2783 }
2784
2785 while(1)
2786 {
2787 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002788 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2789 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2790 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002791 break;
2792
Simon Kelleyf2621c72007-04-29 19:47:21 +01002793 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002794 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002795 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002796 {
Simon Kelley824af852008-02-12 20:43:05 +00002797 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2798 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002799 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002800 /* ignore empty tag */
2801 if (tt->net)
2802 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002803 }
2804 else
2805 {
2806 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002807 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002808 else if (strstr(arg, "set:") == arg)
2809 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002810 else
Simon Kelley824af852008-02-12 20:43:05 +00002811 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002812 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002813 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002814 }
2815 else
2816 {
2817 a[0] = arg;
2818 break;
2819 }
2820 }
2821
Simon Kelley1f776932012-12-16 19:46:08 +00002822 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002823 if (!(a[k] = split(a[k-1])))
2824 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002825
Simon Kelley52b92f42012-01-22 16:05:15 +00002826 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002827 ret_err(_("bad dhcp-range"));
2828
2829 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002830 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002831 new->next = daemon->dhcp;
2832 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002833 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002834 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002835 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002836 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002837 new->flags |= CONTEXT_PROXY;
2838 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002839 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002840
2841 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2842 {
2843 struct in_addr tmp = new->start;
2844 new->start = new->end;
2845 new->end = tmp;
2846 }
2847
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002848 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002849 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002850 {
2851 new->flags |= CONTEXT_NETMASK;
2852 leasepos = 3;
2853 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002854 ret_err(_("inconsistent DHCP range"));
Simon Kelleyfa794662016-03-03 20:33:54 +00002855
Simon Kelley52b92f42012-01-22 16:05:15 +00002856
Simon Kelleyfa794662016-03-03 20:33:54 +00002857 if (k >= 4 && strchr(a[3], '.') &&
2858 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
2859 {
2860 new->flags |= CONTEXT_BRDCAST;
2861 leasepos = 4;
2862 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002863 }
Simon Kelley849a8352006-06-09 21:02:31 +01002864 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002865#ifdef HAVE_DHCP6
2866 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002867 {
Simon Kelley89500e32013-09-20 16:29:20 +01002868 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002869 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002870 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002871 new->next = daemon->dhcp6;
2872 daemon->dhcp6 = new;
2873
Simon Kelley30cd9662012-03-25 20:44:38 +01002874 for (leasepos = 1; leasepos < k; leasepos++)
2875 {
2876 if (strcmp(a[leasepos], "static") == 0)
2877 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2878 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002879 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002880 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002881 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002882 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
2883 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002884 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002885 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01002886 else if (strcmp(a[leasepos], "off-link") == 0)
2887 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01002888 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2889 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002890 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2891 {
2892 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2893 new->flags |= CONTEXT_TEMPLATE;
2894 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002895 else
2896 break;
2897 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002898
Simon Kelley52b92f42012-01-22 16:05:15 +00002899 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002900 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002901 {
2902 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002903 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002904 if (!(*cp >= '0' && *cp <= '9'))
2905 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002906 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002907 {
2908 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002909 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002910 }
2911 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002912
Simon Kelley6692a1a2013-08-20 14:41:31 +01002913 if (new->prefix != 64)
2914 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002915 if (new->flags & CONTEXT_RA)
Simon Kelley6692a1a2013-08-20 14:41:31 +01002916 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2917 else if (new->flags & CONTEXT_TEMPLATE)
2918 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2919 }
2920
2921 if (new->prefix < 64)
2922 ret_err(_("prefix length must be at least 64"));
2923
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002924 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2925 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002926
2927 /* dhcp-range=:: enables DHCP stateless on any interface */
2928 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2929 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002930
2931 if (new->flags & CONTEXT_TEMPLATE)
2932 {
2933 struct in6_addr zero;
2934 memset(&zero, 0, sizeof(zero));
2935 if (!is_same_net6(&zero, &new->start6, new->prefix))
2936 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2937 }
2938
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002939 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002940 {
2941 struct in6_addr tmp = new->start6;
2942 new->start6 = new->end6;
2943 new->end6 = tmp;
2944 }
Simon Kelley849a8352006-06-09 21:02:31 +01002945 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002946#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002947 else
2948 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002949
Simon Kelley30cd9662012-03-25 20:44:38 +01002950 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002951 {
Simon Kelleyfa794662016-03-03 20:33:54 +00002952 if (leasepos != k-1)
2953 ret_err(_("bad dhcp-range"));
2954
Simon Kelley849a8352006-06-09 21:02:31 +01002955 if (strcmp(a[leasepos], "infinite") == 0)
2956 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002957 else if (strcmp(a[leasepos], "deprecated") == 0)
2958 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002959 else
2960 {
2961 int fac = 1;
2962 if (strlen(a[leasepos]) > 0)
2963 {
2964 switch (a[leasepos][strlen(a[leasepos]) - 1])
2965 {
Simon Kelley42243212012-07-20 15:19:18 +01002966 case 'w':
2967 case 'W':
2968 fac *= 7;
2969 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002970 case 'd':
2971 case 'D':
2972 fac *= 24;
2973 /* fall though */
2974 case 'h':
2975 case 'H':
2976 fac *= 60;
2977 /* fall through */
2978 case 'm':
2979 case 'M':
2980 fac *= 60;
2981 /* fall through */
2982 case 's':
2983 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002984 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002985 }
2986
Simon Kelleybe379862012-12-23 12:01:39 +00002987 for (cp = a[leasepos]; *cp; cp++)
2988 if (!(*cp >= '0' && *cp <= '9'))
2989 break;
2990
Simon Kelley54dae552013-02-05 17:55:10 +00002991 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00002992 ret_err(_("bad dhcp-range"));
2993
Simon Kelley849a8352006-06-09 21:02:31 +01002994 new->lease_time = atoi(a[leasepos]) * fac;
2995 /* Leases of a minute or less confuse
2996 some clients, notably Apple's */
2997 if (new->lease_time < 120)
2998 new->lease_time = 120;
2999 }
3000 }
3001 }
3002 break;
3003 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003004
Simon Kelley5aabfc72007-08-29 11:24:47 +01003005 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003006 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003007 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003008 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01003009 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01003010 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003011 struct in_addr in;
3012
Simon Kelley824af852008-02-12 20:43:05 +00003013 new = opt_malloc(sizeof(struct dhcp_config));
3014
Simon Kelley849a8352006-06-09 21:02:31 +01003015 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003016 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3017 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003018 new->netid = NULL;
3019
Simon Kelley849a8352006-06-09 21:02:31 +01003020 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01003021 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003022 if (!(a[k] = split(a[k-1])))
3023 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003024
3025 for (j = 0; j < k; j++)
3026 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
3027 {
3028 char *arg = a[j];
3029
3030 if ((arg[0] == 'i' || arg[0] == 'I') &&
3031 (arg[1] == 'd' || arg[1] == 'D') &&
3032 arg[2] == ':')
3033 {
3034 if (arg[3] == '*')
3035 new->flags |= CONFIG_NOCLID;
3036 else
3037 {
3038 int len;
3039 arg += 3; /* dump id: */
3040 if (strchr(arg, ':'))
3041 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3042 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01003043 {
3044 unhide_metas(arg);
3045 len = (int) strlen(arg);
3046 }
3047
Simon Kelley28866e92011-02-14 20:19:14 +00003048 if (len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003049 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003050 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003051 {
3052 new->flags |= CONFIG_CLID;
3053 new->clid_len = len;
3054 memcpy(new->clid, arg, len);
3055 }
Simon Kelley849a8352006-06-09 21:02:31 +01003056 }
3057 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003058 /* dhcp-host has strange backwards-compat needs. */
3059 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01003060 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003061 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3062 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3063 newtag->net = opt_malloc(strlen(arg + 4) + 1);
3064 newlist->next = new->netid;
3065 new->netid = newlist;
3066 newlist->list = newtag;
3067 strcpy(newtag->net, arg+4);
3068 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01003069 }
Simon Kelley7de060b2011-08-26 17:24:52 +01003070 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003071 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00003072#ifdef HAVE_DHCP6
3073 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3074 {
3075 arg[strlen(arg)-1] = 0;
3076 arg++;
3077
3078 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003079 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00003080
3081 for (i= 0; i < 8; i++)
3082 if (new->addr6.s6_addr[i] != 0)
3083 break;
3084
3085 /* set WILDCARD if network part all zeros */
3086 if (i == 8)
3087 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00003088
3089 new->flags |= CONFIG_ADDR6;
3090 }
3091#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01003092 else
Simon Kelley849a8352006-06-09 21:02:31 +01003093 {
Simon Kelley9009d742008-11-14 20:04:27 +00003094 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00003095 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
3096 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003097 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003098 else
3099 {
3100
3101 newhw->next = new->hwaddr;
3102 new->hwaddr = newhw;
3103 }
Simon Kelley849a8352006-06-09 21:02:31 +01003104 }
3105 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003106 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01003107 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003108 struct dhcp_config *configs;
3109
Simon Kelley849a8352006-06-09 21:02:31 +01003110 new->addr = in;
3111 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003112
3113 /* If the same IP appears in more than one host config, then DISCOVER
3114 for one of the hosts will get the address, but REQUEST will be NAKed,
3115 since the address is reserved by the other one -> protocol loop. */
3116 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3117 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
3118 {
3119 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3120 return 0;
3121 }
Simon Kelley849a8352006-06-09 21:02:31 +01003122 }
3123 else
3124 {
3125 char *cp, *lastp = NULL, last = 0;
Simon Kelley76ff4402013-12-17 16:29:14 +00003126 int fac = 1, isdig = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003127
3128 if (strlen(a[j]) > 1)
3129 {
3130 lastp = a[j] + strlen(a[j]) - 1;
3131 last = *lastp;
3132 switch (last)
3133 {
Simon Kelley42243212012-07-20 15:19:18 +01003134 case 'w':
3135 case 'W':
3136 fac *= 7;
3137 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003138 case 'd':
3139 case 'D':
3140 fac *= 24;
3141 /* fall through */
3142 case 'h':
3143 case 'H':
3144 fac *= 60;
3145 /* fall through */
3146 case 'm':
3147 case 'M':
3148 fac *= 60;
3149 /* fall through */
3150 case 's':
3151 case 'S':
3152 *lastp = 0;
3153 }
3154 }
3155
3156 for (cp = a[j]; *cp; cp++)
Simon Kelley76ff4402013-12-17 16:29:14 +00003157 if (isdigit((unsigned char)*cp))
3158 isdig = 1;
3159 else if (*cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01003160 break;
Simon Kelley76ff4402013-12-17 16:29:14 +00003161
Simon Kelley849a8352006-06-09 21:02:31 +01003162 if (*cp)
3163 {
3164 if (lastp)
3165 *lastp = last;
3166 if (strcmp(a[j], "infinite") == 0)
3167 {
3168 new->lease_time = 0xffffffff;
3169 new->flags |= CONFIG_TIME;
3170 }
3171 else if (strcmp(a[j], "ignore") == 0)
3172 new->flags |= CONFIG_DISABLE;
3173 else
3174 {
Simon Kelley1f15b812009-10-13 17:49:32 +01003175 if (!(new->hostname = canonicalise_opt(a[j])) ||
3176 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003177 ret_err(_("bad DHCP host name"));
3178
3179 new->flags |= CONFIG_NAME;
3180 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01003181 }
3182 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003183 else if (isdig)
Simon Kelley849a8352006-06-09 21:02:31 +01003184 {
3185 new->lease_time = atoi(a[j]) * fac;
3186 /* Leases of a minute or less confuse
3187 some clients, notably Apple's */
3188 if (new->lease_time < 120)
3189 new->lease_time = 120;
3190 new->flags |= CONFIG_TIME;
3191 }
3192 }
3193
Simon Kelley5aabfc72007-08-29 11:24:47 +01003194 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003195 break;
3196 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003197
3198 case LOPT_TAG_IF: /* --tag-if */
3199 {
3200 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3201
3202 new->tag = NULL;
3203 new->set = NULL;
3204 new->next = NULL;
3205
3206 /* preserve order */
3207 if (!daemon->tag_if)
3208 daemon->tag_if = new;
3209 else
3210 {
3211 struct tag_if *tmp;
3212 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3213 tmp->next = new;
3214 }
3215
3216 while (arg)
3217 {
3218 size_t len;
3219
3220 comma = split(arg);
3221 len = strlen(arg);
3222
3223 if (len < 5)
3224 {
3225 new->set = NULL;
3226 break;
3227 }
3228 else
3229 {
3230 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3231 newtag->net = opt_malloc(len - 3);
3232 strcpy(newtag->net, arg+4);
3233 unhide_metas(newtag->net);
3234
3235 if (strstr(arg, "set:") == arg)
3236 {
3237 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3238 newlist->next = new->set;
3239 new->set = newlist;
3240 newlist->list = newtag;
3241 }
3242 else if (strstr(arg, "tag:") == arg)
3243 {
3244 newtag->next = new->tag;
3245 new->tag = newtag;
3246 }
3247 else
3248 {
3249 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00003250 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003251 break;
3252 }
3253 }
3254
3255 arg = comma;
3256 }
3257
3258 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003259 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003260
3261 break;
3262 }
3263
Simon Kelley849a8352006-06-09 21:02:31 +01003264
Simon Kelley73a08a22009-02-05 20:28:08 +00003265 case 'O': /* --dhcp-option */
3266 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003267 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003268 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003269 return parse_dhcp_opt(errstr, arg,
3270 option == LOPT_FORCE ? DHOPT_FORCE :
3271 (option == LOPT_MATCH ? DHOPT_MATCH :
3272 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
3273
Simon Kelleyf2621c72007-04-29 19:47:21 +01003274 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003275 {
3276 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003277 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003278 {
Simon Kelley824af852008-02-12 20:43:05 +00003279 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01003280 newid->next = id;
3281 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003282 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003283 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01003284 arg = comma;
3285 };
3286
3287 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003288 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003289 else
3290 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003291 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003292 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003293 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003294 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003295 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003296 dhcp_next_server.s_addr = 0;
3297 if (comma)
3298 {
3299 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003300 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003301 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003302 if (comma)
3303 {
3304 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003305 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3306 {
3307 /*
3308 * The user may have specified the tftp hostname here.
3309 * save it so that it can be resolved/looked up during
3310 * actual dhcp_reply().
3311 */
3312
3313 tftp_sname = opt_string_alloc(comma);
3314 dhcp_next_server.s_addr = 0;
3315 }
Simon Kelley849a8352006-06-09 21:02:31 +01003316 }
3317 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003318
3319 new = opt_malloc(sizeof(struct dhcp_boot));
3320 new->file = dhcp_file;
3321 new->sname = dhcp_sname;
3322 new->tftp_sname = tftp_sname;
3323 new->next_server = dhcp_next_server;
3324 new->netid = id;
3325 new->next = daemon->boot_config;
3326 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003327 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003328
Simon Kelley849a8352006-06-09 21:02:31 +01003329 break;
3330 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003331
Floris Bos503c6092017-04-09 23:07:13 +01003332 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3333 {
3334 struct dhcp_netid *id = NULL;
3335 while (is_tag_prefix(arg))
3336 {
3337 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
3338 newid->next = id;
3339 id = newid;
3340 comma = split(arg);
3341 newid->net = opt_string_alloc(arg+4);
3342 arg = comma;
3343 };
3344
3345 if (!arg)
3346 ret_err(gen_err);
3347 else
3348 {
3349 struct delay_config *new;
3350 int delay;
3351 if (!atoi_check(arg, &delay))
3352 ret_err(gen_err);
3353
3354 new = opt_malloc(sizeof(struct delay_config));
3355 new->delay = delay;
3356 new->netid = id;
3357 new->next = daemon->delay_conf;
3358 daemon->delay_conf = new;
3359 }
3360
3361 break;
3362 }
3363
Simon Kelley7622fc02009-06-04 20:32:05 +01003364 case LOPT_PXE_PROMT: /* --pxe-prompt */
3365 {
3366 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3367 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003368
Simon Kelley7622fc02009-06-04 20:32:05 +01003369 new->netid = NULL;
3370 new->opt = 10; /* PXE_MENU_PROMPT */
3371
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003372 while (is_tag_prefix(arg))
3373 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003374 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3375 comma = split(arg);
3376 nn->next = new->netid;
3377 new->netid = nn;
3378 nn->net = opt_string_alloc(arg+4);
3379 arg = comma;
3380 }
3381
3382 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003383 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003384 else
3385 {
3386 comma = split(arg);
3387 unhide_metas(arg);
3388 new->len = strlen(arg) + 1;
3389 new->val = opt_malloc(new->len);
3390 memcpy(new->val + 1, arg, new->len - 1);
3391
3392 new->u.vendor_class = (unsigned char *)"PXEClient";
3393 new->flags = DHOPT_VENDOR;
3394
3395 if (comma && atoi_check(comma, &timeout))
3396 *(new->val) = timeout;
3397 else
3398 *(new->val) = 255;
3399
3400 new->next = daemon->dhcp_opts;
3401 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003402 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003403 }
3404
3405 break;
3406 }
3407
3408 case LOPT_PXE_SERV: /* --pxe-service */
3409 {
3410 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3411 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003412 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3413 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003414 static int boottype = 32768;
3415
3416 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003417 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003418 new->server.s_addr = 0;
3419
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003420 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01003421 {
3422 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3423 comma = split(arg);
3424 nn->next = new->netid;
3425 new->netid = nn;
3426 nn->net = opt_string_alloc(arg+4);
3427 arg = comma;
3428 }
3429
3430 if (arg && (comma = split(arg)))
3431 {
3432 for (i = 0; CSA[i]; i++)
3433 if (strcasecmp(CSA[i], arg) == 0)
3434 break;
3435
3436 if (CSA[i] || atoi_check(arg, &i))
3437 {
3438 arg = comma;
3439 comma = split(arg);
3440
3441 new->CSA = i;
3442 new->menu = opt_string_alloc(arg);
3443
Simon Kelley316e2732010-01-22 20:16:09 +00003444 if (!comma)
3445 {
3446 new->type = 0; /* local boot */
3447 new->basename = NULL;
3448 }
3449 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003450 {
3451 arg = comma;
3452 comma = split(arg);
3453 if (atoi_check(arg, &i))
3454 {
3455 new->type = i;
3456 new->basename = NULL;
3457 }
3458 else
3459 {
3460 new->type = boottype++;
3461 new->basename = opt_string_alloc(arg);
3462 }
3463
Simon Kelley751d6f42012-02-10 15:24:51 +00003464 if (comma)
3465 {
3466 if (!inet_pton(AF_INET, comma, &new->server))
3467 {
3468 new->server.s_addr = 0;
3469 new->sname = opt_string_alloc(comma);
3470 }
3471
3472 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003473 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003474
Simon Kelley316e2732010-01-22 20:16:09 +00003475 /* Order matters */
3476 new->next = NULL;
3477 if (!daemon->pxe_services)
3478 daemon->pxe_services = new;
3479 else
3480 {
3481 struct pxe_service *s;
3482 for (s = daemon->pxe_services; s->next; s = s->next);
3483 s->next = new;
3484 }
3485
3486 daemon->enable_pxe = 1;
3487 break;
3488
Simon Kelley7622fc02009-06-04 20:32:05 +01003489 }
3490 }
3491
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003492 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003493 }
3494
Simon Kelleyf2621c72007-04-29 19:47:21 +01003495 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003496 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003497 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003498 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003499 else
3500 {
Simon Kelley824af852008-02-12 20:43:05 +00003501 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003502 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003503 unhide_metas(comma);
3504 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003505 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003506 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003507 else
3508 {
3509 new->next = daemon->dhcp_macs;
3510 daemon->dhcp_macs = new;
3511 }
Simon Kelley849a8352006-06-09 21:02:31 +01003512 }
3513 }
3514 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003515
3516#ifdef OPTION6_PREFIX_CLASS
3517 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3518 {
3519 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3520
3521 if (!(comma = split(arg)) ||
3522 !atoi_check16(comma, &new->class))
3523 ret_err(gen_err);
3524
3525 new->tag.net = opt_string_alloc(set_prefix(arg));
3526 new->next = daemon->prefix_classes;
3527 daemon->prefix_classes = new;
3528
3529 break;
3530 }
3531#endif
3532
3533
Simon Kelleyf2621c72007-04-29 19:47:21 +01003534 case 'U': /* --dhcp-vendorclass */
3535 case 'j': /* --dhcp-userclass */
3536 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3537 case LOPT_REMOTE: /* --dhcp-remoteid */
3538 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003539 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003540 unsigned char *p;
3541 int dig = 0;
3542 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3543
3544 if (!(comma = split(arg)))
3545 ret_err(gen_err);
3546
3547 new->netid.net = opt_string_alloc(set_prefix(arg));
3548 /* check for hex string - must digits may include : must not have nothing else,
3549 only allowed for agent-options. */
3550
3551 arg = comma;
3552 if ((comma = split(arg)))
3553 {
3554 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3555 ret_err(gen_err);
3556 else
3557 new->enterprise = atoi(arg+11);
3558 }
3559 else
3560 comma = arg;
3561
3562 for (p = (unsigned char *)comma; *p; p++)
3563 if (isxdigit(*p))
3564 dig = 1;
3565 else if (*p != ':')
3566 break;
3567 unhide_metas(comma);
3568 if (option == 'U' || option == 'j' || *p || !dig)
3569 {
3570 new->len = strlen(comma);
3571 new->data = opt_malloc(new->len);
3572 memcpy(new->data, comma, new->len);
3573 }
3574 else
3575 {
3576 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3577 new->data = opt_malloc(new->len);
3578 memcpy(new->data, comma, new->len);
3579 }
3580
3581 switch (option)
3582 {
3583 case 'j':
3584 new->match_type = MATCH_USER;
3585 break;
3586 case 'U':
3587 new->match_type = MATCH_VENDOR;
3588 break;
3589 case LOPT_CIRCUIT:
3590 new->match_type = MATCH_CIRCUIT;
3591 break;
3592 case LOPT_REMOTE:
3593 new->match_type = MATCH_REMOTE;
3594 break;
3595 case LOPT_SUBSCR:
3596 new->match_type = MATCH_SUBSCRIBER;
3597 break;
3598 }
3599 new->next = daemon->dhcp_vendors;
3600 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003601
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003602 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003603 }
3604
Simon Kelley9e038942008-05-30 20:06:34 +01003605 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3606 if (!arg)
3607 {
3608 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3609 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3610 }
3611 else
3612 {
3613 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003614 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3615 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003616 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003617 if (!comma)
3618 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3619 }
3620 break;
3621
Simon Kelley824af852008-02-12 20:43:05 +00003622 case 'J': /* --dhcp-ignore */
3623 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3624 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003625 case '3': /* --bootp-dynamic */
3626 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003627 {
Simon Kelley824af852008-02-12 20:43:05 +00003628 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003629 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003630 if (option == 'J')
3631 {
3632 new->next = daemon->dhcp_ignore;
3633 daemon->dhcp_ignore = new;
3634 }
Simon Kelley824af852008-02-12 20:43:05 +00003635 else if (option == LOPT_BROADCAST)
3636 {
3637 new->next = daemon->force_broadcast;
3638 daemon->force_broadcast = new;
3639 }
Simon Kelley9009d742008-11-14 20:04:27 +00003640 else if (option == '3')
3641 {
3642 new->next = daemon->bootp_dynamic;
3643 daemon->bootp_dynamic = new;
3644 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003645 else if (option == LOPT_GEN_NAMES)
3646 {
3647 new->next = daemon->dhcp_gen_names;
3648 daemon->dhcp_gen_names = new;
3649 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003650 else
3651 {
3652 new->next = daemon->dhcp_ignore_names;
3653 daemon->dhcp_ignore_names = new;
3654 }
3655
3656 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003657 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003658 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003659 member->next = list;
3660 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003661 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003662 member->net = opt_string_alloc(arg+4);
3663 else
3664 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003665 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003666 }
Simon Kelley849a8352006-06-09 21:02:31 +01003667
3668 new->list = list;
3669 break;
3670 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003671
3672 case LOPT_PROXY: /* --dhcp-proxy */
3673 daemon->override = 1;
3674 while (arg) {
3675 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3676 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003677 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003678 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003679 new->next = daemon->override_relays;
3680 daemon->override_relays = new;
3681 arg = comma;
3682 }
3683 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003684
3685 case LOPT_RELAY: /* --dhcp-relay */
3686 {
3687 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3688 comma = split(arg);
3689 new->interface = opt_string_alloc(split(comma));
3690 new->iface_index = 0;
3691 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3692 {
3693 new->next = daemon->relay4;
3694 daemon->relay4 = new;
3695 }
3696#ifdef HAVE_DHCP6
3697 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3698 {
3699 new->next = daemon->relay6;
3700 daemon->relay6 = new;
3701 }
3702#endif
3703 else
3704 ret_err(_("Bad dhcp-relay"));
3705
3706 break;
3707 }
3708
Simon Kelley7622fc02009-06-04 20:32:05 +01003709#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003710
Simon Kelley8b372702012-03-09 17:45:10 +00003711#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003712 case LOPT_RA_PARAM: /* --ra-param */
3713 if ((comma = split(arg)))
3714 {
3715 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3716 new->lifetime = -1;
3717 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003718 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003719 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003720 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003721 if (strcasestr(comma, "mtu:") == comma)
3722 {
3723 arg = comma + 4;
3724 if (!(comma = split(comma)))
3725 goto err;
3726 if (!strcasecmp(arg, "off"))
3727 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003728 else if (!atoi_check(arg, &new->mtu))
3729 new->mtu_name = opt_string_alloc(arg);
3730 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003731 goto err;
3732 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003733 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3734 {
3735 if (*comma == 'l' || *comma == 'L')
3736 new->prio = 0x18;
3737 else
3738 new->prio = 0x08;
3739 comma = split(comma);
3740 }
3741 arg = split(comma);
3742 if (!atoi_check(comma, &new->interval) ||
3743 (arg && !atoi_check(arg, &new->lifetime)))
David Flamand005c46d2017-04-11 11:49:54 +01003744err:
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003745 ret_err(_("bad RA-params"));
3746
3747 new->next = daemon->ra_interfaces;
3748 daemon->ra_interfaces = new;
3749 }
3750 break;
3751
Simon Kelley8b372702012-03-09 17:45:10 +00003752 case LOPT_DUID: /* --dhcp-duid */
3753 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003754 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003755 else
3756 {
3757 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3758 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3759 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3760 }
3761 break;
3762#endif
3763
Simon Kelleyf2621c72007-04-29 19:47:21 +01003764 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003765 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003766 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003767 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003768 struct doctor *new = opt_malloc(sizeof(struct doctor));
3769 new->next = daemon->doctors;
3770 daemon->doctors = new;
3771 new->mask.s_addr = 0xffffffff;
3772 new->end.s_addr = 0;
3773
Simon Kelley849a8352006-06-09 21:02:31 +01003774 if ((a[0] = arg))
3775 for (k = 1; k < 3; k++)
3776 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003777 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003778 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003779 unhide_metas(a[k]);
3780 }
Simon Kelley849a8352006-06-09 21:02:31 +01003781
Simon Kelley73a08a22009-02-05 20:28:08 +00003782 dash = split_chr(a[0], '-');
3783
Simon Kelley849a8352006-06-09 21:02:31 +01003784 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003785 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
3786 (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
Simon Kelley73a08a22009-02-05 20:28:08 +00003787 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003788
Simon Kelleya2bc2542016-04-21 22:34:22 +01003789 if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))
3790 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003791
Simon Kelley73a08a22009-02-05 20:28:08 +00003792 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003793 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003794 !is_same_net(new->in, new->end, new->mask) ||
3795 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003796 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003797
3798 break;
3799 }
3800
Simon Kelleyf2621c72007-04-29 19:47:21 +01003801 case LOPT_INTNAME: /* --interface-name */
3802 {
3803 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003804 char *domain = NULL;
3805
Simon Kelleyf2621c72007-04-29 19:47:21 +01003806 comma = split(arg);
3807
Simon Kelley1f15b812009-10-13 17:49:32 +01003808 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003809 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003810
Simon Kelley824af852008-02-12 20:43:05 +00003811 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003812 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00003813 new->addr = NULL;
3814
Simon Kelleyf2621c72007-04-29 19:47:21 +01003815 /* Add to the end of the list, so that first name
3816 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003817 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003818 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003819 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003820 new->family = 0;
3821 arg = split_chr(comma, '/');
3822 if (arg)
3823 {
3824 if (strcmp(arg, "4") == 0)
3825 new->family = AF_INET;
3826#ifdef HAVE_IPV6
3827 else if (strcmp(arg, "6") == 0)
3828 new->family = AF_INET6;
3829#endif
3830 else
3831 ret_err(gen_err);
3832 }
Simon Kelley824af852008-02-12 20:43:05 +00003833 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003834 break;
3835 }
Simon Kelley9009d742008-11-14 20:04:27 +00003836
3837 case LOPT_CNAME: /* --cname */
3838 {
3839 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003840 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003841 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003842
Simon Kelleya1d973f2016-12-22 22:09:50 +00003843 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00003844 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00003845 pen = last;
3846 last = comma;
3847 }
3848
3849 if (!pen)
3850 ret_err(_("bad CNAME"));
3851
3852 if (pen != arg && atoi_check(last, &ttl))
3853 last = pen;
3854
3855 target = canonicalise_opt(last);
3856
3857 while (arg != last)
3858 {
3859 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01003860
3861 if (!alias || !target)
3862 ret_err(_("bad CNAME"));
Simon Kelleya1d973f2016-12-22 22:09:50 +00003863
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003864 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01003865 if (hostname_isequal(new->alias, alias))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003866 ret_err(_("duplicate CNAME"));
3867 new = opt_malloc(sizeof(struct cname));
3868 new->next = daemon->cnames;
3869 daemon->cnames = new;
3870 new->alias = alias;
3871 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003872 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003873
Simon Kelley30858e32017-10-09 22:36:11 +01003874 for (arg += strlen(arg)+1; *arg == ' '; arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00003875 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003876
Simon Kelley9009d742008-11-14 20:04:27 +00003877 break;
3878 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003879
3880 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003881 {
3882 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003883 char *dom, *target = NULL;
3884
Simon Kelleyf2621c72007-04-29 19:47:21 +01003885 comma = split(arg);
3886
Simon Kelley1f15b812009-10-13 17:49:32 +01003887 if (!(dom = canonicalise_opt(arg)) ||
3888 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003889 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003890 else
3891 {
3892 new = opt_malloc(sizeof(struct ptr_record));
3893 new->next = daemon->ptr;
3894 daemon->ptr = new;
3895 new->name = dom;
3896 new->ptr = target;
3897 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003898 break;
3899 }
3900
Simon Kelley1a6bca82008-07-11 11:11:42 +01003901 case LOPT_NAPTR: /* --naptr-record */
3902 {
3903 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3904 int k = 0;
3905 struct naptr *new;
3906 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003907 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003908
3909 if ((a[0] = arg))
3910 for (k = 1; k < 7; k++)
3911 if (!(a[k] = split(a[k-1])))
3912 break;
3913
3914
3915 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003916 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003917 !atoi_check16(a[1], &order) ||
3918 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003919 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003920 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003921 else
3922 {
3923 new = opt_malloc(sizeof(struct naptr));
3924 new->next = daemon->naptr;
3925 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003926 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003927 new->flags = opt_string_alloc(a[3]);
3928 new->services = opt_string_alloc(a[4]);
3929 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003930 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003931 new->order = order;
3932 new->pref = pref;
3933 }
3934 break;
3935 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003936
3937 case LOPT_RR: /* dns-rr */
3938 {
3939 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00003940 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003941 char *data;
3942 int val;
3943
3944 comma = split(arg);
3945 data = split(comma);
3946
3947 new = opt_malloc(sizeof(struct txt_record));
3948 new->next = daemon->rr;
3949 daemon->rr = new;
3950
3951 if (!atoi_check(comma, &val) ||
3952 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003953 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003954 ret_err(_("bad RR record"));
3955
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003956 new->class = val;
3957 new->len = 0;
3958
3959 if (data)
3960 {
3961 new->txt=opt_malloc(len);
3962 new->len = len;
3963 memcpy(new->txt, data, len);
3964 }
3965
3966 break;
3967 }
3968
Simon Kelleyf2621c72007-04-29 19:47:21 +01003969 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003970 {
3971 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003972 unsigned char *p, *cnt;
3973 size_t len;
3974
3975 comma = split(arg);
3976
Simon Kelley824af852008-02-12 20:43:05 +00003977 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003978 new->next = daemon->txt;
3979 daemon->txt = new;
3980 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00003981 new->stat = 0;
3982
Simon Kelley1f15b812009-10-13 17:49:32 +01003983 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003984 ret_err(_("bad TXT record"));
3985
Simon Kelley28866e92011-02-14 20:19:14 +00003986 len = comma ? strlen(comma) : 0;
3987 len += (len/255) + 1; /* room for extra counts */
3988 new->txt = p = opt_malloc(len);
3989
3990 cnt = p++;
3991 *cnt = 0;
3992
3993 while (comma && *comma)
3994 {
3995 unsigned char c = (unsigned char)*comma++;
3996
3997 if (c == ',' || *cnt == 255)
3998 {
3999 if (c != ',')
4000 comma--;
4001 cnt = p++;
4002 *cnt = 0;
4003 }
4004 else
4005 {
4006 *p++ = unhide_meta(c);
4007 (*cnt)++;
4008 }
4009 }
4010
4011 new->len = p - new->txt;
4012
Simon Kelley849a8352006-06-09 21:02:31 +01004013 break;
4014 }
4015
Simon Kelleyf2621c72007-04-29 19:47:21 +01004016 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004017 {
4018 int port = 1, priority = 0, weight = 0;
4019 char *name, *target = NULL;
4020 struct mx_srv_record *new;
4021
Simon Kelleyf2621c72007-04-29 19:47:21 +01004022 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004023
Simon Kelley1f15b812009-10-13 17:49:32 +01004024 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004025 ret_err(_("bad SRV record"));
4026
Simon Kelley849a8352006-06-09 21:02:31 +01004027 if (comma)
4028 {
4029 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004030 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004031 if (!(target = canonicalise_opt(arg)))
4032 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00004033
Simon Kelley849a8352006-06-09 21:02:31 +01004034 if (comma)
4035 {
4036 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004037 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004038 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004039 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00004040
Simon Kelley849a8352006-06-09 21:02:31 +01004041 if (comma)
4042 {
4043 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004044 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004045 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004046 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00004047
Simon Kelley407a1f32016-03-01 17:06:07 +00004048 if (comma && !atoi_check16(comma, &weight))
4049 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01004050 }
4051 }
4052 }
4053
Simon Kelley824af852008-02-12 20:43:05 +00004054 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004055 new->next = daemon->mxnames;
4056 daemon->mxnames = new;
4057 new->issrv = 1;
4058 new->name = name;
4059 new->target = target;
4060 new->srvport = port;
4061 new->priority = priority;
4062 new->weight = weight;
4063 break;
4064 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004065
Simon Kelleye759d422012-03-16 13:18:57 +00004066 case LOPT_HOST_REC: /* --host-record */
4067 {
4068 struct host_record *new = opt_malloc(sizeof(struct host_record));
4069 memset(new, 0, sizeof(struct host_record));
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004070 new->ttl = -1;
4071
Simon Kelleye759d422012-03-16 13:18:57 +00004072 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004073 ret_err(_("Bad host-record"));
4074
4075 while (arg)
4076 {
4077 struct all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004078 char *dig;
4079
4080 for (dig = arg; *dig != 0; dig++)
4081 if (*dig < '0' || *dig > '9')
4082 break;
4083 if (*dig == 0)
4084 new->ttl = atoi(arg);
4085 else if (inet_pton(AF_INET, arg, &addr))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004086 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00004087#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004088 else if (inet_pton(AF_INET6, arg, &addr))
4089 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00004090#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004091 else
4092 {
4093 int nomem;
4094 char *canon = canonicalise(arg, &nomem);
4095 struct name_list *nl = opt_malloc(sizeof(struct name_list));
4096 if (!canon)
4097 ret_err(_("Bad name in host-record"));
4098
4099 nl->name = canon;
4100 /* keep order, so that PTR record goes to first name */
4101 nl->next = NULL;
4102 if (!new->names)
4103 new->names = nl;
4104 else
4105 {
4106 struct name_list *tmp;
4107 for (tmp = new->names; tmp->next; tmp = tmp->next);
4108 tmp->next = nl;
4109 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004110 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004111
4112 arg = comma;
4113 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004114 }
Simon Kelleye759d422012-03-16 13:18:57 +00004115
4116 /* Keep list order */
4117 if (!daemon->host_records_tail)
4118 daemon->host_records = new;
4119 else
4120 daemon->host_records_tail->next = new;
4121 new->next = NULL;
4122 daemon->host_records_tail = new;
4123 break;
4124 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004125
4126#ifdef HAVE_DNSSEC
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004127 case LOPT_DNSSEC_STAMP:
4128 daemon->timestamp_file = opt_string_alloc(arg);
4129 break;
4130
Simon Kelleyee415862014-02-11 11:07:22 +00004131 case LOPT_TRUST_ANCHOR:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004132 {
Simon Kelleyee415862014-02-11 11:07:22 +00004133 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4134 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4135 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004136
4137 new->class = C_IN;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004138
Simon Kelleycbf13a22014-01-25 17:59:14 +00004139 if ((comma = split(arg)) && (algo = split(comma)))
4140 {
4141 int class = 0;
4142 if (strcmp(comma, "IN") == 0)
4143 class = C_IN;
4144 else if (strcmp(comma, "CH") == 0)
4145 class = C_CHAOS;
4146 else if (strcmp(comma, "HS") == 0)
4147 class = C_HESIOD;
4148
4149 if (class != 0)
4150 {
4151 new->class = class;
4152 comma = algo;
4153 algo = split(comma);
4154 }
4155 }
4156
Simon Kelleyee415862014-02-11 11:07:22 +00004157 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4158 !atoi_check16(comma, &new->keytag) ||
4159 !atoi_check8(algo, &new->algo) ||
4160 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004161 !(new->name = canonicalise_opt(arg)))
Simon Kelleyee415862014-02-11 11:07:22 +00004162 ret_err(_("bad trust anchor"));
Simon Kelleycbf13a22014-01-25 17:59:14 +00004163
Simon Kelley0fc2f312014-01-08 10:26:58 +00004164 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004165 len = (2*strlen(keyhex))+1;
4166 new->digest = opt_malloc(len);
4167 unhide_metas(keyhex);
4168 /* 4034: "Whitespace is allowed within digits" */
4169 for (cp = keyhex; *cp; )
4170 if (isspace(*cp))
4171 for (cp1 = cp; *cp1; cp1++)
4172 *cp1 = *(cp1+1);
4173 else
4174 cp++;
4175 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
4176 ret_err(_("bad HEX in trust anchor"));
Simon Kelley0fc2f312014-01-08 10:26:58 +00004177
Simon Kelleyee415862014-02-11 11:07:22 +00004178 new->next = daemon->ds;
4179 daemon->ds = new;
4180
Simon Kelley0fc2f312014-01-08 10:26:58 +00004181 break;
4182 }
4183#endif
4184
Simon Kelley7622fc02009-06-04 20:32:05 +01004185 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004186 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004187
Simon Kelley849a8352006-06-09 21:02:31 +01004188 }
Simon Kelley824af852008-02-12 20:43:05 +00004189
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004190 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004191}
4192
Simon Kelley28866e92011-02-14 20:19:14 +00004193static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004194{
Simon Kelley824af852008-02-12 20:43:05 +00004195 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004196 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004197
4198 while (fgets(buff, MAXDNAME, f))
4199 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004200 int white, i;
4201 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004202 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004203 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004204
Simon Kelley824af852008-02-12 20:43:05 +00004205 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004206 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004207 {
4208 if (setjmp(mem_jmp))
4209 continue;
4210 mem_recover = 1;
4211 }
4212
Simon Kelley13dee6f2017-02-28 16:51:58 +00004213 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004214 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004215 errmess = NULL;
4216
Simon Kelley849a8352006-06-09 21:02:31 +01004217 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4218 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004219 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004220 {
4221 if (*p == '"')
4222 {
4223 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004224
Simon Kelley849a8352006-06-09 21:02:31 +01004225 for(; *p && *p != '"'; p++)
4226 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004227 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004228 {
4229 if (p[1] == 't')
4230 p[1] = '\t';
4231 else if (p[1] == 'n')
4232 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004233 else if (p[1] == 'b')
4234 p[1] = '\b';
4235 else if (p[1] == 'r')
4236 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004237 else if (p[1] == 'e') /* escape */
4238 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004239 memmove(p, p+1, strlen(p+1)+1);
4240 }
4241 *p = hide_meta(*p);
4242 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004243
4244 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004245 {
4246 errmess = _("missing \"");
4247 goto oops;
4248 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004249
4250 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004251 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004252
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004253 if (isspace(*p))
4254 {
4255 *p = ' ';
4256 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004257 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004258 else
4259 {
4260 if (white && *p == '#')
4261 {
4262 *p = 0;
4263 break;
4264 }
4265 white = 0;
4266 }
Simon Kelley849a8352006-06-09 21:02:31 +01004267 }
4268
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004269
4270 /* strip leading spaces */
4271 for (start = buff; *start && *start == ' '; start++);
4272
4273 /* strip trailing spaces */
4274 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4275
4276 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004277 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004278 else
4279 start[len] = 0;
4280
Simon Kelley611ebc52012-07-16 16:23:46 +01004281 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004282 arg = start;
4283 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004284 {
4285 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004286 for (arg = p+1; *arg == ' '; arg++);
4287 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004288 *p = 0;
4289 }
4290 else
4291 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004292
Simon Kelley611ebc52012-07-16 16:23:46 +01004293 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004294 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004295 for (option = 0, i = 0; opts[i].name; i++)
4296 if (strcmp(opts[i].name, start) == 0)
4297 {
4298 option = opts[i].val;
4299 break;
4300 }
4301
4302 if (!option)
4303 errmess = _("bad option");
4304 else if (opts[i].has_arg == 0 && arg)
4305 errmess = _("extraneous parameter");
4306 else if (opts[i].has_arg == 1 && !arg)
4307 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004308 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4309 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004310 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004311
4312 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004313 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004314 strcpy(daemon->namebuff, errmess);
4315
Simon Kelley7b1eae42014-02-20 13:43:28 +00004316 if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004317 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004318 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004319 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004320 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004321 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004322 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004323 }
Simon Kelley849a8352006-06-09 21:02:31 +01004324 }
4325
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004326 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004327 fclose(f);
4328}
4329
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004330#ifdef HAVE_DHCP
Simon Kelley70d18732015-01-31 19:59:29 +00004331int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004332{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004333 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4334
Simon Kelley70d18732015-01-31 19:59:29 +00004335 if (flags & AH_DHCP_HST)
4336 return one_file(file, LOPT_BANK);
4337 else if (flags & AH_DHCP_OPT)
4338 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004339
Simon Kelley70d18732015-01-31 19:59:29 +00004340 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004341}
4342#endif
4343
Simon Kelley395eb712012-07-06 22:07:05 +01004344static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004345{
4346 FILE *f;
4347 int nofile_ok = 0;
4348 static int read_stdin = 0;
4349 static struct fileread {
4350 dev_t dev;
4351 ino_t ino;
4352 struct fileread *next;
4353 } *filesread = NULL;
4354
4355 if (hard_opt == '7')
4356 {
4357 /* default conf-file reading */
4358 hard_opt = 0;
4359 nofile_ok = 1;
4360 }
4361
4362 if (hard_opt == 0 && strcmp(file, "-") == 0)
4363 {
4364 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004365 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004366 read_stdin = 1;
4367 file = "stdin";
4368 f = stdin;
4369 }
4370 else
4371 {
4372 /* ignore repeated files. */
4373 struct stat statbuf;
4374
4375 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4376 {
4377 struct fileread *r;
4378
4379 for (r = filesread; r; r = r->next)
4380 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004381 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004382
4383 r = safe_malloc(sizeof(struct fileread));
4384 r->next = filesread;
4385 filesread = r;
4386 r->dev = statbuf.st_dev;
4387 r->ino = statbuf.st_ino;
4388 }
4389
4390 if (!(f = fopen(file, "r")))
4391 {
4392 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004393 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004394 else
4395 {
4396 char *str = _("cannot read %s: %s");
4397 if (hard_opt != 0)
4398 {
4399 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004400 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004401 }
4402 else
4403 die(str, file, EC_FILE);
4404 }
4405 }
4406 }
4407
4408 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004409 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004410}
4411
4412/* expand any name which is a directory */
4413struct hostsfile *expand_filelist(struct hostsfile *list)
4414{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004415 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004416 struct hostsfile *ah;
4417
Simon Kelley19c51cf2014-03-18 22:38:30 +00004418 /* find largest used index */
4419 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004420 {
4421 if (i <= ah->index)
4422 i = ah->index + 1;
4423
4424 if (ah->flags & AH_DIR)
4425 ah->flags |= AH_INACTIVE;
4426 else
4427 ah->flags &= ~AH_INACTIVE;
4428 }
4429
4430 for (ah = list; ah; ah = ah->next)
4431 if (!(ah->flags & AH_INACTIVE))
4432 {
4433 struct stat buf;
4434 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4435 {
4436 DIR *dir_stream;
4437 struct dirent *ent;
4438
4439 /* don't read this as a file */
4440 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004441
Simon Kelley28866e92011-02-14 20:19:14 +00004442 if (!(dir_stream = opendir(ah->fname)))
4443 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4444 ah->fname, strerror(errno));
4445 else
4446 {
4447 while ((ent = readdir(dir_stream)))
4448 {
4449 size_t lendir = strlen(ah->fname);
4450 size_t lenfile = strlen(ent->d_name);
4451 struct hostsfile *ah1;
4452 char *path;
4453
4454 /* ignore emacs backups and dotfiles */
4455 if (lenfile == 0 ||
4456 ent->d_name[lenfile - 1] == '~' ||
4457 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4458 ent->d_name[0] == '.')
4459 continue;
4460
4461 /* see if we have an existing record.
4462 dir is ah->fname
4463 file is ent->d_name
4464 path to match is ah1->fname */
4465
4466 for (ah1 = list; ah1; ah1 = ah1->next)
4467 {
4468 if (lendir < strlen(ah1->fname) &&
4469 strstr(ah1->fname, ah->fname) == ah1->fname &&
4470 ah1->fname[lendir] == '/' &&
4471 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4472 {
4473 ah1->flags &= ~AH_INACTIVE;
4474 break;
4475 }
4476 }
4477
4478 /* make new record */
4479 if (!ah1)
4480 {
4481 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4482 continue;
4483
4484 if (!(path = whine_malloc(lendir + lenfile + 2)))
4485 {
4486 free(ah1);
4487 continue;
4488 }
4489
4490 strcpy(path, ah->fname);
4491 strcat(path, "/");
4492 strcat(path, ent->d_name);
4493 ah1->fname = path;
4494 ah1->index = i++;
4495 ah1->flags = AH_DIR;
4496 ah1->next = list;
4497 list = ah1;
4498 }
4499
4500 /* inactivate record if not regular file */
4501 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4502 ah1->flags |= AH_INACTIVE;
4503
4504 }
4505 closedir(dir_stream);
4506 }
4507 }
4508 }
4509
4510 return list;
4511}
4512
Simon Kelley7b1eae42014-02-20 13:43:28 +00004513void read_servers_file(void)
4514{
4515 FILE *f;
4516
4517 if (!(f = fopen(daemon->servers_file, "r")))
4518 {
4519 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4520 return;
4521 }
4522
4523 mark_servers(SERV_FROM_FILE);
4524 cleanup_servers();
4525
4526 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4527}
4528
Simon Kelley28866e92011-02-14 20:19:14 +00004529
Simon Kelley7622fc02009-06-04 20:32:05 +01004530#ifdef HAVE_DHCP
Simon Kelley824af852008-02-12 20:43:05 +00004531void reread_dhcp(void)
4532{
Simon Kelley28866e92011-02-14 20:19:14 +00004533 struct hostsfile *hf;
4534
Simon Kelley824af852008-02-12 20:43:05 +00004535 if (daemon->dhcp_hosts_file)
4536 {
4537 struct dhcp_config *configs, *cp, **up;
Simon Kelley28866e92011-02-14 20:19:14 +00004538
Simon Kelley824af852008-02-12 20:43:05 +00004539 /* remove existing... */
4540 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4541 {
4542 cp = configs->next;
4543
4544 if (configs->flags & CONFIG_BANK)
4545 {
Simon Kelley9009d742008-11-14 20:04:27 +00004546 struct hwaddr_config *mac, *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004547 struct dhcp_netid_list *list, *tmplist;
Simon Kelley9009d742008-11-14 20:04:27 +00004548
4549 for (mac = configs->hwaddr; mac; mac = tmp)
4550 {
4551 tmp = mac->next;
4552 free(mac);
4553 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004554
Simon Kelley824af852008-02-12 20:43:05 +00004555 if (configs->flags & CONFIG_CLID)
4556 free(configs->clid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004557
4558 for (list = configs->netid; list; list = tmplist)
4559 {
4560 free(list->list);
4561 tmplist = list->next;
4562 free(list);
4563 }
4564
Simon Kelley824af852008-02-12 20:43:05 +00004565 if (configs->flags & CONFIG_NAME)
4566 free(configs->hostname);
4567
4568 *up = configs->next;
4569 free(configs);
4570 }
4571 else
4572 up = &configs->next;
4573 }
4574
Simon Kelley28866e92011-02-14 20:19:14 +00004575 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4576 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
4577 if (!(hf->flags & AH_INACTIVE))
4578 {
Simon Kelley395eb712012-07-06 22:07:05 +01004579 if (one_file(hf->fname, LOPT_BANK))
4580 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004581 }
Simon Kelley824af852008-02-12 20:43:05 +00004582 }
4583
4584 if (daemon->dhcp_opts_file)
4585 {
4586 struct dhcp_opt *opts, *cp, **up;
4587 struct dhcp_netid *id, *next;
4588
4589 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4590 {
4591 cp = opts->next;
4592
4593 if (opts->flags & DHOPT_BANK)
4594 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004595 if ((opts->flags & DHOPT_VENDOR))
4596 free(opts->u.vendor_class);
Simon Kelley824af852008-02-12 20:43:05 +00004597 free(opts->val);
4598 for (id = opts->netid; id; id = next)
4599 {
4600 next = id->next;
4601 free(id->net);
4602 free(id);
4603 }
4604 *up = opts->next;
4605 free(opts);
4606 }
4607 else
4608 up = &opts->next;
4609 }
4610
Simon Kelley28866e92011-02-14 20:19:14 +00004611 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4612 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4613 if (!(hf->flags & AH_INACTIVE))
4614 {
Simon Kelley395eb712012-07-06 22:07:05 +01004615 if (one_file(hf->fname, LOPT_OPTS))
4616 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004617 }
Simon Kelley824af852008-02-12 20:43:05 +00004618 }
4619}
Simon Kelley7622fc02009-06-04 20:32:05 +01004620#endif
Simon Kelley824af852008-02-12 20:43:05 +00004621
Simon Kelley5aabfc72007-08-29 11:24:47 +01004622void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004623{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004624 size_t argbuf_size = MAXDNAME;
4625 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004626 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004627 int option, conffile_opt = '7', testmode = 0;
Simon Kelley90cb2222015-07-05 21:59:10 +01004628 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004629
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004630 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004631
Simon Kelley824af852008-02-12 20:43:05 +00004632 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004633 memset(daemon, 0, sizeof(struct daemon));
4634 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004635
Simon Kelley3be34542004-09-11 19:12:13 +01004636 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004637 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004638 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004639 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004640 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4641 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004642 daemon->default_resolv.is_default = 1;
4643 daemon->default_resolv.name = RESOLVFILE;
4644 daemon->resolv_files = &daemon->default_resolv;
4645 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004646 daemon->runfile = RUNFILE;
4647 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004648 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004649 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004650 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004651 daemon->auth_ttl = AUTH_TTL;
4652 daemon->soa_refresh = SOA_REFRESH;
4653 daemon->soa_retry = SOA_RETRY;
4654 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00004655 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00004656 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01004657
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004658#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00004659 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
4660 add_txt("authors.bind", "Simon Kelley", 0);
4661 add_txt("copyright.bind", COPYRIGHT, 0);
4662 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
4663 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
4664 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
4665 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
4666 add_txt("hits.bind", NULL, TXT_STAT_HITS);
4667#ifdef HAVE_AUTH
4668 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
4669#endif
4670 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004671#endif
Simon Kelley0a852542005-03-23 20:28:59 +00004672
Simon Kelley849a8352006-06-09 21:02:31 +01004673 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004674 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004675#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004676 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004677#else
Simon Kelley849a8352006-06-09 21:02:31 +01004678 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004679#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004680
4681 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004682 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004683 for (; optind < argc; optind++)
4684 {
4685 unsigned char *c = (unsigned char *)argv[optind];
4686 for (; *c != 0; c++)
4687 if (!isspace(*c))
4688 die(_("junk found in command line"), NULL, EC_BADCONF);
4689 }
Simon Kelley28866e92011-02-14 20:19:14 +00004690 break;
4691 }
4692
Simon Kelley849a8352006-06-09 21:02:31 +01004693 /* Copy optarg so that argv doesn't get changed */
4694 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004695 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00004696 if (strlen(optarg) >= argbuf_size)
4697 {
4698 free(argbuf);
4699 argbuf_size = strlen(optarg) + 1;
4700 argbuf = opt_malloc(argbuf_size);
4701 }
4702 strncpy(argbuf, optarg, argbuf_size);
4703 argbuf[argbuf_size-1] = 0;
4704 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01004705 }
4706 else
4707 arg = NULL;
4708
4709 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004710 if (option == LOPT_TEST)
4711 testmode = 1;
4712 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004713 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004714#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004715 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004716 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004717#ifdef HAVE_DHCP6
4718 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4719 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004720#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004721 else
4722#endif
4723 do_usage();
4724
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004725 exit(0);
4726 }
Simon Kelley849a8352006-06-09 21:02:31 +01004727 else if (option == 'v')
4728 {
4729 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004730 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004731 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4732 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004733 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004734 exit(0);
4735 }
Simon Kelley849a8352006-06-09 21:02:31 +01004736 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004737 {
Simon Kelley28866e92011-02-14 20:19:14 +00004738 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004739 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004740 }
Simon Kelley849a8352006-06-09 21:02:31 +01004741 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004742 {
Simon Kelley26128d22004-11-14 16:43:54 +00004743#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00004744 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004745#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00004746 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004747#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004748 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004749 }
4750 }
Simon Kelley849a8352006-06-09 21:02:31 +01004751
Neil Jerram3bd4c472018-01-18 22:49:38 +00004752 free(argbuf);
4753
Simon Kelley849a8352006-06-09 21:02:31 +01004754 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00004755 {
4756 one_file(conffile, conffile_opt);
Simon Kelley90cb2222015-07-05 21:59:10 +01004757 if (conffile_opt == 0)
4758 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00004759 }
Simon Kelley849a8352006-06-09 21:02:31 +01004760
Simon Kelley1a6bca82008-07-11 11:11:42 +01004761 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004762 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004763 {
4764 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004765 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01004766 if (!(tmp->flags & SERV_HAS_SOURCE))
4767 {
4768 if (tmp->source_addr.sa.sa_family == AF_INET)
4769 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004770#ifdef HAVE_IPV6
Simon Kelley14ffa072016-04-25 16:36:44 +01004771 else if (tmp->source_addr.sa.sa_family == AF_INET6)
4772 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004773#endif
Simon Kelley14ffa072016-04-25 16:36:44 +01004774 }
4775 }
4776
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004777 if (daemon->host_records)
4778 {
4779 struct host_record *hr;
4780
4781 for (hr = daemon->host_records; hr; hr = hr->next)
4782 if (hr->ttl == -1)
4783 hr->ttl = daemon->local_ttl;
4784 }
4785
4786 if (daemon->cnames)
4787 {
Simon Kelley903df072017-01-19 17:22:00 +00004788 struct cname *cn, *cn2, *cn3;
4789
4790#define NOLOOP 1
4791#define TESTLOOP 2
4792
4793 /* Fill in TTL for CNAMES noe we have local_ttl.
4794 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004795 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00004796 {
4797 if (cn->ttl == -1)
4798 cn->ttl = daemon->local_ttl;
4799 cn->flag = 0;
4800 cn->targetp = NULL;
4801 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
4802 if (hostname_isequal(cn->target, cn2->alias))
4803 {
4804 cn->targetp = cn2;
4805 break;
4806 }
4807 }
4808
4809 /* Find any CNAME loops.*/
4810 for (cn = daemon->cnames; cn; cn = cn->next)
4811 {
4812 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
4813 {
4814 if (cn2->flag == NOLOOP)
4815 break;
4816
4817 if (cn2->flag == TESTLOOP)
4818 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
4819
4820 cn2->flag = TESTLOOP;
4821 }
4822
4823 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
4824 cn3->flag = NOLOOP;
4825 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004826 }
4827
Simon Kelley3be34542004-09-11 19:12:13 +01004828 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004829 {
4830 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004831 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004832 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004833 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004834#ifdef HAVE_IPV6
4835 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004836 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004837#endif /* IPv6 */
4838 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004839
4840 /* create default, if not specified */
4841 if (daemon->authserver && !daemon->hostmaster)
4842 {
4843 strcpy(buff, "hostmaster.");
4844 strcat(buff, daemon->authserver);
4845 daemon->hostmaster = opt_string_alloc(buff);
4846 }
4847
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004848 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004849 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004850 {
Simon Kelley0a852542005-03-23 20:28:59 +00004851 struct mx_srv_record *mx;
4852
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004853 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004854 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004855
Simon Kelley0a852542005-03-23 20:28:59 +00004856 for (mx = daemon->mxnames; mx; mx = mx->next)
4857 if (!mx->issrv && hostname_isequal(mx->name, buff))
4858 break;
4859
Simon Kelley28866e92011-02-14 20:19:14 +00004860 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004861 {
Simon Kelley824af852008-02-12 20:43:05 +00004862 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004863 mx->next = daemon->mxnames;
4864 mx->issrv = 0;
4865 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004866 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004867 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004868 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004869
Simon Kelley3be34542004-09-11 19:12:13 +01004870 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004871 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004872
4873 for (mx = daemon->mxnames; mx; mx = mx->next)
4874 if (!mx->issrv && !mx->target)
4875 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004876 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004877
Simon Kelley28866e92011-02-14 20:19:14 +00004878 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004879 daemon->resolv_files &&
4880 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004881 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004882 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004883
Simon Kelley28866e92011-02-14 20:19:14 +00004884 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004885 {
4886 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004887 FILE *f;
4888
Simon Kelley28866e92011-02-14 20:19:14 +00004889 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004890 !daemon->resolv_files ||
4891 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004892 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004893
Simon Kelley3be34542004-09-11 19:12:13 +01004894 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004895 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004896
4897 while ((line = fgets(buff, MAXDNAME, f)))
4898 {
4899 char *token = strtok(line, " \t\n\r");
4900
4901 if (!token || strcmp(token, "search") != 0)
4902 continue;
4903
4904 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004905 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004906 break;
4907 }
Simon Kelley3be34542004-09-11 19:12:13 +01004908
Simon Kelleyde379512004-06-22 20:23:33 +01004909 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004910
Simon Kelley3be34542004-09-11 19:12:13 +01004911 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004912 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004913 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004914
4915 if (daemon->domain_suffix)
4916 {
4917 /* add domain for any srv record without one. */
4918 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004919
Simon Kelley3d8df262005-08-29 12:19:27 +01004920 for (srv = daemon->mxnames; srv; srv = srv->next)
4921 if (srv->issrv &&
4922 strchr(srv->name, '.') &&
4923 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4924 {
4925 strcpy(buff, srv->name);
4926 strcat(buff, ".");
4927 strcat(buff, daemon->domain_suffix);
4928 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004929 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004930 }
4931 }
Simon Kelley28866e92011-02-14 20:19:14 +00004932 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004933 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004934
Simon Kelleyc8a80482014-03-05 14:29:54 +00004935 /* If there's access-control config, then ignore --local-service, it's intended
4936 as a system default to keep otherwise unconfigured installations safe. */
4937 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
4938 reset_option_bool(OPT_LOCAL_SERVICE);
4939
Simon Kelley7622fc02009-06-04 20:32:05 +01004940 if (testmode)
4941 {
4942 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
4943 exit(0);
4944 }
Simon Kelley849a8352006-06-09 21:02:31 +01004945}