blob: 5da5244be7a62bb5ca943cb169fdea338eaaad22 [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley849a8352006-06-09 21:02:31 +010017/* define this to get facilitynames */
18#define SYSLOG_NAMES
Simon Kelley9e4abcb2004-01-22 19:47:41 +000019#include "dnsmasq.h"
Simon Kelley824af852008-02-12 20:43:05 +000020#include <setjmp.h>
21
Simon Kelley7622fc02009-06-04 20:32:05 +010022static volatile int mem_recover = 0;
23static jmp_buf mem_jmp;
Simon Kelley395eb712012-07-06 22:07:05 +010024static int one_file(char *file, int hard_opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010025
Simon Kelley824af852008-02-12 20:43:05 +000026/* Solaris headers don't have facility names. */
27#ifdef HAVE_SOLARIS_NETWORK
28static const struct {
29 char *c_name;
30 unsigned int c_val;
31} facilitynames[] = {
32 { "kern", LOG_KERN },
33 { "user", LOG_USER },
34 { "mail", LOG_MAIL },
35 { "daemon", LOG_DAEMON },
36 { "auth", LOG_AUTH },
37 { "syslog", LOG_SYSLOG },
38 { "lpr", LOG_LPR },
39 { "news", LOG_NEWS },
40 { "uucp", LOG_UUCP },
Simon Kelley824af852008-02-12 20:43:05 +000041 { "audit", LOG_AUDIT },
Simon Kelley824af852008-02-12 20:43:05 +000042 { "cron", LOG_CRON },
43 { "local0", LOG_LOCAL0 },
44 { "local1", LOG_LOCAL1 },
45 { "local2", LOG_LOCAL2 },
46 { "local3", LOG_LOCAL3 },
47 { "local4", LOG_LOCAL4 },
48 { "local5", LOG_LOCAL5 },
49 { "local6", LOG_LOCAL6 },
50 { "local7", LOG_LOCAL7 },
51 { NULL, 0 }
52};
53#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000054
Simon Kelley849a8352006-06-09 21:02:31 +010055#ifndef HAVE_GETOPT_LONG
Simon Kelley9e4abcb2004-01-22 19:47:41 +000056struct myoption {
57 const char *name;
58 int has_arg;
59 int *flag;
60 int val;
61};
Simon Kelley849a8352006-06-09 21:02:31 +010062#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000063
Simon Kelley9009d742008-11-14 20:04:27 +000064#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
Simon Kelley9e4abcb2004-01-22 19:47:41 +000065
Simon Kelley16972692006-10-16 20:04:18 +010066/* options which don't have a one-char version */
Simon Kelleye98bd522014-03-28 20:41:23 +000067#define LOPT_RELOAD 256
68#define LOPT_NO_NAMES 257
69#define LOPT_TFTP 258
70#define LOPT_SECURE 259
71#define LOPT_PREFIX 260
72#define LOPT_PTR 261
73#define LOPT_BRIDGE 262
74#define LOPT_TFTP_MAX 263
75#define LOPT_FORCE 264
76#define LOPT_NOBLOCK 265
77#define LOPT_LOG_OPTS 266
78#define LOPT_MAX_LOGS 267
79#define LOPT_CIRCUIT 268
80#define LOPT_REMOTE 269
81#define LOPT_SUBSCR 270
82#define LOPT_INTNAME 271
83#define LOPT_BANK 272
84#define LOPT_DHCP_HOST 273
85#define LOPT_APREF 274
86#define LOPT_OVERRIDE 275
87#define LOPT_TFTPPORTS 276
88#define LOPT_REBIND 277
89#define LOPT_NOLAST 278
90#define LOPT_OPTS 279
91#define LOPT_DHCP_OPTS 280
92#define LOPT_MATCH 281
93#define LOPT_BROADCAST 282
94#define LOPT_NEGTTL 283
95#define LOPT_ALTPORT 284
96#define LOPT_SCRIPTUSR 285
97#define LOPT_LOCAL 286
98#define LOPT_NAPTR 287
99#define LOPT_MINPORT 288
100#define LOPT_DHCP_FQDN 289
101#define LOPT_CNAME 290
102#define LOPT_PXE_PROMT 291
103#define LOPT_PXE_SERV 292
104#define LOPT_TEST 293
105#define LOPT_TAG_IF 294
106#define LOPT_PROXY 295
107#define LOPT_GEN_NAMES 296
108#define LOPT_MAXTTL 297
109#define LOPT_NO_REBIND 298
110#define LOPT_LOC_REBND 299
111#define LOPT_ADD_MAC 300
112#define LOPT_DNSSEC 301
113#define LOPT_INCR_ADDR 302
114#define LOPT_CONNTRACK 303
115#define LOPT_FQDN 304
116#define LOPT_LUASCRIPT 305
117#define LOPT_RA 306
118#define LOPT_DUID 307
119#define LOPT_HOST_REC 308
120#define LOPT_TFTP_LC 309
121#define LOPT_RR 310
122#define LOPT_CLVERBIND 311
123#define LOPT_MAXCTTL 312
124#define LOPT_AUTHZONE 313
125#define LOPT_AUTHSERV 314
126#define LOPT_AUTHTTL 315
127#define LOPT_AUTHSOA 316
128#define LOPT_AUTHSFS 317
129#define LOPT_AUTHPEER 318
130#define LOPT_IPSET 319
131#define LOPT_SYNTH 320
Simon Kelleyc6309242013-03-07 20:59:28 +0000132#ifdef OPTION6_PREFIX_CLASS
Simon Kelleye98bd522014-03-28 20:41:23 +0000133#define LOPT_PREF_CLSS 321
Simon Kelleyc6309242013-03-07 20:59:28 +0000134#endif
Simon Kelleye98bd522014-03-28 20:41:23 +0000135#define LOPT_RELAY 323
136#define LOPT_RA_PARAM 324
137#define LOPT_ADD_SBNET 325
138#define LOPT_QUIET_DHCP 326
139#define LOPT_QUIET_DHCP6 327
140#define LOPT_QUIET_RA 328
141#define LOPT_SEC_VALID 329
142#define LOPT_TRUST_ANCHOR 330
143#define LOPT_DNSSEC_DEBUG 331
144#define LOPT_REV_SERV 332
145#define LOPT_SERVERS_FILE 333
146#define LOPT_DNSSEC_CHECK 334
Simon Kelleyc8a80482014-03-05 14:29:54 +0000147#define LOPT_LOCAL_SERVICE 335
Simon Kelleye98bd522014-03-28 20:41:23 +0000148#define LOPT_DNSSEC_TIME 336
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100149#define LOPT_LOOP_DETECT 337
Glen Huang32fc6db2014-12-27 15:28:12 +0000150#define LOPT_IGNORE_ADDR 338
RinSatsuki28de3872015-01-10 15:22:21 +0000151#define LOPT_MINCTTL 339
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000152#define LOPT_DHCP_INOTIFY 340
Simon Kelley70d18732015-01-31 19:59:29 +0000153#define LOPT_DHOPT_INOTIFY 341
154#define LOPT_HOST_INOTIFY 342
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000155#define LOPT_DNSSEC_STAMP 343
Stefan Tomanek30d08792015-03-31 22:32:11 +0100156#define LOPT_TFTP_NO_FAIL 344
Hans Dedecker926332a2016-01-23 10:48:12 +0000157#define LOPT_MAXPORT 345
Simon Kelley1e505122016-01-25 21:29:23 +0000158#define LOPT_CPE_ID 346
159#define LOPT_SCRIPT_ARP 347
Simon Kelley832e47b2016-02-24 21:24:45 +0000160#define LOPT_DHCPTTL 348
Simon Kelleybec366b2016-02-24 22:03:26 +0000161#define LOPT_TFTP_MTU 349
Floris Bos503c6092017-04-09 23:07:13 +0100162#define LOPT_REPLY_DELAY 350
Simon Kelley734d5312018-03-23 23:09:53 +0000163#define LOPT_RAPID_COMMIT 351
Simon Kelley6b173352018-05-08 18:32:14 +0100164#define LOPT_DUMPFILE 352
165#define LOPT_DUMPMASK 353
Simon Kelleybec366b2016-02-24 22:03:26 +0000166
Simon Kelley849a8352006-06-09 21:02:31 +0100167#ifdef HAVE_GETOPT_LONG
168static const struct option opts[] =
169#else
170static const struct myoption opts[] =
171#endif
172 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100173 { "version", 0, 0, 'v' },
174 { "no-hosts", 0, 0, 'h' },
175 { "no-poll", 0, 0, 'n' },
176 { "help", 0, 0, 'w' },
177 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000178 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100179 { "user", 2, 0, 'u' },
180 { "group", 2, 0, 'g' },
181 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000182 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100183 { "mx-host", 1, 0, 'm' },
184 { "mx-target", 1, 0, 't' },
185 { "cache-size", 2, 0, 'c' },
186 { "port", 1, 0, 'p' },
187 { "dhcp-leasefile", 2, 0, 'l' },
188 { "dhcp-lease", 1, 0, 'l' },
189 { "dhcp-host", 1, 0, 'G' },
190 { "dhcp-range", 1, 0, 'F' },
191 { "dhcp-option", 1, 0, 'O' },
192 { "dhcp-boot", 1, 0, 'M' },
193 { "domain", 1, 0, 's' },
194 { "domain-suffix", 1, 0, 's' },
195 { "interface", 1, 0, 'i' },
196 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000197 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100198 { "bogus-priv", 0, 0, 'b' },
199 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000200 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100201 { "selfmx", 0, 0, 'e' },
202 { "filterwin2k", 0, 0, 'f' },
203 { "pid-file", 2, 0, 'x' },
204 { "strict-order", 0, 0, 'o' },
205 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000206 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100207 { "local", 1, 0, LOPT_LOCAL },
208 { "address", 1, 0, 'A' },
209 { "conf-file", 2, 0, 'C' },
210 { "no-resolv", 0, 0, 'R' },
211 { "expand-hosts", 0, 0, 'E' },
212 { "localmx", 0, 0, 'L' },
213 { "local-ttl", 1, 0, 'T' },
214 { "no-negcache", 0, 0, 'N' },
215 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000216 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100217 { "query-port", 1, 0, 'Q' },
218 { "except-interface", 1, 0, 'I' },
219 { "no-dhcp-interface", 1, 0, '2' },
220 { "domain-needed", 0, 0, 'D' },
221 { "dhcp-lease-max", 1, 0, 'X' },
222 { "bind-interfaces", 0, 0, 'z' },
223 { "read-ethers", 0, 0, 'Z' },
224 { "alias", 1, 0, 'V' },
225 { "dhcp-vendorclass", 1, 0, 'U' },
226 { "dhcp-userclass", 1, 0, 'j' },
227 { "dhcp-ignore", 1, 0, 'J' },
228 { "edns-packet-max", 1, 0, 'P' },
229 { "keep-in-foreground", 0, 0, 'k' },
230 { "dhcp-authoritative", 0, 0, 'K' },
231 { "srv-host", 1, 0, 'W' },
232 { "localise-queries", 0, 0, 'y' },
233 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100234 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100235 { "enable-dbus", 2, 0, '1' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100236 { "bootp-dynamic", 2, 0, '3' },
237 { "dhcp-mac", 1, 0, '4' },
238 { "no-ping", 0, 0, '5' },
239 { "dhcp-script", 1, 0, '6' },
240 { "conf-dir", 1, 0, '7' },
241 { "log-facility", 1, 0 ,'8' },
242 { "leasefile-ro", 0, 0, '9' },
243 { "dns-forward-max", 1, 0, '0' },
244 { "clear-on-reload", 0, 0, LOPT_RELOAD },
245 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100246 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100247 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100248 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100249 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100250 { "tftp-root", 1, 0, LOPT_PREFIX },
251 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000252 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100253 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100254 { "ptr-record", 1, 0, LOPT_PTR },
255 { "naptr-record", 1, 0, LOPT_NAPTR },
256 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
257 { "dhcp-option-force", 1, 0, LOPT_FORCE },
258 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
259 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
260 { "log-async", 2, 0, LOPT_MAX_LOGS },
261 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
262 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
263 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
264 { "interface-name", 1, 0, LOPT_INTNAME },
265 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
266 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000267 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000268 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100269 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
270 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
271 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100272 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100273 { "all-servers", 0, 0, LOPT_NOLAST },
274 { "dhcp-match", 1, 0, LOPT_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100275 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100276 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100277 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000278 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100279 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100280 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
281 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
282 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000283 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100284 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
285 { "cname", 1, 0, LOPT_CNAME },
286 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
287 { "pxe-service", 1, 0, LOPT_PXE_SERV },
288 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100289 { "tag-if", 1, 0, LOPT_TAG_IF },
290 { "dhcp-proxy", 2, 0, LOPT_PROXY },
291 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
292 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000293 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100294 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000295 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000296 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100297 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
298 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000299 { "dhcp-client-update", 0, 0, LOPT_FQDN },
300 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000301 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000302 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000303 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100304 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000305 { "auth-zone", 1, 0, LOPT_AUTHZONE },
306 { "auth-server", 1, 0, LOPT_AUTHSERV },
307 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
308 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000309 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000310 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000311 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100312 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200313 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000314 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000315 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100316 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000317 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000318 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyc6309242013-03-07 20:59:28 +0000319#ifdef OPTION6_PREFIX_CLASS
320 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
321#endif
Simon Kelleyff7eea22013-09-04 18:01:38 +0100322 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100323 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100324 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
325 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
326 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100327 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000328 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000329 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100330 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000331 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100332 { "dumpfile", 1, 0, LOPT_DUMPFILE },
333 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Simon Kelley849a8352006-06-09 21:02:31 +0100334 { NULL, 0, 0, 0 }
335 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000336
Simon Kelley28866e92011-02-14 20:19:14 +0000337
338#define ARG_DUP OPT_LAST
339#define ARG_ONE OPT_LAST + 1
340#define ARG_USED_CL OPT_LAST + 2
341#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000342
Simon Kelley1a6bca82008-07-11 11:11:42 +0100343static struct {
344 int opt;
345 unsigned int rept;
346 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000347 char * const desc;
348 char * const arg;
349} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000350 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
351 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100352 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000353 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
354 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
355 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100356 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
357 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
358 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
359 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
360 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000361 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
362 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100363 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000364 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
365 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000366 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000367 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100368 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100369 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000370 { '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 +0000371 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000372 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
373 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100374 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
375 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
376 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
377 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
378 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
379 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100380 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
381 { '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 +0000382 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100383 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000384 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100385 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
386 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
387 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
388 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
389 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
390 { 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 +0000391 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
392 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000393 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000394 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100395 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000396 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000397 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000398 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000399 { 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 +0000400 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000401 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000402 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
403 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
404 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
405 { 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 +0000406 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
407 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000408 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100409 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100410 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000411 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
412 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100413 { '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 +0000414 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
415 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100416 { '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 +0000417 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
418 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
419 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100420 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
421 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100422 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000423 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100424 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
425 { '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 +0000426 { 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 +0100427 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000428 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
429 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
430 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000431 { 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 +0000432 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000433 { '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 +0100434 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000435 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100436 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100437 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100438 { 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 +0100439 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100440 { 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 +0100441 { 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 +0100442 { 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 +0100443 { 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 +0000444 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000445 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100446 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100447 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100448 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
449 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000450 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { 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 +0100452 { 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 +0000453 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100454 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100455 { 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 +0100456 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100457 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
458 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000459 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000460 { 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 +0000461 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
462 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100463 { 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 +0000464 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100465 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
466 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
467 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000468 { 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 +0100469 { 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 +0000470 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100471 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100472 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
473 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000474 { 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 +0000475 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000476 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000477 { 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 +0100478 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000479 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000480 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000481 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000482 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000483 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000484 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
485 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100486 { 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 +0100487 { 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 +0000488 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000489 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000490 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100491 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000492 { 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 +0000493 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000494#ifdef OPTION6_PREFIX_CLASS
495 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
496#endif
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100497 { 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 +0100498 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
499 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
500 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000501 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
502 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000503 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000504 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100505 { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
Simon Kelley734d5312018-03-23 23:09:53 +0000506 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100507 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
508 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100509 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000510};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000511
Josh Soref730c6742017-02-06 16:14:04 +0000512/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100513 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 +0100514 following sequence so that they map to themselves: it is therefore possible to call
515 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000516 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100517 couple of other places.
518 Note that space is included here so that
519 --dhcp-option=3, string
520 has five characters, whilst
521 --dhcp-option=3," string"
522 has six.
523*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100524
Simon Kelleyf2621c72007-04-29 19:47:21 +0100525static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100526
527static char hide_meta(char c)
528{
529 unsigned int i;
530
531 for (i = 0; i < (sizeof(meta) - 1); i++)
532 if (c == meta[i])
533 return (char)i;
534
535 return c;
536}
537
538static char unhide_meta(char cr)
539{
540 unsigned int c = cr;
541
542 if (c < (sizeof(meta) - 1))
543 cr = meta[c];
544
545 return cr;
546}
547
548static void unhide_metas(char *cp)
549{
550 if (cp)
551 for(; *cp; cp++)
552 *cp = unhide_meta(*cp);
553}
554
Simon Kelley824af852008-02-12 20:43:05 +0000555static void *opt_malloc(size_t size)
556{
557 void *ret;
558
559 if (mem_recover)
560 {
561 ret = whine_malloc(size);
562 if (!ret)
563 longjmp(mem_jmp, 1);
564 }
565 else
566 ret = safe_malloc(size);
567
568 return ret;
569}
570
571static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100572{
573 char *ret = NULL;
574
575 if (cp && strlen(cp) != 0)
576 {
Simon Kelley824af852008-02-12 20:43:05 +0000577 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100578 strcpy(ret, cp);
579
580 /* restore hidden metachars */
581 unhide_metas(ret);
582 }
583
584 return ret;
585}
586
Simon Kelley3d8df262005-08-29 12:19:27 +0100587
Simon Kelleyf2621c72007-04-29 19:47:21 +0100588/* find next comma, split string with zero and eliminate spaces.
589 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000590
591static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100592{
593 char *comma, *p;
594
Simon Kelley73a08a22009-02-05 20:28:08 +0000595 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100596 return NULL;
597
598 p = comma;
599 *comma = ' ';
600
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100601 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100602
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100603 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100604 *p = 0;
605
606 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100607}
608
Simon Kelley73a08a22009-02-05 20:28:08 +0000609static char *split(char *s)
610{
611 return split_chr(s, ',');
612}
613
Simon Kelley1f15b812009-10-13 17:49:32 +0100614static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100615{
Simon Kelley1f15b812009-10-13 17:49:32 +0100616 char *ret;
617 int nomem;
618
Simon Kelley3d8df262005-08-29 12:19:27 +0100619 if (!s)
620 return 0;
621
622 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100623 if (!(ret = canonicalise(s, &nomem)) && nomem)
624 {
625 if (mem_recover)
626 longjmp(mem_jmp, 1);
627 else
628 die(_("could not get memory"), NULL, EC_NOMEM);
629 }
630
631 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100632}
633
634static int atoi_check(char *a, int *res)
635{
636 char *p;
637
638 if (!a)
639 return 0;
640
641 unhide_metas(a);
642
643 for (p = a; *p; p++)
644 if (*p < '0' || *p > '9')
645 return 0;
646
647 *res = atoi(a);
648 return 1;
649}
650
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100651static int atoi_check16(char *a, int *res)
652{
653 if (!(atoi_check(a, res)) ||
654 *res < 0 ||
655 *res > 0xffff)
656 return 0;
657
658 return 1;
659}
Simon Kelleyee415862014-02-11 11:07:22 +0000660
Simon Kelleyde73a492014-02-17 21:43:27 +0000661#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000662static int atoi_check8(char *a, int *res)
663{
664 if (!(atoi_check(a, res)) ||
665 *res < 0 ||
666 *res > 0xff)
667 return 0;
668
669 return 1;
670}
Simon Kelleyde73a492014-02-17 21:43:27 +0000671#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100672
673#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000674static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000675{
Simon Kelley824af852008-02-12 20:43:05 +0000676 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000677
678 if (txt)
679 {
680 size_t len = strlen(txt);
681 r->txt = opt_malloc(len+1);
682 r->len = len+1;
683 *(r->txt) = len;
684 memcpy((r->txt)+1, txt, len);
685 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100686
Simon Kelleyfec216d2014-03-27 20:54:34 +0000687 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000688 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000689 r->next = daemon->txt;
690 daemon->txt = r;
691 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000692}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100693#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000694
Simon Kelley849a8352006-06-09 21:02:31 +0100695static void do_usage(void)
696{
697 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000698 int i, j;
699
700 struct {
701 char handle;
702 int val;
703 } tab[] = {
704 { '$', CACHESIZ },
705 { '*', EDNS_PKTSZ },
706 { '&', MAXLEASES },
707 { '!', FTABSIZ },
708 { '#', TFTP_MAX_CONNECTIONS },
709 { '\0', 0 }
710 };
Simon Kelley849a8352006-06-09 21:02:31 +0100711
712 printf(_("Usage: dnsmasq [options]\n\n"));
713#ifndef HAVE_GETOPT_LONG
714 printf(_("Use short options only on the command line.\n"));
715#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100716 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100717
Simon Kelley1a6bca82008-07-11 11:11:42 +0100718 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100719 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100720 char *desc = usage[i].flagdesc;
721 char *eq = "=";
722
723 if (!desc || *desc == '[')
724 eq = "";
725
726 if (!desc)
727 desc = "";
728
729 for ( j = 0; opts[j].name; j++)
730 if (opts[j].val == usage[i].opt)
731 break;
732 if (usage[i].opt < 256)
733 sprintf(buff, "-%c, ", usage[i].opt);
734 else
735 sprintf(buff, " ");
736
737 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100738 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100739
Simon Kelley849a8352006-06-09 21:02:31 +0100740 if (usage[i].arg)
741 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000742 strcpy(buff, usage[i].arg);
743 for (j = 0; tab[j].handle; j++)
744 if (tab[j].handle == *(usage[i].arg))
745 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100746 }
Simon Kelley849a8352006-06-09 21:02:31 +0100747 printf(_(usage[i].desc), buff);
748 printf("\n");
749 }
750}
751
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100752#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
753
Ed Bardsleya7369be2015-08-05 21:17:18 +0100754static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
755{
756 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
757 addr->sa.sa_family = AF_INET;
758#ifdef HAVE_IPV6
759 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
760 addr->sa.sa_family = AF_INET6;
761#endif
762 else
763 return _("bad address");
764
765 return NULL;
766}
767
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100768char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
769{
770 int source_port = 0, serv_port = NAMESERVER_PORT;
771 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000772 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100773#ifdef HAVE_IPV6
774 int scope_index = 0;
775 char *scope_id;
776#endif
777
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000778 if (!arg || strlen(arg) == 0)
779 {
780 *flags |= SERV_NO_ADDR;
781 *interface = 0;
782 return NULL;
783 }
784
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100785 if ((source = split_chr(arg, '@')) && /* is there a source. */
786 (portno = split_chr(source, '#')) &&
787 !atoi_check16(portno, &source_port))
788 return _("bad port");
789
790 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
791 !atoi_check16(portno, &serv_port))
792 return _("bad port");
793
794#ifdef HAVE_IPV6
795 scope_id = split_chr(arg, '%');
796#endif
797
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000798 if (source) {
799 interface_opt = split_chr(source, '@');
800
801 if (interface_opt)
802 {
803#if defined(SO_BINDTODEVICE)
804 strncpy(interface, interface_opt, IF_NAMESIZE - 1);
805#else
806 return _("interface binding not supported");
807#endif
808 }
809 }
810
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100811 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100812 {
813 addr->in.sin_port = htons(serv_port);
814 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
815#ifdef HAVE_SOCKADDR_SA_LEN
816 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
817#endif
818 source_addr->in.sin_addr.s_addr = INADDR_ANY;
819 source_addr->in.sin_port = htons(daemon->query_port);
820
821 if (source)
822 {
823 if (flags)
824 *flags |= SERV_HAS_SOURCE;
825 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100826 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100827 {
828#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000829 if (interface_opt)
830 return _("interface can only be specified once");
831
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100832 source_addr->in.sin_addr.s_addr = INADDR_ANY;
833 strncpy(interface, source, IF_NAMESIZE - 1);
834#else
835 return _("interface binding not supported");
836#endif
837 }
838 }
839 }
840#ifdef HAVE_IPV6
841 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
842 {
843 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
844 return _("bad interface name");
845
846 addr->in6.sin6_port = htons(serv_port);
847 addr->in6.sin6_scope_id = scope_index;
848 source_addr->in6.sin6_addr = in6addr_any;
849 source_addr->in6.sin6_port = htons(daemon->query_port);
850 source_addr->in6.sin6_scope_id = 0;
851 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
852 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
853#ifdef HAVE_SOCKADDR_SA_LEN
854 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
855#endif
856 if (source)
857 {
858 if (flags)
859 *flags |= SERV_HAS_SOURCE;
860 source_addr->in6.sin6_port = htons(source_port);
861 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
862 {
863#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000864 if (interface_opt)
865 return _("interface can only be specified once");
866
867 source_addr->in6.sin6_addr = in6addr_any;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100868 strncpy(interface, source, IF_NAMESIZE - 1);
869#else
870 return _("interface binding not supported");
871#endif
872 }
873 }
874 }
875#endif
876 else
877 return _("bad address");
878
879 return NULL;
880}
881
Simon Kelleyde73a492014-02-17 21:43:27 +0000882static struct server *add_rev4(struct in_addr addr, int msize)
883{
884 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000885 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000886 char *p;
887
888 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000889 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
890
891 switch (msize)
892 {
893 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100894 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000895 /* fall through */
896 case 24:
897 p += sprintf(p, "%d.", (a >> 8) & 0xff);
898 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000899 case 16:
900 p += sprintf(p, "%d.", (a >> 16) & 0xff);
901 /* fall through */
902 case 8:
903 p += sprintf(p, "%d.", (a >> 24) & 0xff);
904 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000905 default:
906 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000907 }
908
909 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000910
911 serv->flags = SERV_HAS_DOMAIN;
912 serv->next = daemon->servers;
913 daemon->servers = serv;
914
915 return serv;
916
917}
918
919static struct server *add_rev6(struct in6_addr *addr, int msize)
920{
921 struct server *serv = opt_malloc(sizeof(struct server));
922 char *p;
923 int i;
924
925 memset(serv, 0, sizeof(struct server));
926 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
927
928 for (i = msize-1; i >= 0; i -= 4)
929 {
930 int dig = ((unsigned char *)addr)[i>>3];
931 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
932 }
933 p += sprintf(p, "ip6.arpa");
934
935 serv->flags = SERV_HAS_DOMAIN;
936 serv->next = daemon->servers;
937 daemon->servers = serv;
938
939 return serv;
940}
941
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000942#ifdef HAVE_DHCP
943
944static int is_tag_prefix(char *arg)
945{
946 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
947 return 1;
948
949 return 0;
950}
951
952static char *set_prefix(char *arg)
953{
954 if (strstr(arg, "set:") == arg)
955 return arg+4;
956
957 return arg;
958}
959
Simon Kelley832af0b2007-01-21 20:01:28 +0000960/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100961static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000962{
Simon Kelley824af852008-02-12 20:43:05 +0000963 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000964 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000965 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100966 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100967 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000968 u16 opt_len = 0;
969 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100970 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000971
972 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000973 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000974 new->netid = NULL;
975 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100976 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000977
Simon Kelleyf2621c72007-04-29 19:47:21 +0100978 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000979 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100980 comma = split(arg);
981
982 for (cp = arg; *cp; cp++)
983 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000984 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100985
986 if (!*cp)
987 {
988 new->opt = atoi(arg);
989 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100990 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100991 break;
992 }
993
994 if (strstr(arg, "option:") == arg)
995 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100996 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
997 {
998 opt_len = lookup_dhcp_len(AF_INET, new->opt);
999 /* option:<optname> must follow tag and vendor string. */
1000 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1001 option_ok = 1;
1002 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001003 break;
1004 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001005#ifdef HAVE_DHCP6
1006 else if (strstr(arg, "option6:") == arg)
1007 {
1008 for (cp = arg+8; *cp; cp++)
1009 if (*cp < '0' || *cp > '9')
1010 break;
1011
1012 if (!*cp)
1013 {
1014 new->opt = atoi(arg+8);
1015 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001016 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001017 }
1018 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001019 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001020 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1021 {
1022 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1023 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1024 option_ok = 1;
1025 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001026 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001027 /* option6:<opt>|<optname> must follow tag and vendor string. */
1028 is6 = 1;
1029 break;
1030 }
1031#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001032 else if (strstr(arg, "vendor:") == arg)
1033 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001034 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1035 new->flags |= DHOPT_VENDOR;
1036 }
1037 else if (strstr(arg, "encap:") == arg)
1038 {
1039 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001040 new->flags |= DHOPT_ENCAPSULATE;
1041 }
Simon Kelley316e2732010-01-22 20:16:09 +00001042 else if (strstr(arg, "vi-encap:") == arg)
1043 {
1044 new->u.encap = atoi(arg+9);
1045 new->flags |= DHOPT_RFC3925;
1046 if (flags == DHOPT_MATCH)
1047 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001048 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001049 break;
1050 }
1051 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001052 else
1053 {
Simon Kelley824af852008-02-12 20:43:05 +00001054 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001055 /* allow optional "net:" or "tag:" for consistency */
1056 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +00001057 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001058 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001059 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001060 new->netid->next = np;
1061 np = new->netid;
1062 }
1063
1064 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001065 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001066
1067#ifdef HAVE_DHCP6
1068 if (is6)
1069 {
1070 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001071 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001072
1073 if (opt_len == 0 &&
1074 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001075 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001076 }
1077 else
1078#endif
1079 if (opt_len == 0 &&
1080 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001081 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001082
Simon Kelley316e2732010-01-22 20:16:09 +00001083 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001084 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001085 ret_err(_("bad dhcp-option"));
1086
1087 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001088 {
1089 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001090 char c;
Simon Kelley28866e92011-02-14 20:19:14 +00001091 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001092 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001093 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001094 dots = 0;
1095 for (cp = comma; (c = *cp); cp++)
1096 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001097 {
1098 addrs++;
1099 is_dec = is_hex = 0;
1100 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001101 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001102 {
1103 digs++;
1104 is_dec = is_addr = 0;
1105 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001106 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001107 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001108 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001109 if (cp == comma) /* leading / means a pathname */
1110 is_addr = 0;
1111 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112 else if (c == '.')
1113 {
Simon Kelley23245c02012-07-18 16:21:11 +01001114 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001115 dots++;
1116 }
1117 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001118 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001119 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001120 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001121 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001122 {
1123 is_addr = 0;
1124 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001125 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001126 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001127 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001128 *cp = 0;
1129 }
1130 else
1131 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001132 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001133 (c >='a' && c <= 'f') ||
1134 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001135 {
1136 is_hex = 0;
1137 if (c != '[' && c != ']')
1138 is_addr6 = 0;
1139 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001140 }
Simon Kelley28866e92011-02-14 20:19:14 +00001141 else
1142 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001143
Simon Kelley28866e92011-02-14 20:19:14 +00001144 if (!found_dig)
1145 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001146
Simon Kelleyf2621c72007-04-29 19:47:21 +01001147 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001148 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001149 {
1150 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001151
1152 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001153 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001154
1155 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001156 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001157 }
Simon Kelley28866e92011-02-14 20:19:14 +00001158 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001159 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1160 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001161
1162 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1163 {
1164 int val, fac = 1;
1165
1166 switch (comma[strlen(comma) - 1])
1167 {
Simon Kelley42243212012-07-20 15:19:18 +01001168 case 'w':
1169 case 'W':
1170 fac *= 7;
1171 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001172 case 'd':
1173 case 'D':
1174 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001175 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001176 case 'h':
1177 case 'H':
1178 fac *= 60;
1179 /* fall through */
1180 case 'm':
1181 case 'M':
1182 fac *= 60;
1183 /* fall through */
1184 case 's':
1185 case 'S':
1186 comma[strlen(comma) - 1] = 0;
1187 }
1188
1189 new->len = 4;
1190 new->val = opt_malloc(4);
1191 val = atoi(comma);
1192 *((int *)new->val) = htonl(val * fac);
1193 }
1194 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001195 {
1196 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001197 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001198 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1199 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001200 }
1201 else if (is_dec)
1202 {
1203 int i, val = atoi(comma);
1204 /* assume numeric arg is 1 byte except for
1205 options where it is known otherwise.
1206 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001207 if (opt_len != 0)
1208 new->len = opt_len;
1209 else if (val & 0xffff0000)
1210 new->len = 4;
1211 else if (val & 0xff00)
1212 new->len = 2;
1213 else
1214 new->len = 1;
1215
Simon Kelley832af0b2007-01-21 20:01:28 +00001216 if (lenchar == 'b')
1217 new->len = 1;
1218 else if (lenchar == 's')
1219 new->len = 2;
1220 else if (lenchar == 'i')
1221 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001222
Simon Kelley824af852008-02-12 20:43:05 +00001223 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001224 for (i=0; i<new->len; i++)
1225 new->val[i] = val>>((new->len - i - 1)*8);
1226 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001227 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001228 {
1229 struct in_addr in;
1230 unsigned char *op;
1231 char *slash;
1232 /* max length of address/subnet descriptor is five bytes,
1233 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001234 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001235 new->flags |= DHOPT_ADDR;
1236
Simon Kelley572b41e2011-02-18 18:11:18 +00001237 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1238 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001239 {
Simon Kelley6b010842007-02-12 20:32:07 +00001240 *(op++) = 1; /* RFC 3361 "enc byte" */
1241 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001242 }
1243 while (addrs--)
1244 {
1245 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001246 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001247 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001248 if (!inet_pton(AF_INET, cp, &in))
1249 ret_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001250 if (!slash)
1251 {
1252 memcpy(op, &in, INADDRSZ);
1253 op += INADDRSZ;
1254 }
1255 else
1256 {
1257 unsigned char *p = (unsigned char *)&in;
1258 int netsize = atoi(slash);
1259 *op++ = netsize;
1260 if (netsize > 0)
1261 *op++ = *p++;
1262 if (netsize > 8)
1263 *op++ = *p++;
1264 if (netsize > 16)
1265 *op++ = *p++;
1266 if (netsize > 24)
1267 *op++ = *p++;
1268 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1269 }
1270 }
1271 new->len = op - new->val;
1272 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001273 else if (is_addr6 && is6)
1274 {
1275 unsigned char *op;
1276 new->val = op = opt_malloc(16 * addrs);
1277 new->flags |= DHOPT_ADDR6;
1278 while (addrs--)
1279 {
1280 cp = comma;
1281 comma = split(cp);
1282
1283 /* check for [1234::7] */
1284 if (*cp == '[')
1285 cp++;
1286 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1287 cp[strlen(cp)-1] = 0;
1288
1289 if (inet_pton(AF_INET6, cp, op))
1290 {
1291 op += IN6ADDRSZ;
1292 continue;
1293 }
1294
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001295 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001296 }
1297 new->len = op - new->val;
1298 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001299 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001300 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001301 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001302 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001303 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001304 {
1305 /* dns search, RFC 3397, or SIP, RFC 3361 */
1306 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001307 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001308 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001309 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001310
1311 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001312 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001313
1314 while (arg && *arg)
1315 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001316 char *in, *dom = NULL;
1317 size_t domlen = 1;
1318 /* Allow "." as an empty domain */
1319 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001320 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001321 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001322 ret_err(_("bad domain in dhcp-option"));
1323
Simon Kelleyc52e1892010-06-07 22:01:39 +01001324 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001325 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001326
1327 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001328 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001329 {
1330 memcpy(newp, m, header_size + len);
1331 free(m);
1332 }
Simon Kelley824af852008-02-12 20:43:05 +00001333 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001334 p = m + header_size;
1335 q = p + len;
1336
1337 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001338 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001339 {
1340 unsigned char *cp = q++;
1341 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001342 for (j = 0; *in && (*in != '.'); in++, j++)
1343 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001344 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001345 if (*in)
1346 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001347 }
1348 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001349 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001350
Simon Kelley832af0b2007-01-21 20:01:28 +00001351 /* Now tail-compress using earlier names. */
1352 newlen = q - p;
1353 for (tail = p + len; *tail; tail += (*tail) + 1)
1354 for (r = p; r - p < (int)len; r += (*r) + 1)
1355 if (strcmp((char *)r, (char *)tail) == 0)
1356 {
1357 PUTSHORT((r - p) | 0xc000, tail);
1358 newlen = tail - p;
1359 goto end;
1360 }
1361 end:
1362 len = newlen;
1363
1364 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001365 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001366 }
1367
1368 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001369 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001370 m[0] = 0;
1371 new->len = (int) len + header_size;
1372 new->val = m;
1373 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001374#ifdef HAVE_DHCP6
1375 else if (comma && (opt_len & OT_CSTRING))
1376 {
1377 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001378 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001379 unsigned char *p, *newp;
1380
Simon Kelley40ef23b2012-03-13 21:59:28 +00001381 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001382 if (comma[i] == ',')
1383 commas++;
1384
1385 newp = opt_malloc(strlen(comma)+(2*commas));
1386 p = newp;
1387 arg = comma;
1388 comma = split(arg);
1389
1390 while (arg && *arg)
1391 {
1392 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001393 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001394 PUTSHORT(len, p);
1395 memcpy(p, arg, len);
1396 p += len;
1397
1398 arg = comma;
1399 comma = split(arg);
1400 }
1401
1402 new->val = newp;
1403 new->len = p - newp;
1404 }
1405 else if (comma && (opt_len & OT_RFC1035_NAME))
1406 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001407 unsigned char *p = NULL, *newp, *end;
1408 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001409 arg = comma;
1410 comma = split(arg);
1411
1412 while (arg && *arg)
1413 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001414 char *dom = canonicalise_opt(arg);
1415 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001416 ret_err(_("bad domain in dhcp-option"));
1417
Simon Kelley18f0fb02012-03-31 21:18:55 +01001418 newp = opt_malloc(len + strlen(dom) + 2);
1419
1420 if (p)
1421 {
1422 memcpy(newp, p, len);
1423 free(p);
1424 }
1425
1426 p = newp;
Simon Kelley0549c732017-09-25 18:17:11 +01001427 end = do_rfc1035_name(p + len, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001428 *end++ = 0;
1429 len = end - p;
1430 free(dom);
1431
Simon Kelley4cb1b322012-02-06 14:30:41 +00001432 arg = comma;
1433 comma = split(arg);
1434 }
1435
Simon Kelley18f0fb02012-03-31 21:18:55 +01001436 new->val = p;
1437 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001438 }
1439#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001440 else
1441 {
1442 new->len = strlen(comma);
1443 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001444 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001445 new->flags |= DHOPT_STRING;
1446 }
1447 }
1448 }
1449
Simon Kelley4cb1b322012-02-06 14:30:41 +00001450 if (!is6 &&
1451 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001452 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001453 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001454 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001455
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001456 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001457 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001458 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1459 !new->netid ||
1460 new->netid->next)
1461 ret_err(_("illegal dhcp-match"));
1462
1463 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001464 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001465 new->next = daemon->dhcp_match6;
1466 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001467 }
1468 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001469 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001470 new->next = daemon->dhcp_match;
1471 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001472 }
Simon Kelley824af852008-02-12 20:43:05 +00001473 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001474 else if (is6)
1475 {
1476 new->next = daemon->dhcp_opts6;
1477 daemon->dhcp_opts6 = new;
1478 }
1479 else
1480 {
1481 new->next = daemon->dhcp_opts;
1482 daemon->dhcp_opts = new;
1483 }
1484
1485 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001486}
1487
Simon Kelley7622fc02009-06-04 20:32:05 +01001488#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001489
Simon Kelley28866e92011-02-14 20:19:14 +00001490void set_option_bool(unsigned int opt)
1491{
1492 if (opt < 32)
1493 daemon->options |= 1u << opt;
1494 else
1495 daemon->options2 |= 1u << (opt - 32);
1496}
1497
Simon Kelley2b5bae92012-06-26 16:55:23 +01001498void reset_option_bool(unsigned int opt)
1499{
1500 if (opt < 32)
1501 daemon->options &= ~(1u << opt);
1502 else
1503 daemon->options2 &= ~(1u << (opt - 32));
1504}
1505
Simon Kelley7b1eae42014-02-20 13:43:28 +00001506static 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 +01001507{
1508 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001509 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001510
Simon Kelley832af0b2007-01-21 20:01:28 +00001511 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001512 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001513
Simon Kelley1a6bca82008-07-11 11:11:42 +01001514 for (i=0; usage[i].opt != 0; i++)
1515 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001516 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001517 int rept = usage[i].rept;
1518
Simon Kelley28866e92011-02-14 20:19:14 +00001519 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001520 {
1521 /* command line */
1522 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001523 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001524 if (rept == ARG_ONE)
1525 usage[i].rept = ARG_USED_CL;
1526 }
1527 else
1528 {
1529 /* allow file to override command line */
1530 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001531 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001532 if (rept == ARG_USED_CL || rept == ARG_ONE)
1533 usage[i].rept = ARG_USED_FILE;
1534 }
1535
1536 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1537 {
Simon Kelley28866e92011-02-14 20:19:14 +00001538 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001539 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001540 }
1541
1542 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001543 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001544
Simon Kelley849a8352006-06-09 21:02:31 +01001545 switch (option)
1546 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001547 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001548 {
Simon Kelley824af852008-02-12 20:43:05 +00001549 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001550 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001551 {
Simon Kelley28866e92011-02-14 20:19:14 +00001552 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001553 free(file);
1554 }
Simon Kelley849a8352006-06-09 21:02:31 +01001555 break;
1556 }
1557
Simon Kelleyf2621c72007-04-29 19:47:21 +01001558 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001559 {
1560 DIR *dir_stream;
1561 struct dirent *ent;
1562 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001563 struct list {
1564 char *suffix;
1565 struct list *next;
Simon Kelley3e1551a2014-09-09 21:46:07 +01001566 } *ignore_suffix = NULL, *match_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001567
Simon Kelley1f15b812009-10-13 17:49:32 +01001568 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001569 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001570 break;
1571
Simon Kelley1f15b812009-10-13 17:49:32 +01001572 for (arg = comma; arg; arg = comma)
1573 {
1574 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001575 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001576 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001577 li = opt_malloc(sizeof(struct list));
1578 if (*arg == '*')
1579 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001580 /* "*" with no suffix is a no-op */
1581 if (arg[1] == 0)
1582 free(li);
1583 else
1584 {
1585 li->next = match_suffix;
1586 match_suffix = li;
1587 /* Have to copy: buffer is overwritten */
1588 li->suffix = opt_string_alloc(arg+1);
1589 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001590 }
1591 else
1592 {
1593 li->next = ignore_suffix;
1594 ignore_suffix = li;
1595 /* Have to copy: buffer is overwritten */
1596 li->suffix = opt_string_alloc(arg);
1597 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001598 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001599 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001600
Simon Kelley849a8352006-06-09 21:02:31 +01001601 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001602 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001603
Simon Kelley849a8352006-06-09 21:02:31 +01001604 while ((ent = readdir(dir_stream)))
1605 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001606 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001607 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001608
1609 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001610 if (len == 0 ||
1611 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001612 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1613 ent->d_name[0] == '.')
1614 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001615
Simon Kelley3e1551a2014-09-09 21:46:07 +01001616 if (match_suffix)
1617 {
1618 for (li = match_suffix; li; li = li->next)
1619 {
1620 /* check for required suffices */
1621 size_t ls = strlen(li->suffix);
1622 if (len > ls &&
1623 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1624 break;
1625 }
1626 if (!li)
1627 continue;
1628 }
1629
Simon Kelley1f15b812009-10-13 17:49:32 +01001630 for (li = ignore_suffix; li; li = li->next)
1631 {
1632 /* check for proscribed suffices */
1633 size_t ls = strlen(li->suffix);
1634 if (len > ls &&
1635 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1636 break;
1637 }
1638 if (li)
1639 continue;
1640
Simon Kelley824af852008-02-12 20:43:05 +00001641 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001642 strcpy(path, directory);
1643 strcat(path, "/");
1644 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001645
Simon Kelley39595cf2013-02-04 21:40:07 +00001646 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001647 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001648 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001649
Simon Kelley39595cf2013-02-04 21:40:07 +00001650 /* only reg files allowed. */
1651 if (S_ISREG(buf.st_mode))
1652 one_file(path, 0);
1653
Simon Kelley849a8352006-06-09 21:02:31 +01001654 free(path);
1655 }
1656
1657 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001658 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001659 for(; ignore_suffix; ignore_suffix = li)
1660 {
1661 li = ignore_suffix->next;
1662 free(ignore_suffix->suffix);
1663 free(ignore_suffix);
1664 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001665 for(; match_suffix; match_suffix = li)
1666 {
1667 li = match_suffix->next;
1668 free(match_suffix->suffix);
1669 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001670 }
Simon Kelley849a8352006-06-09 21:02:31 +01001671 break;
1672 }
1673
Simon Kelleyed4c0762013-10-08 20:46:34 +01001674 case LOPT_ADD_SBNET: /* --add-subnet */
1675 set_option_bool(OPT_CLIENT_SUBNET);
1676 if (arg)
1677 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001678 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001679 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001680
1681 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1682 if ((end = split_chr(arg, '/')))
1683 {
1684 /* has subnet+len */
1685 err = parse_mysockaddr(arg, &new->addr);
1686 if (err)
1687 ret_err(err);
1688 if (!atoi_check(end, &new->mask))
1689 ret_err(gen_err);
1690 new->addr_used = 1;
1691 }
1692 else if (!atoi_check(arg, &new->mask))
1693 ret_err(gen_err);
1694
1695 daemon->add_subnet4 = new;
1696
Ed Bardsleya7369be2015-08-05 21:17:18 +01001697 if (comma)
1698 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001699 new = opt_malloc(sizeof(struct mysubnet));
1700 if ((end = split_chr(comma, '/')))
1701 {
1702 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001703 err = parse_mysockaddr(comma, &new->addr);
1704 if (err)
1705 ret_err(err);
1706 if (!atoi_check(end, &new->mask))
1707 ret_err(gen_err);
1708 new->addr_used = 1;
1709 }
1710 else
1711 {
1712 if (!atoi_check(comma, &new->mask))
1713 ret_err(gen_err);
1714 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001715
1716 daemon->add_subnet6 = new;
1717 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001718 }
1719 break;
1720
Simon Kelleyad094272012-08-10 17:10:54 +01001721 case '1': /* --enable-dbus */
1722 set_option_bool(OPT_DBUS);
1723 if (arg)
1724 daemon->dbus_name = opt_string_alloc(arg);
1725 else
1726 daemon->dbus_name = DNSMASQ_SERVICE;
1727 break;
1728
Simon Kelleyf2621c72007-04-29 19:47:21 +01001729 case '8': /* --log-facility */
1730 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001731 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001732 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001733 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001734 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001735#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001736 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001737#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001738 for (i = 0; facilitynames[i].c_name; i++)
1739 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1740 break;
1741
1742 if (facilitynames[i].c_name)
1743 daemon->log_fac = facilitynames[i].c_val;
1744 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001745 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001746#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001747 }
1748 break;
1749
Simon Kelleyf2621c72007-04-29 19:47:21 +01001750 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001751 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001752 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001753
Simon Kelleyf2621c72007-04-29 19:47:21 +01001754 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001755 {
Simon Kelley824af852008-02-12 20:43:05 +00001756 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001757 struct resolvc *new, *list = daemon->resolv_files;
1758
1759 if (list && list->is_default)
1760 {
1761 /* replace default resolv file - possibly with nothing */
1762 if (name)
1763 {
1764 list->is_default = 0;
1765 list->name = name;
1766 }
1767 else
1768 list = NULL;
1769 }
1770 else if (name)
1771 {
Simon Kelley824af852008-02-12 20:43:05 +00001772 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001773 new->next = list;
1774 new->name = name;
1775 new->is_default = 0;
1776 new->mtime = 0;
1777 new->logged = 0;
1778 list = new;
1779 }
1780 daemon->resolv_files = list;
1781 break;
1782 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001783
1784 case LOPT_SERVERS_FILE:
1785 daemon->servers_file = opt_string_alloc(arg);
1786 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001787
Simon Kelleyf2621c72007-04-29 19:47:21 +01001788 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001789 {
1790 int pref = 1;
1791 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001792 char *name, *target = NULL;
1793
Simon Kelleyf2621c72007-04-29 19:47:21 +01001794 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001795 {
1796 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001797 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001798 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001799 }
1800
Simon Kelley1f15b812009-10-13 17:49:32 +01001801 if (!(name = canonicalise_opt(arg)) ||
1802 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001803 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001804
Simon Kelley824af852008-02-12 20:43:05 +00001805 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001806 new->next = daemon->mxnames;
1807 daemon->mxnames = new;
1808 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001809 new->name = name;
1810 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001811 new->weight = pref;
1812 break;
1813 }
1814
Simon Kelleyf2621c72007-04-29 19:47:21 +01001815 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001816 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001817 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001818 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001819
Simon Kelley6b173352018-05-08 18:32:14 +01001820 case LOPT_DUMPFILE: /* --dumpfile */
1821 daemon->dump_file = opt_string_alloc(arg);
1822 break;
1823
1824 case LOPT_DUMPMASK: /* --dumpmask */
1825 daemon->dump_mask = strtol(arg, NULL, 0);
1826 break;
1827
Simon Kelley7622fc02009-06-04 20:32:05 +01001828#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001829 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001830 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001831 break;
1832
Simon Kelleyc72daea2012-01-05 21:33:27 +00001833 /* Sorry about the gross pre-processor abuse */
1834 case '6': /* --dhcp-script */
1835 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001836# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001837 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001838# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001839 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001840# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001841 if (option == LOPT_LUASCRIPT)
1842# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001843 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001844# else
1845 daemon->luascript = opt_string_alloc(arg);
1846# endif
1847 else
1848 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001849# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001850 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001851#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001852
Simon Kelley70d18732015-01-31 19:59:29 +00001853 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
1854 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
1855 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
1856 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
1857 case LOPT_HOST_INOTIFY: /* --hostsdir */
1858 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001859 {
Simon Kelley824af852008-02-12 20:43:05 +00001860 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00001861 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00001862 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001863 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001864 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001865 if (option == 'H')
1866 {
1867 new->next = daemon->addn_hosts;
1868 daemon->addn_hosts = new;
1869 }
1870 else if (option == LOPT_DHCP_HOST)
1871 {
1872 new->next = daemon->dhcp_hosts_file;
1873 daemon->dhcp_hosts_file = new;
1874 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001875 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001876 {
1877 new->next = daemon->dhcp_opts_file;
1878 daemon->dhcp_opts_file = new;
1879 }
Simon Kelley70d18732015-01-31 19:59:29 +00001880 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001881 {
Simon Kelley70d18732015-01-31 19:59:29 +00001882 new->next = daemon->dynamic_dirs;
1883 daemon->dynamic_dirs = new;
1884 if (option == LOPT_DHCP_INOTIFY)
1885 new->flags |= AH_DHCP_HST;
1886 else if (option == LOPT_DHOPT_INOTIFY)
1887 new->flags |= AH_DHCP_OPT;
1888 else if (option == LOPT_HOST_INOTIFY)
1889 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001890 }
1891
Simon Kelley849a8352006-06-09 21:02:31 +01001892 break;
1893 }
1894
Simon Kelleyf373a152013-09-23 12:47:47 +01001895
1896#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +00001897 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001898 if (!(comma = split(arg)))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001899 ret_err(gen_err);
1900
Simon Kelley4f7b3042012-11-28 21:27:02 +00001901 daemon->authserver = opt_string_alloc(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001902 arg = comma;
1903 do {
1904 struct iname *new = opt_malloc(sizeof(struct iname));
1905 comma = split(arg);
1906 new->name = NULL;
1907 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001908 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
Simon Kelley429798f2012-12-10 20:45:53 +00001909 new->addr.sa.sa_family = AF_INET;
1910#ifdef HAVE_IPV6
1911 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1912 new->addr.sa.sa_family = AF_INET6;
1913#endif
1914 else
Simon Kelleyf25e6c62013-11-17 12:23:42 +00001915 {
1916 char *fam = split_chr(arg, '/');
1917 new->name = opt_string_alloc(arg);
1918 new->addr.sa.sa_family = 0;
1919 if (fam)
1920 {
1921 if (strcmp(fam, "4") == 0)
1922 new->addr.sa.sa_family = AF_INET;
1923#ifdef HAVE_IPV6
1924 else if (strcmp(fam, "6") == 0)
1925 new->addr.sa.sa_family = AF_INET6;
1926#endif
1927 else
1928 ret_err(gen_err);
1929 }
1930 }
Simon Kelley429798f2012-12-10 20:45:53 +00001931 new->next = daemon->authinterface;
1932 daemon->authinterface = new;
1933
1934 arg = comma;
1935 } while (arg);
1936
Simon Kelley4f7b3042012-11-28 21:27:02 +00001937 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001938
1939 case LOPT_AUTHSFS: /* --auth-sec-servers */
1940 {
1941 struct name_list *new;
1942
1943 do {
1944 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001945 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001946 new->name = opt_string_alloc(arg);
1947 new->next = daemon->secondary_forward_server;
1948 daemon->secondary_forward_server = new;
1949 arg = comma;
1950 } while (arg);
1951 break;
1952 }
1953
Simon Kelley4f7b3042012-11-28 21:27:02 +00001954 case LOPT_AUTHZONE: /* --auth-zone */
1955 {
1956 struct auth_zone *new;
1957
1958 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001959
Simon Kelley429798f2012-12-10 20:45:53 +00001960 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001961 new->domain = opt_string_alloc(arg);
1962 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001963 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00001964 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001965 new->next = daemon->auth_zones;
1966 daemon->auth_zones = new;
1967
1968 while ((arg = comma))
1969 {
1970 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001971 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001972 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00001973 struct addrlist *subnet = NULL;
1974 struct all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001975
1976 comma = split(arg);
1977 prefix = split_chr(arg, '/');
1978
1979 if (prefix && !atoi_check(prefix, &prefixlen))
1980 ret_err(gen_err);
1981
Mathias Kresin094bfae2016-07-24 14:15:22 +01001982 if (strstr(arg, "exclude:") == arg)
1983 {
1984 is_exclude = 1;
1985 arg = arg+8;
1986 }
1987
Simon Kelley376d48c2013-11-13 13:04:30 +00001988 if (inet_pton(AF_INET, arg, &addr.addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001989 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001990 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001991 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001992 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001993 }
1994#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +00001995 else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001996 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001997 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001998 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001999 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002000 }
2001#endif
Simon Kelley376d48c2013-11-13 13:04:30 +00002002 else
2003 {
2004 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2005 name->name = opt_string_alloc(arg);
2006 name->flags = AUTH4 | AUTH6;
2007 name->next = new->interface_names;
2008 new->interface_names = name;
2009 if (prefix)
2010 {
2011 if (prefixlen == 4)
2012 name->flags &= ~AUTH6;
2013#ifdef HAVE_IPV6
2014 else if (prefixlen == 6)
2015 name->flags &= ~AUTH4;
2016#endif
2017 else
2018 ret_err(gen_err);
2019 }
2020 }
2021
2022 if (subnet)
2023 {
2024 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002025
2026 if (is_exclude)
2027 {
2028 subnet->next = new->exclude;
2029 new->exclude = subnet;
2030 }
2031 else
2032 {
2033 subnet->next = new->subnet;
2034 new->subnet = subnet;
2035 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002036 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002037 }
2038 break;
2039 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002040
Simon Kelley4f7b3042012-11-28 21:27:02 +00002041 case LOPT_AUTHSOA: /* --auth-soa */
2042 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002043 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002044 if (comma)
2045 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002046 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002047 arg = comma;
2048 comma = split(arg);
2049 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002050 for (cp = daemon->hostmaster; *cp; cp++)
2051 if (*cp == '@')
2052 *cp = '.';
2053
Simon Kelley4f7b3042012-11-28 21:27:02 +00002054 if (comma)
2055 {
2056 arg = comma;
2057 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002058 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002059 if (comma)
2060 {
2061 arg = comma;
2062 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002063 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002064 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002065 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002066 }
2067 }
2068 }
2069
2070 break;
Simon Kelleyf373a152013-09-23 12:47:47 +01002071#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +00002072
Simon Kelley2bb73af2013-04-24 17:38:19 +01002073 case 's': /* --domain */
2074 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002075 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002076 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002077 else
Simon Kelley9009d742008-11-14 20:04:27 +00002078 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002079 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002080 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002081 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002082 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002083 else
2084 {
Simon Kelley9009d742008-11-14 20:04:27 +00002085 if (comma)
2086 {
Simon Kelley429798f2012-12-10 20:45:53 +00002087 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002088 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002089
Simon Kelley48fd1c42013-04-25 09:49:38 +01002090 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002091 new->indexed = 0;
2092
Simon Kelley9009d742008-11-14 20:04:27 +00002093 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002094 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002095 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002096 int msize;
2097
Simon Kelley28866e92011-02-14 20:19:14 +00002098 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002099 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002100 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002101 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002102 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002103 int mask = (1 << (32 - msize)) - 1;
2104 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002105 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2106 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002107 if (arg)
2108 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002109 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002110 {
2111 if (!(new->prefix = canonicalise_opt(arg)) ||
2112 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2113 ret_err(_("bad prefix"));
2114 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002115 else if (strcmp(arg, "local") != 0 ||
2116 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002117 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00002118 else
2119 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002120 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002121 local=/xxx.yyy.zzz.in-addr.arpa/ */
2122 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002123 if (!serv)
2124 ret_err(_("bad prefix"));
2125
Simon Kelleyde73a492014-02-17 21:43:27 +00002126 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002127
2128 /* local=/<domain>/ */
2129 serv = opt_malloc(sizeof(struct server));
2130 memset(serv, 0, sizeof(struct server));
2131 serv->domain = d;
2132 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2133 serv->next = daemon->servers;
2134 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002135 }
2136 }
Simon Kelley9009d742008-11-14 20:04:27 +00002137 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002138#ifdef HAVE_IPV6
2139 else if (inet_pton(AF_INET6, comma, &new->start6))
2140 {
2141 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2142 u64 addrpart = addr6part(&new->start6);
2143 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002144
Simon Kelleyd74942a2012-02-07 20:51:56 +00002145 /* prefix==64 overflows the mask calculation above */
2146 if (msize == 64)
2147 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002148
Simon Kelleyd74942a2012-02-07 20:51:56 +00002149 new->end6 = new->start6;
2150 setaddr6part(&new->start6, addrpart & ~mask);
2151 setaddr6part(&new->end6, addrpart | mask);
2152
2153 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002154 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002155 else if (arg)
2156 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002157 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002158 {
2159 if (!(new->prefix = canonicalise_opt(arg)) ||
2160 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
2161 ret_err(_("bad prefix"));
2162 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002163 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002164 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002165 else
2166 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002167 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002168 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002169 struct server *serv = add_rev6(&new->start6, msize);
2170 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002171
2172 /* local=/<domain>/ */
2173 serv = opt_malloc(sizeof(struct server));
2174 memset(serv, 0, sizeof(struct server));
2175 serv->domain = d;
2176 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2177 serv->next = daemon->servers;
2178 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002179 }
2180 }
2181 }
2182#endif
2183 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002184 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002185 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002186 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002187 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002188 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002189 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002190 prefstr = split(arg);
2191
Simon Kelleyd74942a2012-02-07 20:51:56 +00002192 if (inet_pton(AF_INET, comma, &new->start))
2193 {
2194 new->is6 = 0;
2195 if (!arg)
2196 new->end.s_addr = new->start.s_addr;
2197 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002198 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002199 }
2200#ifdef HAVE_IPV6
2201 else if (inet_pton(AF_INET6, comma, &new->start6))
2202 {
2203 new->is6 = 1;
2204 if (!arg)
2205 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2206 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002207 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002208 }
2209#endif
2210 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002211 ret_err(gen_err);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002212
2213 if (option != 's' && prefstr)
2214 {
2215 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2216 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2217 ret_err(_("bad prefix"));
2218 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002219 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002220
2221 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002222 if (option == 's')
2223 {
2224 new->next = daemon->cond_domain;
2225 daemon->cond_domain = new;
2226 }
2227 else
2228 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002229 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002230 new->next = daemon->synth_domains;
2231 daemon->synth_domains = new;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002232 if ((star = strrchr(new->prefix, '*')) && *(star+1) == 0)
2233 {
2234 *star = 0;
2235 new->indexed = 1;
2236 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002237 }
Simon Kelley9009d742008-11-14 20:04:27 +00002238 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002239 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002240 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002241 else
2242 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002243 }
2244 }
Simon Kelley849a8352006-06-09 21:02:31 +01002245 break;
2246
Simon Kelley1e505122016-01-25 21:29:23 +00002247 case LOPT_CPE_ID: /* --add-dns-client */
2248 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002249 daemon->dns_client_id = opt_string_alloc(arg);
2250 break;
2251
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002252 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002253 if (!arg)
2254 set_option_bool(OPT_ADD_MAC);
2255 else
2256 {
2257 unhide_metas(arg);
2258 if (strcmp(arg, "base64") == 0)
2259 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002260 else if (strcmp(arg, "text") == 0)
2261 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002262 else
2263 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002264 }
2265 break;
2266
Simon Kelleyf2621c72007-04-29 19:47:21 +01002267 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002268 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002269 break;
2270
Simon Kelleyf2621c72007-04-29 19:47:21 +01002271 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002272 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002273 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002274 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002275
Simon Kelley7622fc02009-06-04 20:32:05 +01002276#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002277 case LOPT_SCRIPTUSR: /* --scriptuser */
2278 daemon->scriptuser = opt_string_alloc(arg);
2279 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002280#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002281
Simon Kelleyf2621c72007-04-29 19:47:21 +01002282 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002283 do {
Simon Kelley824af852008-02-12 20:43:05 +00002284 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002285 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002286 new->next = daemon->if_names;
2287 daemon->if_names = new;
2288 /* new->name may be NULL if someone does
2289 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002290 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002291 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002292 arg = comma;
2293 } while (arg);
2294 break;
2295
Simon Kelley2937f8a2013-07-29 19:49:07 +01002296 case LOPT_TFTP: /* --enable-tftp */
2297 set_option_bool(OPT_TFTP);
2298 if (!arg)
2299 break;
2300 /* fall through */
2301
Simon Kelleyf2621c72007-04-29 19:47:21 +01002302 case 'I': /* --except-interface */
2303 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002304 do {
Simon Kelley824af852008-02-12 20:43:05 +00002305 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002306 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002307 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002308 if (option == 'I')
2309 {
2310 new->next = daemon->if_except;
2311 daemon->if_except = new;
2312 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002313 else if (option == LOPT_TFTP)
2314 {
2315 new->next = daemon->tftp_interfaces;
2316 daemon->tftp_interfaces = new;
2317 }
Simon Kelley849a8352006-06-09 21:02:31 +01002318 else
2319 {
2320 new->next = daemon->dhcp_except;
2321 daemon->dhcp_except = new;
2322 }
2323 arg = comma;
2324 } while (arg);
2325 break;
2326
Simon Kelleyf2621c72007-04-29 19:47:21 +01002327 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002328 case LOPT_IGNORE_ADDR: /* --ignore-address */
2329 {
Simon Kelley849a8352006-06-09 21:02:31 +01002330 struct in_addr addr;
2331 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002332 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002333 {
Simon Kelley824af852008-02-12 20:43:05 +00002334 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002335 if (option == 'B')
2336 {
2337 baddr->next = daemon->bogus_addr;
2338 daemon->bogus_addr = baddr;
2339 }
2340 else
2341 {
2342 baddr->next = daemon->ignore_addr;
2343 daemon->ignore_addr = baddr;
2344 }
Simon Kelley849a8352006-06-09 21:02:31 +01002345 baddr->addr = addr;
2346 }
2347 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002348 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002349 break;
2350 }
2351
Simon Kelleyf2621c72007-04-29 19:47:21 +01002352 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002353 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002354 do {
Simon Kelley824af852008-02-12 20:43:05 +00002355 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002356 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002357 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002358 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002359 {
2360 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002361 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002362#ifdef HAVE_SOCKADDR_SA_LEN
2363 new->addr.in.sin_len = sizeof(new->addr.in);
2364#endif
2365 }
2366#ifdef HAVE_IPV6
2367 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2368 {
2369 new->addr.sa.sa_family = AF_INET6;
2370 new->addr.in6.sin6_flowinfo = 0;
2371 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002372 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002373#ifdef HAVE_SOCKADDR_SA_LEN
2374 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2375#endif
2376 }
2377#endif
2378 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002379 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002380
2381 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002382 if (option == 'a')
2383 {
2384 new->next = daemon->if_addrs;
2385 daemon->if_addrs = new;
2386 }
2387 else
2388 {
2389 new->next = daemon->auth_peers;
2390 daemon->auth_peers = new;
2391 }
Simon Kelley849a8352006-06-09 21:02:31 +01002392 arg = comma;
2393 } while (arg);
2394 break;
2395
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002396 case 'S': /* --server */
2397 case LOPT_LOCAL: /* --local */
2398 case 'A': /* --address */
2399 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002400 {
2401 struct server *serv, *newlist = NULL;
2402
2403 unhide_metas(arg);
2404
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002405 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002406 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002407 int rebind = !(*arg == '/');
2408 char *end = NULL;
2409 if (!rebind)
2410 arg++;
2411 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002412 {
2413 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002414 /* elide leading dots - they are implied in the search algorithm */
2415 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002416 /* # matches everything and becomes a zero length domain string */
2417 if (strcmp(arg, "#") == 0)
2418 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002419 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002420 option = '?';
Simon Kelley824af852008-02-12 20:43:05 +00002421 serv = opt_malloc(sizeof(struct server));
2422 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002423 serv->next = newlist;
2424 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002425 serv->domain = domain;
2426 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002427 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002428 if (rebind)
2429 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002430 }
2431 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002432 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002433 }
2434 else
2435 {
Simon Kelley824af852008-02-12 20:43:05 +00002436 newlist = opt_malloc(sizeof(struct server));
2437 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002438#ifdef HAVE_LOOP
2439 newlist->uid = rand32();
2440#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002441 }
2442
Simon Kelley7b1eae42014-02-20 13:43:28 +00002443 if (servers_only && option == 'S')
2444 newlist->flags |= SERV_FROM_FILE;
2445
Simon Kelley849a8352006-06-09 21:02:31 +01002446 if (option == 'A')
2447 {
2448 newlist->flags |= SERV_LITERAL_ADDRESS;
2449 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002450 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002451 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002452 else if (option == LOPT_NO_REBIND)
2453 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002454
2455 if (!arg || !*arg)
2456 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002457 if (!(newlist->flags & SERV_NO_REBIND))
2458 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002459 }
2460
2461 else if (strcmp(arg, "#") == 0)
2462 {
2463 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002464 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002465 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002466 }
2467 else
2468 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002469 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2470 if (err)
2471 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002472 }
2473
Simon Kelleyf2621c72007-04-29 19:47:21 +01002474 serv = newlist;
2475 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002476 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002477 serv->next->flags = serv->flags;
2478 serv->next->addr = serv->addr;
2479 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002480 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002481 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002482 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002483 serv->next = daemon->servers;
2484 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002485 break;
2486 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002487
Simon Kelleyde73a492014-02-17 21:43:27 +00002488 case LOPT_REV_SERV: /* --rev-server */
2489 {
2490 char *string;
2491 int size;
2492 struct server *serv;
2493 struct in_addr addr4;
2494#ifdef HAVE_IPV6
2495 struct in6_addr addr6;
2496#endif
2497
2498 unhide_metas(arg);
2499 if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2500 ret_err(gen_err);
2501
2502 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002503 {
2504 serv = add_rev4(addr4, size);
2505 if (!serv)
2506 ret_err(_("bad prefix"));
2507 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002508#ifdef HAVE_IPV6
2509 else if (inet_pton(AF_INET6, arg, &addr6))
2510 serv = add_rev6(&addr6, size);
2511#endif
2512 else
2513 ret_err(gen_err);
2514
2515 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2516
2517 if (string)
2518 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002519
2520 if (servers_only)
2521 serv->flags |= SERV_FROM_FILE;
2522
Simon Kelleyde73a492014-02-17 21:43:27 +00002523 break;
2524 }
2525
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002526 case LOPT_IPSET: /* --ipset */
2527#ifndef HAVE_IPSET
2528 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2529 break;
2530#else
2531 {
2532 struct ipsets ipsets_head;
2533 struct ipsets *ipsets = &ipsets_head;
2534 int size;
2535 char *end;
2536 char **sets, **sets_pos;
2537 memset(ipsets, 0, sizeof(struct ipsets));
2538 unhide_metas(arg);
2539 if (arg && *arg == '/')
2540 {
2541 arg++;
2542 while ((end = split_chr(arg, '/')))
2543 {
2544 char *domain = NULL;
2545 /* elide leading dots - they are implied in the search algorithm */
2546 while (*arg == '.')
2547 arg++;
2548 /* # matches everything and becomes a zero length domain string */
2549 if (strcmp(arg, "#") == 0 || !*arg)
2550 domain = "";
2551 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
2552 option = '?';
2553 ipsets->next = opt_malloc(sizeof(struct ipsets));
2554 ipsets = ipsets->next;
2555 memset(ipsets, 0, sizeof(struct ipsets));
2556 ipsets->domain = domain;
2557 arg = end;
2558 }
2559 }
2560 else
2561 {
2562 ipsets->next = opt_malloc(sizeof(struct ipsets));
2563 ipsets = ipsets->next;
2564 memset(ipsets, 0, sizeof(struct ipsets));
2565 ipsets->domain = "";
2566 }
2567 if (!arg || !*arg)
2568 {
2569 option = '?';
2570 break;
2571 }
2572 size = 2;
2573 for (end = arg; *end; ++end)
2574 if (*end == ',')
2575 ++size;
2576
2577 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2578
2579 do {
2580 end = split(arg);
2581 *sets_pos++ = opt_string_alloc(arg);
2582 arg = end;
2583 } while (end);
2584 *sets_pos = 0;
2585 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2586 ipsets->next->sets = sets;
2587 ipsets->next = daemon->ipsets;
2588 daemon->ipsets = ipsets_head.next;
2589
2590 break;
2591 }
2592#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002593
Simon Kelleyf2621c72007-04-29 19:47:21 +01002594 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002595 {
2596 int size;
2597
2598 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002599 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002600 else
2601 {
2602 /* zero is OK, and means no caching. */
2603
2604 if (size < 0)
2605 size = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002606
2607 daemon->cachesize = size;
2608 }
2609 break;
2610 }
2611
Simon Kelleyf2621c72007-04-29 19:47:21 +01002612 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002613 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002614 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002615 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002616
Simon Kelley1a6bca82008-07-11 11:11:42 +01002617 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002618 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002619 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002620 break;
2621
Hans Dedecker926332a2016-01-23 10:48:12 +00002622 case LOPT_MAXPORT: /* --max-port */
2623 if (!atoi_check16(arg, &daemon->max_port))
2624 ret_err(gen_err);
2625 break;
2626
Simon Kelleyf2621c72007-04-29 19:47:21 +01002627 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002628 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002629 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002630 break;
2631
Simon Kelley25cf5e32015-01-09 15:53:03 +00002632 case 'q': /* --log-queries */
2633 set_option_bool(OPT_LOG);
2634 if (arg && strcmp(arg, "extra") == 0)
2635 set_option_bool(OPT_EXTRALOG);
2636 break;
2637
Simon Kelleyf2621c72007-04-29 19:47:21 +01002638 case LOPT_MAX_LOGS: /* --log-async */
2639 daemon->max_logs = LOG_MAX; /* default */
2640 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002641 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002642 else if (daemon->max_logs > 100)
2643 daemon->max_logs = 100;
2644 break;
2645
2646 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002647 {
2648 int i;
2649 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002650 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002651 daemon->edns_pktsz = (unsigned short)i;
2652 break;
2653 }
2654
Simon Kelleyf2621c72007-04-29 19:47:21 +01002655 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002656 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002657 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002658 /* if explicitly set to zero, use single OS ephemeral port
2659 and disable random ports */
2660 if (daemon->query_port == 0)
2661 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002662 break;
2663
Simon Kelley824af852008-02-12 20:43:05 +00002664 case 'T': /* --local-ttl */
2665 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002666 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002667 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002668 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002669 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002670 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002671 {
2672 int ttl;
2673 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002674 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002675 else if (option == LOPT_NEGTTL)
2676 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002677 else if (option == LOPT_MAXTTL)
2678 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002679 else if (option == LOPT_MINCTTL)
2680 {
2681 if (ttl > TTL_FLOOR_LIMIT)
2682 ttl = TTL_FLOOR_LIMIT;
2683 daemon->min_cache_ttl = (unsigned long)ttl;
2684 }
Simon Kelley1d860412012-09-20 20:48:04 +01002685 else if (option == LOPT_MAXCTTL)
2686 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002687 else if (option == LOPT_AUTHTTL)
2688 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002689 else if (option == LOPT_DHCPTTL)
2690 {
2691 daemon->dhcp_ttl = (unsigned long)ttl;
2692 daemon->use_dhcp_ttl = 1;
2693 }
Simon Kelley849a8352006-06-09 21:02:31 +01002694 else
2695 daemon->local_ttl = (unsigned long)ttl;
2696 break;
2697 }
2698
Simon Kelley7622fc02009-06-04 20:32:05 +01002699#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002700 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002701 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002702 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002703 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002704#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002705
Simon Kelley7622fc02009-06-04 20:32:05 +01002706#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002707 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002708 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002709 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002710 break;
2711
Simon Kelleybec366b2016-02-24 22:03:26 +00002712 case LOPT_TFTP_MTU: /* --tftp-mtu */
2713 if (!atoi_check(arg, &daemon->tftp_mtu))
2714 ret_err(gen_err);
2715 break;
2716
Simon Kelley824af852008-02-12 20:43:05 +00002717 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002718 comma = split(arg);
2719 if (comma)
2720 {
2721 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2722 new->interface = opt_string_alloc(comma);
2723 new->prefix = opt_string_alloc(arg);
2724 new->next = daemon->if_prefix;
2725 daemon->if_prefix = new;
2726 }
2727 else
2728 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002729 break;
2730
Simon Kelley824af852008-02-12 20:43:05 +00002731 case LOPT_TFTPPORTS: /* --tftp-port-range */
2732 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002733 !atoi_check16(arg, &daemon->start_tftp_port) ||
2734 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002735 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002736
2737 if (daemon->start_tftp_port > daemon->end_tftp_port)
2738 {
2739 int tmp = daemon->start_tftp_port;
2740 daemon->start_tftp_port = daemon->end_tftp_port;
2741 daemon->end_tftp_port = tmp;
2742 }
2743
2744 break;
Floris Bos60704f52017-04-09 22:22:49 +01002745
2746 case LOPT_APREF: /* --tftp-unique-root */
2747 if (!arg || strcasecmp(arg, "ip") == 0)
2748 set_option_bool(OPT_TFTP_APREF_IP);
2749 else if (strcasecmp(arg, "mac") == 0)
2750 set_option_bool(OPT_TFTP_APREF_MAC);
2751 else
2752 ret_err(gen_err);
2753 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002754#endif
Simon Kelley824af852008-02-12 20:43:05 +00002755
Simon Kelleyf2621c72007-04-29 19:47:21 +01002756 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002757 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002758 struct dhcp_bridge *new;
2759
Simon Kelley316e2732010-01-22 20:16:09 +00002760 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002761 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002762
Simon Kelley22cd8602018-01-14 22:57:14 +00002763 for (new = daemon->bridges; new; new = new->next)
2764 if (strcmp(new->iface, arg) == 0)
2765 break;
2766
2767 if (!new)
2768 {
2769 new = opt_malloc(sizeof(struct dhcp_bridge));
2770 strcpy(new->iface, arg);
2771 new->alias = NULL;
2772 new->next = daemon->bridges;
2773 daemon->bridges = new;
2774 }
2775
Simon Kelley832af0b2007-01-21 20:01:28 +00002776 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002777 arg = comma;
2778 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002779 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002780 {
Simon Kelley824af852008-02-12 20:43:05 +00002781 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002782 b->next = new->alias;
2783 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002784 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002785 }
2786 } while (comma);
2787
2788 break;
2789 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002790
Simon Kelley7622fc02009-06-04 20:32:05 +01002791#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002792 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002793 {
2794 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002795 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002796 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002797
Simon Kelley52b92f42012-01-22 16:05:15 +00002798 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002799 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002800
Simon Kelley849a8352006-06-09 21:02:31 +01002801 if (!arg)
2802 {
2803 option = '?';
2804 break;
2805 }
2806
2807 while(1)
2808 {
2809 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002810 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2811 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2812 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002813 break;
2814
Simon Kelleyf2621c72007-04-29 19:47:21 +01002815 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002816 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002817 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002818 {
Simon Kelley824af852008-02-12 20:43:05 +00002819 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2820 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002821 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002822 /* ignore empty tag */
2823 if (tt->net)
2824 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002825 }
2826 else
2827 {
2828 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002829 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002830 else if (strstr(arg, "set:") == arg)
2831 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002832 else
Simon Kelley824af852008-02-12 20:43:05 +00002833 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002834 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002835 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002836 }
2837 else
2838 {
2839 a[0] = arg;
2840 break;
2841 }
2842 }
2843
Simon Kelley1f776932012-12-16 19:46:08 +00002844 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002845 if (!(a[k] = split(a[k-1])))
2846 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002847
Simon Kelley52b92f42012-01-22 16:05:15 +00002848 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002849 ret_err(_("bad dhcp-range"));
2850
2851 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002852 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002853 new->next = daemon->dhcp;
2854 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002855 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002856 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002857 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002858 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002859 new->flags |= CONTEXT_PROXY;
2860 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002861 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002862
2863 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2864 {
2865 struct in_addr tmp = new->start;
2866 new->start = new->end;
2867 new->end = tmp;
2868 }
2869
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002870 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002871 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002872 {
2873 new->flags |= CONTEXT_NETMASK;
2874 leasepos = 3;
2875 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002876 ret_err(_("inconsistent DHCP range"));
Simon Kelleyfa794662016-03-03 20:33:54 +00002877
Simon Kelley52b92f42012-01-22 16:05:15 +00002878
Simon Kelleyfa794662016-03-03 20:33:54 +00002879 if (k >= 4 && strchr(a[3], '.') &&
2880 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
2881 {
2882 new->flags |= CONTEXT_BRDCAST;
2883 leasepos = 4;
2884 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002885 }
Simon Kelley849a8352006-06-09 21:02:31 +01002886 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002887#ifdef HAVE_DHCP6
2888 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002889 {
Simon Kelley89500e32013-09-20 16:29:20 +01002890 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002891 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002892 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002893 new->next = daemon->dhcp6;
2894 daemon->dhcp6 = new;
2895
Simon Kelley30cd9662012-03-25 20:44:38 +01002896 for (leasepos = 1; leasepos < k; leasepos++)
2897 {
2898 if (strcmp(a[leasepos], "static") == 0)
2899 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2900 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002901 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002902 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002903 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002904 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
2905 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002906 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002907 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01002908 else if (strcmp(a[leasepos], "off-link") == 0)
2909 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01002910 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2911 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002912 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2913 {
2914 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2915 new->flags |= CONTEXT_TEMPLATE;
2916 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002917 else
2918 break;
2919 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002920
Simon Kelley52b92f42012-01-22 16:05:15 +00002921 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002922 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002923 {
2924 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002925 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002926 if (!(*cp >= '0' && *cp <= '9'))
2927 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002928 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002929 {
2930 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002931 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002932 }
2933 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002934
Simon Kelley6692a1a2013-08-20 14:41:31 +01002935 if (new->prefix != 64)
2936 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002937 if (new->flags & CONTEXT_RA)
Simon Kelley6692a1a2013-08-20 14:41:31 +01002938 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2939 else if (new->flags & CONTEXT_TEMPLATE)
2940 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2941 }
2942
2943 if (new->prefix < 64)
2944 ret_err(_("prefix length must be at least 64"));
2945
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002946 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2947 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002948
2949 /* dhcp-range=:: enables DHCP stateless on any interface */
2950 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2951 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002952
2953 if (new->flags & CONTEXT_TEMPLATE)
2954 {
2955 struct in6_addr zero;
2956 memset(&zero, 0, sizeof(zero));
2957 if (!is_same_net6(&zero, &new->start6, new->prefix))
2958 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2959 }
2960
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002961 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002962 {
2963 struct in6_addr tmp = new->start6;
2964 new->start6 = new->end6;
2965 new->end6 = tmp;
2966 }
Simon Kelley849a8352006-06-09 21:02:31 +01002967 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002968#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002969 else
2970 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002971
Simon Kelley30cd9662012-03-25 20:44:38 +01002972 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002973 {
Simon Kelleyfa794662016-03-03 20:33:54 +00002974 if (leasepos != k-1)
2975 ret_err(_("bad dhcp-range"));
2976
Simon Kelley849a8352006-06-09 21:02:31 +01002977 if (strcmp(a[leasepos], "infinite") == 0)
2978 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002979 else if (strcmp(a[leasepos], "deprecated") == 0)
2980 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002981 else
2982 {
2983 int fac = 1;
2984 if (strlen(a[leasepos]) > 0)
2985 {
2986 switch (a[leasepos][strlen(a[leasepos]) - 1])
2987 {
Simon Kelley42243212012-07-20 15:19:18 +01002988 case 'w':
2989 case 'W':
2990 fac *= 7;
2991 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002992 case 'd':
2993 case 'D':
2994 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00002995 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002996 case 'h':
2997 case 'H':
2998 fac *= 60;
2999 /* fall through */
3000 case 'm':
3001 case 'M':
3002 fac *= 60;
3003 /* fall through */
3004 case 's':
3005 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003006 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003007 }
3008
Simon Kelleybe379862012-12-23 12:01:39 +00003009 for (cp = a[leasepos]; *cp; cp++)
3010 if (!(*cp >= '0' && *cp <= '9'))
3011 break;
3012
Simon Kelley54dae552013-02-05 17:55:10 +00003013 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00003014 ret_err(_("bad dhcp-range"));
3015
Simon Kelley849a8352006-06-09 21:02:31 +01003016 new->lease_time = atoi(a[leasepos]) * fac;
3017 /* Leases of a minute or less confuse
3018 some clients, notably Apple's */
3019 if (new->lease_time < 120)
3020 new->lease_time = 120;
3021 }
3022 }
3023 }
3024 break;
3025 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003026
Simon Kelley5aabfc72007-08-29 11:24:47 +01003027 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003028 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003029 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003030 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01003031 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01003032 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003033 struct in_addr in;
3034
Simon Kelley824af852008-02-12 20:43:05 +00003035 new = opt_malloc(sizeof(struct dhcp_config));
3036
Simon Kelley849a8352006-06-09 21:02:31 +01003037 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003038 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3039 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003040 new->netid = NULL;
3041
Simon Kelley849a8352006-06-09 21:02:31 +01003042 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01003043 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003044 if (!(a[k] = split(a[k-1])))
3045 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003046
3047 for (j = 0; j < k; j++)
3048 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
3049 {
3050 char *arg = a[j];
3051
3052 if ((arg[0] == 'i' || arg[0] == 'I') &&
3053 (arg[1] == 'd' || arg[1] == 'D') &&
3054 arg[2] == ':')
3055 {
3056 if (arg[3] == '*')
3057 new->flags |= CONFIG_NOCLID;
3058 else
3059 {
3060 int len;
3061 arg += 3; /* dump id: */
3062 if (strchr(arg, ':'))
3063 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3064 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01003065 {
3066 unhide_metas(arg);
3067 len = (int) strlen(arg);
3068 }
3069
Simon Kelley28866e92011-02-14 20:19:14 +00003070 if (len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003071 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003072 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003073 {
3074 new->flags |= CONFIG_CLID;
3075 new->clid_len = len;
3076 memcpy(new->clid, arg, len);
3077 }
Simon Kelley849a8352006-06-09 21:02:31 +01003078 }
3079 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003080 /* dhcp-host has strange backwards-compat needs. */
3081 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01003082 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003083 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3084 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3085 newtag->net = opt_malloc(strlen(arg + 4) + 1);
3086 newlist->next = new->netid;
3087 new->netid = newlist;
3088 newlist->list = newtag;
3089 strcpy(newtag->net, arg+4);
3090 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01003091 }
Simon Kelley7de060b2011-08-26 17:24:52 +01003092 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003093 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00003094#ifdef HAVE_DHCP6
3095 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3096 {
3097 arg[strlen(arg)-1] = 0;
3098 arg++;
3099
3100 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003101 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00003102
3103 for (i= 0; i < 8; i++)
3104 if (new->addr6.s6_addr[i] != 0)
3105 break;
3106
3107 /* set WILDCARD if network part all zeros */
3108 if (i == 8)
3109 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00003110
3111 new->flags |= CONFIG_ADDR6;
3112 }
3113#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01003114 else
Simon Kelley849a8352006-06-09 21:02:31 +01003115 {
Simon Kelley9009d742008-11-14 20:04:27 +00003116 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00003117 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
3118 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003119 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003120 else
3121 {
3122
3123 newhw->next = new->hwaddr;
3124 new->hwaddr = newhw;
3125 }
Simon Kelley849a8352006-06-09 21:02:31 +01003126 }
3127 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003128 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01003129 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003130 struct dhcp_config *configs;
3131
Simon Kelley849a8352006-06-09 21:02:31 +01003132 new->addr = in;
3133 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003134
3135 /* If the same IP appears in more than one host config, then DISCOVER
3136 for one of the hosts will get the address, but REQUEST will be NAKed,
3137 since the address is reserved by the other one -> protocol loop. */
3138 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3139 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
3140 {
3141 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3142 return 0;
3143 }
Simon Kelley849a8352006-06-09 21:02:31 +01003144 }
3145 else
3146 {
3147 char *cp, *lastp = NULL, last = 0;
Simon Kelley76ff4402013-12-17 16:29:14 +00003148 int fac = 1, isdig = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003149
3150 if (strlen(a[j]) > 1)
3151 {
3152 lastp = a[j] + strlen(a[j]) - 1;
3153 last = *lastp;
3154 switch (last)
3155 {
Simon Kelley42243212012-07-20 15:19:18 +01003156 case 'w':
3157 case 'W':
3158 fac *= 7;
3159 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003160 case 'd':
3161 case 'D':
3162 fac *= 24;
3163 /* fall through */
3164 case 'h':
3165 case 'H':
3166 fac *= 60;
3167 /* fall through */
3168 case 'm':
3169 case 'M':
3170 fac *= 60;
3171 /* fall through */
3172 case 's':
3173 case 'S':
3174 *lastp = 0;
3175 }
3176 }
3177
3178 for (cp = a[j]; *cp; cp++)
Simon Kelley76ff4402013-12-17 16:29:14 +00003179 if (isdigit((unsigned char)*cp))
3180 isdig = 1;
3181 else if (*cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01003182 break;
Simon Kelley76ff4402013-12-17 16:29:14 +00003183
Simon Kelley849a8352006-06-09 21:02:31 +01003184 if (*cp)
3185 {
3186 if (lastp)
3187 *lastp = last;
3188 if (strcmp(a[j], "infinite") == 0)
3189 {
3190 new->lease_time = 0xffffffff;
3191 new->flags |= CONFIG_TIME;
3192 }
3193 else if (strcmp(a[j], "ignore") == 0)
3194 new->flags |= CONFIG_DISABLE;
3195 else
3196 {
Simon Kelley1f15b812009-10-13 17:49:32 +01003197 if (!(new->hostname = canonicalise_opt(a[j])) ||
3198 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003199 ret_err(_("bad DHCP host name"));
3200
3201 new->flags |= CONFIG_NAME;
3202 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01003203 }
3204 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003205 else if (isdig)
Simon Kelley849a8352006-06-09 21:02:31 +01003206 {
3207 new->lease_time = atoi(a[j]) * fac;
3208 /* Leases of a minute or less confuse
3209 some clients, notably Apple's */
3210 if (new->lease_time < 120)
3211 new->lease_time = 120;
3212 new->flags |= CONFIG_TIME;
3213 }
3214 }
3215
Simon Kelley5aabfc72007-08-29 11:24:47 +01003216 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003217 break;
3218 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003219
3220 case LOPT_TAG_IF: /* --tag-if */
3221 {
3222 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3223
3224 new->tag = NULL;
3225 new->set = NULL;
3226 new->next = NULL;
3227
3228 /* preserve order */
3229 if (!daemon->tag_if)
3230 daemon->tag_if = new;
3231 else
3232 {
3233 struct tag_if *tmp;
3234 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3235 tmp->next = new;
3236 }
3237
3238 while (arg)
3239 {
3240 size_t len;
3241
3242 comma = split(arg);
3243 len = strlen(arg);
3244
3245 if (len < 5)
3246 {
3247 new->set = NULL;
3248 break;
3249 }
3250 else
3251 {
3252 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3253 newtag->net = opt_malloc(len - 3);
3254 strcpy(newtag->net, arg+4);
3255 unhide_metas(newtag->net);
3256
3257 if (strstr(arg, "set:") == arg)
3258 {
3259 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3260 newlist->next = new->set;
3261 new->set = newlist;
3262 newlist->list = newtag;
3263 }
3264 else if (strstr(arg, "tag:") == arg)
3265 {
3266 newtag->next = new->tag;
3267 new->tag = newtag;
3268 }
3269 else
3270 {
3271 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00003272 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003273 break;
3274 }
3275 }
3276
3277 arg = comma;
3278 }
3279
3280 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003281 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003282
3283 break;
3284 }
3285
Simon Kelley849a8352006-06-09 21:02:31 +01003286
Simon Kelley73a08a22009-02-05 20:28:08 +00003287 case 'O': /* --dhcp-option */
3288 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003289 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003290 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003291 return parse_dhcp_opt(errstr, arg,
3292 option == LOPT_FORCE ? DHOPT_FORCE :
3293 (option == LOPT_MATCH ? DHOPT_MATCH :
3294 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
3295
Simon Kelleyf2621c72007-04-29 19:47:21 +01003296 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003297 {
3298 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003299 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003300 {
Simon Kelley824af852008-02-12 20:43:05 +00003301 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01003302 newid->next = id;
3303 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003304 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003305 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01003306 arg = comma;
3307 };
3308
3309 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003310 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003311 else
3312 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003313 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003314 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003315 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003316 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003317 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003318 dhcp_next_server.s_addr = 0;
3319 if (comma)
3320 {
3321 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003322 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003323 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003324 if (comma)
3325 {
3326 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003327 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3328 {
3329 /*
3330 * The user may have specified the tftp hostname here.
3331 * save it so that it can be resolved/looked up during
3332 * actual dhcp_reply().
3333 */
3334
3335 tftp_sname = opt_string_alloc(comma);
3336 dhcp_next_server.s_addr = 0;
3337 }
Simon Kelley849a8352006-06-09 21:02:31 +01003338 }
3339 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003340
3341 new = opt_malloc(sizeof(struct dhcp_boot));
3342 new->file = dhcp_file;
3343 new->sname = dhcp_sname;
3344 new->tftp_sname = tftp_sname;
3345 new->next_server = dhcp_next_server;
3346 new->netid = id;
3347 new->next = daemon->boot_config;
3348 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003349 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003350
Simon Kelley849a8352006-06-09 21:02:31 +01003351 break;
3352 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003353
Floris Bos503c6092017-04-09 23:07:13 +01003354 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3355 {
3356 struct dhcp_netid *id = NULL;
3357 while (is_tag_prefix(arg))
3358 {
3359 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
3360 newid->next = id;
3361 id = newid;
3362 comma = split(arg);
3363 newid->net = opt_string_alloc(arg+4);
3364 arg = comma;
3365 };
3366
3367 if (!arg)
3368 ret_err(gen_err);
3369 else
3370 {
3371 struct delay_config *new;
3372 int delay;
3373 if (!atoi_check(arg, &delay))
3374 ret_err(gen_err);
3375
3376 new = opt_malloc(sizeof(struct delay_config));
3377 new->delay = delay;
3378 new->netid = id;
3379 new->next = daemon->delay_conf;
3380 daemon->delay_conf = new;
3381 }
3382
3383 break;
3384 }
3385
Simon Kelley7622fc02009-06-04 20:32:05 +01003386 case LOPT_PXE_PROMT: /* --pxe-prompt */
3387 {
3388 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3389 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003390
Simon Kelley7622fc02009-06-04 20:32:05 +01003391 new->netid = NULL;
3392 new->opt = 10; /* PXE_MENU_PROMPT */
3393
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003394 while (is_tag_prefix(arg))
3395 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003396 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3397 comma = split(arg);
3398 nn->next = new->netid;
3399 new->netid = nn;
3400 nn->net = opt_string_alloc(arg+4);
3401 arg = comma;
3402 }
3403
3404 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003405 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003406 else
3407 {
3408 comma = split(arg);
3409 unhide_metas(arg);
3410 new->len = strlen(arg) + 1;
3411 new->val = opt_malloc(new->len);
3412 memcpy(new->val + 1, arg, new->len - 1);
3413
3414 new->u.vendor_class = (unsigned char *)"PXEClient";
3415 new->flags = DHOPT_VENDOR;
3416
3417 if (comma && atoi_check(comma, &timeout))
3418 *(new->val) = timeout;
3419 else
3420 *(new->val) = 255;
3421
3422 new->next = daemon->dhcp_opts;
3423 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003424 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003425 }
3426
3427 break;
3428 }
3429
3430 case LOPT_PXE_SERV: /* --pxe-service */
3431 {
3432 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3433 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003434 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3435 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003436 static int boottype = 32768;
3437
3438 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003439 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003440 new->server.s_addr = 0;
3441
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003442 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01003443 {
3444 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3445 comma = split(arg);
3446 nn->next = new->netid;
3447 new->netid = nn;
3448 nn->net = opt_string_alloc(arg+4);
3449 arg = comma;
3450 }
3451
3452 if (arg && (comma = split(arg)))
3453 {
3454 for (i = 0; CSA[i]; i++)
3455 if (strcasecmp(CSA[i], arg) == 0)
3456 break;
3457
3458 if (CSA[i] || atoi_check(arg, &i))
3459 {
3460 arg = comma;
3461 comma = split(arg);
3462
3463 new->CSA = i;
3464 new->menu = opt_string_alloc(arg);
3465
Simon Kelley316e2732010-01-22 20:16:09 +00003466 if (!comma)
3467 {
3468 new->type = 0; /* local boot */
3469 new->basename = NULL;
3470 }
3471 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003472 {
3473 arg = comma;
3474 comma = split(arg);
3475 if (atoi_check(arg, &i))
3476 {
3477 new->type = i;
3478 new->basename = NULL;
3479 }
3480 else
3481 {
3482 new->type = boottype++;
3483 new->basename = opt_string_alloc(arg);
3484 }
3485
Simon Kelley751d6f42012-02-10 15:24:51 +00003486 if (comma)
3487 {
3488 if (!inet_pton(AF_INET, comma, &new->server))
3489 {
3490 new->server.s_addr = 0;
3491 new->sname = opt_string_alloc(comma);
3492 }
3493
3494 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003495 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003496
Simon Kelley316e2732010-01-22 20:16:09 +00003497 /* Order matters */
3498 new->next = NULL;
3499 if (!daemon->pxe_services)
3500 daemon->pxe_services = new;
3501 else
3502 {
3503 struct pxe_service *s;
3504 for (s = daemon->pxe_services; s->next; s = s->next);
3505 s->next = new;
3506 }
3507
3508 daemon->enable_pxe = 1;
3509 break;
3510
Simon Kelley7622fc02009-06-04 20:32:05 +01003511 }
3512 }
3513
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003514 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003515 }
3516
Simon Kelleyf2621c72007-04-29 19:47:21 +01003517 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003518 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003519 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003520 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003521 else
3522 {
Simon Kelley824af852008-02-12 20:43:05 +00003523 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003524 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003525 unhide_metas(comma);
3526 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003527 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003528 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003529 else
3530 {
3531 new->next = daemon->dhcp_macs;
3532 daemon->dhcp_macs = new;
3533 }
Simon Kelley849a8352006-06-09 21:02:31 +01003534 }
3535 }
3536 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003537
3538#ifdef OPTION6_PREFIX_CLASS
3539 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3540 {
3541 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3542
3543 if (!(comma = split(arg)) ||
3544 !atoi_check16(comma, &new->class))
3545 ret_err(gen_err);
3546
3547 new->tag.net = opt_string_alloc(set_prefix(arg));
3548 new->next = daemon->prefix_classes;
3549 daemon->prefix_classes = new;
3550
3551 break;
3552 }
3553#endif
3554
3555
Simon Kelleyf2621c72007-04-29 19:47:21 +01003556 case 'U': /* --dhcp-vendorclass */
3557 case 'j': /* --dhcp-userclass */
3558 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3559 case LOPT_REMOTE: /* --dhcp-remoteid */
3560 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003561 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003562 unsigned char *p;
3563 int dig = 0;
3564 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3565
3566 if (!(comma = split(arg)))
3567 ret_err(gen_err);
3568
3569 new->netid.net = opt_string_alloc(set_prefix(arg));
3570 /* check for hex string - must digits may include : must not have nothing else,
3571 only allowed for agent-options. */
3572
3573 arg = comma;
3574 if ((comma = split(arg)))
3575 {
3576 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3577 ret_err(gen_err);
3578 else
3579 new->enterprise = atoi(arg+11);
3580 }
3581 else
3582 comma = arg;
3583
3584 for (p = (unsigned char *)comma; *p; p++)
3585 if (isxdigit(*p))
3586 dig = 1;
3587 else if (*p != ':')
3588 break;
3589 unhide_metas(comma);
3590 if (option == 'U' || option == 'j' || *p || !dig)
3591 {
3592 new->len = strlen(comma);
3593 new->data = opt_malloc(new->len);
3594 memcpy(new->data, comma, new->len);
3595 }
3596 else
3597 {
3598 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3599 new->data = opt_malloc(new->len);
3600 memcpy(new->data, comma, new->len);
3601 }
3602
3603 switch (option)
3604 {
3605 case 'j':
3606 new->match_type = MATCH_USER;
3607 break;
3608 case 'U':
3609 new->match_type = MATCH_VENDOR;
3610 break;
3611 case LOPT_CIRCUIT:
3612 new->match_type = MATCH_CIRCUIT;
3613 break;
3614 case LOPT_REMOTE:
3615 new->match_type = MATCH_REMOTE;
3616 break;
3617 case LOPT_SUBSCR:
3618 new->match_type = MATCH_SUBSCRIBER;
3619 break;
3620 }
3621 new->next = daemon->dhcp_vendors;
3622 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003623
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003624 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003625 }
3626
Simon Kelley9e038942008-05-30 20:06:34 +01003627 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3628 if (!arg)
3629 {
3630 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3631 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3632 }
3633 else
3634 {
3635 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003636 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3637 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003638 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003639 if (!comma)
3640 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3641 }
3642 break;
3643
Simon Kelley824af852008-02-12 20:43:05 +00003644 case 'J': /* --dhcp-ignore */
3645 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3646 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003647 case '3': /* --bootp-dynamic */
3648 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003649 {
Simon Kelley824af852008-02-12 20:43:05 +00003650 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003651 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003652 if (option == 'J')
3653 {
3654 new->next = daemon->dhcp_ignore;
3655 daemon->dhcp_ignore = new;
3656 }
Simon Kelley824af852008-02-12 20:43:05 +00003657 else if (option == LOPT_BROADCAST)
3658 {
3659 new->next = daemon->force_broadcast;
3660 daemon->force_broadcast = new;
3661 }
Simon Kelley9009d742008-11-14 20:04:27 +00003662 else if (option == '3')
3663 {
3664 new->next = daemon->bootp_dynamic;
3665 daemon->bootp_dynamic = new;
3666 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003667 else if (option == LOPT_GEN_NAMES)
3668 {
3669 new->next = daemon->dhcp_gen_names;
3670 daemon->dhcp_gen_names = new;
3671 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003672 else
3673 {
3674 new->next = daemon->dhcp_ignore_names;
3675 daemon->dhcp_ignore_names = new;
3676 }
3677
3678 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003679 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003680 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003681 member->next = list;
3682 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003683 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003684 member->net = opt_string_alloc(arg+4);
3685 else
3686 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003687 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003688 }
Simon Kelley849a8352006-06-09 21:02:31 +01003689
3690 new->list = list;
3691 break;
3692 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003693
3694 case LOPT_PROXY: /* --dhcp-proxy */
3695 daemon->override = 1;
3696 while (arg) {
3697 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3698 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003699 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003700 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003701 new->next = daemon->override_relays;
3702 daemon->override_relays = new;
3703 arg = comma;
3704 }
3705 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003706
3707 case LOPT_RELAY: /* --dhcp-relay */
3708 {
3709 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3710 comma = split(arg);
3711 new->interface = opt_string_alloc(split(comma));
3712 new->iface_index = 0;
3713 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3714 {
3715 new->next = daemon->relay4;
3716 daemon->relay4 = new;
3717 }
3718#ifdef HAVE_DHCP6
3719 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3720 {
3721 new->next = daemon->relay6;
3722 daemon->relay6 = new;
3723 }
3724#endif
3725 else
3726 ret_err(_("Bad dhcp-relay"));
3727
3728 break;
3729 }
3730
Simon Kelley7622fc02009-06-04 20:32:05 +01003731#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003732
Simon Kelley8b372702012-03-09 17:45:10 +00003733#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003734 case LOPT_RA_PARAM: /* --ra-param */
3735 if ((comma = split(arg)))
3736 {
3737 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3738 new->lifetime = -1;
3739 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003740 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003741 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003742 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003743 if (strcasestr(comma, "mtu:") == comma)
3744 {
3745 arg = comma + 4;
3746 if (!(comma = split(comma)))
3747 goto err;
3748 if (!strcasecmp(arg, "off"))
3749 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003750 else if (!atoi_check(arg, &new->mtu))
3751 new->mtu_name = opt_string_alloc(arg);
3752 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003753 goto err;
3754 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003755 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3756 {
3757 if (*comma == 'l' || *comma == 'L')
3758 new->prio = 0x18;
3759 else
3760 new->prio = 0x08;
3761 comma = split(comma);
3762 }
3763 arg = split(comma);
3764 if (!atoi_check(comma, &new->interval) ||
3765 (arg && !atoi_check(arg, &new->lifetime)))
David Flamand005c46d2017-04-11 11:49:54 +01003766err:
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003767 ret_err(_("bad RA-params"));
3768
3769 new->next = daemon->ra_interfaces;
3770 daemon->ra_interfaces = new;
3771 }
3772 break;
3773
Simon Kelley8b372702012-03-09 17:45:10 +00003774 case LOPT_DUID: /* --dhcp-duid */
3775 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003776 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003777 else
3778 {
3779 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3780 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3781 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3782 }
3783 break;
3784#endif
3785
Simon Kelleyf2621c72007-04-29 19:47:21 +01003786 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003787 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003788 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003789 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003790 struct doctor *new = opt_malloc(sizeof(struct doctor));
3791 new->next = daemon->doctors;
3792 daemon->doctors = new;
3793 new->mask.s_addr = 0xffffffff;
3794 new->end.s_addr = 0;
3795
Simon Kelley849a8352006-06-09 21:02:31 +01003796 if ((a[0] = arg))
3797 for (k = 1; k < 3; k++)
3798 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003799 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003800 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003801 unhide_metas(a[k]);
3802 }
Simon Kelley849a8352006-06-09 21:02:31 +01003803
Simon Kelley73a08a22009-02-05 20:28:08 +00003804 dash = split_chr(a[0], '-');
3805
Simon Kelley849a8352006-06-09 21:02:31 +01003806 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003807 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
3808 (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
Simon Kelley73a08a22009-02-05 20:28:08 +00003809 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003810
Simon Kelleya2bc2542016-04-21 22:34:22 +01003811 if (k == 3 && !inet_pton(AF_INET, a[2], &new->mask))
3812 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003813
Simon Kelley73a08a22009-02-05 20:28:08 +00003814 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003815 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003816 !is_same_net(new->in, new->end, new->mask) ||
3817 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003818 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003819
3820 break;
3821 }
3822
Simon Kelleyf2621c72007-04-29 19:47:21 +01003823 case LOPT_INTNAME: /* --interface-name */
3824 {
3825 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003826 char *domain = NULL;
3827
Simon Kelleyf2621c72007-04-29 19:47:21 +01003828 comma = split(arg);
3829
Simon Kelley1f15b812009-10-13 17:49:32 +01003830 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003831 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003832
Simon Kelley824af852008-02-12 20:43:05 +00003833 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003834 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00003835 new->addr = NULL;
3836
Simon Kelleyf2621c72007-04-29 19:47:21 +01003837 /* Add to the end of the list, so that first name
3838 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003839 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003840 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003841 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003842 new->family = 0;
3843 arg = split_chr(comma, '/');
3844 if (arg)
3845 {
3846 if (strcmp(arg, "4") == 0)
3847 new->family = AF_INET;
3848#ifdef HAVE_IPV6
3849 else if (strcmp(arg, "6") == 0)
3850 new->family = AF_INET6;
3851#endif
3852 else
3853 ret_err(gen_err);
3854 }
Simon Kelley824af852008-02-12 20:43:05 +00003855 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003856 break;
3857 }
Simon Kelley9009d742008-11-14 20:04:27 +00003858
3859 case LOPT_CNAME: /* --cname */
3860 {
3861 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003862 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003863 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003864
Simon Kelleya1d973f2016-12-22 22:09:50 +00003865 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00003866 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00003867 pen = last;
3868 last = comma;
3869 }
3870
3871 if (!pen)
3872 ret_err(_("bad CNAME"));
3873
3874 if (pen != arg && atoi_check(last, &ttl))
3875 last = pen;
3876
3877 target = canonicalise_opt(last);
3878
3879 while (arg != last)
3880 {
Petr Menšík56f06232018-03-06 23:13:32 +00003881 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00003882 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01003883
3884 if (!alias || !target)
3885 ret_err(_("bad CNAME"));
Simon Kelleya1d973f2016-12-22 22:09:50 +00003886
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003887 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01003888 if (hostname_isequal(new->alias, alias))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003889 ret_err(_("duplicate CNAME"));
3890 new = opt_malloc(sizeof(struct cname));
3891 new->next = daemon->cnames;
3892 daemon->cnames = new;
3893 new->alias = alias;
3894 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003895 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003896
Petr Menšík56f06232018-03-06 23:13:32 +00003897 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00003898 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003899
Simon Kelley9009d742008-11-14 20:04:27 +00003900 break;
3901 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003902
3903 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003904 {
3905 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003906 char *dom, *target = NULL;
3907
Simon Kelleyf2621c72007-04-29 19:47:21 +01003908 comma = split(arg);
3909
Simon Kelley1f15b812009-10-13 17:49:32 +01003910 if (!(dom = canonicalise_opt(arg)) ||
3911 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003912 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003913 else
3914 {
3915 new = opt_malloc(sizeof(struct ptr_record));
3916 new->next = daemon->ptr;
3917 daemon->ptr = new;
3918 new->name = dom;
3919 new->ptr = target;
3920 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003921 break;
3922 }
3923
Simon Kelley1a6bca82008-07-11 11:11:42 +01003924 case LOPT_NAPTR: /* --naptr-record */
3925 {
3926 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3927 int k = 0;
3928 struct naptr *new;
3929 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003930 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003931
3932 if ((a[0] = arg))
3933 for (k = 1; k < 7; k++)
3934 if (!(a[k] = split(a[k-1])))
3935 break;
3936
3937
3938 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003939 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003940 !atoi_check16(a[1], &order) ||
3941 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003942 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003943 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003944 else
3945 {
3946 new = opt_malloc(sizeof(struct naptr));
3947 new->next = daemon->naptr;
3948 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003949 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003950 new->flags = opt_string_alloc(a[3]);
3951 new->services = opt_string_alloc(a[4]);
3952 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003953 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003954 new->order = order;
3955 new->pref = pref;
3956 }
3957 break;
3958 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003959
3960 case LOPT_RR: /* dns-rr */
3961 {
3962 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00003963 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003964 char *data;
3965 int val;
3966
3967 comma = split(arg);
3968 data = split(comma);
3969
3970 new = opt_malloc(sizeof(struct txt_record));
3971 new->next = daemon->rr;
3972 daemon->rr = new;
3973
3974 if (!atoi_check(comma, &val) ||
3975 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003976 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003977 ret_err(_("bad RR record"));
3978
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003979 new->class = val;
3980 new->len = 0;
3981
3982 if (data)
3983 {
3984 new->txt=opt_malloc(len);
3985 new->len = len;
3986 memcpy(new->txt, data, len);
3987 }
3988
3989 break;
3990 }
3991
Simon Kelleyf2621c72007-04-29 19:47:21 +01003992 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003993 {
3994 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003995 unsigned char *p, *cnt;
3996 size_t len;
3997
3998 comma = split(arg);
3999
Simon Kelley824af852008-02-12 20:43:05 +00004000 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004001 new->next = daemon->txt;
4002 daemon->txt = new;
4003 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004004 new->stat = 0;
4005
Simon Kelley1f15b812009-10-13 17:49:32 +01004006 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004007 ret_err(_("bad TXT record"));
4008
Simon Kelley28866e92011-02-14 20:19:14 +00004009 len = comma ? strlen(comma) : 0;
4010 len += (len/255) + 1; /* room for extra counts */
4011 new->txt = p = opt_malloc(len);
4012
4013 cnt = p++;
4014 *cnt = 0;
4015
4016 while (comma && *comma)
4017 {
4018 unsigned char c = (unsigned char)*comma++;
4019
4020 if (c == ',' || *cnt == 255)
4021 {
4022 if (c != ',')
4023 comma--;
4024 cnt = p++;
4025 *cnt = 0;
4026 }
4027 else
4028 {
4029 *p++ = unhide_meta(c);
4030 (*cnt)++;
4031 }
4032 }
4033
4034 new->len = p - new->txt;
4035
Simon Kelley849a8352006-06-09 21:02:31 +01004036 break;
4037 }
4038
Simon Kelleyf2621c72007-04-29 19:47:21 +01004039 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004040 {
4041 int port = 1, priority = 0, weight = 0;
4042 char *name, *target = NULL;
4043 struct mx_srv_record *new;
4044
Simon Kelleyf2621c72007-04-29 19:47:21 +01004045 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004046
Simon Kelley1f15b812009-10-13 17:49:32 +01004047 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004048 ret_err(_("bad SRV record"));
4049
Simon Kelley849a8352006-06-09 21:02:31 +01004050 if (comma)
4051 {
4052 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004053 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004054 if (!(target = canonicalise_opt(arg)))
4055 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00004056
Simon Kelley849a8352006-06-09 21:02:31 +01004057 if (comma)
4058 {
4059 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004060 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004061 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004062 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00004063
Simon Kelley849a8352006-06-09 21:02:31 +01004064 if (comma)
4065 {
4066 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004067 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004068 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004069 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00004070
Simon Kelley407a1f32016-03-01 17:06:07 +00004071 if (comma && !atoi_check16(comma, &weight))
4072 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01004073 }
4074 }
4075 }
4076
Simon Kelley824af852008-02-12 20:43:05 +00004077 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004078 new->next = daemon->mxnames;
4079 daemon->mxnames = new;
4080 new->issrv = 1;
4081 new->name = name;
4082 new->target = target;
4083 new->srvport = port;
4084 new->priority = priority;
4085 new->weight = weight;
4086 break;
4087 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004088
Simon Kelleye759d422012-03-16 13:18:57 +00004089 case LOPT_HOST_REC: /* --host-record */
4090 {
4091 struct host_record *new = opt_malloc(sizeof(struct host_record));
4092 memset(new, 0, sizeof(struct host_record));
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004093 new->ttl = -1;
4094
Simon Kelleye759d422012-03-16 13:18:57 +00004095 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004096 ret_err(_("Bad host-record"));
4097
4098 while (arg)
4099 {
4100 struct all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004101 char *dig;
4102
4103 for (dig = arg; *dig != 0; dig++)
4104 if (*dig < '0' || *dig > '9')
4105 break;
4106 if (*dig == 0)
4107 new->ttl = atoi(arg);
4108 else if (inet_pton(AF_INET, arg, &addr))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004109 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00004110#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004111 else if (inet_pton(AF_INET6, arg, &addr))
4112 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00004113#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004114 else
4115 {
4116 int nomem;
4117 char *canon = canonicalise(arg, &nomem);
4118 struct name_list *nl = opt_malloc(sizeof(struct name_list));
4119 if (!canon)
4120 ret_err(_("Bad name in host-record"));
4121
4122 nl->name = canon;
4123 /* keep order, so that PTR record goes to first name */
4124 nl->next = NULL;
4125 if (!new->names)
4126 new->names = nl;
4127 else
4128 {
4129 struct name_list *tmp;
4130 for (tmp = new->names; tmp->next; tmp = tmp->next);
4131 tmp->next = nl;
4132 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004133 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004134
4135 arg = comma;
4136 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004137 }
Simon Kelleye759d422012-03-16 13:18:57 +00004138
4139 /* Keep list order */
4140 if (!daemon->host_records_tail)
4141 daemon->host_records = new;
4142 else
4143 daemon->host_records_tail->next = new;
4144 new->next = NULL;
4145 daemon->host_records_tail = new;
4146 break;
4147 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004148
4149#ifdef HAVE_DNSSEC
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004150 case LOPT_DNSSEC_STAMP:
4151 daemon->timestamp_file = opt_string_alloc(arg);
4152 break;
4153
Simon Kelleya6918532018-04-15 16:20:52 +01004154 case LOPT_DNSSEC_CHECK:
4155 if (arg)
4156 {
4157 if (strcmp(arg, "no") == 0)
4158 set_option_bool(OPT_DNSSEC_IGN_NS);
4159 else
4160 ret_err(_("bad value for dnssec-check-unsigned"));
4161 }
4162 break;
4163
Simon Kelleyee415862014-02-11 11:07:22 +00004164 case LOPT_TRUST_ANCHOR:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004165 {
Simon Kelleyee415862014-02-11 11:07:22 +00004166 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4167 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4168 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004169
4170 new->class = C_IN;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004171
Simon Kelleycbf13a22014-01-25 17:59:14 +00004172 if ((comma = split(arg)) && (algo = split(comma)))
4173 {
4174 int class = 0;
4175 if (strcmp(comma, "IN") == 0)
4176 class = C_IN;
4177 else if (strcmp(comma, "CH") == 0)
4178 class = C_CHAOS;
4179 else if (strcmp(comma, "HS") == 0)
4180 class = C_HESIOD;
4181
4182 if (class != 0)
4183 {
4184 new->class = class;
4185 comma = algo;
4186 algo = split(comma);
4187 }
4188 }
4189
Simon Kelleyee415862014-02-11 11:07:22 +00004190 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4191 !atoi_check16(comma, &new->keytag) ||
4192 !atoi_check8(algo, &new->algo) ||
4193 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004194 !(new->name = canonicalise_opt(arg)))
Simon Kelleyee415862014-02-11 11:07:22 +00004195 ret_err(_("bad trust anchor"));
Simon Kelleycbf13a22014-01-25 17:59:14 +00004196
Simon Kelley0fc2f312014-01-08 10:26:58 +00004197 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004198 len = (2*strlen(keyhex))+1;
4199 new->digest = opt_malloc(len);
4200 unhide_metas(keyhex);
4201 /* 4034: "Whitespace is allowed within digits" */
4202 for (cp = keyhex; *cp; )
4203 if (isspace(*cp))
4204 for (cp1 = cp; *cp1; cp1++)
4205 *cp1 = *(cp1+1);
4206 else
4207 cp++;
4208 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
4209 ret_err(_("bad HEX in trust anchor"));
Simon Kelley0fc2f312014-01-08 10:26:58 +00004210
Simon Kelleyee415862014-02-11 11:07:22 +00004211 new->next = daemon->ds;
4212 daemon->ds = new;
4213
Simon Kelley0fc2f312014-01-08 10:26:58 +00004214 break;
4215 }
4216#endif
4217
Simon Kelley7622fc02009-06-04 20:32:05 +01004218 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004219 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004220
Simon Kelley849a8352006-06-09 21:02:31 +01004221 }
Simon Kelley824af852008-02-12 20:43:05 +00004222
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004223 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004224}
4225
Simon Kelley28866e92011-02-14 20:19:14 +00004226static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004227{
Simon Kelley824af852008-02-12 20:43:05 +00004228 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004229 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004230
4231 while (fgets(buff, MAXDNAME, f))
4232 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004233 int white, i;
4234 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004235 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004236 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004237
Simon Kelley824af852008-02-12 20:43:05 +00004238 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004239 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004240 {
4241 if (setjmp(mem_jmp))
4242 continue;
4243 mem_recover = 1;
4244 }
4245
Simon Kelley13dee6f2017-02-28 16:51:58 +00004246 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004247 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004248 errmess = NULL;
4249
Simon Kelley849a8352006-06-09 21:02:31 +01004250 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4251 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004252 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004253 {
4254 if (*p == '"')
4255 {
4256 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004257
Simon Kelley849a8352006-06-09 21:02:31 +01004258 for(; *p && *p != '"'; p++)
4259 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004260 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004261 {
4262 if (p[1] == 't')
4263 p[1] = '\t';
4264 else if (p[1] == 'n')
4265 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004266 else if (p[1] == 'b')
4267 p[1] = '\b';
4268 else if (p[1] == 'r')
4269 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004270 else if (p[1] == 'e') /* escape */
4271 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004272 memmove(p, p+1, strlen(p+1)+1);
4273 }
4274 *p = hide_meta(*p);
4275 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004276
4277 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004278 {
4279 errmess = _("missing \"");
4280 goto oops;
4281 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004282
4283 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004284 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004285
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004286 if (isspace(*p))
4287 {
4288 *p = ' ';
4289 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004290 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004291 else
4292 {
4293 if (white && *p == '#')
4294 {
4295 *p = 0;
4296 break;
4297 }
4298 white = 0;
4299 }
Simon Kelley849a8352006-06-09 21:02:31 +01004300 }
4301
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004302
4303 /* strip leading spaces */
4304 for (start = buff; *start && *start == ' '; start++);
4305
4306 /* strip trailing spaces */
4307 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4308
4309 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004310 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004311 else
4312 start[len] = 0;
4313
Simon Kelley611ebc52012-07-16 16:23:46 +01004314 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004315 arg = start;
4316 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004317 {
4318 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004319 for (arg = p+1; *arg == ' '; arg++);
4320 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004321 *p = 0;
4322 }
4323 else
4324 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004325
Simon Kelley611ebc52012-07-16 16:23:46 +01004326 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004327 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004328 for (option = 0, i = 0; opts[i].name; i++)
4329 if (strcmp(opts[i].name, start) == 0)
4330 {
4331 option = opts[i].val;
4332 break;
4333 }
4334
4335 if (!option)
4336 errmess = _("bad option");
4337 else if (opts[i].has_arg == 0 && arg)
4338 errmess = _("extraneous parameter");
4339 else if (opts[i].has_arg == 1 && !arg)
4340 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004341 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4342 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004343 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004344
4345 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004346 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004347 strcpy(daemon->namebuff, errmess);
4348
Simon Kelley7b1eae42014-02-20 13:43:28 +00004349 if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004350 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004351 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004352 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004353 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004354 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004355 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004356 }
Simon Kelley849a8352006-06-09 21:02:31 +01004357 }
4358
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004359 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004360 fclose(f);
4361}
4362
Simon Kelley4f7bb572018-03-08 18:47:08 +00004363#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004364int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004365{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004366 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4367
Simon Kelley70d18732015-01-31 19:59:29 +00004368 if (flags & AH_DHCP_HST)
4369 return one_file(file, LOPT_BANK);
4370 else if (flags & AH_DHCP_OPT)
4371 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004372
Simon Kelley70d18732015-01-31 19:59:29 +00004373 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004374}
4375#endif
4376
Simon Kelley395eb712012-07-06 22:07:05 +01004377static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004378{
4379 FILE *f;
4380 int nofile_ok = 0;
4381 static int read_stdin = 0;
4382 static struct fileread {
4383 dev_t dev;
4384 ino_t ino;
4385 struct fileread *next;
4386 } *filesread = NULL;
4387
4388 if (hard_opt == '7')
4389 {
4390 /* default conf-file reading */
4391 hard_opt = 0;
4392 nofile_ok = 1;
4393 }
4394
4395 if (hard_opt == 0 && strcmp(file, "-") == 0)
4396 {
4397 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004398 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004399 read_stdin = 1;
4400 file = "stdin";
4401 f = stdin;
4402 }
4403 else
4404 {
4405 /* ignore repeated files. */
4406 struct stat statbuf;
4407
4408 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4409 {
4410 struct fileread *r;
4411
4412 for (r = filesread; r; r = r->next)
4413 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004414 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004415
4416 r = safe_malloc(sizeof(struct fileread));
4417 r->next = filesread;
4418 filesread = r;
4419 r->dev = statbuf.st_dev;
4420 r->ino = statbuf.st_ino;
4421 }
4422
4423 if (!(f = fopen(file, "r")))
4424 {
4425 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004426 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004427 else
4428 {
4429 char *str = _("cannot read %s: %s");
4430 if (hard_opt != 0)
4431 {
4432 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004433 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004434 }
4435 else
4436 die(str, file, EC_FILE);
4437 }
4438 }
4439 }
4440
4441 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004442 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004443}
4444
4445/* expand any name which is a directory */
4446struct hostsfile *expand_filelist(struct hostsfile *list)
4447{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004448 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004449 struct hostsfile *ah;
4450
Simon Kelley19c51cf2014-03-18 22:38:30 +00004451 /* find largest used index */
4452 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004453 {
4454 if (i <= ah->index)
4455 i = ah->index + 1;
4456
4457 if (ah->flags & AH_DIR)
4458 ah->flags |= AH_INACTIVE;
4459 else
4460 ah->flags &= ~AH_INACTIVE;
4461 }
4462
4463 for (ah = list; ah; ah = ah->next)
4464 if (!(ah->flags & AH_INACTIVE))
4465 {
4466 struct stat buf;
4467 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4468 {
4469 DIR *dir_stream;
4470 struct dirent *ent;
4471
4472 /* don't read this as a file */
4473 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004474
Simon Kelley28866e92011-02-14 20:19:14 +00004475 if (!(dir_stream = opendir(ah->fname)))
4476 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4477 ah->fname, strerror(errno));
4478 else
4479 {
4480 while ((ent = readdir(dir_stream)))
4481 {
4482 size_t lendir = strlen(ah->fname);
4483 size_t lenfile = strlen(ent->d_name);
4484 struct hostsfile *ah1;
4485 char *path;
4486
4487 /* ignore emacs backups and dotfiles */
4488 if (lenfile == 0 ||
4489 ent->d_name[lenfile - 1] == '~' ||
4490 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4491 ent->d_name[0] == '.')
4492 continue;
4493
4494 /* see if we have an existing record.
4495 dir is ah->fname
4496 file is ent->d_name
4497 path to match is ah1->fname */
4498
4499 for (ah1 = list; ah1; ah1 = ah1->next)
4500 {
4501 if (lendir < strlen(ah1->fname) &&
4502 strstr(ah1->fname, ah->fname) == ah1->fname &&
4503 ah1->fname[lendir] == '/' &&
4504 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4505 {
4506 ah1->flags &= ~AH_INACTIVE;
4507 break;
4508 }
4509 }
4510
4511 /* make new record */
4512 if (!ah1)
4513 {
4514 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4515 continue;
4516
4517 if (!(path = whine_malloc(lendir + lenfile + 2)))
4518 {
4519 free(ah1);
4520 continue;
4521 }
4522
4523 strcpy(path, ah->fname);
4524 strcat(path, "/");
4525 strcat(path, ent->d_name);
4526 ah1->fname = path;
4527 ah1->index = i++;
4528 ah1->flags = AH_DIR;
4529 ah1->next = list;
4530 list = ah1;
4531 }
4532
4533 /* inactivate record if not regular file */
4534 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4535 ah1->flags |= AH_INACTIVE;
4536
4537 }
4538 closedir(dir_stream);
4539 }
4540 }
4541 }
4542
4543 return list;
4544}
4545
Simon Kelley7b1eae42014-02-20 13:43:28 +00004546void read_servers_file(void)
4547{
4548 FILE *f;
4549
4550 if (!(f = fopen(daemon->servers_file, "r")))
4551 {
4552 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4553 return;
4554 }
4555
4556 mark_servers(SERV_FROM_FILE);
4557 cleanup_servers();
4558
4559 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4560}
4561
Simon Kelley28866e92011-02-14 20:19:14 +00004562
Simon Kelley7622fc02009-06-04 20:32:05 +01004563#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004564static void clear_dynamic_conf(void)
4565{
4566 struct dhcp_config *configs, *cp, **up;
4567
4568 /* remove existing... */
4569 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4570 {
4571 cp = configs->next;
4572
4573 if (configs->flags & CONFIG_BANK)
4574 {
4575 struct hwaddr_config *mac, *tmp;
4576 struct dhcp_netid_list *list, *tmplist;
4577
4578 for (mac = configs->hwaddr; mac; mac = tmp)
4579 {
4580 tmp = mac->next;
4581 free(mac);
4582 }
4583
4584 if (configs->flags & CONFIG_CLID)
4585 free(configs->clid);
4586
4587 for (list = configs->netid; list; list = tmplist)
4588 {
4589 free(list->list);
4590 tmplist = list->next;
4591 free(list);
4592 }
4593
4594 if (configs->flags & CONFIG_NAME)
4595 free(configs->hostname);
4596
4597 *up = configs->next;
4598 free(configs);
4599 }
4600 else
4601 up = &configs->next;
4602 }
4603}
4604
4605static void clear_dynamic_opt(void)
4606{
4607 struct dhcp_opt *opts, *cp, **up;
4608 struct dhcp_netid *id, *next;
4609
4610 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4611 {
4612 cp = opts->next;
4613
4614 if (opts->flags & DHOPT_BANK)
4615 {
4616 if ((opts->flags & DHOPT_VENDOR))
4617 free(opts->u.vendor_class);
4618 free(opts->val);
4619 for (id = opts->netid; id; id = next)
4620 {
4621 next = id->next;
4622 free(id->net);
4623 free(id);
4624 }
4625 *up = opts->next;
4626 free(opts);
4627 }
4628 else
4629 up = &opts->next;
4630 }
4631}
4632
Simon Kelley824af852008-02-12 20:43:05 +00004633void reread_dhcp(void)
4634{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004635 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004636
Simon Kelley4f7bb572018-03-08 18:47:08 +00004637 /* Do these even if there is no daemon->dhcp_hosts_file or
4638 daemon->dhcp_opts_file since entries may have been created by the
4639 inotify dynamic file reading system. */
4640
4641 clear_dynamic_conf();
4642 clear_dynamic_opt();
4643
4644 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004645 {
Simon Kelley28866e92011-02-14 20:19:14 +00004646 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4647 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004648 if (!(hf->flags & AH_INACTIVE))
4649 {
4650 if (one_file(hf->fname, LOPT_BANK))
4651 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4652 }
Simon Kelley824af852008-02-12 20:43:05 +00004653 }
4654
4655 if (daemon->dhcp_opts_file)
4656 {
Simon Kelley28866e92011-02-14 20:19:14 +00004657 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4658 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4659 if (!(hf->flags & AH_INACTIVE))
4660 {
Simon Kelley395eb712012-07-06 22:07:05 +01004661 if (one_file(hf->fname, LOPT_OPTS))
4662 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004663 }
Simon Kelley824af852008-02-12 20:43:05 +00004664 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004665
4666# ifdef HAVE_INOTIFY
4667 /* Setup notify and read pre-existing files. */
4668 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
4669# endif
Simon Kelley824af852008-02-12 20:43:05 +00004670}
Simon Kelley7622fc02009-06-04 20:32:05 +01004671#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00004672
Simon Kelley5aabfc72007-08-29 11:24:47 +01004673void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004674{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004675 size_t argbuf_size = MAXDNAME;
4676 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004677 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004678 int option, conffile_opt = '7', testmode = 0;
Simon Kelley90cb2222015-07-05 21:59:10 +01004679 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004680
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004681 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004682
Simon Kelley824af852008-02-12 20:43:05 +00004683 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004684 memset(daemon, 0, sizeof(struct daemon));
4685 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004686
Simon Kelley3be34542004-09-11 19:12:13 +01004687 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004688 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004689 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004690 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004691 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4692 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004693 daemon->default_resolv.is_default = 1;
4694 daemon->default_resolv.name = RESOLVFILE;
4695 daemon->resolv_files = &daemon->default_resolv;
4696 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004697 daemon->runfile = RUNFILE;
4698 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004699 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004700 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004701 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004702 daemon->auth_ttl = AUTH_TTL;
4703 daemon->soa_refresh = SOA_REFRESH;
4704 daemon->soa_retry = SOA_RETRY;
4705 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00004706 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00004707 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01004708
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004709#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00004710 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
4711 add_txt("authors.bind", "Simon Kelley", 0);
4712 add_txt("copyright.bind", COPYRIGHT, 0);
4713 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
4714 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
4715 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
4716 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
4717 add_txt("hits.bind", NULL, TXT_STAT_HITS);
4718#ifdef HAVE_AUTH
4719 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
4720#endif
4721 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004722#endif
Simon Kelley0a852542005-03-23 20:28:59 +00004723
Simon Kelley849a8352006-06-09 21:02:31 +01004724 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004725 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004726#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004727 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004728#else
Simon Kelley849a8352006-06-09 21:02:31 +01004729 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004730#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004731
4732 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004733 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004734 for (; optind < argc; optind++)
4735 {
4736 unsigned char *c = (unsigned char *)argv[optind];
4737 for (; *c != 0; c++)
4738 if (!isspace(*c))
4739 die(_("junk found in command line"), NULL, EC_BADCONF);
4740 }
Simon Kelley28866e92011-02-14 20:19:14 +00004741 break;
4742 }
4743
Simon Kelley849a8352006-06-09 21:02:31 +01004744 /* Copy optarg so that argv doesn't get changed */
4745 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004746 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00004747 if (strlen(optarg) >= argbuf_size)
4748 {
4749 free(argbuf);
4750 argbuf_size = strlen(optarg) + 1;
4751 argbuf = opt_malloc(argbuf_size);
4752 }
4753 strncpy(argbuf, optarg, argbuf_size);
4754 argbuf[argbuf_size-1] = 0;
4755 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01004756 }
4757 else
4758 arg = NULL;
4759
4760 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004761 if (option == LOPT_TEST)
4762 testmode = 1;
4763 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004764 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004765#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004766 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004767 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004768#ifdef HAVE_DHCP6
4769 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4770 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004771#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004772 else
4773#endif
4774 do_usage();
4775
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004776 exit(0);
4777 }
Simon Kelley849a8352006-06-09 21:02:31 +01004778 else if (option == 'v')
4779 {
4780 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004781 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004782 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4783 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004784 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004785 exit(0);
4786 }
Simon Kelley849a8352006-06-09 21:02:31 +01004787 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004788 {
Simon Kelley28866e92011-02-14 20:19:14 +00004789 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004790 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004791 }
Simon Kelley849a8352006-06-09 21:02:31 +01004792 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004793 {
Simon Kelley26128d22004-11-14 16:43:54 +00004794#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00004795 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004796#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00004797 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004798#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004799 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004800 }
4801 }
Simon Kelley849a8352006-06-09 21:02:31 +01004802
Neil Jerram3bd4c472018-01-18 22:49:38 +00004803 free(argbuf);
4804
Simon Kelley849a8352006-06-09 21:02:31 +01004805 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00004806 {
4807 one_file(conffile, conffile_opt);
Simon Kelley90cb2222015-07-05 21:59:10 +01004808 if (conffile_opt == 0)
4809 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00004810 }
Simon Kelley849a8352006-06-09 21:02:31 +01004811
Simon Kelley1a6bca82008-07-11 11:11:42 +01004812 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004813 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004814 {
4815 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004816 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01004817 if (!(tmp->flags & SERV_HAS_SOURCE))
4818 {
4819 if (tmp->source_addr.sa.sa_family == AF_INET)
4820 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004821#ifdef HAVE_IPV6
Simon Kelley14ffa072016-04-25 16:36:44 +01004822 else if (tmp->source_addr.sa.sa_family == AF_INET6)
4823 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004824#endif
Simon Kelley14ffa072016-04-25 16:36:44 +01004825 }
4826 }
4827
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004828 if (daemon->host_records)
4829 {
4830 struct host_record *hr;
4831
4832 for (hr = daemon->host_records; hr; hr = hr->next)
4833 if (hr->ttl == -1)
4834 hr->ttl = daemon->local_ttl;
4835 }
4836
4837 if (daemon->cnames)
4838 {
Simon Kelley903df072017-01-19 17:22:00 +00004839 struct cname *cn, *cn2, *cn3;
4840
4841#define NOLOOP 1
4842#define TESTLOOP 2
4843
4844 /* Fill in TTL for CNAMES noe we have local_ttl.
4845 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004846 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00004847 {
4848 if (cn->ttl == -1)
4849 cn->ttl = daemon->local_ttl;
4850 cn->flag = 0;
4851 cn->targetp = NULL;
4852 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
4853 if (hostname_isequal(cn->target, cn2->alias))
4854 {
4855 cn->targetp = cn2;
4856 break;
4857 }
4858 }
4859
4860 /* Find any CNAME loops.*/
4861 for (cn = daemon->cnames; cn; cn = cn->next)
4862 {
4863 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
4864 {
4865 if (cn2->flag == NOLOOP)
4866 break;
4867
4868 if (cn2->flag == TESTLOOP)
4869 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
4870
4871 cn2->flag = TESTLOOP;
4872 }
4873
4874 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
4875 cn3->flag = NOLOOP;
4876 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004877 }
4878
Simon Kelley3be34542004-09-11 19:12:13 +01004879 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004880 {
4881 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004882 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004883 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004884 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004885#ifdef HAVE_IPV6
4886 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004887 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004888#endif /* IPv6 */
4889 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004890
4891 /* create default, if not specified */
4892 if (daemon->authserver && !daemon->hostmaster)
4893 {
4894 strcpy(buff, "hostmaster.");
4895 strcat(buff, daemon->authserver);
4896 daemon->hostmaster = opt_string_alloc(buff);
4897 }
4898
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004899 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004900 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004901 {
Simon Kelley0a852542005-03-23 20:28:59 +00004902 struct mx_srv_record *mx;
4903
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004904 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004905 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004906
Simon Kelley0a852542005-03-23 20:28:59 +00004907 for (mx = daemon->mxnames; mx; mx = mx->next)
4908 if (!mx->issrv && hostname_isequal(mx->name, buff))
4909 break;
4910
Simon Kelley28866e92011-02-14 20:19:14 +00004911 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004912 {
Simon Kelley824af852008-02-12 20:43:05 +00004913 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004914 mx->next = daemon->mxnames;
4915 mx->issrv = 0;
4916 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004917 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004918 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004919 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004920
Simon Kelley3be34542004-09-11 19:12:13 +01004921 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004922 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004923
4924 for (mx = daemon->mxnames; mx; mx = mx->next)
4925 if (!mx->issrv && !mx->target)
4926 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004927 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004928
Simon Kelley28866e92011-02-14 20:19:14 +00004929 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004930 daemon->resolv_files &&
4931 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004932 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004933 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004934
Simon Kelley28866e92011-02-14 20:19:14 +00004935 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004936 {
4937 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004938 FILE *f;
4939
Simon Kelley28866e92011-02-14 20:19:14 +00004940 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004941 !daemon->resolv_files ||
4942 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004943 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004944
Simon Kelley3be34542004-09-11 19:12:13 +01004945 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004946 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004947
4948 while ((line = fgets(buff, MAXDNAME, f)))
4949 {
4950 char *token = strtok(line, " \t\n\r");
4951
4952 if (!token || strcmp(token, "search") != 0)
4953 continue;
4954
4955 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004956 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004957 break;
4958 }
Simon Kelley3be34542004-09-11 19:12:13 +01004959
Simon Kelleyde379512004-06-22 20:23:33 +01004960 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004961
Simon Kelley3be34542004-09-11 19:12:13 +01004962 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004963 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004964 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004965
4966 if (daemon->domain_suffix)
4967 {
4968 /* add domain for any srv record without one. */
4969 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004970
Simon Kelley3d8df262005-08-29 12:19:27 +01004971 for (srv = daemon->mxnames; srv; srv = srv->next)
4972 if (srv->issrv &&
4973 strchr(srv->name, '.') &&
4974 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4975 {
4976 strcpy(buff, srv->name);
4977 strcat(buff, ".");
4978 strcat(buff, daemon->domain_suffix);
4979 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004980 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004981 }
4982 }
Simon Kelley28866e92011-02-14 20:19:14 +00004983 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004984 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004985
Simon Kelleyc8a80482014-03-05 14:29:54 +00004986 /* If there's access-control config, then ignore --local-service, it's intended
4987 as a system default to keep otherwise unconfigured installations safe. */
4988 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
4989 reset_option_bool(OPT_LOCAL_SERVICE);
4990
Simon Kelley7622fc02009-06-04 20:32:05 +01004991 if (testmode)
4992 {
4993 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
4994 exit(0);
4995 }
Simon Kelley849a8352006-06-09 21:02:31 +01004996}