blob: 0837d4d501c6b31c19514e83adbdbe6ff3557d93 [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
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100166#define LOPT_UBUS 354
Simon Kelleyc8226202018-08-08 23:46:03 +0100167#define LOPT_NAME_MATCH 355
Simon Kelleybec366b2016-02-24 22:03:26 +0000168
Simon Kelley849a8352006-06-09 21:02:31 +0100169#ifdef HAVE_GETOPT_LONG
170static const struct option opts[] =
171#else
172static const struct myoption opts[] =
173#endif
174 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100175 { "version", 0, 0, 'v' },
176 { "no-hosts", 0, 0, 'h' },
177 { "no-poll", 0, 0, 'n' },
178 { "help", 0, 0, 'w' },
179 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000180 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100181 { "user", 2, 0, 'u' },
182 { "group", 2, 0, 'g' },
183 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000184 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100185 { "mx-host", 1, 0, 'm' },
186 { "mx-target", 1, 0, 't' },
187 { "cache-size", 2, 0, 'c' },
188 { "port", 1, 0, 'p' },
189 { "dhcp-leasefile", 2, 0, 'l' },
190 { "dhcp-lease", 1, 0, 'l' },
191 { "dhcp-host", 1, 0, 'G' },
192 { "dhcp-range", 1, 0, 'F' },
193 { "dhcp-option", 1, 0, 'O' },
194 { "dhcp-boot", 1, 0, 'M' },
195 { "domain", 1, 0, 's' },
196 { "domain-suffix", 1, 0, 's' },
197 { "interface", 1, 0, 'i' },
198 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000199 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100200 { "bogus-priv", 0, 0, 'b' },
201 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000202 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100203 { "selfmx", 0, 0, 'e' },
204 { "filterwin2k", 0, 0, 'f' },
205 { "pid-file", 2, 0, 'x' },
206 { "strict-order", 0, 0, 'o' },
207 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000208 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100209 { "local", 1, 0, LOPT_LOCAL },
210 { "address", 1, 0, 'A' },
211 { "conf-file", 2, 0, 'C' },
212 { "no-resolv", 0, 0, 'R' },
213 { "expand-hosts", 0, 0, 'E' },
214 { "localmx", 0, 0, 'L' },
215 { "local-ttl", 1, 0, 'T' },
216 { "no-negcache", 0, 0, 'N' },
217 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000218 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100219 { "query-port", 1, 0, 'Q' },
220 { "except-interface", 1, 0, 'I' },
221 { "no-dhcp-interface", 1, 0, '2' },
222 { "domain-needed", 0, 0, 'D' },
223 { "dhcp-lease-max", 1, 0, 'X' },
224 { "bind-interfaces", 0, 0, 'z' },
225 { "read-ethers", 0, 0, 'Z' },
226 { "alias", 1, 0, 'V' },
227 { "dhcp-vendorclass", 1, 0, 'U' },
228 { "dhcp-userclass", 1, 0, 'j' },
229 { "dhcp-ignore", 1, 0, 'J' },
230 { "edns-packet-max", 1, 0, 'P' },
231 { "keep-in-foreground", 0, 0, 'k' },
232 { "dhcp-authoritative", 0, 0, 'K' },
233 { "srv-host", 1, 0, 'W' },
234 { "localise-queries", 0, 0, 'y' },
235 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100236 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100237 { "enable-dbus", 2, 0, '1' },
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100238 { "enable-ubus", 0, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100239 { "bootp-dynamic", 2, 0, '3' },
240 { "dhcp-mac", 1, 0, '4' },
241 { "no-ping", 0, 0, '5' },
242 { "dhcp-script", 1, 0, '6' },
243 { "conf-dir", 1, 0, '7' },
244 { "log-facility", 1, 0 ,'8' },
245 { "leasefile-ro", 0, 0, '9' },
246 { "dns-forward-max", 1, 0, '0' },
247 { "clear-on-reload", 0, 0, LOPT_RELOAD },
248 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100249 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100250 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100251 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100252 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100253 { "tftp-root", 1, 0, LOPT_PREFIX },
254 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000255 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100256 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100257 { "ptr-record", 1, 0, LOPT_PTR },
258 { "naptr-record", 1, 0, LOPT_NAPTR },
259 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
260 { "dhcp-option-force", 1, 0, LOPT_FORCE },
261 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
262 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
263 { "log-async", 2, 0, LOPT_MAX_LOGS },
264 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
265 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
266 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
267 { "interface-name", 1, 0, LOPT_INTNAME },
268 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
269 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000270 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000271 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100272 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
273 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
274 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100275 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100276 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100277 { "dhcp-match", 1, 0, LOPT_MATCH },
278 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100279 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100280 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100281 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000282 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100283 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100284 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
285 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
286 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000287 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100288 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
289 { "cname", 1, 0, LOPT_CNAME },
290 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
291 { "pxe-service", 1, 0, LOPT_PXE_SERV },
292 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100293 { "tag-if", 1, 0, LOPT_TAG_IF },
294 { "dhcp-proxy", 2, 0, LOPT_PROXY },
295 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
296 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000297 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100298 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000299 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000300 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100301 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
302 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000303 { "dhcp-client-update", 0, 0, LOPT_FQDN },
304 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000305 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000306 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000307 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100308 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000309 { "auth-zone", 1, 0, LOPT_AUTHZONE },
310 { "auth-server", 1, 0, LOPT_AUTHSERV },
311 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
312 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000313 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000314 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000315 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100316 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200317 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000318 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000319 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100320 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000321 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000322 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyc6309242013-03-07 20:59:28 +0000323#ifdef OPTION6_PREFIX_CLASS
324 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
325#endif
Simon Kelleyff7eea22013-09-04 18:01:38 +0100326 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100327 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100328 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
329 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
330 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100331 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000332 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000333 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100334 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000335 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100336 { "dumpfile", 1, 0, LOPT_DUMPFILE },
337 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Simon Kelley849a8352006-06-09 21:02:31 +0100338 { NULL, 0, 0, 0 }
339 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000340
Simon Kelley28866e92011-02-14 20:19:14 +0000341
342#define ARG_DUP OPT_LAST
343#define ARG_ONE OPT_LAST + 1
344#define ARG_USED_CL OPT_LAST + 2
345#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000346
Simon Kelley1a6bca82008-07-11 11:11:42 +0100347static struct {
348 int opt;
349 unsigned int rept;
350 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000351 char * const desc;
352 char * const arg;
353} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000354 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
355 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100356 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000357 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
358 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
359 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100360 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
361 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
362 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
363 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
364 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000365 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
366 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100367 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000368 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
369 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000370 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000371 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100372 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100373 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000374 { '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 +0000375 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000376 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
377 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100378 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
379 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
380 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
381 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
382 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
383 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100384 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
385 { '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 +0000386 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100387 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000388 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100389 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
390 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
391 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
392 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
393 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
394 { 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 +0000395 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
396 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000397 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000398 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100399 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000400 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000401 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000402 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000403 { 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 +0000404 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000405 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000406 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
407 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
408 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
409 { 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 +0000410 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
411 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000412 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100413 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100414 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000415 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
416 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100417 { '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 +0000418 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
419 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100420 { '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 +0000421 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
422 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
423 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100424 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
425 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100426 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100427 { LOPT_UBUS, OPT_UBUS, NULL, gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000428 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100429 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
430 { '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 +0000431 { 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 +0100432 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000433 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
434 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
435 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000436 { 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 +0000437 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000438 { '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 +0100439 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000440 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100441 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100442 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100443 { 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 +0100444 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100445 { 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 +0100446 { 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 +0100447 { 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 +0100448 { 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 +0000449 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000450 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100451 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100452 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100453 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
454 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000455 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100456 { 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 +0100457 { 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 +0000458 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100459 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100460 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelleyc8226202018-08-08 23:46:03 +0100461 { LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100462 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100463 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
464 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000465 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000466 { 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 +0000467 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
468 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100469 { 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 +0000470 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100471 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
472 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
473 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000474 { 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 +0100475 { 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 +0000476 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100477 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100478 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
479 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000480 { 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 +0000481 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000482 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000483 { 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 +0100484 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000485 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000486 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000487 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000488 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000489 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000490 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
491 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100492 { 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 +0100493 { 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 +0000494 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000495 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000496 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100497 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000498 { 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 +0000499 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000500#ifdef OPTION6_PREFIX_CLASS
501 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
502#endif
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100503 { 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 +0100504 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
505 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
506 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000507 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
508 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000509 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000510 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100511 { 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 +0000512 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100513 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
514 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100515 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000516};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000517
Josh Soref730c6742017-02-06 16:14:04 +0000518/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100519 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 +0100520 following sequence so that they map to themselves: it is therefore possible to call
521 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000522 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100523 couple of other places.
524 Note that space is included here so that
525 --dhcp-option=3, string
526 has five characters, whilst
527 --dhcp-option=3," string"
528 has six.
529*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100530
Simon Kelleyf2621c72007-04-29 19:47:21 +0100531static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100532
533static char hide_meta(char c)
534{
535 unsigned int i;
536
537 for (i = 0; i < (sizeof(meta) - 1); i++)
538 if (c == meta[i])
539 return (char)i;
540
541 return c;
542}
543
544static char unhide_meta(char cr)
545{
546 unsigned int c = cr;
547
548 if (c < (sizeof(meta) - 1))
549 cr = meta[c];
550
551 return cr;
552}
553
554static void unhide_metas(char *cp)
555{
556 if (cp)
557 for(; *cp; cp++)
558 *cp = unhide_meta(*cp);
559}
560
Simon Kelley824af852008-02-12 20:43:05 +0000561static void *opt_malloc(size_t size)
562{
563 void *ret;
564
565 if (mem_recover)
566 {
567 ret = whine_malloc(size);
568 if (!ret)
569 longjmp(mem_jmp, 1);
570 }
571 else
572 ret = safe_malloc(size);
573
574 return ret;
575}
576
577static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100578{
579 char *ret = NULL;
580
581 if (cp && strlen(cp) != 0)
582 {
Simon Kelley824af852008-02-12 20:43:05 +0000583 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100584 strcpy(ret, cp);
585
586 /* restore hidden metachars */
587 unhide_metas(ret);
588 }
589
590 return ret;
591}
592
Simon Kelley3d8df262005-08-29 12:19:27 +0100593
Simon Kelleyf2621c72007-04-29 19:47:21 +0100594/* find next comma, split string with zero and eliminate spaces.
595 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000596
597static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100598{
599 char *comma, *p;
600
Simon Kelley73a08a22009-02-05 20:28:08 +0000601 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100602 return NULL;
603
604 p = comma;
605 *comma = ' ';
606
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100607 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100608
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100609 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100610 *p = 0;
611
612 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100613}
614
Simon Kelley73a08a22009-02-05 20:28:08 +0000615static char *split(char *s)
616{
617 return split_chr(s, ',');
618}
619
Simon Kelley1f15b812009-10-13 17:49:32 +0100620static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100621{
Simon Kelley1f15b812009-10-13 17:49:32 +0100622 char *ret;
623 int nomem;
624
Simon Kelley3d8df262005-08-29 12:19:27 +0100625 if (!s)
626 return 0;
627
628 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100629 if (!(ret = canonicalise(s, &nomem)) && nomem)
630 {
631 if (mem_recover)
632 longjmp(mem_jmp, 1);
633 else
634 die(_("could not get memory"), NULL, EC_NOMEM);
635 }
636
637 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100638}
639
640static int atoi_check(char *a, int *res)
641{
642 char *p;
643
644 if (!a)
645 return 0;
646
647 unhide_metas(a);
648
649 for (p = a; *p; p++)
650 if (*p < '0' || *p > '9')
651 return 0;
652
653 *res = atoi(a);
654 return 1;
655}
656
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100657static int atoi_check16(char *a, int *res)
658{
659 if (!(atoi_check(a, res)) ||
660 *res < 0 ||
661 *res > 0xffff)
662 return 0;
663
664 return 1;
665}
Simon Kelleyee415862014-02-11 11:07:22 +0000666
Simon Kelleyde73a492014-02-17 21:43:27 +0000667#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000668static int atoi_check8(char *a, int *res)
669{
670 if (!(atoi_check(a, res)) ||
671 *res < 0 ||
672 *res > 0xff)
673 return 0;
674
675 return 1;
676}
Simon Kelleyde73a492014-02-17 21:43:27 +0000677#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100678
679#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000680static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000681{
Simon Kelley824af852008-02-12 20:43:05 +0000682 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000683
684 if (txt)
685 {
686 size_t len = strlen(txt);
687 r->txt = opt_malloc(len+1);
688 r->len = len+1;
689 *(r->txt) = len;
690 memcpy((r->txt)+1, txt, len);
691 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100692
Simon Kelleyfec216d2014-03-27 20:54:34 +0000693 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000694 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000695 r->next = daemon->txt;
696 daemon->txt = r;
697 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000698}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100699#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000700
Simon Kelley849a8352006-06-09 21:02:31 +0100701static void do_usage(void)
702{
703 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000704 int i, j;
705
706 struct {
707 char handle;
708 int val;
709 } tab[] = {
710 { '$', CACHESIZ },
711 { '*', EDNS_PKTSZ },
712 { '&', MAXLEASES },
713 { '!', FTABSIZ },
714 { '#', TFTP_MAX_CONNECTIONS },
715 { '\0', 0 }
716 };
Simon Kelley849a8352006-06-09 21:02:31 +0100717
718 printf(_("Usage: dnsmasq [options]\n\n"));
719#ifndef HAVE_GETOPT_LONG
720 printf(_("Use short options only on the command line.\n"));
721#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100722 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100723
Simon Kelley1a6bca82008-07-11 11:11:42 +0100724 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100725 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100726 char *desc = usage[i].flagdesc;
727 char *eq = "=";
728
729 if (!desc || *desc == '[')
730 eq = "";
731
732 if (!desc)
733 desc = "";
734
735 for ( j = 0; opts[j].name; j++)
736 if (opts[j].val == usage[i].opt)
737 break;
738 if (usage[i].opt < 256)
739 sprintf(buff, "-%c, ", usage[i].opt);
740 else
741 sprintf(buff, " ");
742
743 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100744 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100745
Simon Kelley849a8352006-06-09 21:02:31 +0100746 if (usage[i].arg)
747 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000748 strcpy(buff, usage[i].arg);
749 for (j = 0; tab[j].handle; j++)
750 if (tab[j].handle == *(usage[i].arg))
751 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100752 }
Simon Kelley849a8352006-06-09 21:02:31 +0100753 printf(_(usage[i].desc), buff);
754 printf("\n");
755 }
756}
757
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100758#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
759
Ed Bardsleya7369be2015-08-05 21:17:18 +0100760static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
761{
762 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
763 addr->sa.sa_family = AF_INET;
764#ifdef HAVE_IPV6
765 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
766 addr->sa.sa_family = AF_INET6;
767#endif
768 else
769 return _("bad address");
770
771 return NULL;
772}
773
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100774char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
775{
776 int source_port = 0, serv_port = NAMESERVER_PORT;
777 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000778 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100779#ifdef HAVE_IPV6
780 int scope_index = 0;
781 char *scope_id;
782#endif
783
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000784 if (!arg || strlen(arg) == 0)
785 {
786 *flags |= SERV_NO_ADDR;
787 *interface = 0;
788 return NULL;
789 }
790
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100791 if ((source = split_chr(arg, '@')) && /* is there a source. */
792 (portno = split_chr(source, '#')) &&
793 !atoi_check16(portno, &source_port))
794 return _("bad port");
795
796 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
797 !atoi_check16(portno, &serv_port))
798 return _("bad port");
799
800#ifdef HAVE_IPV6
801 scope_id = split_chr(arg, '%');
802#endif
803
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000804 if (source) {
805 interface_opt = split_chr(source, '@');
806
807 if (interface_opt)
808 {
809#if defined(SO_BINDTODEVICE)
810 strncpy(interface, interface_opt, IF_NAMESIZE - 1);
811#else
812 return _("interface binding not supported");
813#endif
814 }
815 }
816
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100817 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100818 {
819 addr->in.sin_port = htons(serv_port);
820 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
821#ifdef HAVE_SOCKADDR_SA_LEN
822 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
823#endif
824 source_addr->in.sin_addr.s_addr = INADDR_ANY;
825 source_addr->in.sin_port = htons(daemon->query_port);
826
827 if (source)
828 {
829 if (flags)
830 *flags |= SERV_HAS_SOURCE;
831 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100832 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100833 {
834#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000835 if (interface_opt)
836 return _("interface can only be specified once");
837
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100838 source_addr->in.sin_addr.s_addr = INADDR_ANY;
839 strncpy(interface, source, IF_NAMESIZE - 1);
840#else
841 return _("interface binding not supported");
842#endif
843 }
844 }
845 }
846#ifdef HAVE_IPV6
847 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
848 {
849 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
850 return _("bad interface name");
851
852 addr->in6.sin6_port = htons(serv_port);
853 addr->in6.sin6_scope_id = scope_index;
854 source_addr->in6.sin6_addr = in6addr_any;
855 source_addr->in6.sin6_port = htons(daemon->query_port);
856 source_addr->in6.sin6_scope_id = 0;
857 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
858 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
859#ifdef HAVE_SOCKADDR_SA_LEN
860 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
861#endif
862 if (source)
863 {
864 if (flags)
865 *flags |= SERV_HAS_SOURCE;
866 source_addr->in6.sin6_port = htons(source_port);
867 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
868 {
869#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000870 if (interface_opt)
871 return _("interface can only be specified once");
872
873 source_addr->in6.sin6_addr = in6addr_any;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100874 strncpy(interface, source, IF_NAMESIZE - 1);
875#else
876 return _("interface binding not supported");
877#endif
878 }
879 }
880 }
881#endif
882 else
883 return _("bad address");
884
885 return NULL;
886}
887
Simon Kelleyde73a492014-02-17 21:43:27 +0000888static struct server *add_rev4(struct in_addr addr, int msize)
889{
890 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000891 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000892 char *p;
893
894 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000895 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
896
897 switch (msize)
898 {
899 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100900 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000901 /* fall through */
902 case 24:
903 p += sprintf(p, "%d.", (a >> 8) & 0xff);
904 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000905 case 16:
906 p += sprintf(p, "%d.", (a >> 16) & 0xff);
907 /* fall through */
908 case 8:
909 p += sprintf(p, "%d.", (a >> 24) & 0xff);
910 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000911 default:
912 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000913 }
914
915 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000916
917 serv->flags = SERV_HAS_DOMAIN;
918 serv->next = daemon->servers;
919 daemon->servers = serv;
920
921 return serv;
922
923}
924
925static struct server *add_rev6(struct in6_addr *addr, int msize)
926{
927 struct server *serv = opt_malloc(sizeof(struct server));
928 char *p;
929 int i;
930
931 memset(serv, 0, sizeof(struct server));
932 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
933
934 for (i = msize-1; i >= 0; i -= 4)
935 {
936 int dig = ((unsigned char *)addr)[i>>3];
937 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
938 }
939 p += sprintf(p, "ip6.arpa");
940
941 serv->flags = SERV_HAS_DOMAIN;
942 serv->next = daemon->servers;
943 daemon->servers = serv;
944
945 return serv;
946}
947
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000948#ifdef HAVE_DHCP
949
950static int is_tag_prefix(char *arg)
951{
952 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
953 return 1;
954
955 return 0;
956}
957
958static char *set_prefix(char *arg)
959{
960 if (strstr(arg, "set:") == arg)
961 return arg+4;
962
963 return arg;
964}
965
Simon Kelley832af0b2007-01-21 20:01:28 +0000966/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100967static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000968{
Simon Kelley824af852008-02-12 20:43:05 +0000969 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000970 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000971 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100972 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100973 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000974 u16 opt_len = 0;
975 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100976 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000977
978 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000979 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000980 new->netid = NULL;
981 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100982 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000983
Simon Kelleyf2621c72007-04-29 19:47:21 +0100984 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000985 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100986 comma = split(arg);
987
988 for (cp = arg; *cp; cp++)
989 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000990 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100991
992 if (!*cp)
993 {
994 new->opt = atoi(arg);
995 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100996 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100997 break;
998 }
999
1000 if (strstr(arg, "option:") == arg)
1001 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001002 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
1003 {
1004 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1005 /* option:<optname> must follow tag and vendor string. */
1006 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1007 option_ok = 1;
1008 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001009 break;
1010 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001011#ifdef HAVE_DHCP6
1012 else if (strstr(arg, "option6:") == arg)
1013 {
1014 for (cp = arg+8; *cp; cp++)
1015 if (*cp < '0' || *cp > '9')
1016 break;
1017
1018 if (!*cp)
1019 {
1020 new->opt = atoi(arg+8);
1021 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001022 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001023 }
1024 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001025 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001026 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1027 {
1028 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1029 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1030 option_ok = 1;
1031 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001032 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001033 /* option6:<opt>|<optname> must follow tag and vendor string. */
1034 is6 = 1;
1035 break;
1036 }
1037#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001038 else if (strstr(arg, "vendor:") == arg)
1039 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001040 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1041 new->flags |= DHOPT_VENDOR;
1042 }
1043 else if (strstr(arg, "encap:") == arg)
1044 {
1045 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001046 new->flags |= DHOPT_ENCAPSULATE;
1047 }
Simon Kelley316e2732010-01-22 20:16:09 +00001048 else if (strstr(arg, "vi-encap:") == arg)
1049 {
1050 new->u.encap = atoi(arg+9);
1051 new->flags |= DHOPT_RFC3925;
1052 if (flags == DHOPT_MATCH)
1053 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001054 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001055 break;
1056 }
1057 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001058 else
1059 {
Simon Kelley824af852008-02-12 20:43:05 +00001060 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001061 /* allow optional "net:" or "tag:" for consistency */
1062 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +00001063 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001064 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001065 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001066 new->netid->next = np;
1067 np = new->netid;
1068 }
1069
1070 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001071 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001072
1073#ifdef HAVE_DHCP6
1074 if (is6)
1075 {
1076 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001077 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001078
1079 if (opt_len == 0 &&
1080 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001081 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001082 }
1083 else
1084#endif
1085 if (opt_len == 0 &&
1086 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001087 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001088
Simon Kelley316e2732010-01-22 20:16:09 +00001089 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001090 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001091 ret_err(_("bad dhcp-option"));
1092
1093 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001094 {
1095 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001096 char c;
Simon Kelley28866e92011-02-14 20:19:14 +00001097 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001098 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001099 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001100 dots = 0;
1101 for (cp = comma; (c = *cp); cp++)
1102 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001103 {
1104 addrs++;
1105 is_dec = is_hex = 0;
1106 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001107 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001108 {
1109 digs++;
1110 is_dec = is_addr = 0;
1111 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001112 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001113 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001114 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001115 if (cp == comma) /* leading / means a pathname */
1116 is_addr = 0;
1117 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001118 else if (c == '.')
1119 {
Simon Kelley23245c02012-07-18 16:21:11 +01001120 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001121 dots++;
1122 }
1123 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001124 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001125 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001126 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001127 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001128 {
1129 is_addr = 0;
1130 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001131 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001132 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001133 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001134 *cp = 0;
1135 }
1136 else
1137 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001138 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001139 (c >='a' && c <= 'f') ||
1140 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001141 {
1142 is_hex = 0;
1143 if (c != '[' && c != ']')
1144 is_addr6 = 0;
1145 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001146 }
Simon Kelley28866e92011-02-14 20:19:14 +00001147 else
1148 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001149
Simon Kelley28866e92011-02-14 20:19:14 +00001150 if (!found_dig)
1151 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001152
Simon Kelleyf2621c72007-04-29 19:47:21 +01001153 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001154 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001155 {
1156 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001157
1158 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001159 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001160
1161 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001162 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001163 }
Simon Kelley28866e92011-02-14 20:19:14 +00001164 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001165 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1166 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001167
1168 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1169 {
1170 int val, fac = 1;
1171
1172 switch (comma[strlen(comma) - 1])
1173 {
Simon Kelley42243212012-07-20 15:19:18 +01001174 case 'w':
1175 case 'W':
1176 fac *= 7;
1177 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001178 case 'd':
1179 case 'D':
1180 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001181 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001182 case 'h':
1183 case 'H':
1184 fac *= 60;
1185 /* fall through */
1186 case 'm':
1187 case 'M':
1188 fac *= 60;
1189 /* fall through */
1190 case 's':
1191 case 'S':
1192 comma[strlen(comma) - 1] = 0;
1193 }
1194
1195 new->len = 4;
1196 new->val = opt_malloc(4);
1197 val = atoi(comma);
1198 *((int *)new->val) = htonl(val * fac);
1199 }
1200 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001201 {
1202 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001203 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001204 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1205 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001206 }
1207 else if (is_dec)
1208 {
1209 int i, val = atoi(comma);
1210 /* assume numeric arg is 1 byte except for
1211 options where it is known otherwise.
1212 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001213 if (opt_len != 0)
1214 new->len = opt_len;
1215 else if (val & 0xffff0000)
1216 new->len = 4;
1217 else if (val & 0xff00)
1218 new->len = 2;
1219 else
1220 new->len = 1;
1221
Simon Kelley832af0b2007-01-21 20:01:28 +00001222 if (lenchar == 'b')
1223 new->len = 1;
1224 else if (lenchar == 's')
1225 new->len = 2;
1226 else if (lenchar == 'i')
1227 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001228
Simon Kelley824af852008-02-12 20:43:05 +00001229 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001230 for (i=0; i<new->len; i++)
1231 new->val[i] = val>>((new->len - i - 1)*8);
1232 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001233 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001234 {
1235 struct in_addr in;
1236 unsigned char *op;
1237 char *slash;
1238 /* max length of address/subnet descriptor is five bytes,
1239 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001240 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001241 new->flags |= DHOPT_ADDR;
1242
Simon Kelley572b41e2011-02-18 18:11:18 +00001243 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1244 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001245 {
Simon Kelley6b010842007-02-12 20:32:07 +00001246 *(op++) = 1; /* RFC 3361 "enc byte" */
1247 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001248 }
1249 while (addrs--)
1250 {
1251 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001252 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001253 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001254 if (!inet_pton(AF_INET, cp, &in))
1255 ret_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001256 if (!slash)
1257 {
1258 memcpy(op, &in, INADDRSZ);
1259 op += INADDRSZ;
1260 }
1261 else
1262 {
1263 unsigned char *p = (unsigned char *)&in;
1264 int netsize = atoi(slash);
1265 *op++ = netsize;
1266 if (netsize > 0)
1267 *op++ = *p++;
1268 if (netsize > 8)
1269 *op++ = *p++;
1270 if (netsize > 16)
1271 *op++ = *p++;
1272 if (netsize > 24)
1273 *op++ = *p++;
1274 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1275 }
1276 }
1277 new->len = op - new->val;
1278 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001279 else if (is_addr6 && is6)
1280 {
1281 unsigned char *op;
1282 new->val = op = opt_malloc(16 * addrs);
1283 new->flags |= DHOPT_ADDR6;
1284 while (addrs--)
1285 {
1286 cp = comma;
1287 comma = split(cp);
1288
1289 /* check for [1234::7] */
1290 if (*cp == '[')
1291 cp++;
1292 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1293 cp[strlen(cp)-1] = 0;
1294
1295 if (inet_pton(AF_INET6, cp, op))
1296 {
1297 op += IN6ADDRSZ;
1298 continue;
1299 }
1300
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001301 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001302 }
1303 new->len = op - new->val;
1304 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001305 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001306 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001307 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001308 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001309 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001310 {
1311 /* dns search, RFC 3397, or SIP, RFC 3361 */
1312 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001313 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001314 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001315 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001316
1317 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001318 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001319
1320 while (arg && *arg)
1321 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001322 char *in, *dom = NULL;
1323 size_t domlen = 1;
1324 /* Allow "." as an empty domain */
1325 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001326 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001327 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001328 ret_err(_("bad domain in dhcp-option"));
1329
Simon Kelleyc52e1892010-06-07 22:01:39 +01001330 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001331 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001332
1333 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001334 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001335 {
1336 memcpy(newp, m, header_size + len);
1337 free(m);
1338 }
Simon Kelley824af852008-02-12 20:43:05 +00001339 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001340 p = m + header_size;
1341 q = p + len;
1342
1343 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001344 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001345 {
1346 unsigned char *cp = q++;
1347 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001348 for (j = 0; *in && (*in != '.'); in++, j++)
1349 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001350 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001351 if (*in)
1352 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001353 }
1354 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001355 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001356
Simon Kelley832af0b2007-01-21 20:01:28 +00001357 /* Now tail-compress using earlier names. */
1358 newlen = q - p;
1359 for (tail = p + len; *tail; tail += (*tail) + 1)
1360 for (r = p; r - p < (int)len; r += (*r) + 1)
1361 if (strcmp((char *)r, (char *)tail) == 0)
1362 {
1363 PUTSHORT((r - p) | 0xc000, tail);
1364 newlen = tail - p;
1365 goto end;
1366 }
1367 end:
1368 len = newlen;
1369
1370 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001371 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001372 }
1373
1374 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001375 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001376 m[0] = 0;
1377 new->len = (int) len + header_size;
1378 new->val = m;
1379 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001380#ifdef HAVE_DHCP6
1381 else if (comma && (opt_len & OT_CSTRING))
1382 {
1383 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001384 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001385 unsigned char *p, *newp;
1386
Simon Kelley40ef23b2012-03-13 21:59:28 +00001387 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001388 if (comma[i] == ',')
1389 commas++;
1390
1391 newp = opt_malloc(strlen(comma)+(2*commas));
1392 p = newp;
1393 arg = comma;
1394 comma = split(arg);
1395
1396 while (arg && *arg)
1397 {
1398 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001399 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001400 PUTSHORT(len, p);
1401 memcpy(p, arg, len);
1402 p += len;
1403
1404 arg = comma;
1405 comma = split(arg);
1406 }
1407
1408 new->val = newp;
1409 new->len = p - newp;
1410 }
1411 else if (comma && (opt_len & OT_RFC1035_NAME))
1412 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001413 unsigned char *p = NULL, *newp, *end;
1414 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001415 arg = comma;
1416 comma = split(arg);
1417
1418 while (arg && *arg)
1419 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001420 char *dom = canonicalise_opt(arg);
1421 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001422 ret_err(_("bad domain in dhcp-option"));
1423
Simon Kelley18f0fb02012-03-31 21:18:55 +01001424 newp = opt_malloc(len + strlen(dom) + 2);
1425
1426 if (p)
1427 {
1428 memcpy(newp, p, len);
1429 free(p);
1430 }
1431
1432 p = newp;
Simon Kelley0549c732017-09-25 18:17:11 +01001433 end = do_rfc1035_name(p + len, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001434 *end++ = 0;
1435 len = end - p;
1436 free(dom);
1437
Simon Kelley4cb1b322012-02-06 14:30:41 +00001438 arg = comma;
1439 comma = split(arg);
1440 }
1441
Simon Kelley18f0fb02012-03-31 21:18:55 +01001442 new->val = p;
1443 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001444 }
1445#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001446 else
1447 {
1448 new->len = strlen(comma);
1449 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001450 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001451 new->flags |= DHOPT_STRING;
1452 }
1453 }
1454 }
1455
Simon Kelley4cb1b322012-02-06 14:30:41 +00001456 if (!is6 &&
1457 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001458 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001459 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001460 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001461
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001462 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001463 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001464 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1465 !new->netid ||
1466 new->netid->next)
1467 ret_err(_("illegal dhcp-match"));
1468
1469 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001470 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001471 new->next = daemon->dhcp_match6;
1472 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001473 }
1474 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001475 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001476 new->next = daemon->dhcp_match;
1477 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001478 }
Simon Kelley824af852008-02-12 20:43:05 +00001479 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001480 else if (is6)
1481 {
1482 new->next = daemon->dhcp_opts6;
1483 daemon->dhcp_opts6 = new;
1484 }
1485 else
1486 {
1487 new->next = daemon->dhcp_opts;
1488 daemon->dhcp_opts = new;
1489 }
1490
1491 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001492}
1493
Simon Kelley7622fc02009-06-04 20:32:05 +01001494#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001495
Simon Kelley28866e92011-02-14 20:19:14 +00001496void set_option_bool(unsigned int opt)
1497{
1498 if (opt < 32)
1499 daemon->options |= 1u << opt;
1500 else
1501 daemon->options2 |= 1u << (opt - 32);
1502}
1503
Simon Kelley2b5bae92012-06-26 16:55:23 +01001504void reset_option_bool(unsigned int opt)
1505{
1506 if (opt < 32)
1507 daemon->options &= ~(1u << opt);
1508 else
1509 daemon->options2 &= ~(1u << (opt - 32));
1510}
1511
Simon Kelley7b1eae42014-02-20 13:43:28 +00001512static 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 +01001513{
1514 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001515 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001516
Simon Kelley832af0b2007-01-21 20:01:28 +00001517 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001518 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001519
Simon Kelley1a6bca82008-07-11 11:11:42 +01001520 for (i=0; usage[i].opt != 0; i++)
1521 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001522 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001523 int rept = usage[i].rept;
1524
Simon Kelley28866e92011-02-14 20:19:14 +00001525 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001526 {
1527 /* command line */
1528 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001529 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001530 if (rept == ARG_ONE)
1531 usage[i].rept = ARG_USED_CL;
1532 }
1533 else
1534 {
1535 /* allow file to override command line */
1536 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001537 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001538 if (rept == ARG_USED_CL || rept == ARG_ONE)
1539 usage[i].rept = ARG_USED_FILE;
1540 }
1541
1542 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1543 {
Simon Kelley28866e92011-02-14 20:19:14 +00001544 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001545 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001546 }
1547
1548 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001549 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001550
Simon Kelley849a8352006-06-09 21:02:31 +01001551 switch (option)
1552 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001553 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001554 {
Simon Kelley824af852008-02-12 20:43:05 +00001555 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001556 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001557 {
Simon Kelley28866e92011-02-14 20:19:14 +00001558 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001559 free(file);
1560 }
Simon Kelley849a8352006-06-09 21:02:31 +01001561 break;
1562 }
1563
Simon Kelleyf2621c72007-04-29 19:47:21 +01001564 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001565 {
1566 DIR *dir_stream;
1567 struct dirent *ent;
1568 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001569 struct list {
1570 char *suffix;
1571 struct list *next;
Simon Kelley3e1551a2014-09-09 21:46:07 +01001572 } *ignore_suffix = NULL, *match_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001573
Simon Kelley1f15b812009-10-13 17:49:32 +01001574 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001575 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001576 break;
1577
Simon Kelley1f15b812009-10-13 17:49:32 +01001578 for (arg = comma; arg; arg = comma)
1579 {
1580 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001581 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001582 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001583 li = opt_malloc(sizeof(struct list));
1584 if (*arg == '*')
1585 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001586 /* "*" with no suffix is a no-op */
1587 if (arg[1] == 0)
1588 free(li);
1589 else
1590 {
1591 li->next = match_suffix;
1592 match_suffix = li;
1593 /* Have to copy: buffer is overwritten */
1594 li->suffix = opt_string_alloc(arg+1);
1595 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001596 }
1597 else
1598 {
1599 li->next = ignore_suffix;
1600 ignore_suffix = li;
1601 /* Have to copy: buffer is overwritten */
1602 li->suffix = opt_string_alloc(arg);
1603 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001604 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001605 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001606
Simon Kelley849a8352006-06-09 21:02:31 +01001607 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001608 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001609
Simon Kelley849a8352006-06-09 21:02:31 +01001610 while ((ent = readdir(dir_stream)))
1611 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001612 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001613 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001614
1615 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001616 if (len == 0 ||
1617 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001618 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1619 ent->d_name[0] == '.')
1620 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001621
Simon Kelley3e1551a2014-09-09 21:46:07 +01001622 if (match_suffix)
1623 {
1624 for (li = match_suffix; li; li = li->next)
1625 {
1626 /* check for required suffices */
1627 size_t ls = strlen(li->suffix);
1628 if (len > ls &&
1629 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1630 break;
1631 }
1632 if (!li)
1633 continue;
1634 }
1635
Simon Kelley1f15b812009-10-13 17:49:32 +01001636 for (li = ignore_suffix; li; li = li->next)
1637 {
1638 /* check for proscribed suffices */
1639 size_t ls = strlen(li->suffix);
1640 if (len > ls &&
1641 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1642 break;
1643 }
1644 if (li)
1645 continue;
1646
Simon Kelley824af852008-02-12 20:43:05 +00001647 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001648 strcpy(path, directory);
1649 strcat(path, "/");
1650 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001651
Simon Kelley39595cf2013-02-04 21:40:07 +00001652 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001653 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001654 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001655
Simon Kelley39595cf2013-02-04 21:40:07 +00001656 /* only reg files allowed. */
1657 if (S_ISREG(buf.st_mode))
1658 one_file(path, 0);
1659
Simon Kelley849a8352006-06-09 21:02:31 +01001660 free(path);
1661 }
1662
1663 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001664 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001665 for(; ignore_suffix; ignore_suffix = li)
1666 {
1667 li = ignore_suffix->next;
1668 free(ignore_suffix->suffix);
1669 free(ignore_suffix);
1670 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001671 for(; match_suffix; match_suffix = li)
1672 {
1673 li = match_suffix->next;
1674 free(match_suffix->suffix);
1675 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001676 }
Simon Kelley849a8352006-06-09 21:02:31 +01001677 break;
1678 }
1679
Simon Kelleyed4c0762013-10-08 20:46:34 +01001680 case LOPT_ADD_SBNET: /* --add-subnet */
1681 set_option_bool(OPT_CLIENT_SUBNET);
1682 if (arg)
1683 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001684 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001685 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001686
1687 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1688 if ((end = split_chr(arg, '/')))
1689 {
1690 /* has subnet+len */
1691 err = parse_mysockaddr(arg, &new->addr);
1692 if (err)
1693 ret_err(err);
1694 if (!atoi_check(end, &new->mask))
1695 ret_err(gen_err);
1696 new->addr_used = 1;
1697 }
1698 else if (!atoi_check(arg, &new->mask))
1699 ret_err(gen_err);
1700
1701 daemon->add_subnet4 = new;
1702
Ed Bardsleya7369be2015-08-05 21:17:18 +01001703 if (comma)
1704 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001705 new = opt_malloc(sizeof(struct mysubnet));
1706 if ((end = split_chr(comma, '/')))
1707 {
1708 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001709 err = parse_mysockaddr(comma, &new->addr);
1710 if (err)
1711 ret_err(err);
1712 if (!atoi_check(end, &new->mask))
1713 ret_err(gen_err);
1714 new->addr_used = 1;
1715 }
1716 else
1717 {
1718 if (!atoi_check(comma, &new->mask))
1719 ret_err(gen_err);
1720 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001721
1722 daemon->add_subnet6 = new;
1723 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001724 }
1725 break;
1726
Simon Kelleyad094272012-08-10 17:10:54 +01001727 case '1': /* --enable-dbus */
1728 set_option_bool(OPT_DBUS);
1729 if (arg)
1730 daemon->dbus_name = opt_string_alloc(arg);
1731 else
1732 daemon->dbus_name = DNSMASQ_SERVICE;
1733 break;
1734
Simon Kelleyf2621c72007-04-29 19:47:21 +01001735 case '8': /* --log-facility */
1736 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001737 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001738 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001739 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001740 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001741#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001742 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001743#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001744 for (i = 0; facilitynames[i].c_name; i++)
1745 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1746 break;
1747
1748 if (facilitynames[i].c_name)
1749 daemon->log_fac = facilitynames[i].c_val;
1750 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001751 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001752#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001753 }
1754 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001755
Simon Kelleyf2621c72007-04-29 19:47:21 +01001756 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001757 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001758 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001759
Simon Kelleyf2621c72007-04-29 19:47:21 +01001760 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001761 {
Simon Kelley824af852008-02-12 20:43:05 +00001762 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001763 struct resolvc *new, *list = daemon->resolv_files;
1764
1765 if (list && list->is_default)
1766 {
1767 /* replace default resolv file - possibly with nothing */
1768 if (name)
1769 {
1770 list->is_default = 0;
1771 list->name = name;
1772 }
1773 else
1774 list = NULL;
1775 }
1776 else if (name)
1777 {
Simon Kelley824af852008-02-12 20:43:05 +00001778 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001779 new->next = list;
1780 new->name = name;
1781 new->is_default = 0;
1782 new->mtime = 0;
1783 new->logged = 0;
1784 list = new;
1785 }
1786 daemon->resolv_files = list;
1787 break;
1788 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001789
1790 case LOPT_SERVERS_FILE:
1791 daemon->servers_file = opt_string_alloc(arg);
1792 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001793
Simon Kelleyf2621c72007-04-29 19:47:21 +01001794 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001795 {
1796 int pref = 1;
1797 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001798 char *name, *target = NULL;
1799
Simon Kelleyf2621c72007-04-29 19:47:21 +01001800 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001801 {
1802 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001803 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001804 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001805 }
1806
Simon Kelley1f15b812009-10-13 17:49:32 +01001807 if (!(name = canonicalise_opt(arg)) ||
1808 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001809 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001810
Simon Kelley824af852008-02-12 20:43:05 +00001811 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001812 new->next = daemon->mxnames;
1813 daemon->mxnames = new;
1814 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001815 new->name = name;
1816 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001817 new->weight = pref;
1818 break;
1819 }
1820
Simon Kelleyf2621c72007-04-29 19:47:21 +01001821 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001822 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001823 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001824 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001825
Simon Kelley6b173352018-05-08 18:32:14 +01001826 case LOPT_DUMPFILE: /* --dumpfile */
1827 daemon->dump_file = opt_string_alloc(arg);
1828 break;
1829
1830 case LOPT_DUMPMASK: /* --dumpmask */
1831 daemon->dump_mask = strtol(arg, NULL, 0);
1832 break;
1833
Simon Kelley7622fc02009-06-04 20:32:05 +01001834#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001835 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001836 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001837 break;
1838
Simon Kelleyc72daea2012-01-05 21:33:27 +00001839 /* Sorry about the gross pre-processor abuse */
1840 case '6': /* --dhcp-script */
1841 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001842# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001843 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001844# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001845 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001846# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001847 if (option == LOPT_LUASCRIPT)
1848# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001849 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001850# else
1851 daemon->luascript = opt_string_alloc(arg);
1852# endif
1853 else
1854 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001855# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001856 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001857#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001858
Simon Kelley70d18732015-01-31 19:59:29 +00001859 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
1860 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
1861 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
1862 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
1863 case LOPT_HOST_INOTIFY: /* --hostsdir */
1864 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001865 {
Simon Kelley824af852008-02-12 20:43:05 +00001866 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00001867 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00001868 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001869 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001870 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001871 if (option == 'H')
1872 {
1873 new->next = daemon->addn_hosts;
1874 daemon->addn_hosts = new;
1875 }
1876 else if (option == LOPT_DHCP_HOST)
1877 {
1878 new->next = daemon->dhcp_hosts_file;
1879 daemon->dhcp_hosts_file = new;
1880 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001881 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001882 {
1883 new->next = daemon->dhcp_opts_file;
1884 daemon->dhcp_opts_file = new;
1885 }
Simon Kelley70d18732015-01-31 19:59:29 +00001886 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001887 {
Simon Kelley70d18732015-01-31 19:59:29 +00001888 new->next = daemon->dynamic_dirs;
1889 daemon->dynamic_dirs = new;
1890 if (option == LOPT_DHCP_INOTIFY)
1891 new->flags |= AH_DHCP_HST;
1892 else if (option == LOPT_DHOPT_INOTIFY)
1893 new->flags |= AH_DHCP_OPT;
1894 else if (option == LOPT_HOST_INOTIFY)
1895 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001896 }
1897
Simon Kelley849a8352006-06-09 21:02:31 +01001898 break;
1899 }
1900
Simon Kelley4f7b3042012-11-28 21:27:02 +00001901 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001902 if (!(comma = split(arg)))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001903 ret_err(gen_err);
1904
Simon Kelley4f7b3042012-11-28 21:27:02 +00001905 daemon->authserver = opt_string_alloc(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001906 arg = comma;
1907 do {
1908 struct iname *new = opt_malloc(sizeof(struct iname));
1909 comma = split(arg);
1910 new->name = NULL;
1911 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001912 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
Simon Kelley429798f2012-12-10 20:45:53 +00001913 new->addr.sa.sa_family = AF_INET;
1914#ifdef HAVE_IPV6
1915 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1916 new->addr.sa.sa_family = AF_INET6;
1917#endif
1918 else
Simon Kelleyf25e6c62013-11-17 12:23:42 +00001919 {
1920 char *fam = split_chr(arg, '/');
1921 new->name = opt_string_alloc(arg);
1922 new->addr.sa.sa_family = 0;
1923 if (fam)
1924 {
1925 if (strcmp(fam, "4") == 0)
1926 new->addr.sa.sa_family = AF_INET;
1927#ifdef HAVE_IPV6
1928 else if (strcmp(fam, "6") == 0)
1929 new->addr.sa.sa_family = AF_INET6;
1930#endif
1931 else
1932 ret_err(gen_err);
1933 }
1934 }
Simon Kelley429798f2012-12-10 20:45:53 +00001935 new->next = daemon->authinterface;
1936 daemon->authinterface = new;
1937
1938 arg = comma;
1939 } while (arg);
1940
Simon Kelley4f7b3042012-11-28 21:27:02 +00001941 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001942
1943 case LOPT_AUTHSFS: /* --auth-sec-servers */
1944 {
1945 struct name_list *new;
1946
1947 do {
1948 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001949 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001950 new->name = opt_string_alloc(arg);
1951 new->next = daemon->secondary_forward_server;
1952 daemon->secondary_forward_server = new;
1953 arg = comma;
1954 } while (arg);
1955 break;
1956 }
1957
Simon Kelley4f7b3042012-11-28 21:27:02 +00001958 case LOPT_AUTHZONE: /* --auth-zone */
1959 {
1960 struct auth_zone *new;
1961
1962 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001963
Simon Kelley429798f2012-12-10 20:45:53 +00001964 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001965 new->domain = opt_string_alloc(arg);
1966 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001967 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00001968 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001969 new->next = daemon->auth_zones;
1970 daemon->auth_zones = new;
1971
1972 while ((arg = comma))
1973 {
1974 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001975 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001976 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00001977 struct addrlist *subnet = NULL;
1978 struct all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001979
1980 comma = split(arg);
1981 prefix = split_chr(arg, '/');
1982
1983 if (prefix && !atoi_check(prefix, &prefixlen))
1984 ret_err(gen_err);
1985
Mathias Kresin094bfae2016-07-24 14:15:22 +01001986 if (strstr(arg, "exclude:") == arg)
1987 {
1988 is_exclude = 1;
1989 arg = arg+8;
1990 }
1991
Simon Kelley376d48c2013-11-13 13:04:30 +00001992 if (inet_pton(AF_INET, arg, &addr.addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001993 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001994 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001995 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001996 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001997 }
1998#ifdef HAVE_IPV6
Simon Kelley376d48c2013-11-13 13:04:30 +00001999 else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00002000 {
Simon Kelley376d48c2013-11-13 13:04:30 +00002001 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00002002 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00002003 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002004 }
2005#endif
Simon Kelley376d48c2013-11-13 13:04:30 +00002006 else
2007 {
2008 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
2009 name->name = opt_string_alloc(arg);
2010 name->flags = AUTH4 | AUTH6;
2011 name->next = new->interface_names;
2012 new->interface_names = name;
2013 if (prefix)
2014 {
2015 if (prefixlen == 4)
2016 name->flags &= ~AUTH6;
2017#ifdef HAVE_IPV6
2018 else if (prefixlen == 6)
2019 name->flags &= ~AUTH4;
2020#endif
2021 else
2022 ret_err(gen_err);
2023 }
2024 }
2025
2026 if (subnet)
2027 {
2028 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002029
2030 if (is_exclude)
2031 {
2032 subnet->next = new->exclude;
2033 new->exclude = subnet;
2034 }
2035 else
2036 {
2037 subnet->next = new->subnet;
2038 new->subnet = subnet;
2039 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002040 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002041 }
2042 break;
2043 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002044
Simon Kelley4f7b3042012-11-28 21:27:02 +00002045 case LOPT_AUTHSOA: /* --auth-soa */
2046 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002047 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002048 if (comma)
2049 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002050 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002051 arg = comma;
2052 comma = split(arg);
2053 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002054 for (cp = daemon->hostmaster; *cp; cp++)
2055 if (*cp == '@')
2056 *cp = '.';
2057
Simon Kelley4f7b3042012-11-28 21:27:02 +00002058 if (comma)
2059 {
2060 arg = comma;
2061 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002062 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002063 if (comma)
2064 {
2065 arg = comma;
2066 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002067 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002068 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002069 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002070 }
2071 }
2072 }
2073
2074 break;
2075
Simon Kelley2bb73af2013-04-24 17:38:19 +01002076 case 's': /* --domain */
2077 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002078 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002079 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002080 else
Simon Kelley9009d742008-11-14 20:04:27 +00002081 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002082 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002083 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002084 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002085 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002086 else
2087 {
Simon Kelley9009d742008-11-14 20:04:27 +00002088 if (comma)
2089 {
Simon Kelley429798f2012-12-10 20:45:53 +00002090 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002091 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002092
Simon Kelley48fd1c42013-04-25 09:49:38 +01002093 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002094 new->indexed = 0;
2095
Simon Kelley9009d742008-11-14 20:04:27 +00002096 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002097 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002098 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002099 int msize;
2100
Simon Kelley28866e92011-02-14 20:19:14 +00002101 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002102 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002103 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002104 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002105 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002106 int mask = (1 << (32 - msize)) - 1;
2107 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002108 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2109 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002110 if (arg)
2111 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002112 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002113 {
2114 if (!(new->prefix = canonicalise_opt(arg)) ||
2115 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2116 ret_err(_("bad prefix"));
2117 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002118 else if (strcmp(arg, "local") != 0 ||
2119 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002120 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00002121 else
2122 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002123 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002124 local=/xxx.yyy.zzz.in-addr.arpa/ */
2125 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002126 if (!serv)
2127 ret_err(_("bad prefix"));
2128
Simon Kelleyde73a492014-02-17 21:43:27 +00002129 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002130
2131 /* local=/<domain>/ */
2132 serv = opt_malloc(sizeof(struct server));
2133 memset(serv, 0, sizeof(struct server));
2134 serv->domain = d;
2135 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2136 serv->next = daemon->servers;
2137 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002138 }
2139 }
Simon Kelley9009d742008-11-14 20:04:27 +00002140 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002141#ifdef HAVE_IPV6
2142 else if (inet_pton(AF_INET6, comma, &new->start6))
2143 {
2144 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2145 u64 addrpart = addr6part(&new->start6);
2146 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002147
Simon Kelleyd74942a2012-02-07 20:51:56 +00002148 /* prefix==64 overflows the mask calculation above */
2149 if (msize == 64)
2150 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002151
Simon Kelleyd74942a2012-02-07 20:51:56 +00002152 new->end6 = new->start6;
2153 setaddr6part(&new->start6, addrpart & ~mask);
2154 setaddr6part(&new->end6, addrpart | mask);
2155
2156 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002157 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002158 else if (arg)
2159 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002160 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002161 {
2162 if (!(new->prefix = canonicalise_opt(arg)) ||
2163 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
2164 ret_err(_("bad prefix"));
2165 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002166 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002167 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002168 else
2169 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002170 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002171 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002172 struct server *serv = add_rev6(&new->start6, msize);
2173 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002174
2175 /* local=/<domain>/ */
2176 serv = opt_malloc(sizeof(struct server));
2177 memset(serv, 0, sizeof(struct server));
2178 serv->domain = d;
2179 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2180 serv->next = daemon->servers;
2181 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002182 }
2183 }
2184 }
2185#endif
2186 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002187 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002188 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002189 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002190 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002191 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002192 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002193 prefstr = split(arg);
2194
Simon Kelleyd74942a2012-02-07 20:51:56 +00002195 if (inet_pton(AF_INET, comma, &new->start))
2196 {
2197 new->is6 = 0;
2198 if (!arg)
2199 new->end.s_addr = new->start.s_addr;
2200 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002201 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002202 }
2203#ifdef HAVE_IPV6
2204 else if (inet_pton(AF_INET6, comma, &new->start6))
2205 {
2206 new->is6 = 1;
2207 if (!arg)
2208 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2209 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002210 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002211 }
2212#endif
2213 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002214 ret_err(gen_err);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002215
2216 if (option != 's' && prefstr)
2217 {
2218 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2219 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2220 ret_err(_("bad prefix"));
2221 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002222 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002223
2224 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002225 if (option == 's')
2226 {
2227 new->next = daemon->cond_domain;
2228 daemon->cond_domain = new;
2229 }
2230 else
2231 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002232 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002233 new->next = daemon->synth_domains;
2234 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002235 if (new->prefix &&
2236 (star = strrchr(new->prefix, '*'))
2237 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002238 {
2239 *star = 0;
2240 new->indexed = 1;
2241 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002242 }
Simon Kelley9009d742008-11-14 20:04:27 +00002243 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002244 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002245 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002246 else
2247 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002248 }
2249 }
Simon Kelley849a8352006-06-09 21:02:31 +01002250 break;
2251
Simon Kelley1e505122016-01-25 21:29:23 +00002252 case LOPT_CPE_ID: /* --add-dns-client */
2253 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002254 daemon->dns_client_id = opt_string_alloc(arg);
2255 break;
2256
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002257 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002258 if (!arg)
2259 set_option_bool(OPT_ADD_MAC);
2260 else
2261 {
2262 unhide_metas(arg);
2263 if (strcmp(arg, "base64") == 0)
2264 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002265 else if (strcmp(arg, "text") == 0)
2266 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002267 else
2268 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002269 }
2270 break;
2271
Simon Kelleyf2621c72007-04-29 19:47:21 +01002272 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002273 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002274 break;
2275
Simon Kelleyf2621c72007-04-29 19:47:21 +01002276 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002277 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002278 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002279 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002280
Simon Kelley7622fc02009-06-04 20:32:05 +01002281#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002282 case LOPT_SCRIPTUSR: /* --scriptuser */
2283 daemon->scriptuser = opt_string_alloc(arg);
2284 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002285#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002286
Simon Kelleyf2621c72007-04-29 19:47:21 +01002287 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002288 do {
Simon Kelley824af852008-02-12 20:43:05 +00002289 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002290 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002291 new->next = daemon->if_names;
2292 daemon->if_names = new;
2293 /* new->name may be NULL if someone does
2294 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002295 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002296 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002297 arg = comma;
2298 } while (arg);
2299 break;
2300
Simon Kelley2937f8a2013-07-29 19:49:07 +01002301 case LOPT_TFTP: /* --enable-tftp */
2302 set_option_bool(OPT_TFTP);
2303 if (!arg)
2304 break;
2305 /* fall through */
2306
Simon Kelleyf2621c72007-04-29 19:47:21 +01002307 case 'I': /* --except-interface */
2308 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002309 do {
Simon Kelley824af852008-02-12 20:43:05 +00002310 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002311 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002312 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002313 if (option == 'I')
2314 {
2315 new->next = daemon->if_except;
2316 daemon->if_except = new;
2317 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002318 else if (option == LOPT_TFTP)
2319 {
2320 new->next = daemon->tftp_interfaces;
2321 daemon->tftp_interfaces = new;
2322 }
Simon Kelley849a8352006-06-09 21:02:31 +01002323 else
2324 {
2325 new->next = daemon->dhcp_except;
2326 daemon->dhcp_except = new;
2327 }
2328 arg = comma;
2329 } while (arg);
2330 break;
2331
Simon Kelleyf2621c72007-04-29 19:47:21 +01002332 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002333 case LOPT_IGNORE_ADDR: /* --ignore-address */
2334 {
Simon Kelley849a8352006-06-09 21:02:31 +01002335 struct in_addr addr;
2336 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002337 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002338 {
Simon Kelley824af852008-02-12 20:43:05 +00002339 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002340 if (option == 'B')
2341 {
2342 baddr->next = daemon->bogus_addr;
2343 daemon->bogus_addr = baddr;
2344 }
2345 else
2346 {
2347 baddr->next = daemon->ignore_addr;
2348 daemon->ignore_addr = baddr;
2349 }
Simon Kelley849a8352006-06-09 21:02:31 +01002350 baddr->addr = addr;
2351 }
2352 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002353 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002354 break;
2355 }
2356
Simon Kelleyf2621c72007-04-29 19:47:21 +01002357 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002358 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002359 do {
Simon Kelley824af852008-02-12 20:43:05 +00002360 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002361 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002362 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002363 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002364 {
2365 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002366 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002367#ifdef HAVE_SOCKADDR_SA_LEN
2368 new->addr.in.sin_len = sizeof(new->addr.in);
2369#endif
2370 }
2371#ifdef HAVE_IPV6
2372 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2373 {
2374 new->addr.sa.sa_family = AF_INET6;
2375 new->addr.in6.sin6_flowinfo = 0;
2376 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002377 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002378#ifdef HAVE_SOCKADDR_SA_LEN
2379 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2380#endif
2381 }
2382#endif
2383 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002384 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002385
2386 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002387 if (option == 'a')
2388 {
2389 new->next = daemon->if_addrs;
2390 daemon->if_addrs = new;
2391 }
2392 else
2393 {
2394 new->next = daemon->auth_peers;
2395 daemon->auth_peers = new;
2396 }
Simon Kelley849a8352006-06-09 21:02:31 +01002397 arg = comma;
2398 } while (arg);
2399 break;
2400
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002401 case 'S': /* --server */
2402 case LOPT_LOCAL: /* --local */
2403 case 'A': /* --address */
2404 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002405 {
2406 struct server *serv, *newlist = NULL;
2407
2408 unhide_metas(arg);
2409
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002410 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002411 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002412 int rebind = !(*arg == '/');
2413 char *end = NULL;
2414 if (!rebind)
2415 arg++;
2416 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002417 {
2418 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002419 /* elide leading dots - they are implied in the search algorithm */
2420 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002421 /* # matches everything and becomes a zero length domain string */
2422 if (strcmp(arg, "#") == 0)
2423 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002424 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002425 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002426 serv = opt_malloc(sizeof(struct server));
2427 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002428 serv->next = newlist;
2429 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002430 serv->domain = domain;
2431 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002432 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002433 if (rebind)
2434 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002435 }
2436 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002437 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002438 }
2439 else
2440 {
Simon Kelley824af852008-02-12 20:43:05 +00002441 newlist = opt_malloc(sizeof(struct server));
2442 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002443#ifdef HAVE_LOOP
2444 newlist->uid = rand32();
2445#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002446 }
2447
Simon Kelley7b1eae42014-02-20 13:43:28 +00002448 if (servers_only && option == 'S')
2449 newlist->flags |= SERV_FROM_FILE;
2450
Simon Kelley849a8352006-06-09 21:02:31 +01002451 if (option == 'A')
2452 {
2453 newlist->flags |= SERV_LITERAL_ADDRESS;
2454 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002455 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002456 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002457 else if (option == LOPT_NO_REBIND)
2458 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002459
2460 if (!arg || !*arg)
2461 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002462 if (!(newlist->flags & SERV_NO_REBIND))
2463 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002464 }
2465
2466 else if (strcmp(arg, "#") == 0)
2467 {
2468 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002469 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002470 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002471 }
2472 else
2473 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002474 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2475 if (err)
2476 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002477 }
2478
Simon Kelleyf2621c72007-04-29 19:47:21 +01002479 serv = newlist;
2480 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002481 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002482 serv->next->flags = serv->flags;
2483 serv->next->addr = serv->addr;
2484 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002485 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002486 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002487 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002488 serv->next = daemon->servers;
2489 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002490 break;
2491 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002492
Simon Kelleyde73a492014-02-17 21:43:27 +00002493 case LOPT_REV_SERV: /* --rev-server */
2494 {
2495 char *string;
2496 int size;
2497 struct server *serv;
2498 struct in_addr addr4;
2499#ifdef HAVE_IPV6
2500 struct in6_addr addr6;
2501#endif
2502
2503 unhide_metas(arg);
2504 if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2505 ret_err(gen_err);
2506
2507 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002508 {
2509 serv = add_rev4(addr4, size);
2510 if (!serv)
2511 ret_err(_("bad prefix"));
2512 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002513#ifdef HAVE_IPV6
2514 else if (inet_pton(AF_INET6, arg, &addr6))
2515 serv = add_rev6(&addr6, size);
2516#endif
2517 else
2518 ret_err(gen_err);
2519
2520 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2521
2522 if (string)
2523 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002524
2525 if (servers_only)
2526 serv->flags |= SERV_FROM_FILE;
2527
Simon Kelleyde73a492014-02-17 21:43:27 +00002528 break;
2529 }
2530
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002531 case LOPT_IPSET: /* --ipset */
2532#ifndef HAVE_IPSET
2533 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2534 break;
2535#else
2536 {
2537 struct ipsets ipsets_head;
2538 struct ipsets *ipsets = &ipsets_head;
2539 int size;
2540 char *end;
2541 char **sets, **sets_pos;
2542 memset(ipsets, 0, sizeof(struct ipsets));
2543 unhide_metas(arg);
2544 if (arg && *arg == '/')
2545 {
2546 arg++;
2547 while ((end = split_chr(arg, '/')))
2548 {
2549 char *domain = NULL;
2550 /* elide leading dots - they are implied in the search algorithm */
2551 while (*arg == '.')
2552 arg++;
2553 /* # matches everything and becomes a zero length domain string */
2554 if (strcmp(arg, "#") == 0 || !*arg)
2555 domain = "";
2556 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002557 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002558 ipsets->next = opt_malloc(sizeof(struct ipsets));
2559 ipsets = ipsets->next;
2560 memset(ipsets, 0, sizeof(struct ipsets));
2561 ipsets->domain = domain;
2562 arg = end;
2563 }
2564 }
2565 else
2566 {
2567 ipsets->next = opt_malloc(sizeof(struct ipsets));
2568 ipsets = ipsets->next;
2569 memset(ipsets, 0, sizeof(struct ipsets));
2570 ipsets->domain = "";
2571 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002572
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002573 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002574 ret_err(gen_err);
2575
2576 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002577 if (*end == ',')
2578 ++size;
2579
2580 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2581
2582 do {
2583 end = split(arg);
2584 *sets_pos++ = opt_string_alloc(arg);
2585 arg = end;
2586 } while (end);
2587 *sets_pos = 0;
2588 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2589 ipsets->next->sets = sets;
2590 ipsets->next = daemon->ipsets;
2591 daemon->ipsets = ipsets_head.next;
2592
2593 break;
2594 }
2595#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002596
Simon Kelleyf2621c72007-04-29 19:47:21 +01002597 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002598 {
2599 int size;
2600
2601 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002602 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002603 else
2604 {
2605 /* zero is OK, and means no caching. */
2606
2607 if (size < 0)
2608 size = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002609
2610 daemon->cachesize = size;
2611 }
2612 break;
2613 }
2614
Simon Kelleyf2621c72007-04-29 19:47:21 +01002615 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002616 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002617 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002618 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002619
Simon Kelley1a6bca82008-07-11 11:11:42 +01002620 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002621 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002622 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002623 break;
2624
Hans Dedecker926332a2016-01-23 10:48:12 +00002625 case LOPT_MAXPORT: /* --max-port */
2626 if (!atoi_check16(arg, &daemon->max_port))
2627 ret_err(gen_err);
2628 break;
2629
Simon Kelleyf2621c72007-04-29 19:47:21 +01002630 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002631 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002632 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002633 break;
2634
Simon Kelley25cf5e32015-01-09 15:53:03 +00002635 case 'q': /* --log-queries */
2636 set_option_bool(OPT_LOG);
2637 if (arg && strcmp(arg, "extra") == 0)
2638 set_option_bool(OPT_EXTRALOG);
2639 break;
2640
Simon Kelleyf2621c72007-04-29 19:47:21 +01002641 case LOPT_MAX_LOGS: /* --log-async */
2642 daemon->max_logs = LOG_MAX; /* default */
2643 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002644 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002645 else if (daemon->max_logs > 100)
2646 daemon->max_logs = 100;
2647 break;
2648
2649 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002650 {
2651 int i;
2652 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002653 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002654 daemon->edns_pktsz = (unsigned short)i;
2655 break;
2656 }
2657
Simon Kelleyf2621c72007-04-29 19:47:21 +01002658 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002659 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002660 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002661 /* if explicitly set to zero, use single OS ephemeral port
2662 and disable random ports */
2663 if (daemon->query_port == 0)
2664 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002665 break;
2666
Simon Kelley824af852008-02-12 20:43:05 +00002667 case 'T': /* --local-ttl */
2668 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002669 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002670 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002671 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002672 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002673 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002674 {
2675 int ttl;
2676 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002677 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002678 else if (option == LOPT_NEGTTL)
2679 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002680 else if (option == LOPT_MAXTTL)
2681 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002682 else if (option == LOPT_MINCTTL)
2683 {
2684 if (ttl > TTL_FLOOR_LIMIT)
2685 ttl = TTL_FLOOR_LIMIT;
2686 daemon->min_cache_ttl = (unsigned long)ttl;
2687 }
Simon Kelley1d860412012-09-20 20:48:04 +01002688 else if (option == LOPT_MAXCTTL)
2689 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002690 else if (option == LOPT_AUTHTTL)
2691 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002692 else if (option == LOPT_DHCPTTL)
2693 {
2694 daemon->dhcp_ttl = (unsigned long)ttl;
2695 daemon->use_dhcp_ttl = 1;
2696 }
Simon Kelley849a8352006-06-09 21:02:31 +01002697 else
2698 daemon->local_ttl = (unsigned long)ttl;
2699 break;
2700 }
2701
Simon Kelley7622fc02009-06-04 20:32:05 +01002702#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002703 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002704 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002705 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002706 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002707#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002708
Simon Kelley7622fc02009-06-04 20:32:05 +01002709#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002710 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002711 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002712 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002713 break;
2714
Simon Kelleybec366b2016-02-24 22:03:26 +00002715 case LOPT_TFTP_MTU: /* --tftp-mtu */
2716 if (!atoi_check(arg, &daemon->tftp_mtu))
2717 ret_err(gen_err);
2718 break;
2719
Simon Kelley824af852008-02-12 20:43:05 +00002720 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002721 comma = split(arg);
2722 if (comma)
2723 {
2724 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2725 new->interface = opt_string_alloc(comma);
2726 new->prefix = opt_string_alloc(arg);
2727 new->next = daemon->if_prefix;
2728 daemon->if_prefix = new;
2729 }
2730 else
2731 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002732 break;
2733
Simon Kelley824af852008-02-12 20:43:05 +00002734 case LOPT_TFTPPORTS: /* --tftp-port-range */
2735 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002736 !atoi_check16(arg, &daemon->start_tftp_port) ||
2737 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002738 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002739
2740 if (daemon->start_tftp_port > daemon->end_tftp_port)
2741 {
2742 int tmp = daemon->start_tftp_port;
2743 daemon->start_tftp_port = daemon->end_tftp_port;
2744 daemon->end_tftp_port = tmp;
2745 }
2746
2747 break;
Floris Bos60704f52017-04-09 22:22:49 +01002748
2749 case LOPT_APREF: /* --tftp-unique-root */
2750 if (!arg || strcasecmp(arg, "ip") == 0)
2751 set_option_bool(OPT_TFTP_APREF_IP);
2752 else if (strcasecmp(arg, "mac") == 0)
2753 set_option_bool(OPT_TFTP_APREF_MAC);
2754 else
2755 ret_err(gen_err);
2756 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002757#endif
Simon Kelley824af852008-02-12 20:43:05 +00002758
Simon Kelleyf2621c72007-04-29 19:47:21 +01002759 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002760 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002761 struct dhcp_bridge *new;
2762
Simon Kelley316e2732010-01-22 20:16:09 +00002763 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002764 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002765
Simon Kelley22cd8602018-01-14 22:57:14 +00002766 for (new = daemon->bridges; new; new = new->next)
2767 if (strcmp(new->iface, arg) == 0)
2768 break;
2769
2770 if (!new)
2771 {
2772 new = opt_malloc(sizeof(struct dhcp_bridge));
2773 strcpy(new->iface, arg);
2774 new->alias = NULL;
2775 new->next = daemon->bridges;
2776 daemon->bridges = new;
2777 }
2778
Simon Kelley832af0b2007-01-21 20:01:28 +00002779 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002780 arg = comma;
2781 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002782 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002783 {
Simon Kelley824af852008-02-12 20:43:05 +00002784 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002785 b->next = new->alias;
2786 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002787 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002788 }
2789 } while (comma);
2790
2791 break;
2792 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002793
Simon Kelley7622fc02009-06-04 20:32:05 +01002794#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002795 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002796 {
2797 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002798 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002799 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002800
Simon Kelley52b92f42012-01-22 16:05:15 +00002801 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002802 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002803
Simon Kelley849a8352006-06-09 21:02:31 +01002804 while(1)
2805 {
2806 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002807 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2808 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2809 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002810 break;
2811
Simon Kelleyf2621c72007-04-29 19:47:21 +01002812 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002813 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002814 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002815 {
Simon Kelley824af852008-02-12 20:43:05 +00002816 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2817 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002818 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002819 /* ignore empty tag */
2820 if (tt->net)
2821 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002822 }
2823 else
2824 {
2825 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002826 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002827 else if (strstr(arg, "set:") == arg)
2828 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002829 else
Simon Kelley824af852008-02-12 20:43:05 +00002830 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002831 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002832 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002833 }
2834 else
2835 {
2836 a[0] = arg;
2837 break;
2838 }
2839 }
2840
Simon Kelley1f776932012-12-16 19:46:08 +00002841 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002842 if (!(a[k] = split(a[k-1])))
2843 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002844
Simon Kelley52b92f42012-01-22 16:05:15 +00002845 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002846 ret_err(_("bad dhcp-range"));
2847
2848 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002849 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002850 new->next = daemon->dhcp;
2851 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002852 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002853 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002854 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002855 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002856 new->flags |= CONTEXT_PROXY;
2857 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002858 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002859
2860 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2861 {
2862 struct in_addr tmp = new->start;
2863 new->start = new->end;
2864 new->end = tmp;
2865 }
2866
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002867 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002868 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002869 {
2870 new->flags |= CONTEXT_NETMASK;
2871 leasepos = 3;
2872 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002873 ret_err(_("inconsistent DHCP range"));
Simon Kelleyfa794662016-03-03 20:33:54 +00002874
Simon Kelley52b92f42012-01-22 16:05:15 +00002875
Simon Kelleyfa794662016-03-03 20:33:54 +00002876 if (k >= 4 && strchr(a[3], '.') &&
2877 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
2878 {
2879 new->flags |= CONTEXT_BRDCAST;
2880 leasepos = 4;
2881 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002882 }
Simon Kelley849a8352006-06-09 21:02:31 +01002883 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002884#ifdef HAVE_DHCP6
2885 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002886 {
Simon Kelley89500e32013-09-20 16:29:20 +01002887 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002888 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002889 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002890 new->next = daemon->dhcp6;
2891 daemon->dhcp6 = new;
2892
Simon Kelley30cd9662012-03-25 20:44:38 +01002893 for (leasepos = 1; leasepos < k; leasepos++)
2894 {
2895 if (strcmp(a[leasepos], "static") == 0)
2896 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2897 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002898 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002899 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002900 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002901 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
2902 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002903 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002904 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01002905 else if (strcmp(a[leasepos], "off-link") == 0)
2906 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01002907 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2908 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002909 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2910 {
2911 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2912 new->flags |= CONTEXT_TEMPLATE;
2913 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002914 else
2915 break;
2916 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002917
Simon Kelley52b92f42012-01-22 16:05:15 +00002918 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002919 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002920 {
2921 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002922 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002923 if (!(*cp >= '0' && *cp <= '9'))
2924 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002925 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002926 {
2927 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002928 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002929 }
2930 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002931
Simon Kelley6692a1a2013-08-20 14:41:31 +01002932 if (new->prefix != 64)
2933 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002934 if (new->flags & CONTEXT_RA)
Simon Kelley6692a1a2013-08-20 14:41:31 +01002935 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2936 else if (new->flags & CONTEXT_TEMPLATE)
2937 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2938 }
2939
2940 if (new->prefix < 64)
2941 ret_err(_("prefix length must be at least 64"));
2942
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002943 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2944 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002945
2946 /* dhcp-range=:: enables DHCP stateless on any interface */
2947 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2948 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002949
2950 if (new->flags & CONTEXT_TEMPLATE)
2951 {
2952 struct in6_addr zero;
2953 memset(&zero, 0, sizeof(zero));
2954 if (!is_same_net6(&zero, &new->start6, new->prefix))
2955 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2956 }
2957
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002958 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002959 {
2960 struct in6_addr tmp = new->start6;
2961 new->start6 = new->end6;
2962 new->end6 = tmp;
2963 }
Simon Kelley849a8352006-06-09 21:02:31 +01002964 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002965#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002966 else
2967 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002968
Simon Kelley30cd9662012-03-25 20:44:38 +01002969 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002970 {
Simon Kelleyfa794662016-03-03 20:33:54 +00002971 if (leasepos != k-1)
2972 ret_err(_("bad dhcp-range"));
2973
Simon Kelley849a8352006-06-09 21:02:31 +01002974 if (strcmp(a[leasepos], "infinite") == 0)
2975 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002976 else if (strcmp(a[leasepos], "deprecated") == 0)
2977 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002978 else
2979 {
2980 int fac = 1;
2981 if (strlen(a[leasepos]) > 0)
2982 {
2983 switch (a[leasepos][strlen(a[leasepos]) - 1])
2984 {
Simon Kelley42243212012-07-20 15:19:18 +01002985 case 'w':
2986 case 'W':
2987 fac *= 7;
2988 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002989 case 'd':
2990 case 'D':
2991 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00002992 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002993 case 'h':
2994 case 'H':
2995 fac *= 60;
2996 /* fall through */
2997 case 'm':
2998 case 'M':
2999 fac *= 60;
3000 /* fall through */
3001 case 's':
3002 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01003003 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003004 }
3005
Simon Kelleybe379862012-12-23 12:01:39 +00003006 for (cp = a[leasepos]; *cp; cp++)
3007 if (!(*cp >= '0' && *cp <= '9'))
3008 break;
3009
Simon Kelley54dae552013-02-05 17:55:10 +00003010 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00003011 ret_err(_("bad dhcp-range"));
3012
Simon Kelley849a8352006-06-09 21:02:31 +01003013 new->lease_time = atoi(a[leasepos]) * fac;
3014 /* Leases of a minute or less confuse
3015 some clients, notably Apple's */
3016 if (new->lease_time < 120)
3017 new->lease_time = 120;
3018 }
3019 }
3020 }
3021 break;
3022 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01003023
Simon Kelley5aabfc72007-08-29 11:24:47 +01003024 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01003025 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003026 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003027 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01003028 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01003029 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01003030 struct in_addr in;
3031
Simon Kelley824af852008-02-12 20:43:05 +00003032 new = opt_malloc(sizeof(struct dhcp_config));
3033
Simon Kelley849a8352006-06-09 21:02:31 +01003034 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00003035 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
3036 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003037 new->netid = NULL;
3038
Simon Kelley849a8352006-06-09 21:02:31 +01003039 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01003040 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003041 if (!(a[k] = split(a[k-1])))
3042 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003043
3044 for (j = 0; j < k; j++)
3045 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
3046 {
3047 char *arg = a[j];
3048
3049 if ((arg[0] == 'i' || arg[0] == 'I') &&
3050 (arg[1] == 'd' || arg[1] == 'D') &&
3051 arg[2] == ':')
3052 {
3053 if (arg[3] == '*')
3054 new->flags |= CONFIG_NOCLID;
3055 else
3056 {
3057 int len;
3058 arg += 3; /* dump id: */
3059 if (strchr(arg, ':'))
3060 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3061 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01003062 {
3063 unhide_metas(arg);
3064 len = (int) strlen(arg);
3065 }
3066
Simon Kelley28866e92011-02-14 20:19:14 +00003067 if (len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003068 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003069 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003070 {
3071 new->flags |= CONFIG_CLID;
3072 new->clid_len = len;
3073 memcpy(new->clid, arg, len);
3074 }
Simon Kelley849a8352006-06-09 21:02:31 +01003075 }
3076 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003077 /* dhcp-host has strange backwards-compat needs. */
3078 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01003079 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003080 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3081 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3082 newtag->net = opt_malloc(strlen(arg + 4) + 1);
3083 newlist->next = new->netid;
3084 new->netid = newlist;
3085 newlist->list = newtag;
3086 strcpy(newtag->net, arg+4);
3087 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01003088 }
Simon Kelley7de060b2011-08-26 17:24:52 +01003089 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003090 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00003091#ifdef HAVE_DHCP6
3092 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3093 {
3094 arg[strlen(arg)-1] = 0;
3095 arg++;
3096
3097 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003098 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00003099
3100 for (i= 0; i < 8; i++)
3101 if (new->addr6.s6_addr[i] != 0)
3102 break;
3103
3104 /* set WILDCARD if network part all zeros */
3105 if (i == 8)
3106 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00003107
3108 new->flags |= CONFIG_ADDR6;
3109 }
3110#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01003111 else
Simon Kelley849a8352006-06-09 21:02:31 +01003112 {
Simon Kelley9009d742008-11-14 20:04:27 +00003113 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00003114 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
3115 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003116 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003117 else
3118 {
3119
3120 newhw->next = new->hwaddr;
3121 new->hwaddr = newhw;
3122 }
Simon Kelley849a8352006-06-09 21:02:31 +01003123 }
3124 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003125 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01003126 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003127 struct dhcp_config *configs;
3128
Simon Kelley849a8352006-06-09 21:02:31 +01003129 new->addr = in;
3130 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003131
3132 /* If the same IP appears in more than one host config, then DISCOVER
3133 for one of the hosts will get the address, but REQUEST will be NAKed,
3134 since the address is reserved by the other one -> protocol loop. */
3135 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3136 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
3137 {
3138 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3139 return 0;
3140 }
Simon Kelley849a8352006-06-09 21:02:31 +01003141 }
3142 else
3143 {
3144 char *cp, *lastp = NULL, last = 0;
Simon Kelley76ff4402013-12-17 16:29:14 +00003145 int fac = 1, isdig = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003146
3147 if (strlen(a[j]) > 1)
3148 {
3149 lastp = a[j] + strlen(a[j]) - 1;
3150 last = *lastp;
3151 switch (last)
3152 {
Simon Kelley42243212012-07-20 15:19:18 +01003153 case 'w':
3154 case 'W':
3155 fac *= 7;
3156 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003157 case 'd':
3158 case 'D':
3159 fac *= 24;
3160 /* fall through */
3161 case 'h':
3162 case 'H':
3163 fac *= 60;
3164 /* fall through */
3165 case 'm':
3166 case 'M':
3167 fac *= 60;
3168 /* fall through */
3169 case 's':
3170 case 'S':
3171 *lastp = 0;
3172 }
3173 }
3174
3175 for (cp = a[j]; *cp; cp++)
Simon Kelley76ff4402013-12-17 16:29:14 +00003176 if (isdigit((unsigned char)*cp))
3177 isdig = 1;
3178 else if (*cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01003179 break;
Simon Kelley76ff4402013-12-17 16:29:14 +00003180
Simon Kelley849a8352006-06-09 21:02:31 +01003181 if (*cp)
3182 {
3183 if (lastp)
3184 *lastp = last;
3185 if (strcmp(a[j], "infinite") == 0)
3186 {
3187 new->lease_time = 0xffffffff;
3188 new->flags |= CONFIG_TIME;
3189 }
3190 else if (strcmp(a[j], "ignore") == 0)
3191 new->flags |= CONFIG_DISABLE;
3192 else
3193 {
Simon Kelley1f15b812009-10-13 17:49:32 +01003194 if (!(new->hostname = canonicalise_opt(a[j])) ||
3195 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003196 ret_err(_("bad DHCP host name"));
3197
3198 new->flags |= CONFIG_NAME;
3199 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01003200 }
3201 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003202 else if (isdig)
Simon Kelley849a8352006-06-09 21:02:31 +01003203 {
3204 new->lease_time = atoi(a[j]) * fac;
3205 /* Leases of a minute or less confuse
3206 some clients, notably Apple's */
3207 if (new->lease_time < 120)
3208 new->lease_time = 120;
3209 new->flags |= CONFIG_TIME;
3210 }
3211 }
3212
Simon Kelley5aabfc72007-08-29 11:24:47 +01003213 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003214 break;
3215 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003216
3217 case LOPT_TAG_IF: /* --tag-if */
3218 {
3219 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3220
3221 new->tag = NULL;
3222 new->set = NULL;
3223 new->next = NULL;
3224
3225 /* preserve order */
3226 if (!daemon->tag_if)
3227 daemon->tag_if = new;
3228 else
3229 {
3230 struct tag_if *tmp;
3231 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3232 tmp->next = new;
3233 }
3234
3235 while (arg)
3236 {
3237 size_t len;
3238
3239 comma = split(arg);
3240 len = strlen(arg);
3241
3242 if (len < 5)
3243 {
3244 new->set = NULL;
3245 break;
3246 }
3247 else
3248 {
3249 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3250 newtag->net = opt_malloc(len - 3);
3251 strcpy(newtag->net, arg+4);
3252 unhide_metas(newtag->net);
3253
3254 if (strstr(arg, "set:") == arg)
3255 {
3256 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3257 newlist->next = new->set;
3258 new->set = newlist;
3259 newlist->list = newtag;
3260 }
3261 else if (strstr(arg, "tag:") == arg)
3262 {
3263 newtag->next = new->tag;
3264 new->tag = newtag;
3265 }
3266 else
3267 {
3268 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00003269 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003270 break;
3271 }
3272 }
3273
3274 arg = comma;
3275 }
3276
3277 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003278 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003279
3280 break;
3281 }
3282
Simon Kelley849a8352006-06-09 21:02:31 +01003283
Simon Kelley73a08a22009-02-05 20:28:08 +00003284 case 'O': /* --dhcp-option */
3285 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003286 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003287 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003288 return parse_dhcp_opt(errstr, arg,
3289 option == LOPT_FORCE ? DHOPT_FORCE :
3290 (option == LOPT_MATCH ? DHOPT_MATCH :
3291 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003292
3293 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3294 {
3295 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3296 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3297 ssize_t len;
3298
3299 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3300 ret_err(gen_err);
3301
3302 new->wildcard = 0;
3303 new->netid = id;
3304 id->net = opt_string_alloc(set_prefix(arg));
3305
3306 if (comma[len-1] == '*')
3307 {
3308 comma[len-1] = 0;
3309 new->wildcard = 1;
3310 }
3311 new->name = opt_string_alloc(comma);
3312
3313 new->next = daemon->dhcp_name_match;
3314 daemon->dhcp_name_match = new;
3315
3316 break;
3317 }
3318
Simon Kelleyf2621c72007-04-29 19:47:21 +01003319 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003320 {
3321 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003322 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003323 {
Simon Kelley824af852008-02-12 20:43:05 +00003324 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01003325 newid->next = id;
3326 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003327 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003328 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01003329 arg = comma;
3330 };
3331
3332 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003333 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003334 else
3335 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003336 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003337 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003338 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003339 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003340 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003341 dhcp_next_server.s_addr = 0;
3342 if (comma)
3343 {
3344 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003345 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003346 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003347 if (comma)
3348 {
3349 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003350 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3351 {
3352 /*
3353 * The user may have specified the tftp hostname here.
3354 * save it so that it can be resolved/looked up during
3355 * actual dhcp_reply().
3356 */
3357
3358 tftp_sname = opt_string_alloc(comma);
3359 dhcp_next_server.s_addr = 0;
3360 }
Simon Kelley849a8352006-06-09 21:02:31 +01003361 }
3362 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003363
3364 new = opt_malloc(sizeof(struct dhcp_boot));
3365 new->file = dhcp_file;
3366 new->sname = dhcp_sname;
3367 new->tftp_sname = tftp_sname;
3368 new->next_server = dhcp_next_server;
3369 new->netid = id;
3370 new->next = daemon->boot_config;
3371 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003372 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003373
Simon Kelley849a8352006-06-09 21:02:31 +01003374 break;
3375 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003376
Floris Bos503c6092017-04-09 23:07:13 +01003377 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3378 {
3379 struct dhcp_netid *id = NULL;
3380 while (is_tag_prefix(arg))
3381 {
3382 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
3383 newid->next = id;
3384 id = newid;
3385 comma = split(arg);
3386 newid->net = opt_string_alloc(arg+4);
3387 arg = comma;
3388 };
3389
3390 if (!arg)
3391 ret_err(gen_err);
3392 else
3393 {
3394 struct delay_config *new;
3395 int delay;
3396 if (!atoi_check(arg, &delay))
3397 ret_err(gen_err);
3398
3399 new = opt_malloc(sizeof(struct delay_config));
3400 new->delay = delay;
3401 new->netid = id;
3402 new->next = daemon->delay_conf;
3403 daemon->delay_conf = new;
3404 }
3405
3406 break;
3407 }
3408
Simon Kelley7622fc02009-06-04 20:32:05 +01003409 case LOPT_PXE_PROMT: /* --pxe-prompt */
3410 {
3411 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3412 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003413
Simon Kelley7622fc02009-06-04 20:32:05 +01003414 new->netid = NULL;
3415 new->opt = 10; /* PXE_MENU_PROMPT */
3416
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003417 while (is_tag_prefix(arg))
3418 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003419 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3420 comma = split(arg);
3421 nn->next = new->netid;
3422 new->netid = nn;
3423 nn->net = opt_string_alloc(arg+4);
3424 arg = comma;
3425 }
3426
3427 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003428 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003429 else
3430 {
3431 comma = split(arg);
3432 unhide_metas(arg);
3433 new->len = strlen(arg) + 1;
3434 new->val = opt_malloc(new->len);
3435 memcpy(new->val + 1, arg, new->len - 1);
3436
3437 new->u.vendor_class = (unsigned char *)"PXEClient";
3438 new->flags = DHOPT_VENDOR;
3439
3440 if (comma && atoi_check(comma, &timeout))
3441 *(new->val) = timeout;
3442 else
3443 *(new->val) = 255;
3444
3445 new->next = daemon->dhcp_opts;
3446 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003447 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003448 }
3449
3450 break;
3451 }
3452
3453 case LOPT_PXE_SERV: /* --pxe-service */
3454 {
3455 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3456 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003457 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3458 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003459 static int boottype = 32768;
3460
3461 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003462 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003463 new->server.s_addr = 0;
3464
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003465 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01003466 {
3467 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3468 comma = split(arg);
3469 nn->next = new->netid;
3470 new->netid = nn;
3471 nn->net = opt_string_alloc(arg+4);
3472 arg = comma;
3473 }
3474
3475 if (arg && (comma = split(arg)))
3476 {
3477 for (i = 0; CSA[i]; i++)
3478 if (strcasecmp(CSA[i], arg) == 0)
3479 break;
3480
3481 if (CSA[i] || atoi_check(arg, &i))
3482 {
3483 arg = comma;
3484 comma = split(arg);
3485
3486 new->CSA = i;
3487 new->menu = opt_string_alloc(arg);
3488
Simon Kelley316e2732010-01-22 20:16:09 +00003489 if (!comma)
3490 {
3491 new->type = 0; /* local boot */
3492 new->basename = NULL;
3493 }
3494 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003495 {
3496 arg = comma;
3497 comma = split(arg);
3498 if (atoi_check(arg, &i))
3499 {
3500 new->type = i;
3501 new->basename = NULL;
3502 }
3503 else
3504 {
3505 new->type = boottype++;
3506 new->basename = opt_string_alloc(arg);
3507 }
3508
Simon Kelley751d6f42012-02-10 15:24:51 +00003509 if (comma)
3510 {
3511 if (!inet_pton(AF_INET, comma, &new->server))
3512 {
3513 new->server.s_addr = 0;
3514 new->sname = opt_string_alloc(comma);
3515 }
3516
3517 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003518 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003519
Simon Kelley316e2732010-01-22 20:16:09 +00003520 /* Order matters */
3521 new->next = NULL;
3522 if (!daemon->pxe_services)
3523 daemon->pxe_services = new;
3524 else
3525 {
3526 struct pxe_service *s;
3527 for (s = daemon->pxe_services; s->next; s = s->next);
3528 s->next = new;
3529 }
3530
3531 daemon->enable_pxe = 1;
3532 break;
3533
Simon Kelley7622fc02009-06-04 20:32:05 +01003534 }
3535 }
3536
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003537 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003538 }
3539
Simon Kelleyf2621c72007-04-29 19:47:21 +01003540 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003541 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003542 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003543 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003544 else
3545 {
Simon Kelley824af852008-02-12 20:43:05 +00003546 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003547 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003548 unhide_metas(comma);
3549 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003550 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003551 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003552 else
3553 {
3554 new->next = daemon->dhcp_macs;
3555 daemon->dhcp_macs = new;
3556 }
Simon Kelley849a8352006-06-09 21:02:31 +01003557 }
3558 }
3559 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003560
3561#ifdef OPTION6_PREFIX_CLASS
3562 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3563 {
3564 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3565
3566 if (!(comma = split(arg)) ||
3567 !atoi_check16(comma, &new->class))
3568 ret_err(gen_err);
3569
3570 new->tag.net = opt_string_alloc(set_prefix(arg));
3571 new->next = daemon->prefix_classes;
3572 daemon->prefix_classes = new;
3573
3574 break;
3575 }
3576#endif
3577
3578
Simon Kelleyf2621c72007-04-29 19:47:21 +01003579 case 'U': /* --dhcp-vendorclass */
3580 case 'j': /* --dhcp-userclass */
3581 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3582 case LOPT_REMOTE: /* --dhcp-remoteid */
3583 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003584 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003585 unsigned char *p;
3586 int dig = 0;
3587 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3588
3589 if (!(comma = split(arg)))
3590 ret_err(gen_err);
3591
3592 new->netid.net = opt_string_alloc(set_prefix(arg));
3593 /* check for hex string - must digits may include : must not have nothing else,
3594 only allowed for agent-options. */
3595
3596 arg = comma;
3597 if ((comma = split(arg)))
3598 {
3599 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3600 ret_err(gen_err);
3601 else
3602 new->enterprise = atoi(arg+11);
3603 }
3604 else
3605 comma = arg;
3606
3607 for (p = (unsigned char *)comma; *p; p++)
3608 if (isxdigit(*p))
3609 dig = 1;
3610 else if (*p != ':')
3611 break;
3612 unhide_metas(comma);
3613 if (option == 'U' || option == 'j' || *p || !dig)
3614 {
3615 new->len = strlen(comma);
3616 new->data = opt_malloc(new->len);
3617 memcpy(new->data, comma, new->len);
3618 }
3619 else
3620 {
3621 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3622 new->data = opt_malloc(new->len);
3623 memcpy(new->data, comma, new->len);
3624 }
3625
3626 switch (option)
3627 {
3628 case 'j':
3629 new->match_type = MATCH_USER;
3630 break;
3631 case 'U':
3632 new->match_type = MATCH_VENDOR;
3633 break;
3634 case LOPT_CIRCUIT:
3635 new->match_type = MATCH_CIRCUIT;
3636 break;
3637 case LOPT_REMOTE:
3638 new->match_type = MATCH_REMOTE;
3639 break;
3640 case LOPT_SUBSCR:
3641 new->match_type = MATCH_SUBSCRIBER;
3642 break;
3643 }
3644 new->next = daemon->dhcp_vendors;
3645 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003646
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003647 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003648 }
3649
Simon Kelley9e038942008-05-30 20:06:34 +01003650 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3651 if (!arg)
3652 {
3653 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3654 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3655 }
3656 else
3657 {
3658 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003659 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3660 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003661 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003662 if (!comma)
3663 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3664 }
3665 break;
3666
Simon Kelley824af852008-02-12 20:43:05 +00003667 case 'J': /* --dhcp-ignore */
3668 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3669 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003670 case '3': /* --bootp-dynamic */
3671 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003672 {
Simon Kelley824af852008-02-12 20:43:05 +00003673 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003674 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003675 if (option == 'J')
3676 {
3677 new->next = daemon->dhcp_ignore;
3678 daemon->dhcp_ignore = new;
3679 }
Simon Kelley824af852008-02-12 20:43:05 +00003680 else if (option == LOPT_BROADCAST)
3681 {
3682 new->next = daemon->force_broadcast;
3683 daemon->force_broadcast = new;
3684 }
Simon Kelley9009d742008-11-14 20:04:27 +00003685 else if (option == '3')
3686 {
3687 new->next = daemon->bootp_dynamic;
3688 daemon->bootp_dynamic = new;
3689 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003690 else if (option == LOPT_GEN_NAMES)
3691 {
3692 new->next = daemon->dhcp_gen_names;
3693 daemon->dhcp_gen_names = new;
3694 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003695 else
3696 {
3697 new->next = daemon->dhcp_ignore_names;
3698 daemon->dhcp_ignore_names = new;
3699 }
3700
3701 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003702 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003703 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003704 member->next = list;
3705 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003706 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003707 member->net = opt_string_alloc(arg+4);
3708 else
3709 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003710 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003711 }
Simon Kelley849a8352006-06-09 21:02:31 +01003712
3713 new->list = list;
3714 break;
3715 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003716
3717 case LOPT_PROXY: /* --dhcp-proxy */
3718 daemon->override = 1;
3719 while (arg) {
3720 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3721 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003722 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003723 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003724 new->next = daemon->override_relays;
3725 daemon->override_relays = new;
3726 arg = comma;
3727 }
3728 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003729
3730 case LOPT_RELAY: /* --dhcp-relay */
3731 {
3732 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3733 comma = split(arg);
3734 new->interface = opt_string_alloc(split(comma));
3735 new->iface_index = 0;
3736 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3737 {
3738 new->next = daemon->relay4;
3739 daemon->relay4 = new;
3740 }
3741#ifdef HAVE_DHCP6
3742 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3743 {
3744 new->next = daemon->relay6;
3745 daemon->relay6 = new;
3746 }
3747#endif
3748 else
3749 ret_err(_("Bad dhcp-relay"));
3750
3751 break;
3752 }
3753
Simon Kelley7622fc02009-06-04 20:32:05 +01003754#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003755
Simon Kelley8b372702012-03-09 17:45:10 +00003756#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003757 case LOPT_RA_PARAM: /* --ra-param */
3758 if ((comma = split(arg)))
3759 {
3760 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3761 new->lifetime = -1;
3762 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003763 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003764 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003765 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003766 if (strcasestr(comma, "mtu:") == comma)
3767 {
3768 arg = comma + 4;
3769 if (!(comma = split(comma)))
3770 goto err;
3771 if (!strcasecmp(arg, "off"))
3772 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003773 else if (!atoi_check(arg, &new->mtu))
3774 new->mtu_name = opt_string_alloc(arg);
3775 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003776 goto err;
3777 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003778 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3779 {
3780 if (*comma == 'l' || *comma == 'L')
3781 new->prio = 0x18;
3782 else
3783 new->prio = 0x08;
3784 comma = split(comma);
3785 }
3786 arg = split(comma);
3787 if (!atoi_check(comma, &new->interval) ||
3788 (arg && !atoi_check(arg, &new->lifetime)))
David Flamand005c46d2017-04-11 11:49:54 +01003789err:
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003790 ret_err(_("bad RA-params"));
3791
3792 new->next = daemon->ra_interfaces;
3793 daemon->ra_interfaces = new;
3794 }
3795 break;
3796
Simon Kelley8b372702012-03-09 17:45:10 +00003797 case LOPT_DUID: /* --dhcp-duid */
3798 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003799 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003800 else
3801 {
3802 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3803 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3804 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3805 }
3806 break;
3807#endif
3808
Simon Kelleyf2621c72007-04-29 19:47:21 +01003809 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003810 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003811 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003812 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003813 struct doctor *new = opt_malloc(sizeof(struct doctor));
3814 new->next = daemon->doctors;
3815 daemon->doctors = new;
3816 new->mask.s_addr = 0xffffffff;
3817 new->end.s_addr = 0;
3818
Simon Kelley849a8352006-06-09 21:02:31 +01003819 if ((a[0] = arg))
3820 for (k = 1; k < 3; k++)
3821 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003822 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003823 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003824 unhide_metas(a[k]);
3825 }
Simon Kelley849a8352006-06-09 21:02:31 +01003826
Simon Kelley73a08a22009-02-05 20:28:08 +00003827 dash = split_chr(a[0], '-');
3828
Simon Kelley849a8352006-06-09 21:02:31 +01003829 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003830 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01003831 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
3832 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
3833 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01003834
Simon Kelley73a08a22009-02-05 20:28:08 +00003835 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003836 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003837 !is_same_net(new->in, new->end, new->mask) ||
3838 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003839 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003840
3841 break;
3842 }
3843
Simon Kelleyf2621c72007-04-29 19:47:21 +01003844 case LOPT_INTNAME: /* --interface-name */
3845 {
3846 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003847 char *domain = NULL;
3848
Simon Kelleyf2621c72007-04-29 19:47:21 +01003849 comma = split(arg);
3850
Simon Kelley1f15b812009-10-13 17:49:32 +01003851 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003852 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003853
Simon Kelley824af852008-02-12 20:43:05 +00003854 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003855 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00003856 new->addr = NULL;
3857
Simon Kelleyf2621c72007-04-29 19:47:21 +01003858 /* Add to the end of the list, so that first name
3859 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003860 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003861 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003862 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003863 new->family = 0;
3864 arg = split_chr(comma, '/');
3865 if (arg)
3866 {
3867 if (strcmp(arg, "4") == 0)
3868 new->family = AF_INET;
3869#ifdef HAVE_IPV6
3870 else if (strcmp(arg, "6") == 0)
3871 new->family = AF_INET6;
3872#endif
3873 else
3874 ret_err(gen_err);
3875 }
Simon Kelley824af852008-02-12 20:43:05 +00003876 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003877 break;
3878 }
Simon Kelley9009d742008-11-14 20:04:27 +00003879
3880 case LOPT_CNAME: /* --cname */
3881 {
3882 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003883 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003884 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003885
Simon Kelleya1d973f2016-12-22 22:09:50 +00003886 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00003887 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00003888 pen = last;
3889 last = comma;
3890 }
3891
3892 if (!pen)
3893 ret_err(_("bad CNAME"));
3894
3895 if (pen != arg && atoi_check(last, &ttl))
3896 last = pen;
3897
3898 target = canonicalise_opt(last);
3899
3900 while (arg != last)
3901 {
Petr Menšík56f06232018-03-06 23:13:32 +00003902 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00003903 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01003904
3905 if (!alias || !target)
3906 ret_err(_("bad CNAME"));
Simon Kelleya1d973f2016-12-22 22:09:50 +00003907
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003908 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01003909 if (hostname_isequal(new->alias, alias))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003910 ret_err(_("duplicate CNAME"));
3911 new = opt_malloc(sizeof(struct cname));
3912 new->next = daemon->cnames;
3913 daemon->cnames = new;
3914 new->alias = alias;
3915 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003916 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003917
Petr Menšík56f06232018-03-06 23:13:32 +00003918 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00003919 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003920
Simon Kelley9009d742008-11-14 20:04:27 +00003921 break;
3922 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003923
3924 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003925 {
3926 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003927 char *dom, *target = NULL;
3928
Simon Kelleyf2621c72007-04-29 19:47:21 +01003929 comma = split(arg);
3930
Simon Kelley1f15b812009-10-13 17:49:32 +01003931 if (!(dom = canonicalise_opt(arg)) ||
3932 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003933 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003934 else
3935 {
3936 new = opt_malloc(sizeof(struct ptr_record));
3937 new->next = daemon->ptr;
3938 daemon->ptr = new;
3939 new->name = dom;
3940 new->ptr = target;
3941 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003942 break;
3943 }
3944
Simon Kelley1a6bca82008-07-11 11:11:42 +01003945 case LOPT_NAPTR: /* --naptr-record */
3946 {
3947 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3948 int k = 0;
3949 struct naptr *new;
3950 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003951 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003952
3953 if ((a[0] = arg))
3954 for (k = 1; k < 7; k++)
3955 if (!(a[k] = split(a[k-1])))
3956 break;
3957
3958
3959 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003960 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003961 !atoi_check16(a[1], &order) ||
3962 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003963 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003964 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003965 else
3966 {
3967 new = opt_malloc(sizeof(struct naptr));
3968 new->next = daemon->naptr;
3969 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003970 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003971 new->flags = opt_string_alloc(a[3]);
3972 new->services = opt_string_alloc(a[4]);
3973 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003974 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003975 new->order = order;
3976 new->pref = pref;
3977 }
3978 break;
3979 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003980
3981 case LOPT_RR: /* dns-rr */
3982 {
3983 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00003984 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003985 char *data;
3986 int val;
3987
3988 comma = split(arg);
3989 data = split(comma);
3990
3991 new = opt_malloc(sizeof(struct txt_record));
3992 new->next = daemon->rr;
3993 daemon->rr = new;
3994
3995 if (!atoi_check(comma, &val) ||
3996 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003997 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003998 ret_err(_("bad RR record"));
3999
Simon Kelley9f7f3b12012-05-28 21:39:57 +01004000 new->class = val;
4001 new->len = 0;
4002
4003 if (data)
4004 {
4005 new->txt=opt_malloc(len);
4006 new->len = len;
4007 memcpy(new->txt, data, len);
4008 }
4009
4010 break;
4011 }
4012
Simon Kelleyf2621c72007-04-29 19:47:21 +01004013 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004014 {
4015 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004016 unsigned char *p, *cnt;
4017 size_t len;
4018
4019 comma = split(arg);
4020
Simon Kelley824af852008-02-12 20:43:05 +00004021 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004022 new->next = daemon->txt;
4023 daemon->txt = new;
4024 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004025 new->stat = 0;
4026
Simon Kelley1f15b812009-10-13 17:49:32 +01004027 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004028 ret_err(_("bad TXT record"));
4029
Simon Kelley28866e92011-02-14 20:19:14 +00004030 len = comma ? strlen(comma) : 0;
4031 len += (len/255) + 1; /* room for extra counts */
4032 new->txt = p = opt_malloc(len);
4033
4034 cnt = p++;
4035 *cnt = 0;
4036
4037 while (comma && *comma)
4038 {
4039 unsigned char c = (unsigned char)*comma++;
4040
4041 if (c == ',' || *cnt == 255)
4042 {
4043 if (c != ',')
4044 comma--;
4045 cnt = p++;
4046 *cnt = 0;
4047 }
4048 else
4049 {
4050 *p++ = unhide_meta(c);
4051 (*cnt)++;
4052 }
4053 }
4054
4055 new->len = p - new->txt;
4056
Simon Kelley849a8352006-06-09 21:02:31 +01004057 break;
4058 }
4059
Simon Kelleyf2621c72007-04-29 19:47:21 +01004060 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004061 {
4062 int port = 1, priority = 0, weight = 0;
4063 char *name, *target = NULL;
4064 struct mx_srv_record *new;
4065
Simon Kelleyf2621c72007-04-29 19:47:21 +01004066 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004067
Simon Kelley1f15b812009-10-13 17:49:32 +01004068 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004069 ret_err(_("bad SRV record"));
4070
Simon Kelley849a8352006-06-09 21:02:31 +01004071 if (comma)
4072 {
4073 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004074 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004075 if (!(target = canonicalise_opt(arg)))
4076 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00004077
Simon Kelley849a8352006-06-09 21:02:31 +01004078 if (comma)
4079 {
4080 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004081 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004082 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004083 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00004084
Simon Kelley849a8352006-06-09 21:02:31 +01004085 if (comma)
4086 {
4087 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004088 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004089 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004090 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00004091
Simon Kelley407a1f32016-03-01 17:06:07 +00004092 if (comma && !atoi_check16(comma, &weight))
4093 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01004094 }
4095 }
4096 }
4097
Simon Kelley824af852008-02-12 20:43:05 +00004098 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004099 new->next = daemon->mxnames;
4100 daemon->mxnames = new;
4101 new->issrv = 1;
4102 new->name = name;
4103 new->target = target;
4104 new->srvport = port;
4105 new->priority = priority;
4106 new->weight = weight;
4107 break;
4108 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004109
Simon Kelleye759d422012-03-16 13:18:57 +00004110 case LOPT_HOST_REC: /* --host-record */
4111 {
4112 struct host_record *new = opt_malloc(sizeof(struct host_record));
4113 memset(new, 0, sizeof(struct host_record));
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004114 new->ttl = -1;
4115
Simon Kelleye759d422012-03-16 13:18:57 +00004116 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004117 ret_err(_("Bad host-record"));
4118
4119 while (arg)
4120 {
4121 struct all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004122 char *dig;
4123
4124 for (dig = arg; *dig != 0; dig++)
4125 if (*dig < '0' || *dig > '9')
4126 break;
4127 if (*dig == 0)
4128 new->ttl = atoi(arg);
4129 else if (inet_pton(AF_INET, arg, &addr))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004130 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00004131#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004132 else if (inet_pton(AF_INET6, arg, &addr))
4133 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00004134#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004135 else
4136 {
4137 int nomem;
4138 char *canon = canonicalise(arg, &nomem);
4139 struct name_list *nl = opt_malloc(sizeof(struct name_list));
4140 if (!canon)
4141 ret_err(_("Bad name in host-record"));
4142
4143 nl->name = canon;
4144 /* keep order, so that PTR record goes to first name */
4145 nl->next = NULL;
4146 if (!new->names)
4147 new->names = nl;
4148 else
4149 {
4150 struct name_list *tmp;
4151 for (tmp = new->names; tmp->next; tmp = tmp->next);
4152 tmp->next = nl;
4153 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004154 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004155
4156 arg = comma;
4157 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004158 }
Simon Kelleye759d422012-03-16 13:18:57 +00004159
4160 /* Keep list order */
4161 if (!daemon->host_records_tail)
4162 daemon->host_records = new;
4163 else
4164 daemon->host_records_tail->next = new;
4165 new->next = NULL;
4166 daemon->host_records_tail = new;
4167 break;
4168 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004169
4170#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004171 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004172 daemon->timestamp_file = opt_string_alloc(arg);
4173 break;
4174
Simon Kelleyf3e57872018-07-20 21:10:48 +01004175 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004176 if (arg)
4177 {
4178 if (strcmp(arg, "no") == 0)
4179 set_option_bool(OPT_DNSSEC_IGN_NS);
4180 else
4181 ret_err(_("bad value for dnssec-check-unsigned"));
4182 }
4183 break;
4184
Simon Kelleyf3e57872018-07-20 21:10:48 +01004185 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004186 {
Simon Kelleyee415862014-02-11 11:07:22 +00004187 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4188 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4189 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004190
4191 new->class = C_IN;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004192
Simon Kelleycbf13a22014-01-25 17:59:14 +00004193 if ((comma = split(arg)) && (algo = split(comma)))
4194 {
4195 int class = 0;
4196 if (strcmp(comma, "IN") == 0)
4197 class = C_IN;
4198 else if (strcmp(comma, "CH") == 0)
4199 class = C_CHAOS;
4200 else if (strcmp(comma, "HS") == 0)
4201 class = C_HESIOD;
4202
4203 if (class != 0)
4204 {
4205 new->class = class;
4206 comma = algo;
4207 algo = split(comma);
4208 }
4209 }
4210
Simon Kelleyee415862014-02-11 11:07:22 +00004211 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4212 !atoi_check16(comma, &new->keytag) ||
4213 !atoi_check8(algo, &new->algo) ||
4214 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004215 !(new->name = canonicalise_opt(arg)))
Simon Kelleyee415862014-02-11 11:07:22 +00004216 ret_err(_("bad trust anchor"));
Simon Kelleycbf13a22014-01-25 17:59:14 +00004217
Simon Kelley0fc2f312014-01-08 10:26:58 +00004218 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004219 len = (2*strlen(keyhex))+1;
4220 new->digest = opt_malloc(len);
4221 unhide_metas(keyhex);
4222 /* 4034: "Whitespace is allowed within digits" */
4223 for (cp = keyhex; *cp; )
4224 if (isspace(*cp))
4225 for (cp1 = cp; *cp1; cp1++)
4226 *cp1 = *(cp1+1);
4227 else
4228 cp++;
4229 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
4230 ret_err(_("bad HEX in trust anchor"));
Simon Kelley0fc2f312014-01-08 10:26:58 +00004231
Simon Kelleyee415862014-02-11 11:07:22 +00004232 new->next = daemon->ds;
4233 daemon->ds = new;
4234
Simon Kelley0fc2f312014-01-08 10:26:58 +00004235 break;
4236 }
4237#endif
4238
Simon Kelley7622fc02009-06-04 20:32:05 +01004239 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004240 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004241
Simon Kelley849a8352006-06-09 21:02:31 +01004242 }
Simon Kelley824af852008-02-12 20:43:05 +00004243
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004244 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004245}
4246
Simon Kelley28866e92011-02-14 20:19:14 +00004247static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004248{
Simon Kelley824af852008-02-12 20:43:05 +00004249 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004250 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004251
4252 while (fgets(buff, MAXDNAME, f))
4253 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004254 int white, i;
4255 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004256 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004257 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004258
Simon Kelley824af852008-02-12 20:43:05 +00004259 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004260 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004261 {
4262 if (setjmp(mem_jmp))
4263 continue;
4264 mem_recover = 1;
4265 }
4266
Simon Kelley13dee6f2017-02-28 16:51:58 +00004267 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004268 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004269 errmess = NULL;
4270
Simon Kelley849a8352006-06-09 21:02:31 +01004271 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4272 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004273 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004274 {
4275 if (*p == '"')
4276 {
4277 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004278
Simon Kelley849a8352006-06-09 21:02:31 +01004279 for(; *p && *p != '"'; p++)
4280 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004281 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004282 {
4283 if (p[1] == 't')
4284 p[1] = '\t';
4285 else if (p[1] == 'n')
4286 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004287 else if (p[1] == 'b')
4288 p[1] = '\b';
4289 else if (p[1] == 'r')
4290 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004291 else if (p[1] == 'e') /* escape */
4292 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004293 memmove(p, p+1, strlen(p+1)+1);
4294 }
4295 *p = hide_meta(*p);
4296 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004297
4298 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004299 {
4300 errmess = _("missing \"");
4301 goto oops;
4302 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004303
4304 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004305 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004306
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004307 if (isspace(*p))
4308 {
4309 *p = ' ';
4310 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004311 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004312 else
4313 {
4314 if (white && *p == '#')
4315 {
4316 *p = 0;
4317 break;
4318 }
4319 white = 0;
4320 }
Simon Kelley849a8352006-06-09 21:02:31 +01004321 }
4322
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004323
4324 /* strip leading spaces */
4325 for (start = buff; *start && *start == ' '; start++);
4326
4327 /* strip trailing spaces */
4328 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4329
4330 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004331 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004332 else
4333 start[len] = 0;
4334
Simon Kelley611ebc52012-07-16 16:23:46 +01004335 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004336 arg = start;
4337 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004338 {
4339 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004340 for (arg = p+1; *arg == ' '; arg++);
4341 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004342 *p = 0;
4343 }
4344 else
4345 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004346
Simon Kelley611ebc52012-07-16 16:23:46 +01004347 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004348 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004349 for (option = 0, i = 0; opts[i].name; i++)
4350 if (strcmp(opts[i].name, start) == 0)
4351 {
4352 option = opts[i].val;
4353 break;
4354 }
4355
4356 if (!option)
4357 errmess = _("bad option");
4358 else if (opts[i].has_arg == 0 && arg)
4359 errmess = _("extraneous parameter");
4360 else if (opts[i].has_arg == 1 && !arg)
4361 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004362 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4363 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004364 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004365
4366 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004367 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004368 strcpy(daemon->namebuff, errmess);
4369
Simon Kelley7b1eae42014-02-20 13:43:28 +00004370 if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004371 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004372 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004373 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004374 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004375 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004376 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004377 }
Simon Kelley849a8352006-06-09 21:02:31 +01004378 }
4379
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004380 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004381 fclose(f);
4382}
4383
Simon Kelley4f7bb572018-03-08 18:47:08 +00004384#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004385int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004386{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004387 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4388
Simon Kelley70d18732015-01-31 19:59:29 +00004389 if (flags & AH_DHCP_HST)
4390 return one_file(file, LOPT_BANK);
4391 else if (flags & AH_DHCP_OPT)
4392 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004393
Simon Kelley70d18732015-01-31 19:59:29 +00004394 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004395}
4396#endif
4397
Simon Kelley395eb712012-07-06 22:07:05 +01004398static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004399{
4400 FILE *f;
4401 int nofile_ok = 0;
4402 static int read_stdin = 0;
4403 static struct fileread {
4404 dev_t dev;
4405 ino_t ino;
4406 struct fileread *next;
4407 } *filesread = NULL;
4408
4409 if (hard_opt == '7')
4410 {
4411 /* default conf-file reading */
4412 hard_opt = 0;
4413 nofile_ok = 1;
4414 }
4415
4416 if (hard_opt == 0 && strcmp(file, "-") == 0)
4417 {
4418 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004419 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004420 read_stdin = 1;
4421 file = "stdin";
4422 f = stdin;
4423 }
4424 else
4425 {
4426 /* ignore repeated files. */
4427 struct stat statbuf;
4428
4429 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4430 {
4431 struct fileread *r;
4432
4433 for (r = filesread; r; r = r->next)
4434 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004435 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004436
4437 r = safe_malloc(sizeof(struct fileread));
4438 r->next = filesread;
4439 filesread = r;
4440 r->dev = statbuf.st_dev;
4441 r->ino = statbuf.st_ino;
4442 }
4443
4444 if (!(f = fopen(file, "r")))
4445 {
4446 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004447 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004448 else
4449 {
4450 char *str = _("cannot read %s: %s");
4451 if (hard_opt != 0)
4452 {
4453 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004454 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004455 }
4456 else
4457 die(str, file, EC_FILE);
4458 }
4459 }
4460 }
4461
4462 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004463 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004464}
4465
4466/* expand any name which is a directory */
4467struct hostsfile *expand_filelist(struct hostsfile *list)
4468{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004469 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004470 struct hostsfile *ah;
4471
Simon Kelley19c51cf2014-03-18 22:38:30 +00004472 /* find largest used index */
4473 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004474 {
4475 if (i <= ah->index)
4476 i = ah->index + 1;
4477
4478 if (ah->flags & AH_DIR)
4479 ah->flags |= AH_INACTIVE;
4480 else
4481 ah->flags &= ~AH_INACTIVE;
4482 }
4483
4484 for (ah = list; ah; ah = ah->next)
4485 if (!(ah->flags & AH_INACTIVE))
4486 {
4487 struct stat buf;
4488 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4489 {
4490 DIR *dir_stream;
4491 struct dirent *ent;
4492
4493 /* don't read this as a file */
4494 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004495
Simon Kelley28866e92011-02-14 20:19:14 +00004496 if (!(dir_stream = opendir(ah->fname)))
4497 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4498 ah->fname, strerror(errno));
4499 else
4500 {
4501 while ((ent = readdir(dir_stream)))
4502 {
4503 size_t lendir = strlen(ah->fname);
4504 size_t lenfile = strlen(ent->d_name);
4505 struct hostsfile *ah1;
4506 char *path;
4507
4508 /* ignore emacs backups and dotfiles */
4509 if (lenfile == 0 ||
4510 ent->d_name[lenfile - 1] == '~' ||
4511 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4512 ent->d_name[0] == '.')
4513 continue;
4514
4515 /* see if we have an existing record.
4516 dir is ah->fname
4517 file is ent->d_name
4518 path to match is ah1->fname */
4519
4520 for (ah1 = list; ah1; ah1 = ah1->next)
4521 {
4522 if (lendir < strlen(ah1->fname) &&
4523 strstr(ah1->fname, ah->fname) == ah1->fname &&
4524 ah1->fname[lendir] == '/' &&
4525 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4526 {
4527 ah1->flags &= ~AH_INACTIVE;
4528 break;
4529 }
4530 }
4531
4532 /* make new record */
4533 if (!ah1)
4534 {
4535 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4536 continue;
4537
4538 if (!(path = whine_malloc(lendir + lenfile + 2)))
4539 {
4540 free(ah1);
4541 continue;
4542 }
4543
4544 strcpy(path, ah->fname);
4545 strcat(path, "/");
4546 strcat(path, ent->d_name);
4547 ah1->fname = path;
4548 ah1->index = i++;
4549 ah1->flags = AH_DIR;
4550 ah1->next = list;
4551 list = ah1;
4552 }
4553
4554 /* inactivate record if not regular file */
4555 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4556 ah1->flags |= AH_INACTIVE;
4557
4558 }
4559 closedir(dir_stream);
4560 }
4561 }
4562 }
4563
4564 return list;
4565}
4566
Simon Kelley7b1eae42014-02-20 13:43:28 +00004567void read_servers_file(void)
4568{
4569 FILE *f;
4570
4571 if (!(f = fopen(daemon->servers_file, "r")))
4572 {
4573 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4574 return;
4575 }
4576
4577 mark_servers(SERV_FROM_FILE);
4578 cleanup_servers();
4579
4580 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4581}
4582
Simon Kelley28866e92011-02-14 20:19:14 +00004583
Simon Kelley7622fc02009-06-04 20:32:05 +01004584#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004585static void clear_dynamic_conf(void)
4586{
4587 struct dhcp_config *configs, *cp, **up;
4588
4589 /* remove existing... */
4590 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4591 {
4592 cp = configs->next;
4593
4594 if (configs->flags & CONFIG_BANK)
4595 {
4596 struct hwaddr_config *mac, *tmp;
4597 struct dhcp_netid_list *list, *tmplist;
4598
4599 for (mac = configs->hwaddr; mac; mac = tmp)
4600 {
4601 tmp = mac->next;
4602 free(mac);
4603 }
4604
4605 if (configs->flags & CONFIG_CLID)
4606 free(configs->clid);
4607
4608 for (list = configs->netid; list; list = tmplist)
4609 {
4610 free(list->list);
4611 tmplist = list->next;
4612 free(list);
4613 }
4614
4615 if (configs->flags & CONFIG_NAME)
4616 free(configs->hostname);
4617
4618 *up = configs->next;
4619 free(configs);
4620 }
4621 else
4622 up = &configs->next;
4623 }
4624}
4625
4626static void clear_dynamic_opt(void)
4627{
4628 struct dhcp_opt *opts, *cp, **up;
4629 struct dhcp_netid *id, *next;
4630
4631 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4632 {
4633 cp = opts->next;
4634
4635 if (opts->flags & DHOPT_BANK)
4636 {
4637 if ((opts->flags & DHOPT_VENDOR))
4638 free(opts->u.vendor_class);
4639 free(opts->val);
4640 for (id = opts->netid; id; id = next)
4641 {
4642 next = id->next;
4643 free(id->net);
4644 free(id);
4645 }
4646 *up = opts->next;
4647 free(opts);
4648 }
4649 else
4650 up = &opts->next;
4651 }
4652}
4653
Simon Kelley824af852008-02-12 20:43:05 +00004654void reread_dhcp(void)
4655{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004656 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004657
Simon Kelley4f7bb572018-03-08 18:47:08 +00004658 /* Do these even if there is no daemon->dhcp_hosts_file or
4659 daemon->dhcp_opts_file since entries may have been created by the
4660 inotify dynamic file reading system. */
4661
4662 clear_dynamic_conf();
4663 clear_dynamic_opt();
4664
4665 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004666 {
Simon Kelley28866e92011-02-14 20:19:14 +00004667 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4668 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004669 if (!(hf->flags & AH_INACTIVE))
4670 {
4671 if (one_file(hf->fname, LOPT_BANK))
4672 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4673 }
Simon Kelley824af852008-02-12 20:43:05 +00004674 }
4675
4676 if (daemon->dhcp_opts_file)
4677 {
Simon Kelley28866e92011-02-14 20:19:14 +00004678 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4679 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4680 if (!(hf->flags & AH_INACTIVE))
4681 {
Simon Kelley395eb712012-07-06 22:07:05 +01004682 if (one_file(hf->fname, LOPT_OPTS))
4683 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004684 }
Simon Kelley824af852008-02-12 20:43:05 +00004685 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004686
4687# ifdef HAVE_INOTIFY
4688 /* Setup notify and read pre-existing files. */
4689 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
4690# endif
Simon Kelley824af852008-02-12 20:43:05 +00004691}
Simon Kelley7622fc02009-06-04 20:32:05 +01004692#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00004693
Simon Kelley5aabfc72007-08-29 11:24:47 +01004694void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004695{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004696 size_t argbuf_size = MAXDNAME;
4697 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004698 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004699 int option, conffile_opt = '7', testmode = 0;
Simon Kelley90cb2222015-07-05 21:59:10 +01004700 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004701
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004702 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004703
Simon Kelley824af852008-02-12 20:43:05 +00004704 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004705 memset(daemon, 0, sizeof(struct daemon));
4706 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004707
Simon Kelley3be34542004-09-11 19:12:13 +01004708 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004709 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004710 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004711 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004712 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4713 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004714 daemon->default_resolv.is_default = 1;
4715 daemon->default_resolv.name = RESOLVFILE;
4716 daemon->resolv_files = &daemon->default_resolv;
4717 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004718 daemon->runfile = RUNFILE;
4719 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004720 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004721 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004722 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004723 daemon->auth_ttl = AUTH_TTL;
4724 daemon->soa_refresh = SOA_REFRESH;
4725 daemon->soa_retry = SOA_RETRY;
4726 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00004727 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00004728 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01004729
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004730#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00004731 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
4732 add_txt("authors.bind", "Simon Kelley", 0);
4733 add_txt("copyright.bind", COPYRIGHT, 0);
4734 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
4735 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
4736 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
4737 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
4738 add_txt("hits.bind", NULL, TXT_STAT_HITS);
4739#ifdef HAVE_AUTH
4740 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
4741#endif
4742 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004743#endif
Simon Kelley0a852542005-03-23 20:28:59 +00004744
Simon Kelley849a8352006-06-09 21:02:31 +01004745 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004746 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004747#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004748 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004749#else
Simon Kelley849a8352006-06-09 21:02:31 +01004750 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004751#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004752
4753 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004754 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004755 for (; optind < argc; optind++)
4756 {
4757 unsigned char *c = (unsigned char *)argv[optind];
4758 for (; *c != 0; c++)
4759 if (!isspace(*c))
4760 die(_("junk found in command line"), NULL, EC_BADCONF);
4761 }
Simon Kelley28866e92011-02-14 20:19:14 +00004762 break;
4763 }
4764
Simon Kelley849a8352006-06-09 21:02:31 +01004765 /* Copy optarg so that argv doesn't get changed */
4766 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004767 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00004768 if (strlen(optarg) >= argbuf_size)
4769 {
4770 free(argbuf);
4771 argbuf_size = strlen(optarg) + 1;
4772 argbuf = opt_malloc(argbuf_size);
4773 }
4774 strncpy(argbuf, optarg, argbuf_size);
4775 argbuf[argbuf_size-1] = 0;
4776 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01004777 }
4778 else
4779 arg = NULL;
4780
4781 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004782 if (option == LOPT_TEST)
4783 testmode = 1;
4784 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004785 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004786#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004787 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004788 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004789#ifdef HAVE_DHCP6
4790 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4791 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004792#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004793 else
4794#endif
4795 do_usage();
4796
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004797 exit(0);
4798 }
Simon Kelley849a8352006-06-09 21:02:31 +01004799 else if (option == 'v')
4800 {
4801 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004802 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004803 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4804 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004805 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004806 exit(0);
4807 }
Simon Kelley849a8352006-06-09 21:02:31 +01004808 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004809 {
Simon Kelley28866e92011-02-14 20:19:14 +00004810 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004811 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004812 }
Simon Kelley849a8352006-06-09 21:02:31 +01004813 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004814 {
Simon Kelley26128d22004-11-14 16:43:54 +00004815#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00004816 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004817#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00004818 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004819#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004820 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004821 }
4822 }
Simon Kelley849a8352006-06-09 21:02:31 +01004823
Neil Jerram3bd4c472018-01-18 22:49:38 +00004824 free(argbuf);
4825
Simon Kelley849a8352006-06-09 21:02:31 +01004826 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00004827 {
4828 one_file(conffile, conffile_opt);
Simon Kelley90cb2222015-07-05 21:59:10 +01004829 if (conffile_opt == 0)
4830 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00004831 }
Simon Kelley849a8352006-06-09 21:02:31 +01004832
Simon Kelley1a6bca82008-07-11 11:11:42 +01004833 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004834 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004835 {
4836 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004837 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01004838 if (!(tmp->flags & SERV_HAS_SOURCE))
4839 {
4840 if (tmp->source_addr.sa.sa_family == AF_INET)
4841 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004842#ifdef HAVE_IPV6
Simon Kelley14ffa072016-04-25 16:36:44 +01004843 else if (tmp->source_addr.sa.sa_family == AF_INET6)
4844 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004845#endif
Simon Kelley14ffa072016-04-25 16:36:44 +01004846 }
4847 }
4848
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004849 if (daemon->host_records)
4850 {
4851 struct host_record *hr;
4852
4853 for (hr = daemon->host_records; hr; hr = hr->next)
4854 if (hr->ttl == -1)
4855 hr->ttl = daemon->local_ttl;
4856 }
4857
4858 if (daemon->cnames)
4859 {
Simon Kelley903df072017-01-19 17:22:00 +00004860 struct cname *cn, *cn2, *cn3;
4861
4862#define NOLOOP 1
4863#define TESTLOOP 2
4864
4865 /* Fill in TTL for CNAMES noe we have local_ttl.
4866 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004867 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00004868 {
4869 if (cn->ttl == -1)
4870 cn->ttl = daemon->local_ttl;
4871 cn->flag = 0;
4872 cn->targetp = NULL;
4873 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
4874 if (hostname_isequal(cn->target, cn2->alias))
4875 {
4876 cn->targetp = cn2;
4877 break;
4878 }
4879 }
4880
4881 /* Find any CNAME loops.*/
4882 for (cn = daemon->cnames; cn; cn = cn->next)
4883 {
4884 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
4885 {
4886 if (cn2->flag == NOLOOP)
4887 break;
4888
4889 if (cn2->flag == TESTLOOP)
4890 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
4891
4892 cn2->flag = TESTLOOP;
4893 }
4894
4895 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
4896 cn3->flag = NOLOOP;
4897 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004898 }
4899
Simon Kelley3be34542004-09-11 19:12:13 +01004900 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004901 {
4902 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004903 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004904 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004905 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004906#ifdef HAVE_IPV6
4907 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004908 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004909#endif /* IPv6 */
4910 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004911
4912 /* create default, if not specified */
4913 if (daemon->authserver && !daemon->hostmaster)
4914 {
4915 strcpy(buff, "hostmaster.");
4916 strcat(buff, daemon->authserver);
4917 daemon->hostmaster = opt_string_alloc(buff);
4918 }
4919
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004920 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004921 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004922 {
Simon Kelley0a852542005-03-23 20:28:59 +00004923 struct mx_srv_record *mx;
4924
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004925 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004926 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004927
Simon Kelley0a852542005-03-23 20:28:59 +00004928 for (mx = daemon->mxnames; mx; mx = mx->next)
4929 if (!mx->issrv && hostname_isequal(mx->name, buff))
4930 break;
4931
Simon Kelley28866e92011-02-14 20:19:14 +00004932 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004933 {
Simon Kelley824af852008-02-12 20:43:05 +00004934 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004935 mx->next = daemon->mxnames;
4936 mx->issrv = 0;
4937 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004938 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004939 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004940 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004941
Simon Kelley3be34542004-09-11 19:12:13 +01004942 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004943 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004944
4945 for (mx = daemon->mxnames; mx; mx = mx->next)
4946 if (!mx->issrv && !mx->target)
4947 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004948 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004949
Simon Kelley28866e92011-02-14 20:19:14 +00004950 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004951 daemon->resolv_files &&
4952 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004953 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004954 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004955
Simon Kelley28866e92011-02-14 20:19:14 +00004956 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004957 {
4958 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004959 FILE *f;
4960
Simon Kelley28866e92011-02-14 20:19:14 +00004961 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004962 !daemon->resolv_files ||
4963 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004964 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004965
Simon Kelley3be34542004-09-11 19:12:13 +01004966 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004967 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004968
4969 while ((line = fgets(buff, MAXDNAME, f)))
4970 {
4971 char *token = strtok(line, " \t\n\r");
4972
4973 if (!token || strcmp(token, "search") != 0)
4974 continue;
4975
4976 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004977 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004978 break;
4979 }
Simon Kelley3be34542004-09-11 19:12:13 +01004980
Simon Kelleyde379512004-06-22 20:23:33 +01004981 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004982
Simon Kelley3be34542004-09-11 19:12:13 +01004983 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004984 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004985 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004986
4987 if (daemon->domain_suffix)
4988 {
4989 /* add domain for any srv record without one. */
4990 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004991
Simon Kelley3d8df262005-08-29 12:19:27 +01004992 for (srv = daemon->mxnames; srv; srv = srv->next)
4993 if (srv->issrv &&
4994 strchr(srv->name, '.') &&
4995 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4996 {
4997 strcpy(buff, srv->name);
4998 strcat(buff, ".");
4999 strcat(buff, daemon->domain_suffix);
5000 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00005001 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01005002 }
5003 }
Simon Kelley28866e92011-02-14 20:19:14 +00005004 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00005005 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01005006
Simon Kelleyc8a80482014-03-05 14:29:54 +00005007 /* If there's access-control config, then ignore --local-service, it's intended
5008 as a system default to keep otherwise unconfigured installations safe. */
5009 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
5010 reset_option_bool(OPT_LOCAL_SERVICE);
5011
Simon Kelley7622fc02009-06-04 20:32:05 +01005012 if (testmode)
5013 {
5014 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5015 exit(0);
5016 }
Simon Kelley849a8352006-06-09 21:02:31 +01005017}