blob: 78ad81e145bd7c0eb564748b8a6f0767ddea3a58 [file] [log] [blame]
Simon Kelley50ca8552017-06-24 22:36:43 +01001/* dnsmasq is Copyright (c) 2000-2017 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:
885 p += sprintf(p, "%d.", a & 0xff);
886 /* 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;
1418 end = do_rfc1035_name(p + len, dom);
1419 *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 Kelley824af852008-02-12 20:43:05 +00002736 struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley316e2732010-01-22 20:16:09 +00002737 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002738 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002739
Simon Kelley316e2732010-01-22 20:16:09 +00002740 strcpy(new->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002741 new->alias = NULL;
2742 new->next = daemon->bridges;
2743 daemon->bridges = new;
2744
2745 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002746 arg = comma;
2747 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002748 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002749 {
Simon Kelley824af852008-02-12 20:43:05 +00002750 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002751 b->next = new->alias;
2752 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002753 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002754 }
2755 } while (comma);
2756
2757 break;
2758 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002759
Simon Kelley7622fc02009-06-04 20:32:05 +01002760#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002761 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002762 {
2763 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002764 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002765 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002766
Simon Kelley52b92f42012-01-22 16:05:15 +00002767 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002768 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002769
Simon Kelley849a8352006-06-09 21:02:31 +01002770 if (!arg)
2771 {
2772 option = '?';
2773 break;
2774 }
2775
2776 while(1)
2777 {
2778 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002779 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2780 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2781 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002782 break;
2783
Simon Kelleyf2621c72007-04-29 19:47:21 +01002784 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002785 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002786 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002787 {
Simon Kelley824af852008-02-12 20:43:05 +00002788 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2789 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002790 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002791 /* ignore empty tag */
2792 if (tt->net)
2793 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002794 }
2795 else
2796 {
2797 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002798 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002799 else if (strstr(arg, "set:") == arg)
2800 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002801 else
Simon Kelley824af852008-02-12 20:43:05 +00002802 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002803 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002804 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002805 }
2806 else
2807 {
2808 a[0] = arg;
2809 break;
2810 }
2811 }
2812
Simon Kelley1f776932012-12-16 19:46:08 +00002813 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002814 if (!(a[k] = split(a[k-1])))
2815 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002816
Simon Kelley52b92f42012-01-22 16:05:15 +00002817 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002818 ret_err(_("bad dhcp-range"));
2819
2820 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002821 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002822 new->next = daemon->dhcp;
2823 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002824 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002825 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002826 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002827 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002828 new->flags |= CONTEXT_PROXY;
2829 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002830 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002831
2832 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2833 {
2834 struct in_addr tmp = new->start;
2835 new->start = new->end;
2836 new->end = tmp;
2837 }
2838
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002839 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002840 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002841 {
2842 new->flags |= CONTEXT_NETMASK;
2843 leasepos = 3;
2844 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002845 ret_err(_("inconsistent DHCP range"));
Simon Kelleyfa794662016-03-03 20:33:54 +00002846
Simon Kelley52b92f42012-01-22 16:05:15 +00002847
Simon Kelleyfa794662016-03-03 20:33:54 +00002848 if (k >= 4 && strchr(a[3], '.') &&
2849 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
2850 {
2851 new->flags |= CONTEXT_BRDCAST;
2852 leasepos = 4;
2853 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002854 }
Simon Kelley849a8352006-06-09 21:02:31 +01002855 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002856#ifdef HAVE_DHCP6
2857 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002858 {
Simon Kelley89500e32013-09-20 16:29:20 +01002859 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002860 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002861 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002862 new->next = daemon->dhcp6;
2863 daemon->dhcp6 = new;
2864
Simon Kelley30cd9662012-03-25 20:44:38 +01002865 for (leasepos = 1; leasepos < k; leasepos++)
2866 {
2867 if (strcmp(a[leasepos], "static") == 0)
2868 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2869 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002870 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002871 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002872 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002873 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
2874 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002875 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002876 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01002877 else if (strcmp(a[leasepos], "off-link") == 0)
2878 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01002879 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2880 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002881 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2882 {
2883 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2884 new->flags |= CONTEXT_TEMPLATE;
2885 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002886 else
2887 break;
2888 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002889
Simon Kelley52b92f42012-01-22 16:05:15 +00002890 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002891 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002892 {
2893 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002894 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002895 if (!(*cp >= '0' && *cp <= '9'))
2896 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002897 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002898 {
2899 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002900 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002901 }
2902 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002903
Simon Kelley6692a1a2013-08-20 14:41:31 +01002904 if (new->prefix != 64)
2905 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002906 if (new->flags & CONTEXT_RA)
Simon Kelley6692a1a2013-08-20 14:41:31 +01002907 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2908 else if (new->flags & CONTEXT_TEMPLATE)
2909 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2910 }
2911
2912 if (new->prefix < 64)
2913 ret_err(_("prefix length must be at least 64"));
2914
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002915 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2916 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002917
2918 /* dhcp-range=:: enables DHCP stateless on any interface */
2919 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2920 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002921
2922 if (new->flags & CONTEXT_TEMPLATE)
2923 {
2924 struct in6_addr zero;
2925 memset(&zero, 0, sizeof(zero));
2926 if (!is_same_net6(&zero, &new->start6, new->prefix))
2927 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2928 }
2929
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002930 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002931 {
2932 struct in6_addr tmp = new->start6;
2933 new->start6 = new->end6;
2934 new->end6 = tmp;
2935 }
Simon Kelley849a8352006-06-09 21:02:31 +01002936 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002937#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002938 else
2939 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002940
Simon Kelley30cd9662012-03-25 20:44:38 +01002941 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002942 {
Simon Kelleyfa794662016-03-03 20:33:54 +00002943 if (leasepos != k-1)
2944 ret_err(_("bad dhcp-range"));
2945
Simon Kelley849a8352006-06-09 21:02:31 +01002946 if (strcmp(a[leasepos], "infinite") == 0)
2947 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002948 else if (strcmp(a[leasepos], "deprecated") == 0)
2949 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002950 else
2951 {
2952 int fac = 1;
2953 if (strlen(a[leasepos]) > 0)
2954 {
2955 switch (a[leasepos][strlen(a[leasepos]) - 1])
2956 {
Simon Kelley42243212012-07-20 15:19:18 +01002957 case 'w':
2958 case 'W':
2959 fac *= 7;
2960 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002961 case 'd':
2962 case 'D':
2963 fac *= 24;
2964 /* fall though */
2965 case 'h':
2966 case 'H':
2967 fac *= 60;
2968 /* fall through */
2969 case 'm':
2970 case 'M':
2971 fac *= 60;
2972 /* fall through */
2973 case 's':
2974 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002975 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002976 }
2977
Simon Kelleybe379862012-12-23 12:01:39 +00002978 for (cp = a[leasepos]; *cp; cp++)
2979 if (!(*cp >= '0' && *cp <= '9'))
2980 break;
2981
Simon Kelley54dae552013-02-05 17:55:10 +00002982 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00002983 ret_err(_("bad dhcp-range"));
2984
Simon Kelley849a8352006-06-09 21:02:31 +01002985 new->lease_time = atoi(a[leasepos]) * fac;
2986 /* Leases of a minute or less confuse
2987 some clients, notably Apple's */
2988 if (new->lease_time < 120)
2989 new->lease_time = 120;
2990 }
2991 }
2992 }
2993 break;
2994 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01002995
Simon Kelley5aabfc72007-08-29 11:24:47 +01002996 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01002997 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01002998 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002999 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01003000 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01003001 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003002 struct in_addr in;
3003
Simon Kelley824af852008-02-12 20:43:05 +00003004 new = opt_malloc(sizeof(struct dhcp_config));
3005
Simon Kelley849a8352006-06-09 21:02:31 +01003006 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003007 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3008 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003009 new->netid = NULL;
3010
Simon Kelley849a8352006-06-09 21:02:31 +01003011 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01003012 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003013 if (!(a[k] = split(a[k-1])))
3014 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003015
3016 for (j = 0; j < k; j++)
3017 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
3018 {
3019 char *arg = a[j];
3020
3021 if ((arg[0] == 'i' || arg[0] == 'I') &&
3022 (arg[1] == 'd' || arg[1] == 'D') &&
3023 arg[2] == ':')
3024 {
3025 if (arg[3] == '*')
3026 new->flags |= CONFIG_NOCLID;
3027 else
3028 {
3029 int len;
3030 arg += 3; /* dump id: */
3031 if (strchr(arg, ':'))
3032 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3033 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01003034 {
3035 unhide_metas(arg);
3036 len = (int) strlen(arg);
3037 }
3038
Simon Kelley28866e92011-02-14 20:19:14 +00003039 if (len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003040 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003041 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003042 {
3043 new->flags |= CONFIG_CLID;
3044 new->clid_len = len;
3045 memcpy(new->clid, arg, len);
3046 }
Simon Kelley849a8352006-06-09 21:02:31 +01003047 }
3048 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003049 /* dhcp-host has strange backwards-compat needs. */
3050 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01003051 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003052 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3053 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3054 newtag->net = opt_malloc(strlen(arg + 4) + 1);
3055 newlist->next = new->netid;
3056 new->netid = newlist;
3057 newlist->list = newtag;
3058 strcpy(newtag->net, arg+4);
3059 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01003060 }
Simon Kelley7de060b2011-08-26 17:24:52 +01003061 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003062 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00003063#ifdef HAVE_DHCP6
3064 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3065 {
3066 arg[strlen(arg)-1] = 0;
3067 arg++;
3068
3069 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003070 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00003071
3072 for (i= 0; i < 8; i++)
3073 if (new->addr6.s6_addr[i] != 0)
3074 break;
3075
3076 /* set WILDCARD if network part all zeros */
3077 if (i == 8)
3078 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00003079
3080 new->flags |= CONFIG_ADDR6;
3081 }
3082#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01003083 else
Simon Kelley849a8352006-06-09 21:02:31 +01003084 {
Simon Kelley9009d742008-11-14 20:04:27 +00003085 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00003086 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
3087 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003088 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003089 else
3090 {
3091
3092 newhw->next = new->hwaddr;
3093 new->hwaddr = newhw;
3094 }
Simon Kelley849a8352006-06-09 21:02:31 +01003095 }
3096 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003097 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01003098 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003099 struct dhcp_config *configs;
3100
Simon Kelley849a8352006-06-09 21:02:31 +01003101 new->addr = in;
3102 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003103
3104 /* If the same IP appears in more than one host config, then DISCOVER
3105 for one of the hosts will get the address, but REQUEST will be NAKed,
3106 since the address is reserved by the other one -> protocol loop. */
3107 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3108 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
3109 {
3110 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3111 return 0;
3112 }
Simon Kelley849a8352006-06-09 21:02:31 +01003113 }
3114 else
3115 {
3116 char *cp, *lastp = NULL, last = 0;
Simon Kelley76ff4402013-12-17 16:29:14 +00003117 int fac = 1, isdig = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003118
3119 if (strlen(a[j]) > 1)
3120 {
3121 lastp = a[j] + strlen(a[j]) - 1;
3122 last = *lastp;
3123 switch (last)
3124 {
Simon Kelley42243212012-07-20 15:19:18 +01003125 case 'w':
3126 case 'W':
3127 fac *= 7;
3128 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003129 case 'd':
3130 case 'D':
3131 fac *= 24;
3132 /* fall through */
3133 case 'h':
3134 case 'H':
3135 fac *= 60;
3136 /* fall through */
3137 case 'm':
3138 case 'M':
3139 fac *= 60;
3140 /* fall through */
3141 case 's':
3142 case 'S':
3143 *lastp = 0;
3144 }
3145 }
3146
3147 for (cp = a[j]; *cp; cp++)
Simon Kelley76ff4402013-12-17 16:29:14 +00003148 if (isdigit((unsigned char)*cp))
3149 isdig = 1;
3150 else if (*cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01003151 break;
Simon Kelley76ff4402013-12-17 16:29:14 +00003152
Simon Kelley849a8352006-06-09 21:02:31 +01003153 if (*cp)
3154 {
3155 if (lastp)
3156 *lastp = last;
3157 if (strcmp(a[j], "infinite") == 0)
3158 {
3159 new->lease_time = 0xffffffff;
3160 new->flags |= CONFIG_TIME;
3161 }
3162 else if (strcmp(a[j], "ignore") == 0)
3163 new->flags |= CONFIG_DISABLE;
3164 else
3165 {
Simon Kelley1f15b812009-10-13 17:49:32 +01003166 if (!(new->hostname = canonicalise_opt(a[j])) ||
3167 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003168 ret_err(_("bad DHCP host name"));
3169
3170 new->flags |= CONFIG_NAME;
3171 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01003172 }
3173 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003174 else if (isdig)
Simon Kelley849a8352006-06-09 21:02:31 +01003175 {
3176 new->lease_time = atoi(a[j]) * fac;
3177 /* Leases of a minute or less confuse
3178 some clients, notably Apple's */
3179 if (new->lease_time < 120)
3180 new->lease_time = 120;
3181 new->flags |= CONFIG_TIME;
3182 }
3183 }
3184
Simon Kelley5aabfc72007-08-29 11:24:47 +01003185 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003186 break;
3187 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003188
3189 case LOPT_TAG_IF: /* --tag-if */
3190 {
3191 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3192
3193 new->tag = NULL;
3194 new->set = NULL;
3195 new->next = NULL;
3196
3197 /* preserve order */
3198 if (!daemon->tag_if)
3199 daemon->tag_if = new;
3200 else
3201 {
3202 struct tag_if *tmp;
3203 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3204 tmp->next = new;
3205 }
3206
3207 while (arg)
3208 {
3209 size_t len;
3210
3211 comma = split(arg);
3212 len = strlen(arg);
3213
3214 if (len < 5)
3215 {
3216 new->set = NULL;
3217 break;
3218 }
3219 else
3220 {
3221 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3222 newtag->net = opt_malloc(len - 3);
3223 strcpy(newtag->net, arg+4);
3224 unhide_metas(newtag->net);
3225
3226 if (strstr(arg, "set:") == arg)
3227 {
3228 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3229 newlist->next = new->set;
3230 new->set = newlist;
3231 newlist->list = newtag;
3232 }
3233 else if (strstr(arg, "tag:") == arg)
3234 {
3235 newtag->next = new->tag;
3236 new->tag = newtag;
3237 }
3238 else
3239 {
3240 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00003241 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003242 break;
3243 }
3244 }
3245
3246 arg = comma;
3247 }
3248
3249 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003250 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003251
3252 break;
3253 }
3254
Simon Kelley849a8352006-06-09 21:02:31 +01003255
Simon Kelley73a08a22009-02-05 20:28:08 +00003256 case 'O': /* --dhcp-option */
3257 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003258 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003259 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003260 return parse_dhcp_opt(errstr, arg,
3261 option == LOPT_FORCE ? DHOPT_FORCE :
3262 (option == LOPT_MATCH ? DHOPT_MATCH :
3263 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
3264
Simon Kelleyf2621c72007-04-29 19:47:21 +01003265 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003266 {
3267 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003268 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003269 {
Simon Kelley824af852008-02-12 20:43:05 +00003270 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01003271 newid->next = id;
3272 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003273 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003274 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01003275 arg = comma;
3276 };
3277
3278 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003279 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003280 else
3281 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003282 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003283 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003284 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003285 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003286 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003287 dhcp_next_server.s_addr = 0;
3288 if (comma)
3289 {
3290 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003291 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003292 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003293 if (comma)
3294 {
3295 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003296 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3297 {
3298 /*
3299 * The user may have specified the tftp hostname here.
3300 * save it so that it can be resolved/looked up during
3301 * actual dhcp_reply().
3302 */
3303
3304 tftp_sname = opt_string_alloc(comma);
3305 dhcp_next_server.s_addr = 0;
3306 }
Simon Kelley849a8352006-06-09 21:02:31 +01003307 }
3308 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003309
3310 new = opt_malloc(sizeof(struct dhcp_boot));
3311 new->file = dhcp_file;
3312 new->sname = dhcp_sname;
3313 new->tftp_sname = tftp_sname;
3314 new->next_server = dhcp_next_server;
3315 new->netid = id;
3316 new->next = daemon->boot_config;
3317 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003318 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003319
Simon Kelley849a8352006-06-09 21:02:31 +01003320 break;
3321 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003322
Floris Bos503c6092017-04-09 23:07:13 +01003323 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3324 {
3325 struct dhcp_netid *id = NULL;
3326 while (is_tag_prefix(arg))
3327 {
3328 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
3329 newid->next = id;
3330 id = newid;
3331 comma = split(arg);
3332 newid->net = opt_string_alloc(arg+4);
3333 arg = comma;
3334 };
3335
3336 if (!arg)
3337 ret_err(gen_err);
3338 else
3339 {
3340 struct delay_config *new;
3341 int delay;
3342 if (!atoi_check(arg, &delay))
3343 ret_err(gen_err);
3344
3345 new = opt_malloc(sizeof(struct delay_config));
3346 new->delay = delay;
3347 new->netid = id;
3348 new->next = daemon->delay_conf;
3349 daemon->delay_conf = new;
3350 }
3351
3352 break;
3353 }
3354
Simon Kelley7622fc02009-06-04 20:32:05 +01003355 case LOPT_PXE_PROMT: /* --pxe-prompt */
3356 {
3357 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3358 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003359
Simon Kelley7622fc02009-06-04 20:32:05 +01003360 new->netid = NULL;
3361 new->opt = 10; /* PXE_MENU_PROMPT */
3362
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003363 while (is_tag_prefix(arg))
3364 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003365 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3366 comma = split(arg);
3367 nn->next = new->netid;
3368 new->netid = nn;
3369 nn->net = opt_string_alloc(arg+4);
3370 arg = comma;
3371 }
3372
3373 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003374 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003375 else
3376 {
3377 comma = split(arg);
3378 unhide_metas(arg);
3379 new->len = strlen(arg) + 1;
3380 new->val = opt_malloc(new->len);
3381 memcpy(new->val + 1, arg, new->len - 1);
3382
3383 new->u.vendor_class = (unsigned char *)"PXEClient";
3384 new->flags = DHOPT_VENDOR;
3385
3386 if (comma && atoi_check(comma, &timeout))
3387 *(new->val) = timeout;
3388 else
3389 *(new->val) = 255;
3390
3391 new->next = daemon->dhcp_opts;
3392 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003393 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003394 }
3395
3396 break;
3397 }
3398
3399 case LOPT_PXE_SERV: /* --pxe-service */
3400 {
3401 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3402 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003403 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3404 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003405 static int boottype = 32768;
3406
3407 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003408 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003409 new->server.s_addr = 0;
3410
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003411 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01003412 {
3413 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3414 comma = split(arg);
3415 nn->next = new->netid;
3416 new->netid = nn;
3417 nn->net = opt_string_alloc(arg+4);
3418 arg = comma;
3419 }
3420
3421 if (arg && (comma = split(arg)))
3422 {
3423 for (i = 0; CSA[i]; i++)
3424 if (strcasecmp(CSA[i], arg) == 0)
3425 break;
3426
3427 if (CSA[i] || atoi_check(arg, &i))
3428 {
3429 arg = comma;
3430 comma = split(arg);
3431
3432 new->CSA = i;
3433 new->menu = opt_string_alloc(arg);
3434
Simon Kelley316e2732010-01-22 20:16:09 +00003435 if (!comma)
3436 {
3437 new->type = 0; /* local boot */
3438 new->basename = NULL;
3439 }
3440 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003441 {
3442 arg = comma;
3443 comma = split(arg);
3444 if (atoi_check(arg, &i))
3445 {
3446 new->type = i;
3447 new->basename = NULL;
3448 }
3449 else
3450 {
3451 new->type = boottype++;
3452 new->basename = opt_string_alloc(arg);
3453 }
3454
Simon Kelley751d6f42012-02-10 15:24:51 +00003455 if (comma)
3456 {
3457 if (!inet_pton(AF_INET, comma, &new->server))
3458 {
3459 new->server.s_addr = 0;
3460 new->sname = opt_string_alloc(comma);
3461 }
3462
3463 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003464 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003465
Simon Kelley316e2732010-01-22 20:16:09 +00003466 /* Order matters */
3467 new->next = NULL;
3468 if (!daemon->pxe_services)
3469 daemon->pxe_services = new;
3470 else
3471 {
3472 struct pxe_service *s;
3473 for (s = daemon->pxe_services; s->next; s = s->next);
3474 s->next = new;
3475 }
3476
3477 daemon->enable_pxe = 1;
3478 break;
3479
Simon Kelley7622fc02009-06-04 20:32:05 +01003480 }
3481 }
3482
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003483 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003484 }
3485
Simon Kelleyf2621c72007-04-29 19:47:21 +01003486 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003487 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003488 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003489 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003490 else
3491 {
Simon Kelley824af852008-02-12 20:43:05 +00003492 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003493 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003494 unhide_metas(comma);
3495 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003496 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003497 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003498 else
3499 {
3500 new->next = daemon->dhcp_macs;
3501 daemon->dhcp_macs = new;
3502 }
Simon Kelley849a8352006-06-09 21:02:31 +01003503 }
3504 }
3505 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003506
3507#ifdef OPTION6_PREFIX_CLASS
3508 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3509 {
3510 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3511
3512 if (!(comma = split(arg)) ||
3513 !atoi_check16(comma, &new->class))
3514 ret_err(gen_err);
3515
3516 new->tag.net = opt_string_alloc(set_prefix(arg));
3517 new->next = daemon->prefix_classes;
3518 daemon->prefix_classes = new;
3519
3520 break;
3521 }
3522#endif
3523
3524
Simon Kelleyf2621c72007-04-29 19:47:21 +01003525 case 'U': /* --dhcp-vendorclass */
3526 case 'j': /* --dhcp-userclass */
3527 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3528 case LOPT_REMOTE: /* --dhcp-remoteid */
3529 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003530 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003531 unsigned char *p;
3532 int dig = 0;
3533 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3534
3535 if (!(comma = split(arg)))
3536 ret_err(gen_err);
3537
3538 new->netid.net = opt_string_alloc(set_prefix(arg));
3539 /* check for hex string - must digits may include : must not have nothing else,
3540 only allowed for agent-options. */
3541
3542 arg = comma;
3543 if ((comma = split(arg)))
3544 {
3545 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3546 ret_err(gen_err);
3547 else
3548 new->enterprise = atoi(arg+11);
3549 }
3550 else
3551 comma = arg;
3552
3553 for (p = (unsigned char *)comma; *p; p++)
3554 if (isxdigit(*p))
3555 dig = 1;
3556 else if (*p != ':')
3557 break;
3558 unhide_metas(comma);
3559 if (option == 'U' || option == 'j' || *p || !dig)
3560 {
3561 new->len = strlen(comma);
3562 new->data = opt_malloc(new->len);
3563 memcpy(new->data, comma, new->len);
3564 }
3565 else
3566 {
3567 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3568 new->data = opt_malloc(new->len);
3569 memcpy(new->data, comma, new->len);
3570 }
3571
3572 switch (option)
3573 {
3574 case 'j':
3575 new->match_type = MATCH_USER;
3576 break;
3577 case 'U':
3578 new->match_type = MATCH_VENDOR;
3579 break;
3580 case LOPT_CIRCUIT:
3581 new->match_type = MATCH_CIRCUIT;
3582 break;
3583 case LOPT_REMOTE:
3584 new->match_type = MATCH_REMOTE;
3585 break;
3586 case LOPT_SUBSCR:
3587 new->match_type = MATCH_SUBSCRIBER;
3588 break;
3589 }
3590 new->next = daemon->dhcp_vendors;
3591 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003592
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003593 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003594 }
3595
Simon Kelley9e038942008-05-30 20:06:34 +01003596 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3597 if (!arg)
3598 {
3599 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3600 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3601 }
3602 else
3603 {
3604 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003605 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3606 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003607 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003608 if (!comma)
3609 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3610 }
3611 break;
3612
Simon Kelley824af852008-02-12 20:43:05 +00003613 case 'J': /* --dhcp-ignore */
3614 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3615 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003616 case '3': /* --bootp-dynamic */
3617 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003618 {
Simon Kelley824af852008-02-12 20:43:05 +00003619 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003620 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003621 if (option == 'J')
3622 {
3623 new->next = daemon->dhcp_ignore;
3624 daemon->dhcp_ignore = new;
3625 }
Simon Kelley824af852008-02-12 20:43:05 +00003626 else if (option == LOPT_BROADCAST)
3627 {
3628 new->next = daemon->force_broadcast;
3629 daemon->force_broadcast = new;
3630 }
Simon Kelley9009d742008-11-14 20:04:27 +00003631 else if (option == '3')
3632 {
3633 new->next = daemon->bootp_dynamic;
3634 daemon->bootp_dynamic = new;
3635 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003636 else if (option == LOPT_GEN_NAMES)
3637 {
3638 new->next = daemon->dhcp_gen_names;
3639 daemon->dhcp_gen_names = new;
3640 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003641 else
3642 {
3643 new->next = daemon->dhcp_ignore_names;
3644 daemon->dhcp_ignore_names = new;
3645 }
3646
3647 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003648 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003649 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003650 member->next = list;
3651 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003652 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003653 member->net = opt_string_alloc(arg+4);
3654 else
3655 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003656 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003657 }
Simon Kelley849a8352006-06-09 21:02:31 +01003658
3659 new->list = list;
3660 break;
3661 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003662
3663 case LOPT_PROXY: /* --dhcp-proxy */
3664 daemon->override = 1;
3665 while (arg) {
3666 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3667 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003668 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003669 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003670 new->next = daemon->override_relays;
3671 daemon->override_relays = new;
3672 arg = comma;
3673 }
3674 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003675
3676 case LOPT_RELAY: /* --dhcp-relay */
3677 {
3678 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3679 comma = split(arg);
3680 new->interface = opt_string_alloc(split(comma));
3681 new->iface_index = 0;
3682 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3683 {
3684 new->next = daemon->relay4;
3685 daemon->relay4 = new;
3686 }
3687#ifdef HAVE_DHCP6
3688 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3689 {
3690 new->next = daemon->relay6;
3691 daemon->relay6 = new;
3692 }
3693#endif
3694 else
3695 ret_err(_("Bad dhcp-relay"));
3696
3697 break;
3698 }
3699
Simon Kelley7622fc02009-06-04 20:32:05 +01003700#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003701
Simon Kelley8b372702012-03-09 17:45:10 +00003702#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003703 case LOPT_RA_PARAM: /* --ra-param */
3704 if ((comma = split(arg)))
3705 {
3706 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3707 new->lifetime = -1;
3708 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003709 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003710 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003711 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003712 if (strcasestr(comma, "mtu:") == comma)
3713 {
3714 arg = comma + 4;
3715 if (!(comma = split(comma)))
3716 goto err;
3717 if (!strcasecmp(arg, "off"))
3718 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003719 else if (!atoi_check(arg, &new->mtu))
3720 new->mtu_name = opt_string_alloc(arg);
3721 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003722 goto err;
3723 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003724 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3725 {
3726 if (*comma == 'l' || *comma == 'L')
3727 new->prio = 0x18;
3728 else
3729 new->prio = 0x08;
3730 comma = split(comma);
3731 }
3732 arg = split(comma);
3733 if (!atoi_check(comma, &new->interval) ||
3734 (arg && !atoi_check(arg, &new->lifetime)))
David Flamand005c46d2017-04-11 11:49:54 +01003735err:
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003736 ret_err(_("bad RA-params"));
3737
3738 new->next = daemon->ra_interfaces;
3739 daemon->ra_interfaces = new;
3740 }
3741 break;
3742
Simon Kelley8b372702012-03-09 17:45:10 +00003743 case LOPT_DUID: /* --dhcp-duid */
3744 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003745 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003746 else
3747 {
3748 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3749 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3750 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3751 }
3752 break;
3753#endif
3754
Simon Kelleyf2621c72007-04-29 19:47:21 +01003755 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003756 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003757 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003758 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003759 struct doctor *new = opt_malloc(sizeof(struct doctor));
3760 new->next = daemon->doctors;
3761 daemon->doctors = new;
3762 new->mask.s_addr = 0xffffffff;
3763 new->end.s_addr = 0;
3764
Simon Kelley849a8352006-06-09 21:02:31 +01003765 if ((a[0] = arg))
3766 for (k = 1; k < 3; k++)
3767 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003768 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003769 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003770 unhide_metas(a[k]);
3771 }
Simon Kelley849a8352006-06-09 21:02:31 +01003772
Simon Kelley73a08a22009-02-05 20:28:08 +00003773 dash = split_chr(a[0], '-');
3774
Simon Kelley849a8352006-06-09 21:02:31 +01003775 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003776 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
3777 (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
Simon Kelley73a08a22009-02-05 20:28:08 +00003778 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003779
Simon Kelleya2bc2542016-04-21 22:34:22 +01003780 if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))
3781 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003782
Simon Kelley73a08a22009-02-05 20:28:08 +00003783 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003784 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003785 !is_same_net(new->in, new->end, new->mask) ||
3786 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003787 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003788
3789 break;
3790 }
3791
Simon Kelleyf2621c72007-04-29 19:47:21 +01003792 case LOPT_INTNAME: /* --interface-name */
3793 {
3794 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003795 char *domain = NULL;
3796
Simon Kelleyf2621c72007-04-29 19:47:21 +01003797 comma = split(arg);
3798
Simon Kelley1f15b812009-10-13 17:49:32 +01003799 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003800 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003801
Simon Kelley824af852008-02-12 20:43:05 +00003802 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003803 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00003804 new->addr = NULL;
3805
Simon Kelleyf2621c72007-04-29 19:47:21 +01003806 /* Add to the end of the list, so that first name
3807 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003808 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003809 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003810 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003811 new->family = 0;
3812 arg = split_chr(comma, '/');
3813 if (arg)
3814 {
3815 if (strcmp(arg, "4") == 0)
3816 new->family = AF_INET;
3817#ifdef HAVE_IPV6
3818 else if (strcmp(arg, "6") == 0)
3819 new->family = AF_INET6;
3820#endif
3821 else
3822 ret_err(gen_err);
3823 }
Simon Kelley824af852008-02-12 20:43:05 +00003824 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003825 break;
3826 }
Simon Kelley9009d742008-11-14 20:04:27 +00003827
3828 case LOPT_CNAME: /* --cname */
3829 {
3830 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003831 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003832 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003833
Simon Kelleya1d973f2016-12-22 22:09:50 +00003834 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00003835 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00003836 pen = last;
3837 last = comma;
3838 }
3839
3840 if (!pen)
3841 ret_err(_("bad CNAME"));
3842
3843 if (pen != arg && atoi_check(last, &ttl))
3844 last = pen;
3845
3846 target = canonicalise_opt(last);
3847
3848 while (arg != last)
3849 {
3850 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01003851
3852 if (!alias || !target)
3853 ret_err(_("bad CNAME"));
Simon Kelleya1d973f2016-12-22 22:09:50 +00003854
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003855 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01003856 if (hostname_isequal(new->alias, alias))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003857 ret_err(_("duplicate CNAME"));
3858 new = opt_malloc(sizeof(struct cname));
3859 new->next = daemon->cnames;
3860 daemon->cnames = new;
3861 new->alias = alias;
3862 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003863 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003864
3865 arg += strlen(arg)+1;
Simon Kelley9009d742008-11-14 20:04:27 +00003866 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003867
Simon Kelley9009d742008-11-14 20:04:27 +00003868 break;
3869 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003870
3871 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003872 {
3873 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003874 char *dom, *target = NULL;
3875
Simon Kelleyf2621c72007-04-29 19:47:21 +01003876 comma = split(arg);
3877
Simon Kelley1f15b812009-10-13 17:49:32 +01003878 if (!(dom = canonicalise_opt(arg)) ||
3879 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003880 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003881 else
3882 {
3883 new = opt_malloc(sizeof(struct ptr_record));
3884 new->next = daemon->ptr;
3885 daemon->ptr = new;
3886 new->name = dom;
3887 new->ptr = target;
3888 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003889 break;
3890 }
3891
Simon Kelley1a6bca82008-07-11 11:11:42 +01003892 case LOPT_NAPTR: /* --naptr-record */
3893 {
3894 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3895 int k = 0;
3896 struct naptr *new;
3897 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003898 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003899
3900 if ((a[0] = arg))
3901 for (k = 1; k < 7; k++)
3902 if (!(a[k] = split(a[k-1])))
3903 break;
3904
3905
3906 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003907 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003908 !atoi_check16(a[1], &order) ||
3909 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003910 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003911 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003912 else
3913 {
3914 new = opt_malloc(sizeof(struct naptr));
3915 new->next = daemon->naptr;
3916 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003917 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003918 new->flags = opt_string_alloc(a[3]);
3919 new->services = opt_string_alloc(a[4]);
3920 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003921 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003922 new->order = order;
3923 new->pref = pref;
3924 }
3925 break;
3926 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003927
3928 case LOPT_RR: /* dns-rr */
3929 {
3930 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00003931 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003932 char *data;
3933 int val;
3934
3935 comma = split(arg);
3936 data = split(comma);
3937
3938 new = opt_malloc(sizeof(struct txt_record));
3939 new->next = daemon->rr;
3940 daemon->rr = new;
3941
3942 if (!atoi_check(comma, &val) ||
3943 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003944 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003945 ret_err(_("bad RR record"));
3946
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003947 new->class = val;
3948 new->len = 0;
3949
3950 if (data)
3951 {
3952 new->txt=opt_malloc(len);
3953 new->len = len;
3954 memcpy(new->txt, data, len);
3955 }
3956
3957 break;
3958 }
3959
Simon Kelleyf2621c72007-04-29 19:47:21 +01003960 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003961 {
3962 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003963 unsigned char *p, *cnt;
3964 size_t len;
3965
3966 comma = split(arg);
3967
Simon Kelley824af852008-02-12 20:43:05 +00003968 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003969 new->next = daemon->txt;
3970 daemon->txt = new;
3971 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00003972 new->stat = 0;
3973
Simon Kelley1f15b812009-10-13 17:49:32 +01003974 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003975 ret_err(_("bad TXT record"));
3976
Simon Kelley28866e92011-02-14 20:19:14 +00003977 len = comma ? strlen(comma) : 0;
3978 len += (len/255) + 1; /* room for extra counts */
3979 new->txt = p = opt_malloc(len);
3980
3981 cnt = p++;
3982 *cnt = 0;
3983
3984 while (comma && *comma)
3985 {
3986 unsigned char c = (unsigned char)*comma++;
3987
3988 if (c == ',' || *cnt == 255)
3989 {
3990 if (c != ',')
3991 comma--;
3992 cnt = p++;
3993 *cnt = 0;
3994 }
3995 else
3996 {
3997 *p++ = unhide_meta(c);
3998 (*cnt)++;
3999 }
4000 }
4001
4002 new->len = p - new->txt;
4003
Simon Kelley849a8352006-06-09 21:02:31 +01004004 break;
4005 }
4006
Simon Kelleyf2621c72007-04-29 19:47:21 +01004007 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004008 {
4009 int port = 1, priority = 0, weight = 0;
4010 char *name, *target = NULL;
4011 struct mx_srv_record *new;
4012
Simon Kelleyf2621c72007-04-29 19:47:21 +01004013 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004014
Simon Kelley1f15b812009-10-13 17:49:32 +01004015 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004016 ret_err(_("bad SRV record"));
4017
Simon Kelley849a8352006-06-09 21:02:31 +01004018 if (comma)
4019 {
4020 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004021 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004022 if (!(target = canonicalise_opt(arg)))
4023 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00004024
Simon Kelley849a8352006-06-09 21:02:31 +01004025 if (comma)
4026 {
4027 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004028 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004029 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004030 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00004031
Simon Kelley849a8352006-06-09 21:02:31 +01004032 if (comma)
4033 {
4034 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004035 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004036 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004037 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00004038
Simon Kelley407a1f32016-03-01 17:06:07 +00004039 if (comma && !atoi_check16(comma, &weight))
4040 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01004041 }
4042 }
4043 }
4044
Simon Kelley824af852008-02-12 20:43:05 +00004045 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004046 new->next = daemon->mxnames;
4047 daemon->mxnames = new;
4048 new->issrv = 1;
4049 new->name = name;
4050 new->target = target;
4051 new->srvport = port;
4052 new->priority = priority;
4053 new->weight = weight;
4054 break;
4055 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004056
Simon Kelleye759d422012-03-16 13:18:57 +00004057 case LOPT_HOST_REC: /* --host-record */
4058 {
4059 struct host_record *new = opt_malloc(sizeof(struct host_record));
4060 memset(new, 0, sizeof(struct host_record));
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004061 new->ttl = -1;
4062
Simon Kelleye759d422012-03-16 13:18:57 +00004063 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004064 ret_err(_("Bad host-record"));
4065
4066 while (arg)
4067 {
4068 struct all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004069 char *dig;
4070
4071 for (dig = arg; *dig != 0; dig++)
4072 if (*dig < '0' || *dig > '9')
4073 break;
4074 if (*dig == 0)
4075 new->ttl = atoi(arg);
4076 else if (inet_pton(AF_INET, arg, &addr))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004077 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00004078#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004079 else if (inet_pton(AF_INET6, arg, &addr))
4080 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00004081#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004082 else
4083 {
4084 int nomem;
4085 char *canon = canonicalise(arg, &nomem);
4086 struct name_list *nl = opt_malloc(sizeof(struct name_list));
4087 if (!canon)
4088 ret_err(_("Bad name in host-record"));
4089
4090 nl->name = canon;
4091 /* keep order, so that PTR record goes to first name */
4092 nl->next = NULL;
4093 if (!new->names)
4094 new->names = nl;
4095 else
4096 {
4097 struct name_list *tmp;
4098 for (tmp = new->names; tmp->next; tmp = tmp->next);
4099 tmp->next = nl;
4100 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004101 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004102
4103 arg = comma;
4104 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004105 }
Simon Kelleye759d422012-03-16 13:18:57 +00004106
4107 /* Keep list order */
4108 if (!daemon->host_records_tail)
4109 daemon->host_records = new;
4110 else
4111 daemon->host_records_tail->next = new;
4112 new->next = NULL;
4113 daemon->host_records_tail = new;
4114 break;
4115 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004116
4117#ifdef HAVE_DNSSEC
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004118 case LOPT_DNSSEC_STAMP:
4119 daemon->timestamp_file = opt_string_alloc(arg);
4120 break;
4121
Simon Kelleyee415862014-02-11 11:07:22 +00004122 case LOPT_TRUST_ANCHOR:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004123 {
Simon Kelleyee415862014-02-11 11:07:22 +00004124 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4125 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4126 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004127
4128 new->class = C_IN;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004129
Simon Kelleycbf13a22014-01-25 17:59:14 +00004130 if ((comma = split(arg)) && (algo = split(comma)))
4131 {
4132 int class = 0;
4133 if (strcmp(comma, "IN") == 0)
4134 class = C_IN;
4135 else if (strcmp(comma, "CH") == 0)
4136 class = C_CHAOS;
4137 else if (strcmp(comma, "HS") == 0)
4138 class = C_HESIOD;
4139
4140 if (class != 0)
4141 {
4142 new->class = class;
4143 comma = algo;
4144 algo = split(comma);
4145 }
4146 }
4147
Simon Kelleyee415862014-02-11 11:07:22 +00004148 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4149 !atoi_check16(comma, &new->keytag) ||
4150 !atoi_check8(algo, &new->algo) ||
4151 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004152 !(new->name = canonicalise_opt(arg)))
Simon Kelleyee415862014-02-11 11:07:22 +00004153 ret_err(_("bad trust anchor"));
Simon Kelleycbf13a22014-01-25 17:59:14 +00004154
Simon Kelley0fc2f312014-01-08 10:26:58 +00004155 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004156 len = (2*strlen(keyhex))+1;
4157 new->digest = opt_malloc(len);
4158 unhide_metas(keyhex);
4159 /* 4034: "Whitespace is allowed within digits" */
4160 for (cp = keyhex; *cp; )
4161 if (isspace(*cp))
4162 for (cp1 = cp; *cp1; cp1++)
4163 *cp1 = *(cp1+1);
4164 else
4165 cp++;
4166 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
4167 ret_err(_("bad HEX in trust anchor"));
Simon Kelley0fc2f312014-01-08 10:26:58 +00004168
Simon Kelleyee415862014-02-11 11:07:22 +00004169 new->next = daemon->ds;
4170 daemon->ds = new;
4171
Simon Kelley0fc2f312014-01-08 10:26:58 +00004172 break;
4173 }
4174#endif
4175
Simon Kelley7622fc02009-06-04 20:32:05 +01004176 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004177 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004178
Simon Kelley849a8352006-06-09 21:02:31 +01004179 }
Simon Kelley824af852008-02-12 20:43:05 +00004180
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004181 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004182}
4183
Simon Kelley28866e92011-02-14 20:19:14 +00004184static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004185{
Simon Kelley824af852008-02-12 20:43:05 +00004186 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004187 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004188
4189 while (fgets(buff, MAXDNAME, f))
4190 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004191 int white, i;
4192 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004193 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004194 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004195
Simon Kelley824af852008-02-12 20:43:05 +00004196 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004197 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004198 {
4199 if (setjmp(mem_jmp))
4200 continue;
4201 mem_recover = 1;
4202 }
4203
Simon Kelley13dee6f2017-02-28 16:51:58 +00004204 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004205 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004206 errmess = NULL;
4207
Simon Kelley849a8352006-06-09 21:02:31 +01004208 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4209 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004210 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004211 {
4212 if (*p == '"')
4213 {
4214 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004215
Simon Kelley849a8352006-06-09 21:02:31 +01004216 for(; *p && *p != '"'; p++)
4217 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004218 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004219 {
4220 if (p[1] == 't')
4221 p[1] = '\t';
4222 else if (p[1] == 'n')
4223 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004224 else if (p[1] == 'b')
4225 p[1] = '\b';
4226 else if (p[1] == 'r')
4227 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004228 else if (p[1] == 'e') /* escape */
4229 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004230 memmove(p, p+1, strlen(p+1)+1);
4231 }
4232 *p = hide_meta(*p);
4233 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004234
4235 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004236 {
4237 errmess = _("missing \"");
4238 goto oops;
4239 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004240
4241 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004242 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004243
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004244 if (isspace(*p))
4245 {
4246 *p = ' ';
4247 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004248 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004249 else
4250 {
4251 if (white && *p == '#')
4252 {
4253 *p = 0;
4254 break;
4255 }
4256 white = 0;
4257 }
Simon Kelley849a8352006-06-09 21:02:31 +01004258 }
4259
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004260
4261 /* strip leading spaces */
4262 for (start = buff; *start && *start == ' '; start++);
4263
4264 /* strip trailing spaces */
4265 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4266
4267 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004268 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004269 else
4270 start[len] = 0;
4271
Simon Kelley611ebc52012-07-16 16:23:46 +01004272 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004273 arg = start;
4274 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004275 {
4276 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004277 for (arg = p+1; *arg == ' '; arg++);
4278 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004279 *p = 0;
4280 }
4281 else
4282 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004283
Simon Kelley611ebc52012-07-16 16:23:46 +01004284 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004285 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004286 for (option = 0, i = 0; opts[i].name; i++)
4287 if (strcmp(opts[i].name, start) == 0)
4288 {
4289 option = opts[i].val;
4290 break;
4291 }
4292
4293 if (!option)
4294 errmess = _("bad option");
4295 else if (opts[i].has_arg == 0 && arg)
4296 errmess = _("extraneous parameter");
4297 else if (opts[i].has_arg == 1 && !arg)
4298 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004299 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4300 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004301 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004302
4303 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004304 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004305 strcpy(daemon->namebuff, errmess);
4306
Simon Kelley7b1eae42014-02-20 13:43:28 +00004307 if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004308 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004309 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004310 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004311 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004312 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004313 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004314 }
Simon Kelley849a8352006-06-09 21:02:31 +01004315 }
4316
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004317 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004318 fclose(f);
4319}
4320
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004321#ifdef HAVE_DHCP
Simon Kelley70d18732015-01-31 19:59:29 +00004322int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004323{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004324 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4325
Simon Kelley70d18732015-01-31 19:59:29 +00004326 if (flags & AH_DHCP_HST)
4327 return one_file(file, LOPT_BANK);
4328 else if (flags & AH_DHCP_OPT)
4329 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004330
Simon Kelley70d18732015-01-31 19:59:29 +00004331 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004332}
4333#endif
4334
Simon Kelley395eb712012-07-06 22:07:05 +01004335static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004336{
4337 FILE *f;
4338 int nofile_ok = 0;
4339 static int read_stdin = 0;
4340 static struct fileread {
4341 dev_t dev;
4342 ino_t ino;
4343 struct fileread *next;
4344 } *filesread = NULL;
4345
4346 if (hard_opt == '7')
4347 {
4348 /* default conf-file reading */
4349 hard_opt = 0;
4350 nofile_ok = 1;
4351 }
4352
4353 if (hard_opt == 0 && strcmp(file, "-") == 0)
4354 {
4355 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004356 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004357 read_stdin = 1;
4358 file = "stdin";
4359 f = stdin;
4360 }
4361 else
4362 {
4363 /* ignore repeated files. */
4364 struct stat statbuf;
4365
4366 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4367 {
4368 struct fileread *r;
4369
4370 for (r = filesread; r; r = r->next)
4371 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004372 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004373
4374 r = safe_malloc(sizeof(struct fileread));
4375 r->next = filesread;
4376 filesread = r;
4377 r->dev = statbuf.st_dev;
4378 r->ino = statbuf.st_ino;
4379 }
4380
4381 if (!(f = fopen(file, "r")))
4382 {
4383 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004384 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004385 else
4386 {
4387 char *str = _("cannot read %s: %s");
4388 if (hard_opt != 0)
4389 {
4390 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004391 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004392 }
4393 else
4394 die(str, file, EC_FILE);
4395 }
4396 }
4397 }
4398
4399 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004400 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004401}
4402
4403/* expand any name which is a directory */
4404struct hostsfile *expand_filelist(struct hostsfile *list)
4405{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004406 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004407 struct hostsfile *ah;
4408
Simon Kelley19c51cf2014-03-18 22:38:30 +00004409 /* find largest used index */
4410 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004411 {
4412 if (i <= ah->index)
4413 i = ah->index + 1;
4414
4415 if (ah->flags & AH_DIR)
4416 ah->flags |= AH_INACTIVE;
4417 else
4418 ah->flags &= ~AH_INACTIVE;
4419 }
4420
4421 for (ah = list; ah; ah = ah->next)
4422 if (!(ah->flags & AH_INACTIVE))
4423 {
4424 struct stat buf;
4425 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4426 {
4427 DIR *dir_stream;
4428 struct dirent *ent;
4429
4430 /* don't read this as a file */
4431 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004432
Simon Kelley28866e92011-02-14 20:19:14 +00004433 if (!(dir_stream = opendir(ah->fname)))
4434 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4435 ah->fname, strerror(errno));
4436 else
4437 {
4438 while ((ent = readdir(dir_stream)))
4439 {
4440 size_t lendir = strlen(ah->fname);
4441 size_t lenfile = strlen(ent->d_name);
4442 struct hostsfile *ah1;
4443 char *path;
4444
4445 /* ignore emacs backups and dotfiles */
4446 if (lenfile == 0 ||
4447 ent->d_name[lenfile - 1] == '~' ||
4448 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4449 ent->d_name[0] == '.')
4450 continue;
4451
4452 /* see if we have an existing record.
4453 dir is ah->fname
4454 file is ent->d_name
4455 path to match is ah1->fname */
4456
4457 for (ah1 = list; ah1; ah1 = ah1->next)
4458 {
4459 if (lendir < strlen(ah1->fname) &&
4460 strstr(ah1->fname, ah->fname) == ah1->fname &&
4461 ah1->fname[lendir] == '/' &&
4462 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4463 {
4464 ah1->flags &= ~AH_INACTIVE;
4465 break;
4466 }
4467 }
4468
4469 /* make new record */
4470 if (!ah1)
4471 {
4472 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4473 continue;
4474
4475 if (!(path = whine_malloc(lendir + lenfile + 2)))
4476 {
4477 free(ah1);
4478 continue;
4479 }
4480
4481 strcpy(path, ah->fname);
4482 strcat(path, "/");
4483 strcat(path, ent->d_name);
4484 ah1->fname = path;
4485 ah1->index = i++;
4486 ah1->flags = AH_DIR;
4487 ah1->next = list;
4488 list = ah1;
4489 }
4490
4491 /* inactivate record if not regular file */
4492 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4493 ah1->flags |= AH_INACTIVE;
4494
4495 }
4496 closedir(dir_stream);
4497 }
4498 }
4499 }
4500
4501 return list;
4502}
4503
Simon Kelley7b1eae42014-02-20 13:43:28 +00004504void read_servers_file(void)
4505{
4506 FILE *f;
4507
4508 if (!(f = fopen(daemon->servers_file, "r")))
4509 {
4510 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4511 return;
4512 }
4513
4514 mark_servers(SERV_FROM_FILE);
4515 cleanup_servers();
4516
4517 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4518}
4519
Simon Kelley28866e92011-02-14 20:19:14 +00004520
Simon Kelley7622fc02009-06-04 20:32:05 +01004521#ifdef HAVE_DHCP
Simon Kelley824af852008-02-12 20:43:05 +00004522void reread_dhcp(void)
4523{
Simon Kelley28866e92011-02-14 20:19:14 +00004524 struct hostsfile *hf;
4525
Simon Kelley824af852008-02-12 20:43:05 +00004526 if (daemon->dhcp_hosts_file)
4527 {
4528 struct dhcp_config *configs, *cp, **up;
Simon Kelley28866e92011-02-14 20:19:14 +00004529
Simon Kelley824af852008-02-12 20:43:05 +00004530 /* remove existing... */
4531 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4532 {
4533 cp = configs->next;
4534
4535 if (configs->flags & CONFIG_BANK)
4536 {
Simon Kelley9009d742008-11-14 20:04:27 +00004537 struct hwaddr_config *mac, *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004538 struct dhcp_netid_list *list, *tmplist;
Simon Kelley9009d742008-11-14 20:04:27 +00004539
4540 for (mac = configs->hwaddr; mac; mac = tmp)
4541 {
4542 tmp = mac->next;
4543 free(mac);
4544 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004545
Simon Kelley824af852008-02-12 20:43:05 +00004546 if (configs->flags & CONFIG_CLID)
4547 free(configs->clid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004548
4549 for (list = configs->netid; list; list = tmplist)
4550 {
4551 free(list->list);
4552 tmplist = list->next;
4553 free(list);
4554 }
4555
Simon Kelley824af852008-02-12 20:43:05 +00004556 if (configs->flags & CONFIG_NAME)
4557 free(configs->hostname);
4558
4559 *up = configs->next;
4560 free(configs);
4561 }
4562 else
4563 up = &configs->next;
4564 }
4565
Simon Kelley28866e92011-02-14 20:19:14 +00004566 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4567 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
4568 if (!(hf->flags & AH_INACTIVE))
4569 {
Simon Kelley395eb712012-07-06 22:07:05 +01004570 if (one_file(hf->fname, LOPT_BANK))
4571 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004572 }
Simon Kelley824af852008-02-12 20:43:05 +00004573 }
4574
4575 if (daemon->dhcp_opts_file)
4576 {
4577 struct dhcp_opt *opts, *cp, **up;
4578 struct dhcp_netid *id, *next;
4579
4580 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4581 {
4582 cp = opts->next;
4583
4584 if (opts->flags & DHOPT_BANK)
4585 {
Simon Kelley73a08a22009-02-05 20:28:08 +00004586 if ((opts->flags & DHOPT_VENDOR))
4587 free(opts->u.vendor_class);
Simon Kelley824af852008-02-12 20:43:05 +00004588 free(opts->val);
4589 for (id = opts->netid; id; id = next)
4590 {
4591 next = id->next;
4592 free(id->net);
4593 free(id);
4594 }
4595 *up = opts->next;
4596 free(opts);
4597 }
4598 else
4599 up = &opts->next;
4600 }
4601
Simon Kelley28866e92011-02-14 20:19:14 +00004602 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4603 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4604 if (!(hf->flags & AH_INACTIVE))
4605 {
Simon Kelley395eb712012-07-06 22:07:05 +01004606 if (one_file(hf->fname, LOPT_OPTS))
4607 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004608 }
Simon Kelley824af852008-02-12 20:43:05 +00004609 }
4610}
Simon Kelley7622fc02009-06-04 20:32:05 +01004611#endif
Simon Kelley824af852008-02-12 20:43:05 +00004612
Simon Kelley5aabfc72007-08-29 11:24:47 +01004613void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004614{
Simon Kelley824af852008-02-12 20:43:05 +00004615 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004616 int option, conffile_opt = '7', testmode = 0;
Simon Kelley90cb2222015-07-05 21:59:10 +01004617 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004618
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004619 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004620
Simon Kelley824af852008-02-12 20:43:05 +00004621 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004622 memset(daemon, 0, sizeof(struct daemon));
4623 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004624
Simon Kelley3be34542004-09-11 19:12:13 +01004625 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004626 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004627 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004628 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004629 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4630 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004631 daemon->default_resolv.is_default = 1;
4632 daemon->default_resolv.name = RESOLVFILE;
4633 daemon->resolv_files = &daemon->default_resolv;
4634 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004635 daemon->runfile = RUNFILE;
4636 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004637 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004638 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004639 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004640 daemon->auth_ttl = AUTH_TTL;
4641 daemon->soa_refresh = SOA_REFRESH;
4642 daemon->soa_retry = SOA_RETRY;
4643 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00004644 daemon->max_port = MAX_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01004645
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004646#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00004647 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
4648 add_txt("authors.bind", "Simon Kelley", 0);
4649 add_txt("copyright.bind", COPYRIGHT, 0);
4650 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
4651 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
4652 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
4653 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
4654 add_txt("hits.bind", NULL, TXT_STAT_HITS);
4655#ifdef HAVE_AUTH
4656 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
4657#endif
4658 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004659#endif
Simon Kelley0a852542005-03-23 20:28:59 +00004660
Simon Kelley849a8352006-06-09 21:02:31 +01004661 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004662 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004663#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004664 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004665#else
Simon Kelley849a8352006-06-09 21:02:31 +01004666 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004667#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004668
4669 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004670 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004671 for (; optind < argc; optind++)
4672 {
4673 unsigned char *c = (unsigned char *)argv[optind];
4674 for (; *c != 0; c++)
4675 if (!isspace(*c))
4676 die(_("junk found in command line"), NULL, EC_BADCONF);
4677 }
Simon Kelley28866e92011-02-14 20:19:14 +00004678 break;
4679 }
4680
Simon Kelley849a8352006-06-09 21:02:31 +01004681 /* Copy optarg so that argv doesn't get changed */
4682 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004683 {
Simon Kelley849a8352006-06-09 21:02:31 +01004684 strncpy(buff, optarg, MAXDNAME);
4685 buff[MAXDNAME-1] = 0;
4686 arg = buff;
4687 }
4688 else
4689 arg = NULL;
4690
4691 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004692 if (option == LOPT_TEST)
4693 testmode = 1;
4694 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004695 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004696#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004697 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004698 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004699#ifdef HAVE_DHCP6
4700 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4701 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004702#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004703 else
4704#endif
4705 do_usage();
4706
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004707 exit(0);
4708 }
Simon Kelley849a8352006-06-09 21:02:31 +01004709 else if (option == 'v')
4710 {
4711 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004712 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004713 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4714 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004715 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004716 exit(0);
4717 }
Simon Kelley849a8352006-06-09 21:02:31 +01004718 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004719 {
Simon Kelley28866e92011-02-14 20:19:14 +00004720 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004721 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004722 }
Simon Kelley849a8352006-06-09 21:02:31 +01004723 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004724 {
Simon Kelley26128d22004-11-14 16:43:54 +00004725#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00004726 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004727#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00004728 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004729#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004730 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004731 }
4732 }
Simon Kelley849a8352006-06-09 21:02:31 +01004733
4734 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00004735 {
4736 one_file(conffile, conffile_opt);
Simon Kelley90cb2222015-07-05 21:59:10 +01004737 if (conffile_opt == 0)
4738 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00004739 }
Simon Kelley849a8352006-06-09 21:02:31 +01004740
Simon Kelley1a6bca82008-07-11 11:11:42 +01004741 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004742 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004743 {
4744 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004745 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01004746 if (!(tmp->flags & SERV_HAS_SOURCE))
4747 {
4748 if (tmp->source_addr.sa.sa_family == AF_INET)
4749 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004750#ifdef HAVE_IPV6
Simon Kelley14ffa072016-04-25 16:36:44 +01004751 else if (tmp->source_addr.sa.sa_family == AF_INET6)
4752 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004753#endif
Simon Kelley14ffa072016-04-25 16:36:44 +01004754 }
4755 }
4756
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004757 if (daemon->host_records)
4758 {
4759 struct host_record *hr;
4760
4761 for (hr = daemon->host_records; hr; hr = hr->next)
4762 if (hr->ttl == -1)
4763 hr->ttl = daemon->local_ttl;
4764 }
4765
4766 if (daemon->cnames)
4767 {
Simon Kelley903df072017-01-19 17:22:00 +00004768 struct cname *cn, *cn2, *cn3;
4769
4770#define NOLOOP 1
4771#define TESTLOOP 2
4772
4773 /* Fill in TTL for CNAMES noe we have local_ttl.
4774 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004775 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00004776 {
4777 if (cn->ttl == -1)
4778 cn->ttl = daemon->local_ttl;
4779 cn->flag = 0;
4780 cn->targetp = NULL;
4781 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
4782 if (hostname_isequal(cn->target, cn2->alias))
4783 {
4784 cn->targetp = cn2;
4785 break;
4786 }
4787 }
4788
4789 /* Find any CNAME loops.*/
4790 for (cn = daemon->cnames; cn; cn = cn->next)
4791 {
4792 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
4793 {
4794 if (cn2->flag == NOLOOP)
4795 break;
4796
4797 if (cn2->flag == TESTLOOP)
4798 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
4799
4800 cn2->flag = TESTLOOP;
4801 }
4802
4803 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
4804 cn3->flag = NOLOOP;
4805 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004806 }
4807
Simon Kelley3be34542004-09-11 19:12:13 +01004808 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004809 {
4810 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004811 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004812 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004813 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004814#ifdef HAVE_IPV6
4815 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004816 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004817#endif /* IPv6 */
4818 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004819
4820 /* create default, if not specified */
4821 if (daemon->authserver && !daemon->hostmaster)
4822 {
4823 strcpy(buff, "hostmaster.");
4824 strcat(buff, daemon->authserver);
4825 daemon->hostmaster = opt_string_alloc(buff);
4826 }
4827
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004828 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004829 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004830 {
Simon Kelley0a852542005-03-23 20:28:59 +00004831 struct mx_srv_record *mx;
4832
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004833 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004834 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004835
Simon Kelley0a852542005-03-23 20:28:59 +00004836 for (mx = daemon->mxnames; mx; mx = mx->next)
4837 if (!mx->issrv && hostname_isequal(mx->name, buff))
4838 break;
4839
Simon Kelley28866e92011-02-14 20:19:14 +00004840 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004841 {
Simon Kelley824af852008-02-12 20:43:05 +00004842 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004843 mx->next = daemon->mxnames;
4844 mx->issrv = 0;
4845 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004846 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004847 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004848 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004849
Simon Kelley3be34542004-09-11 19:12:13 +01004850 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004851 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004852
4853 for (mx = daemon->mxnames; mx; mx = mx->next)
4854 if (!mx->issrv && !mx->target)
4855 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004856 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004857
Simon Kelley28866e92011-02-14 20:19:14 +00004858 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004859 daemon->resolv_files &&
4860 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004861 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004862 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004863
Simon Kelley28866e92011-02-14 20:19:14 +00004864 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004865 {
4866 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004867 FILE *f;
4868
Simon Kelley28866e92011-02-14 20:19:14 +00004869 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004870 !daemon->resolv_files ||
4871 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004872 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004873
Simon Kelley3be34542004-09-11 19:12:13 +01004874 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004875 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004876
4877 while ((line = fgets(buff, MAXDNAME, f)))
4878 {
4879 char *token = strtok(line, " \t\n\r");
4880
4881 if (!token || strcmp(token, "search") != 0)
4882 continue;
4883
4884 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004885 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004886 break;
4887 }
Simon Kelley3be34542004-09-11 19:12:13 +01004888
Simon Kelleyde379512004-06-22 20:23:33 +01004889 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004890
Simon Kelley3be34542004-09-11 19:12:13 +01004891 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004892 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004893 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004894
4895 if (daemon->domain_suffix)
4896 {
4897 /* add domain for any srv record without one. */
4898 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004899
Simon Kelley3d8df262005-08-29 12:19:27 +01004900 for (srv = daemon->mxnames; srv; srv = srv->next)
4901 if (srv->issrv &&
4902 strchr(srv->name, '.') &&
4903 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4904 {
4905 strcpy(buff, srv->name);
4906 strcat(buff, ".");
4907 strcat(buff, daemon->domain_suffix);
4908 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004909 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004910 }
4911 }
Simon Kelley28866e92011-02-14 20:19:14 +00004912 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004913 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004914
Simon Kelleyc8a80482014-03-05 14:29:54 +00004915 /* If there's access-control config, then ignore --local-service, it's intended
4916 as a system default to keep otherwise unconfigured installations safe. */
4917 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
4918 reset_option_bool(OPT_LOCAL_SERVICE);
4919
Simon Kelley7622fc02009-06-04 20:32:05 +01004920 if (testmode)
4921 {
4922 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
4923 exit(0);
4924 }
Simon Kelley849a8352006-06-09 21:02:31 +01004925}