blob: d89389afa0b3ae2d361fed3ca39f360a089b2c78 [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 Kelley974a6d02018-08-23 23:01:16 +0100168#define LOPT_CAA 356
Simon Kelleybec366b2016-02-24 22:03:26 +0000169
Simon Kelley849a8352006-06-09 21:02:31 +0100170#ifdef HAVE_GETOPT_LONG
171static const struct option opts[] =
172#else
173static const struct myoption opts[] =
174#endif
175 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100176 { "version", 0, 0, 'v' },
177 { "no-hosts", 0, 0, 'h' },
178 { "no-poll", 0, 0, 'n' },
179 { "help", 0, 0, 'w' },
180 { "no-daemon", 0, 0, 'd' },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000181 { "log-queries", 2, 0, 'q' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100182 { "user", 2, 0, 'u' },
183 { "group", 2, 0, 'g' },
184 { "resolv-file", 2, 0, 'r' },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000185 { "servers-file", 1, 0, LOPT_SERVERS_FILE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100186 { "mx-host", 1, 0, 'm' },
187 { "mx-target", 1, 0, 't' },
188 { "cache-size", 2, 0, 'c' },
189 { "port", 1, 0, 'p' },
190 { "dhcp-leasefile", 2, 0, 'l' },
191 { "dhcp-lease", 1, 0, 'l' },
192 { "dhcp-host", 1, 0, 'G' },
193 { "dhcp-range", 1, 0, 'F' },
194 { "dhcp-option", 1, 0, 'O' },
195 { "dhcp-boot", 1, 0, 'M' },
196 { "domain", 1, 0, 's' },
197 { "domain-suffix", 1, 0, 's' },
198 { "interface", 1, 0, 'i' },
199 { "listen-address", 1, 0, 'a' },
Simon Kelleyc8a80482014-03-05 14:29:54 +0000200 { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
Simon Kelley7622fc02009-06-04 20:32:05 +0100201 { "bogus-priv", 0, 0, 'b' },
202 { "bogus-nxdomain", 1, 0, 'B' },
Glen Huang32fc6db2014-12-27 15:28:12 +0000203 { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
Simon Kelley7622fc02009-06-04 20:32:05 +0100204 { "selfmx", 0, 0, 'e' },
205 { "filterwin2k", 0, 0, 'f' },
206 { "pid-file", 2, 0, 'x' },
207 { "strict-order", 0, 0, 'o' },
208 { "server", 1, 0, 'S' },
Simon Kelleyde73a492014-02-17 21:43:27 +0000209 { "rev-server", 1, 0, LOPT_REV_SERV },
Simon Kelley7622fc02009-06-04 20:32:05 +0100210 { "local", 1, 0, LOPT_LOCAL },
211 { "address", 1, 0, 'A' },
212 { "conf-file", 2, 0, 'C' },
213 { "no-resolv", 0, 0, 'R' },
214 { "expand-hosts", 0, 0, 'E' },
215 { "localmx", 0, 0, 'L' },
216 { "local-ttl", 1, 0, 'T' },
217 { "no-negcache", 0, 0, 'N' },
218 { "addn-hosts", 1, 0, 'H' },
Simon Kelley70d18732015-01-31 19:59:29 +0000219 { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100220 { "query-port", 1, 0, 'Q' },
221 { "except-interface", 1, 0, 'I' },
222 { "no-dhcp-interface", 1, 0, '2' },
223 { "domain-needed", 0, 0, 'D' },
224 { "dhcp-lease-max", 1, 0, 'X' },
225 { "bind-interfaces", 0, 0, 'z' },
226 { "read-ethers", 0, 0, 'Z' },
227 { "alias", 1, 0, 'V' },
228 { "dhcp-vendorclass", 1, 0, 'U' },
229 { "dhcp-userclass", 1, 0, 'j' },
230 { "dhcp-ignore", 1, 0, 'J' },
231 { "edns-packet-max", 1, 0, 'P' },
232 { "keep-in-foreground", 0, 0, 'k' },
233 { "dhcp-authoritative", 0, 0, 'K' },
234 { "srv-host", 1, 0, 'W' },
235 { "localise-queries", 0, 0, 'y' },
236 { "txt-record", 1, 0, 'Y' },
Simon Kelley974a6d02018-08-23 23:01:16 +0100237 { "caa-record", 1, 0 , LOPT_CAA },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100238 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100239 { "enable-dbus", 2, 0, '1' },
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100240 { "enable-ubus", 0, 0, LOPT_UBUS },
Simon Kelley7622fc02009-06-04 20:32:05 +0100241 { "bootp-dynamic", 2, 0, '3' },
242 { "dhcp-mac", 1, 0, '4' },
243 { "no-ping", 0, 0, '5' },
244 { "dhcp-script", 1, 0, '6' },
245 { "conf-dir", 1, 0, '7' },
246 { "log-facility", 1, 0 ,'8' },
247 { "leasefile-ro", 0, 0, '9' },
248 { "dns-forward-max", 1, 0, '0' },
249 { "clear-on-reload", 0, 0, LOPT_RELOAD },
250 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100251 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100252 { "tftp-secure", 0, 0, LOPT_SECURE },
Stefan Tomanek30d08792015-03-31 22:32:11 +0100253 { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL },
Floris Bos60704f52017-04-09 22:22:49 +0100254 { "tftp-unique-root", 2, 0, LOPT_APREF },
Simon Kelley7622fc02009-06-04 20:32:05 +0100255 { "tftp-root", 1, 0, LOPT_PREFIX },
256 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelleybec366b2016-02-24 22:03:26 +0000257 { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
Simon Kelley61ce6002012-04-20 21:28:49 +0100258 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100259 { "ptr-record", 1, 0, LOPT_PTR },
260 { "naptr-record", 1, 0, LOPT_NAPTR },
261 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
262 { "dhcp-option-force", 1, 0, LOPT_FORCE },
263 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
264 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
265 { "log-async", 2, 0, LOPT_MAX_LOGS },
266 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
267 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
268 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
269 { "interface-name", 1, 0, LOPT_INTNAME },
270 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
271 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000272 { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
Simon Kelley70d18732015-01-31 19:59:29 +0000273 { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
Simon Kelley7622fc02009-06-04 20:32:05 +0100274 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
275 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
276 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100277 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100278 { "all-servers", 0, 0, LOPT_NOLAST },
Simon Kelleyc8226202018-08-08 23:46:03 +0100279 { "dhcp-match", 1, 0, LOPT_MATCH },
280 { "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100281 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100282 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100283 { "max-ttl", 1, 0, LOPT_MAXTTL },
RinSatsuki28de3872015-01-10 15:22:21 +0000284 { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100285 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100286 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
287 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
288 { "min-port", 1, 0, LOPT_MINPORT },
Hans Dedecker926332a2016-01-23 10:48:12 +0000289 { "max-port", 1, 0, LOPT_MAXPORT },
Simon Kelley7622fc02009-06-04 20:32:05 +0100290 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
291 { "cname", 1, 0, LOPT_CNAME },
292 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
293 { "pxe-service", 1, 0, LOPT_PXE_SERV },
294 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100295 { "tag-if", 1, 0, LOPT_TAG_IF },
296 { "dhcp-proxy", 2, 0, LOPT_PROXY },
297 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
298 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley1e505122016-01-25 21:29:23 +0000299 { "add-mac", 2, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100300 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley1e505122016-01-25 21:29:23 +0000301 { "add-cpe-id", 1, 0 , LOPT_CPE_ID },
Simon Kelley28866e92011-02-14 20:19:14 +0000302 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100303 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
304 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000305 { "dhcp-client-update", 0, 0, LOPT_FQDN },
306 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000307 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000308 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000309 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100310 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000311 { "auth-zone", 1, 0, LOPT_AUTHZONE },
312 { "auth-server", 1, 0, LOPT_AUTHSERV },
313 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
314 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000315 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000316 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000317 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100318 { "synth-domain", 1, 0, LOPT_SYNTH },
Giovanni Bajo7dbe1932012-04-05 02:50:13 +0200319 { "dnssec", 0, 0, LOPT_SEC_VALID },
Simon Kelleyee415862014-02-11 11:07:22 +0000320 { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000321 { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
Simon Kelleya6918532018-04-15 16:20:52 +0100322 { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK },
Simon Kelleye98bd522014-03-28 20:41:23 +0000323 { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
Simon Kelleyf6e62e22015-03-01 18:17:54 +0000324 { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
Simon Kelleyc6309242013-03-07 20:59:28 +0000325#ifdef OPTION6_PREFIX_CLASS
326 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
327#endif
Simon Kelleyff7eea22013-09-04 18:01:38 +0100328 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100329 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100330 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
331 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
332 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +0100333 { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
Simon Kelley1e505122016-01-25 21:29:23 +0000334 { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
Simon Kelley832e47b2016-02-24 21:24:45 +0000335 { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
Floris Bos503c6092017-04-09 23:07:13 +0100336 { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
Simon Kelley734d5312018-03-23 23:09:53 +0000337 { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
Simon Kelley6b173352018-05-08 18:32:14 +0100338 { "dumpfile", 1, 0, LOPT_DUMPFILE },
339 { "dumpmask", 1, 0, LOPT_DUMPMASK },
Simon Kelley849a8352006-06-09 21:02:31 +0100340 { NULL, 0, 0, 0 }
341 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000342
Simon Kelley28866e92011-02-14 20:19:14 +0000343
344#define ARG_DUP OPT_LAST
345#define ARG_ONE OPT_LAST + 1
346#define ARG_USED_CL OPT_LAST + 2
347#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000348
Simon Kelley1a6bca82008-07-11 11:11:42 +0100349static struct {
350 int opt;
351 unsigned int rept;
352 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000353 char * const desc;
354 char * const arg;
355} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000356 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
357 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100358 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000359 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
360 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
361 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100362 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
363 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
364 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
365 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
366 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000367 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
368 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100369 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000370 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
371 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley5f4dc5c2015-01-20 20:51:02 +0000372 { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
Simon Kelley70d18732015-01-31 19:59:29 +0000373 { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100374 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100375 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000376 { '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 +0000377 { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
379 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100380 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
381 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
382 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
383 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
384 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
385 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100386 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
387 { '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 +0000388 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100389 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000390 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100391 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
392 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
393 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
394 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
395 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
396 { 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 +0000397 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
398 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley25cf5e32015-01-09 15:53:03 +0000399 { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000400 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100401 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000402 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
Simon Kelley7b1eae42014-02-20 13:43:28 +0000403 { LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000404 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
Simon Kelleyde73a492014-02-17 21:43:27 +0000405 { 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 +0000406 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000407 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000408 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
409 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
410 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
411 { 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 +0000412 { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
413 { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000414 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100415 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100416 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000417 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
418 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley09217a12016-05-03 17:04:35 +0100419 { '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 +0000420 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
421 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100422 { '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 +0000423 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
424 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
425 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100426 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
427 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100428 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Julian Kornberger8dcdb332018-07-21 22:11:08 +0100429 { LOPT_UBUS, OPT_UBUS, NULL, gettext_noop("Enable the UBus interface."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000430 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100431 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
432 { '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 +0000433 { 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 +0100434 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000435 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
436 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
437 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
Simon Kelley1e505122016-01-25 21:29:23 +0000438 { 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 +0000439 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000440 { '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 +0100441 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000442 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100443 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100444 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100445 { 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 +0100446 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100447 { 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 +0100448 { 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 +0100449 { 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 +0100450 { 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 +0000451 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelleybec366b2016-02-24 22:03:26 +0000452 { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100453 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100454 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100455 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
456 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000457 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100458 { 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 +0100459 { 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 +0000460 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100461 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100462 { 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 +0100463 { 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 +0100464 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100465 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
466 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Hans Dedecker926332a2016-01-23 10:48:12 +0000467 { LOPT_MAXPORT, ARG_ONE, "<port>", gettext_noop("Specify highest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000468 { 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 +0000469 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
470 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100471 { 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 +0000472 { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100473 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
474 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
475 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley22c0f4f2016-02-17 22:12:31 +0000476 { 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 +0100477 { 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 +0000478 { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
Simon Kelley5a4120d2013-10-25 13:13:11 +0100479 { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100480 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
481 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000482 { 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 +0000483 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000484 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleydf3d54f2016-02-24 21:03:38 +0000485 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley974a6d02018-08-23 23:01:16 +0100486 { LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100487 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000488 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000489 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000490 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000491 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
Josh Soref730c6742017-02-06 16:14:04 +0000492 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritative zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000493 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
494 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Peter Wu3c0c1112016-08-28 20:53:09 +0100495 { 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 +0100496 { 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 +0000497 { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
Simon Kelleyee415862014-02-11 11:07:22 +0000498 { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
Simon Kelley5b3bf922014-01-25 17:03:07 +0000499 { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
Simon Kelleya6918532018-04-15 16:20:52 +0100500 { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
Simon Kelleye98bd522014-03-28 20:41:23 +0000501 { 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 +0000502 { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000503#ifdef OPTION6_PREFIX_CLASS
504 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
505#endif
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +0100506 { 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 +0100507 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
508 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
509 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000510 { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
511 { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
Glen Huang32fc6db2014-12-27 15:28:12 +0000512 { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
Simon Kelley832e47b2016-02-24 21:24:45 +0000513 { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
Floris Bos503c6092017-04-09 23:07:13 +0100514 { 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 +0000515 { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
Simon Kelley6b173352018-05-08 18:32:14 +0100516 { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
517 { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100518 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000519};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000520
Josh Soref730c6742017-02-06 16:14:04 +0000521/* We hide metacharacters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100522 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 +0100523 following sequence so that they map to themselves: it is therefore possible to call
524 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000525 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100526 couple of other places.
527 Note that space is included here so that
528 --dhcp-option=3, string
529 has five characters, whilst
530 --dhcp-option=3," string"
531 has six.
532*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100533
Simon Kelleyf2621c72007-04-29 19:47:21 +0100534static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100535
536static char hide_meta(char c)
537{
538 unsigned int i;
539
540 for (i = 0; i < (sizeof(meta) - 1); i++)
541 if (c == meta[i])
542 return (char)i;
543
544 return c;
545}
546
547static char unhide_meta(char cr)
548{
549 unsigned int c = cr;
550
551 if (c < (sizeof(meta) - 1))
552 cr = meta[c];
553
554 return cr;
555}
556
557static void unhide_metas(char *cp)
558{
559 if (cp)
560 for(; *cp; cp++)
561 *cp = unhide_meta(*cp);
562}
563
Simon Kelley824af852008-02-12 20:43:05 +0000564static void *opt_malloc(size_t size)
565{
566 void *ret;
567
568 if (mem_recover)
569 {
570 ret = whine_malloc(size);
571 if (!ret)
572 longjmp(mem_jmp, 1);
573 }
574 else
575 ret = safe_malloc(size);
576
577 return ret;
578}
579
580static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100581{
582 char *ret = NULL;
583
584 if (cp && strlen(cp) != 0)
585 {
Simon Kelley824af852008-02-12 20:43:05 +0000586 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100587 strcpy(ret, cp);
588
589 /* restore hidden metachars */
590 unhide_metas(ret);
591 }
592
593 return ret;
594}
595
Simon Kelley3d8df262005-08-29 12:19:27 +0100596
Simon Kelleyf2621c72007-04-29 19:47:21 +0100597/* find next comma, split string with zero and eliminate spaces.
598 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000599
600static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100601{
602 char *comma, *p;
603
Simon Kelley73a08a22009-02-05 20:28:08 +0000604 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100605 return NULL;
606
607 p = comma;
608 *comma = ' ';
609
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100610 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100611
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100612 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100613 *p = 0;
614
615 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100616}
617
Simon Kelley73a08a22009-02-05 20:28:08 +0000618static char *split(char *s)
619{
620 return split_chr(s, ',');
621}
622
Simon Kelley1f15b812009-10-13 17:49:32 +0100623static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100624{
Simon Kelley1f15b812009-10-13 17:49:32 +0100625 char *ret;
626 int nomem;
627
Simon Kelley3d8df262005-08-29 12:19:27 +0100628 if (!s)
629 return 0;
630
631 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100632 if (!(ret = canonicalise(s, &nomem)) && nomem)
633 {
634 if (mem_recover)
635 longjmp(mem_jmp, 1);
636 else
637 die(_("could not get memory"), NULL, EC_NOMEM);
638 }
639
640 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100641}
642
643static int atoi_check(char *a, int *res)
644{
645 char *p;
646
647 if (!a)
648 return 0;
649
650 unhide_metas(a);
651
652 for (p = a; *p; p++)
653 if (*p < '0' || *p > '9')
654 return 0;
655
656 *res = atoi(a);
657 return 1;
658}
659
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100660static int atoi_check16(char *a, int *res)
661{
662 if (!(atoi_check(a, res)) ||
663 *res < 0 ||
664 *res > 0xffff)
665 return 0;
666
667 return 1;
668}
Simon Kelleyee415862014-02-11 11:07:22 +0000669
Simon Kelleyde73a492014-02-17 21:43:27 +0000670#ifdef HAVE_DNSSEC
Simon Kelleyee415862014-02-11 11:07:22 +0000671static int atoi_check8(char *a, int *res)
672{
673 if (!(atoi_check(a, res)) ||
674 *res < 0 ||
675 *res > 0xff)
676 return 0;
677
678 return 1;
679}
Simon Kelleyde73a492014-02-17 21:43:27 +0000680#endif
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100681
682#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +0000683static void add_txt(char *name, char *txt, int stat)
Simon Kelley0a852542005-03-23 20:28:59 +0000684{
Simon Kelley824af852008-02-12 20:43:05 +0000685 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelleyfec216d2014-03-27 20:54:34 +0000686
687 if (txt)
688 {
689 size_t len = strlen(txt);
690 r->txt = opt_malloc(len+1);
691 r->len = len+1;
692 *(r->txt) = len;
693 memcpy((r->txt)+1, txt, len);
694 }
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100695
Simon Kelleyfec216d2014-03-27 20:54:34 +0000696 r->stat = stat;
Simon Kelley824af852008-02-12 20:43:05 +0000697 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000698 r->next = daemon->txt;
699 daemon->txt = r;
700 r->class = C_CHAOS;
Simon Kelley0a852542005-03-23 20:28:59 +0000701}
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +0100702#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000703
Simon Kelley849a8352006-06-09 21:02:31 +0100704static void do_usage(void)
705{
706 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000707 int i, j;
708
709 struct {
710 char handle;
711 int val;
712 } tab[] = {
713 { '$', CACHESIZ },
714 { '*', EDNS_PKTSZ },
715 { '&', MAXLEASES },
716 { '!', FTABSIZ },
717 { '#', TFTP_MAX_CONNECTIONS },
718 { '\0', 0 }
719 };
Simon Kelley849a8352006-06-09 21:02:31 +0100720
721 printf(_("Usage: dnsmasq [options]\n\n"));
722#ifndef HAVE_GETOPT_LONG
723 printf(_("Use short options only on the command line.\n"));
724#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100725 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100726
Simon Kelley1a6bca82008-07-11 11:11:42 +0100727 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100728 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100729 char *desc = usage[i].flagdesc;
730 char *eq = "=";
731
732 if (!desc || *desc == '[')
733 eq = "";
734
735 if (!desc)
736 desc = "";
737
738 for ( j = 0; opts[j].name; j++)
739 if (opts[j].val == usage[i].opt)
740 break;
741 if (usage[i].opt < 256)
742 sprintf(buff, "-%c, ", usage[i].opt);
743 else
744 sprintf(buff, " ");
745
746 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Peter Wu3c0c1112016-08-28 20:53:09 +0100747 printf("%-55.55s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100748
Simon Kelley849a8352006-06-09 21:02:31 +0100749 if (usage[i].arg)
750 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000751 strcpy(buff, usage[i].arg);
752 for (j = 0; tab[j].handle; j++)
753 if (tab[j].handle == *(usage[i].arg))
754 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100755 }
Simon Kelley849a8352006-06-09 21:02:31 +0100756 printf(_(usage[i].desc), buff);
757 printf("\n");
758 }
759}
760
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100761#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
762
Ed Bardsleya7369be2015-08-05 21:17:18 +0100763static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
764{
765 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
766 addr->sa.sa_family = AF_INET;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100767 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
768 addr->sa.sa_family = AF_INET6;
Ed Bardsleya7369be2015-08-05 21:17:18 +0100769 else
770 return _("bad address");
771
772 return NULL;
773}
774
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100775char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
776{
777 int source_port = 0, serv_port = NAMESERVER_PORT;
778 char *portno, *source;
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000779 char *interface_opt = NULL;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100780 int scope_index = 0;
781 char *scope_id;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100782
Simon Kelleyd68c2ca2014-02-18 22:30:30 +0000783 if (!arg || strlen(arg) == 0)
784 {
785 *flags |= SERV_NO_ADDR;
786 *interface = 0;
787 return NULL;
788 }
789
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100790 if ((source = split_chr(arg, '@')) && /* is there a source. */
791 (portno = split_chr(source, '#')) &&
792 !atoi_check16(portno, &source_port))
793 return _("bad port");
794
795 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
796 !atoi_check16(portno, &serv_port))
797 return _("bad port");
798
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100799 scope_id = split_chr(arg, '%');
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100800
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000801 if (source) {
802 interface_opt = split_chr(source, '@');
803
804 if (interface_opt)
805 {
806#if defined(SO_BINDTODEVICE)
Petr Menšík47b45b22018-08-15 18:17:00 +0200807 safe_strncpy(interface, interface_opt, IF_NAMESIZE);
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000808#else
809 return _("interface binding not supported");
810#endif
811 }
812 }
813
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100814 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100815 {
816 addr->in.sin_port = htons(serv_port);
817 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
818#ifdef HAVE_SOCKADDR_SA_LEN
819 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
820#endif
821 source_addr->in.sin_addr.s_addr = INADDR_ANY;
822 source_addr->in.sin_port = htons(daemon->query_port);
823
824 if (source)
825 {
826 if (flags)
827 *flags |= SERV_HAS_SOURCE;
828 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100829 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100830 {
831#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000832 if (interface_opt)
833 return _("interface can only be specified once");
834
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100835 source_addr->in.sin_addr.s_addr = INADDR_ANY;
Petr Menšík47b45b22018-08-15 18:17:00 +0200836 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100837#else
838 return _("interface binding not supported");
839#endif
840 }
841 }
842 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100843 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
844 {
845 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
846 return _("bad interface name");
847
848 addr->in6.sin6_port = htons(serv_port);
849 addr->in6.sin6_scope_id = scope_index;
850 source_addr->in6.sin6_addr = in6addr_any;
851 source_addr->in6.sin6_port = htons(daemon->query_port);
852 source_addr->in6.sin6_scope_id = 0;
853 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
854 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
855#ifdef HAVE_SOCKADDR_SA_LEN
856 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
857#endif
858 if (source)
859 {
860 if (flags)
861 *flags |= SERV_HAS_SOURCE;
862 source_addr->in6.sin6_port = htons(source_port);
863 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
864 {
865#if defined(SO_BINDTODEVICE)
Kristian Evensen4e7694d2017-03-22 21:32:50 +0000866 if (interface_opt)
867 return _("interface can only be specified once");
868
869 source_addr->in6.sin6_addr = in6addr_any;
Petr Menšík47b45b22018-08-15 18:17:00 +0200870 safe_strncpy(interface, source, IF_NAMESIZE);
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100871#else
872 return _("interface binding not supported");
873#endif
874 }
875 }
876 }
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100877 else
878 return _("bad address");
879
880 return NULL;
881}
882
Simon Kelleyde73a492014-02-17 21:43:27 +0000883static struct server *add_rev4(struct in_addr addr, int msize)
884{
885 struct server *serv = opt_malloc(sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000886 in_addr_t a = ntohl(addr.s_addr);
Simon Kelleyde73a492014-02-17 21:43:27 +0000887 char *p;
888
889 memset(serv, 0, sizeof(struct server));
Olivier Gayot916959c2017-03-06 22:14:50 +0000890 p = serv->domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
891
892 switch (msize)
893 {
894 case 32:
Rosen Penevcbd29e52017-06-27 22:29:51 +0100895 p += sprintf(p, "%u.", a & 0xff);
Olivier Gayot916959c2017-03-06 22:14:50 +0000896 /* fall through */
897 case 24:
898 p += sprintf(p, "%d.", (a >> 8) & 0xff);
899 /* fall through */
Olivier Gayot916959c2017-03-06 22:14:50 +0000900 case 16:
901 p += sprintf(p, "%d.", (a >> 16) & 0xff);
902 /* fall through */
903 case 8:
904 p += sprintf(p, "%d.", (a >> 24) & 0xff);
905 break;
Olivier Gayotdc990582017-03-06 22:17:21 +0000906 default:
907 return NULL;
Olivier Gayot916959c2017-03-06 22:14:50 +0000908 }
909
910 p += sprintf(p, "in-addr.arpa");
Simon Kelleyde73a492014-02-17 21:43:27 +0000911
912 serv->flags = SERV_HAS_DOMAIN;
913 serv->next = daemon->servers;
914 daemon->servers = serv;
915
916 return serv;
917
918}
919
920static struct server *add_rev6(struct in6_addr *addr, int msize)
921{
922 struct server *serv = opt_malloc(sizeof(struct server));
923 char *p;
924 int i;
925
926 memset(serv, 0, sizeof(struct server));
927 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
928
929 for (i = msize-1; i >= 0; i -= 4)
930 {
931 int dig = ((unsigned char *)addr)[i>>3];
932 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
933 }
934 p += sprintf(p, "ip6.arpa");
935
936 serv->flags = SERV_HAS_DOMAIN;
937 serv->next = daemon->servers;
938 daemon->servers = serv;
939
940 return serv;
941}
942
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000943#ifdef HAVE_DHCP
944
945static int is_tag_prefix(char *arg)
946{
947 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
948 return 1;
949
950 return 0;
951}
952
953static char *set_prefix(char *arg)
954{
955 if (strstr(arg, "set:") == arg)
956 return arg+4;
957
958 return arg;
959}
960
Simon Kelley832af0b2007-01-21 20:01:28 +0000961/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100962static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000963{
Simon Kelley824af852008-02-12 20:43:05 +0000964 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000965 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000966 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100967 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100968 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000969 u16 opt_len = 0;
970 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100971 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000972
973 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000974 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000975 new->netid = NULL;
976 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100977 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000978
Simon Kelleyf2621c72007-04-29 19:47:21 +0100979 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000980 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100981 comma = split(arg);
982
983 for (cp = arg; *cp; cp++)
984 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000985 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100986
987 if (!*cp)
988 {
989 new->opt = atoi(arg);
990 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100991 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100992 break;
993 }
994
995 if (strstr(arg, "option:") == arg)
996 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100997 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
998 {
999 opt_len = lookup_dhcp_len(AF_INET, new->opt);
1000 /* option:<optname> must follow tag and vendor string. */
1001 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1002 option_ok = 1;
1003 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001004 break;
1005 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001006#ifdef HAVE_DHCP6
1007 else if (strstr(arg, "option6:") == arg)
1008 {
1009 for (cp = arg+8; *cp; cp++)
1010 if (*cp < '0' || *cp > '9')
1011 break;
1012
1013 if (!*cp)
1014 {
1015 new->opt = atoi(arg+8);
1016 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +01001017 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001018 }
1019 else
Simon Kelley40ef23b2012-03-13 21:59:28 +00001020 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001021 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
1022 {
1023 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
1024 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
1025 option_ok = 1;
1026 }
Simon Kelley40ef23b2012-03-13 21:59:28 +00001027 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001028 /* option6:<opt>|<optname> must follow tag and vendor string. */
1029 is6 = 1;
1030 break;
1031 }
1032#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001033 else if (strstr(arg, "vendor:") == arg)
1034 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001035 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
1036 new->flags |= DHOPT_VENDOR;
1037 }
1038 else if (strstr(arg, "encap:") == arg)
1039 {
1040 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001041 new->flags |= DHOPT_ENCAPSULATE;
1042 }
Simon Kelley316e2732010-01-22 20:16:09 +00001043 else if (strstr(arg, "vi-encap:") == arg)
1044 {
1045 new->u.encap = atoi(arg+9);
1046 new->flags |= DHOPT_RFC3925;
1047 if (flags == DHOPT_MATCH)
1048 {
Simon Kelleybd08ae62013-04-19 10:22:06 +01001049 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001050 break;
1051 }
1052 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001053 else
1054 {
Simon Kelley824af852008-02-12 20:43:05 +00001055 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001056 /* allow optional "net:" or "tag:" for consistency */
1057 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +00001058 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001059 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001060 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001061 new->netid->next = np;
1062 np = new->netid;
1063 }
1064
1065 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00001066 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001067
1068#ifdef HAVE_DHCP6
1069 if (is6)
1070 {
1071 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001072 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001073
1074 if (opt_len == 0 &&
1075 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001076 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001077 }
1078 else
1079#endif
1080 if (opt_len == 0 &&
1081 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +01001082 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +00001083
Simon Kelley316e2732010-01-22 20:16:09 +00001084 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +01001085 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001086 ret_err(_("bad dhcp-option"));
1087
1088 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +00001089 {
1090 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001091 char c;
Simon Kelley28866e92011-02-14 20:19:14 +00001092 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001093 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001094 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001095 dots = 0;
1096 for (cp = comma; (c = *cp); cp++)
1097 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +00001098 {
1099 addrs++;
1100 is_dec = is_hex = 0;
1101 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001102 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +00001103 {
1104 digs++;
1105 is_dec = is_addr = 0;
1106 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001107 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +00001108 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001109 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001110 if (cp == comma) /* leading / means a pathname */
1111 is_addr = 0;
1112 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001113 else if (c == '.')
1114 {
Simon Kelley23245c02012-07-18 16:21:11 +01001115 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001116 dots++;
1117 }
1118 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +00001119 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001120 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +00001121 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001122 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001123 {
1124 is_addr = 0;
1125 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +01001126 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +00001127 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001128 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +00001129 *cp = 0;
1130 }
1131 else
1132 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001133 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001134 (c >='a' && c <= 'f') ||
1135 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001136 {
1137 is_hex = 0;
1138 if (c != '[' && c != ']')
1139 is_addr6 = 0;
1140 }
Simon Kelley832af0b2007-01-21 20:01:28 +00001141 }
Simon Kelley28866e92011-02-14 20:19:14 +00001142 else
1143 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001144
Simon Kelley28866e92011-02-14 20:19:14 +00001145 if (!found_dig)
1146 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001147
Simon Kelleyf2621c72007-04-29 19:47:21 +01001148 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +01001149 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001150 {
1151 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001152
1153 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001154 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001155
1156 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001157 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001158 }
Simon Kelley28866e92011-02-14 20:19:14 +00001159 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001160 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
1161 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +01001162
1163 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
1164 {
1165 int val, fac = 1;
1166
1167 switch (comma[strlen(comma) - 1])
1168 {
Simon Kelley42243212012-07-20 15:19:18 +01001169 case 'w':
1170 case 'W':
1171 fac *= 7;
1172 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001173 case 'd':
1174 case 'D':
1175 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00001176 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +01001177 case 'h':
1178 case 'H':
1179 fac *= 60;
1180 /* fall through */
1181 case 'm':
1182 case 'M':
1183 fac *= 60;
1184 /* fall through */
1185 case 's':
1186 case 'S':
1187 comma[strlen(comma) - 1] = 0;
1188 }
1189
1190 new->len = 4;
1191 new->val = opt_malloc(4);
1192 val = atoi(comma);
1193 *((int *)new->val) = htonl(val * fac);
1194 }
1195 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001196 {
1197 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001198 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001199 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1200 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001201 }
1202 else if (is_dec)
1203 {
1204 int i, val = atoi(comma);
1205 /* assume numeric arg is 1 byte except for
1206 options where it is known otherwise.
1207 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001208 if (opt_len != 0)
1209 new->len = opt_len;
1210 else if (val & 0xffff0000)
1211 new->len = 4;
1212 else if (val & 0xff00)
1213 new->len = 2;
1214 else
1215 new->len = 1;
1216
Simon Kelley832af0b2007-01-21 20:01:28 +00001217 if (lenchar == 'b')
1218 new->len = 1;
1219 else if (lenchar == 's')
1220 new->len = 2;
1221 else if (lenchar == 'i')
1222 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001223
Simon Kelley824af852008-02-12 20:43:05 +00001224 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001225 for (i=0; i<new->len; i++)
1226 new->val[i] = val>>((new->len - i - 1)*8);
1227 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001228 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001229 {
1230 struct in_addr in;
1231 unsigned char *op;
1232 char *slash;
1233 /* max length of address/subnet descriptor is five bytes,
1234 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001235 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001236 new->flags |= DHOPT_ADDR;
1237
Simon Kelley572b41e2011-02-18 18:11:18 +00001238 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1239 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001240 {
Simon Kelley6b010842007-02-12 20:32:07 +00001241 *(op++) = 1; /* RFC 3361 "enc byte" */
1242 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001243 }
1244 while (addrs--)
1245 {
1246 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001247 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001248 slash = split_chr(cp, '/');
Simon Kelleya2bc2542016-04-21 22:34:22 +01001249 if (!inet_pton(AF_INET, cp, &in))
1250 ret_err(_("bad IPv4 address"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001251 if (!slash)
1252 {
1253 memcpy(op, &in, INADDRSZ);
1254 op += INADDRSZ;
1255 }
1256 else
1257 {
1258 unsigned char *p = (unsigned char *)&in;
1259 int netsize = atoi(slash);
1260 *op++ = netsize;
1261 if (netsize > 0)
1262 *op++ = *p++;
1263 if (netsize > 8)
1264 *op++ = *p++;
1265 if (netsize > 16)
1266 *op++ = *p++;
1267 if (netsize > 24)
1268 *op++ = *p++;
1269 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1270 }
1271 }
1272 new->len = op - new->val;
1273 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001274 else if (is_addr6 && is6)
1275 {
1276 unsigned char *op;
1277 new->val = op = opt_malloc(16 * addrs);
1278 new->flags |= DHOPT_ADDR6;
1279 while (addrs--)
1280 {
1281 cp = comma;
1282 comma = split(cp);
1283
1284 /* check for [1234::7] */
1285 if (*cp == '[')
1286 cp++;
1287 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1288 cp[strlen(cp)-1] = 0;
1289
1290 if (inet_pton(AF_INET6, cp, op))
1291 {
1292 op += IN6ADDRSZ;
1293 continue;
1294 }
1295
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001296 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001297 }
1298 new->len = op - new->val;
1299 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001300 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001301 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001302 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001303 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001304 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001305 {
1306 /* dns search, RFC 3397, or SIP, RFC 3361 */
1307 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001308 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001309 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001310 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001311
1312 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001313 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001314
1315 while (arg && *arg)
1316 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001317 char *in, *dom = NULL;
1318 size_t domlen = 1;
1319 /* Allow "." as an empty domain */
1320 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001321 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001322 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001323 ret_err(_("bad domain in dhcp-option"));
1324
Simon Kelleyc52e1892010-06-07 22:01:39 +01001325 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001326 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001327
1328 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001329 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001330 {
1331 memcpy(newp, m, header_size + len);
1332 free(m);
1333 }
Simon Kelley824af852008-02-12 20:43:05 +00001334 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001335 p = m + header_size;
1336 q = p + len;
1337
1338 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001339 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001340 {
1341 unsigned char *cp = q++;
1342 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001343 for (j = 0; *in && (*in != '.'); in++, j++)
1344 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001345 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001346 if (*in)
1347 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001348 }
1349 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001350 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001351
Simon Kelley832af0b2007-01-21 20:01:28 +00001352 /* Now tail-compress using earlier names. */
1353 newlen = q - p;
1354 for (tail = p + len; *tail; tail += (*tail) + 1)
1355 for (r = p; r - p < (int)len; r += (*r) + 1)
1356 if (strcmp((char *)r, (char *)tail) == 0)
1357 {
1358 PUTSHORT((r - p) | 0xc000, tail);
1359 newlen = tail - p;
1360 goto end;
1361 }
1362 end:
1363 len = newlen;
1364
1365 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001366 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001367 }
1368
1369 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001370 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001371 m[0] = 0;
1372 new->len = (int) len + header_size;
1373 new->val = m;
1374 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001375#ifdef HAVE_DHCP6
1376 else if (comma && (opt_len & OT_CSTRING))
1377 {
1378 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001379 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001380 unsigned char *p, *newp;
1381
Simon Kelley40ef23b2012-03-13 21:59:28 +00001382 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001383 if (comma[i] == ',')
1384 commas++;
1385
1386 newp = opt_malloc(strlen(comma)+(2*commas));
1387 p = newp;
1388 arg = comma;
1389 comma = split(arg);
1390
1391 while (arg && *arg)
1392 {
1393 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001394 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001395 PUTSHORT(len, p);
1396 memcpy(p, arg, len);
1397 p += len;
1398
1399 arg = comma;
1400 comma = split(arg);
1401 }
1402
1403 new->val = newp;
1404 new->len = p - newp;
1405 }
1406 else if (comma && (opt_len & OT_RFC1035_NAME))
1407 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001408 unsigned char *p = NULL, *newp, *end;
1409 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001410 arg = comma;
1411 comma = split(arg);
1412
1413 while (arg && *arg)
1414 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001415 char *dom = canonicalise_opt(arg);
1416 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001417 ret_err(_("bad domain in dhcp-option"));
1418
Simon Kelley18f0fb02012-03-31 21:18:55 +01001419 newp = opt_malloc(len + strlen(dom) + 2);
1420
1421 if (p)
1422 {
1423 memcpy(newp, p, len);
1424 free(p);
1425 }
1426
1427 p = newp;
Simon Kelley0549c732017-09-25 18:17:11 +01001428 end = do_rfc1035_name(p + len, dom, NULL);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001429 *end++ = 0;
1430 len = end - p;
1431 free(dom);
1432
Simon Kelley4cb1b322012-02-06 14:30:41 +00001433 arg = comma;
1434 comma = split(arg);
1435 }
1436
Simon Kelley18f0fb02012-03-31 21:18:55 +01001437 new->val = p;
1438 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001439 }
1440#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001441 else
1442 {
1443 new->len = strlen(comma);
1444 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001445 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001446 new->flags |= DHOPT_STRING;
1447 }
1448 }
1449 }
1450
Simon Kelley4cb1b322012-02-06 14:30:41 +00001451 if (!is6 &&
1452 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001453 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001454 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001455 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001456
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001457 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001458 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001459 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1460 !new->netid ||
1461 new->netid->next)
1462 ret_err(_("illegal dhcp-match"));
1463
1464 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001465 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001466 new->next = daemon->dhcp_match6;
1467 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001468 }
1469 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001470 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001471 new->next = daemon->dhcp_match;
1472 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001473 }
Simon Kelley824af852008-02-12 20:43:05 +00001474 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001475 else if (is6)
1476 {
1477 new->next = daemon->dhcp_opts6;
1478 daemon->dhcp_opts6 = new;
1479 }
1480 else
1481 {
1482 new->next = daemon->dhcp_opts;
1483 daemon->dhcp_opts = new;
1484 }
1485
1486 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001487}
1488
Simon Kelley7622fc02009-06-04 20:32:05 +01001489#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001490
Simon Kelley28866e92011-02-14 20:19:14 +00001491void set_option_bool(unsigned int opt)
1492{
Petr Menšík24b87602018-10-24 22:30:18 +01001493 option_var(opt) |= option_val(opt);
Simon Kelley28866e92011-02-14 20:19:14 +00001494}
1495
Simon Kelley2b5bae92012-06-26 16:55:23 +01001496void reset_option_bool(unsigned int opt)
1497{
Petr Menšík24b87602018-10-24 22:30:18 +01001498 option_var(opt) &= ~(option_val(opt));
Simon Kelley2b5bae92012-06-26 16:55:23 +01001499}
1500
Simon Kelley7b1eae42014-02-20 13:43:28 +00001501static 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 +01001502{
1503 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001504 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001505
Simon Kelley832af0b2007-01-21 20:01:28 +00001506 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001507 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001508
Simon Kelley1a6bca82008-07-11 11:11:42 +01001509 for (i=0; usage[i].opt != 0; i++)
1510 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001511 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001512 int rept = usage[i].rept;
1513
Simon Kelley28866e92011-02-14 20:19:14 +00001514 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001515 {
1516 /* command line */
1517 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001518 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001519 if (rept == ARG_ONE)
1520 usage[i].rept = ARG_USED_CL;
1521 }
1522 else
1523 {
1524 /* allow file to override command line */
1525 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001526 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001527 if (rept == ARG_USED_CL || rept == ARG_ONE)
1528 usage[i].rept = ARG_USED_FILE;
1529 }
1530
1531 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1532 {
Simon Kelley28866e92011-02-14 20:19:14 +00001533 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001534 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001535 }
1536
1537 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001538 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001539
Simon Kelley849a8352006-06-09 21:02:31 +01001540 switch (option)
1541 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001542 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001543 {
Simon Kelley824af852008-02-12 20:43:05 +00001544 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001545 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001546 {
Simon Kelley28866e92011-02-14 20:19:14 +00001547 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001548 free(file);
1549 }
Simon Kelley849a8352006-06-09 21:02:31 +01001550 break;
1551 }
1552
Simon Kelleyf2621c72007-04-29 19:47:21 +01001553 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001554 {
1555 DIR *dir_stream;
1556 struct dirent *ent;
1557 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001558 struct list {
1559 char *suffix;
1560 struct list *next;
Simon Kelley3e1551a2014-09-09 21:46:07 +01001561 } *ignore_suffix = NULL, *match_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001562
Simon Kelley1f15b812009-10-13 17:49:32 +01001563 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001564 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001565 break;
1566
Simon Kelley1f15b812009-10-13 17:49:32 +01001567 for (arg = comma; arg; arg = comma)
1568 {
1569 comma = split(arg);
Simon Kelley00cd9d52014-10-02 21:44:21 +01001570 if (strlen(arg) != 0)
Simon Kelley3e1551a2014-09-09 21:46:07 +01001571 {
Simon Kelley00cd9d52014-10-02 21:44:21 +01001572 li = opt_malloc(sizeof(struct list));
1573 if (*arg == '*')
1574 {
Simon Kelley0007ee92015-11-21 21:47:41 +00001575 /* "*" with no suffix is a no-op */
1576 if (arg[1] == 0)
1577 free(li);
1578 else
1579 {
1580 li->next = match_suffix;
1581 match_suffix = li;
1582 /* Have to copy: buffer is overwritten */
1583 li->suffix = opt_string_alloc(arg+1);
1584 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001585 }
1586 else
1587 {
1588 li->next = ignore_suffix;
1589 ignore_suffix = li;
1590 /* Have to copy: buffer is overwritten */
1591 li->suffix = opt_string_alloc(arg);
1592 }
Simon Kelley3e1551a2014-09-09 21:46:07 +01001593 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001594 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001595
Simon Kelley849a8352006-06-09 21:02:31 +01001596 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001597 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001598
Simon Kelley849a8352006-06-09 21:02:31 +01001599 while ((ent = readdir(dir_stream)))
1600 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001601 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001602 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001603
1604 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001605 if (len == 0 ||
1606 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001607 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1608 ent->d_name[0] == '.')
1609 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001610
Simon Kelley3e1551a2014-09-09 21:46:07 +01001611 if (match_suffix)
1612 {
1613 for (li = match_suffix; li; li = li->next)
1614 {
1615 /* check for required suffices */
1616 size_t ls = strlen(li->suffix);
1617 if (len > ls &&
1618 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1619 break;
1620 }
1621 if (!li)
1622 continue;
1623 }
1624
Simon Kelley1f15b812009-10-13 17:49:32 +01001625 for (li = ignore_suffix; li; li = li->next)
1626 {
1627 /* check for proscribed suffices */
1628 size_t ls = strlen(li->suffix);
1629 if (len > ls &&
1630 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1631 break;
1632 }
1633 if (li)
1634 continue;
1635
Simon Kelley824af852008-02-12 20:43:05 +00001636 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001637 strcpy(path, directory);
1638 strcat(path, "/");
1639 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001640
Simon Kelley39595cf2013-02-04 21:40:07 +00001641 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001642 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001643 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001644
Simon Kelley39595cf2013-02-04 21:40:07 +00001645 /* only reg files allowed. */
1646 if (S_ISREG(buf.st_mode))
1647 one_file(path, 0);
1648
Simon Kelley849a8352006-06-09 21:02:31 +01001649 free(path);
1650 }
1651
1652 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001653 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001654 for(; ignore_suffix; ignore_suffix = li)
1655 {
1656 li = ignore_suffix->next;
1657 free(ignore_suffix->suffix);
1658 free(ignore_suffix);
1659 }
Simon Kelley00cd9d52014-10-02 21:44:21 +01001660 for(; match_suffix; match_suffix = li)
1661 {
1662 li = match_suffix->next;
1663 free(match_suffix->suffix);
1664 free(match_suffix);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001665 }
Simon Kelley849a8352006-06-09 21:02:31 +01001666 break;
1667 }
1668
Simon Kelleyed4c0762013-10-08 20:46:34 +01001669 case LOPT_ADD_SBNET: /* --add-subnet */
1670 set_option_bool(OPT_CLIENT_SUBNET);
1671 if (arg)
1672 {
Ed Bardsleya7369be2015-08-05 21:17:18 +01001673 char *err, *end;
Simon Kelleyed4c0762013-10-08 20:46:34 +01001674 comma = split(arg);
Ed Bardsleya7369be2015-08-05 21:17:18 +01001675
1676 struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
1677 if ((end = split_chr(arg, '/')))
1678 {
1679 /* has subnet+len */
1680 err = parse_mysockaddr(arg, &new->addr);
1681 if (err)
1682 ret_err(err);
1683 if (!atoi_check(end, &new->mask))
1684 ret_err(gen_err);
1685 new->addr_used = 1;
1686 }
1687 else if (!atoi_check(arg, &new->mask))
1688 ret_err(gen_err);
1689
1690 daemon->add_subnet4 = new;
1691
Ed Bardsleya7369be2015-08-05 21:17:18 +01001692 if (comma)
1693 {
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001694 new = opt_malloc(sizeof(struct mysubnet));
1695 if ((end = split_chr(comma, '/')))
1696 {
1697 /* has subnet+len */
Ed Bardsleya7369be2015-08-05 21:17:18 +01001698 err = parse_mysockaddr(comma, &new->addr);
1699 if (err)
1700 ret_err(err);
1701 if (!atoi_check(end, &new->mask))
1702 ret_err(gen_err);
1703 new->addr_used = 1;
1704 }
1705 else
1706 {
1707 if (!atoi_check(comma, &new->mask))
1708 ret_err(gen_err);
1709 }
Simon Kelley22fe2fd2016-02-28 17:07:10 +00001710
1711 daemon->add_subnet6 = new;
1712 }
Simon Kelleyed4c0762013-10-08 20:46:34 +01001713 }
1714 break;
1715
Simon Kelleyad094272012-08-10 17:10:54 +01001716 case '1': /* --enable-dbus */
1717 set_option_bool(OPT_DBUS);
1718 if (arg)
1719 daemon->dbus_name = opt_string_alloc(arg);
1720 else
1721 daemon->dbus_name = DNSMASQ_SERVICE;
1722 break;
1723
Simon Kelleyf2621c72007-04-29 19:47:21 +01001724 case '8': /* --log-facility */
1725 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001726 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001727 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001728 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001729 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001730#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001731 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001732#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001733 for (i = 0; facilitynames[i].c_name; i++)
1734 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1735 break;
1736
1737 if (facilitynames[i].c_name)
1738 daemon->log_fac = facilitynames[i].c_val;
1739 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001740 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001741#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001742 }
1743 break;
Julian Kornberger8dcdb332018-07-21 22:11:08 +01001744
Simon Kelleyf2621c72007-04-29 19:47:21 +01001745 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001746 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001747 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001748
Simon Kelleyf2621c72007-04-29 19:47:21 +01001749 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001750 {
Simon Kelley824af852008-02-12 20:43:05 +00001751 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001752 struct resolvc *new, *list = daemon->resolv_files;
1753
1754 if (list && list->is_default)
1755 {
1756 /* replace default resolv file - possibly with nothing */
1757 if (name)
1758 {
1759 list->is_default = 0;
1760 list->name = name;
1761 }
1762 else
1763 list = NULL;
1764 }
1765 else if (name)
1766 {
Simon Kelley824af852008-02-12 20:43:05 +00001767 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001768 new->next = list;
1769 new->name = name;
1770 new->is_default = 0;
1771 new->mtime = 0;
1772 new->logged = 0;
1773 list = new;
1774 }
1775 daemon->resolv_files = list;
1776 break;
1777 }
Simon Kelley7b1eae42014-02-20 13:43:28 +00001778
1779 case LOPT_SERVERS_FILE:
1780 daemon->servers_file = opt_string_alloc(arg);
1781 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001782
Simon Kelleyf2621c72007-04-29 19:47:21 +01001783 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001784 {
1785 int pref = 1;
1786 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001787 char *name, *target = NULL;
1788
Simon Kelleyf2621c72007-04-29 19:47:21 +01001789 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001790 {
1791 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001792 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001793 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001794 }
1795
Simon Kelley1f15b812009-10-13 17:49:32 +01001796 if (!(name = canonicalise_opt(arg)) ||
1797 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001798 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001799
Simon Kelley824af852008-02-12 20:43:05 +00001800 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001801 new->next = daemon->mxnames;
1802 daemon->mxnames = new;
1803 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001804 new->name = name;
1805 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001806 new->weight = pref;
1807 break;
1808 }
1809
Simon Kelleyf2621c72007-04-29 19:47:21 +01001810 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001811 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001812 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001813 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001814
Simon Kelley6b173352018-05-08 18:32:14 +01001815 case LOPT_DUMPFILE: /* --dumpfile */
1816 daemon->dump_file = opt_string_alloc(arg);
1817 break;
1818
1819 case LOPT_DUMPMASK: /* --dumpmask */
1820 daemon->dump_mask = strtol(arg, NULL, 0);
1821 break;
1822
Simon Kelley7622fc02009-06-04 20:32:05 +01001823#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001824 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001825 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001826 break;
1827
Simon Kelleyc72daea2012-01-05 21:33:27 +00001828 /* Sorry about the gross pre-processor abuse */
1829 case '6': /* --dhcp-script */
1830 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley48d12f12018-11-02 21:55:04 +00001831# if !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001832 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001833# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001834 if (option == LOPT_LUASCRIPT)
1835# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001836 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001837# else
1838 daemon->luascript = opt_string_alloc(arg);
1839# endif
1840 else
1841 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001842# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001843 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001844#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001845
Simon Kelley70d18732015-01-31 19:59:29 +00001846 case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
1847 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
1848 case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
1849 case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
1850 case LOPT_HOST_INOTIFY: /* --hostsdir */
1851 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001852 {
Simon Kelley824af852008-02-12 20:43:05 +00001853 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley19c51cf2014-03-18 22:38:30 +00001854 static unsigned int hosts_index = SRC_AH;
Simon Kelley824af852008-02-12 20:43:05 +00001855 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001856 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001857 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001858 if (option == 'H')
1859 {
1860 new->next = daemon->addn_hosts;
1861 daemon->addn_hosts = new;
1862 }
1863 else if (option == LOPT_DHCP_HOST)
1864 {
1865 new->next = daemon->dhcp_hosts_file;
1866 daemon->dhcp_hosts_file = new;
1867 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001868 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001869 {
1870 new->next = daemon->dhcp_opts_file;
1871 daemon->dhcp_opts_file = new;
1872 }
Simon Kelley70d18732015-01-31 19:59:29 +00001873 else
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001874 {
Simon Kelley70d18732015-01-31 19:59:29 +00001875 new->next = daemon->dynamic_dirs;
1876 daemon->dynamic_dirs = new;
1877 if (option == LOPT_DHCP_INOTIFY)
1878 new->flags |= AH_DHCP_HST;
1879 else if (option == LOPT_DHOPT_INOTIFY)
1880 new->flags |= AH_DHCP_OPT;
1881 else if (option == LOPT_HOST_INOTIFY)
1882 new->flags |= AH_HOSTS;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00001883 }
1884
Simon Kelley849a8352006-06-09 21:02:31 +01001885 break;
1886 }
1887
Simon Kelley4f7b3042012-11-28 21:27:02 +00001888 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley08933472018-10-05 16:34:35 +01001889 comma = split(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001890
Simon Kelley4f7b3042012-11-28 21:27:02 +00001891 daemon->authserver = opt_string_alloc(arg);
Simon Kelley08933472018-10-05 16:34:35 +01001892
1893 while ((arg = comma))
1894 {
1895 struct iname *new = opt_malloc(sizeof(struct iname));
1896 comma = split(arg);
1897 new->name = NULL;
1898 unhide_metas(arg);
1899 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
1900 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01001901 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1902 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01001903 else
1904 {
1905 char *fam = split_chr(arg, '/');
1906 new->name = opt_string_alloc(arg);
1907 new->addr.sa.sa_family = 0;
1908 if (fam)
1909 {
1910 if (strcmp(fam, "4") == 0)
1911 new->addr.sa.sa_family = AF_INET;
Simon Kelley08933472018-10-05 16:34:35 +01001912 else if (strcmp(fam, "6") == 0)
1913 new->addr.sa.sa_family = AF_INET6;
Simon Kelley08933472018-10-05 16:34:35 +01001914 else
1915 ret_err(gen_err);
1916 }
1917 }
1918 new->next = daemon->authinterface;
1919 daemon->authinterface = new;
1920 };
Simon Kelley429798f2012-12-10 20:45:53 +00001921
Simon Kelley4f7b3042012-11-28 21:27:02 +00001922 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001923
1924 case LOPT_AUTHSFS: /* --auth-sec-servers */
1925 {
1926 struct name_list *new;
1927
1928 do {
1929 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001930 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001931 new->name = opt_string_alloc(arg);
1932 new->next = daemon->secondary_forward_server;
1933 daemon->secondary_forward_server = new;
1934 arg = comma;
1935 } while (arg);
1936 break;
1937 }
1938
Simon Kelley4f7b3042012-11-28 21:27:02 +00001939 case LOPT_AUTHZONE: /* --auth-zone */
1940 {
1941 struct auth_zone *new;
1942
1943 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001944
Simon Kelley429798f2012-12-10 20:45:53 +00001945 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001946 new->domain = opt_string_alloc(arg);
1947 new->subnet = NULL;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001948 new->exclude = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00001949 new->interface_names = NULL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001950 new->next = daemon->auth_zones;
1951 daemon->auth_zones = new;
1952
1953 while ((arg = comma))
1954 {
1955 int prefixlen = 0;
Mathias Kresin094bfae2016-07-24 14:15:22 +01001956 int is_exclude = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001957 char *prefix;
Simon Kelley376d48c2013-11-13 13:04:30 +00001958 struct addrlist *subnet = NULL;
1959 struct all_addr addr;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001960
1961 comma = split(arg);
1962 prefix = split_chr(arg, '/');
1963
1964 if (prefix && !atoi_check(prefix, &prefixlen))
1965 ret_err(gen_err);
1966
Mathias Kresin094bfae2016-07-24 14:15:22 +01001967 if (strstr(arg, "exclude:") == arg)
1968 {
1969 is_exclude = 1;
1970 arg = arg+8;
1971 }
1972
Simon Kelley376d48c2013-11-13 13:04:30 +00001973 if (inet_pton(AF_INET, arg, &addr.addr.addr4))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001974 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001975 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001976 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001977 subnet->flags = ADDRLIST_LITERAL;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001978 }
Simon Kelley376d48c2013-11-13 13:04:30 +00001979 else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001980 {
Simon Kelley376d48c2013-11-13 13:04:30 +00001981 subnet = opt_malloc(sizeof(struct addrlist));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001982 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
Simon Kelley376d48c2013-11-13 13:04:30 +00001983 subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001984 }
Simon Kelley376d48c2013-11-13 13:04:30 +00001985 else
1986 {
1987 struct auth_name_list *name = opt_malloc(sizeof(struct auth_name_list));
1988 name->name = opt_string_alloc(arg);
1989 name->flags = AUTH4 | AUTH6;
1990 name->next = new->interface_names;
1991 new->interface_names = name;
1992 if (prefix)
1993 {
1994 if (prefixlen == 4)
1995 name->flags &= ~AUTH6;
Simon Kelley376d48c2013-11-13 13:04:30 +00001996 else if (prefixlen == 6)
1997 name->flags &= ~AUTH4;
Simon Kelley376d48c2013-11-13 13:04:30 +00001998 else
1999 ret_err(gen_err);
2000 }
2001 }
2002
2003 if (subnet)
2004 {
2005 subnet->addr = addr;
Mathias Kresin094bfae2016-07-24 14:15:22 +01002006
2007 if (is_exclude)
2008 {
2009 subnet->next = new->exclude;
2010 new->exclude = subnet;
2011 }
2012 else
2013 {
2014 subnet->next = new->subnet;
2015 new->subnet = subnet;
2016 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002017 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00002018 }
2019 break;
2020 }
Simon Kelley376d48c2013-11-13 13:04:30 +00002021
Simon Kelley4f7b3042012-11-28 21:27:02 +00002022 case LOPT_AUTHSOA: /* --auth-soa */
2023 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002024 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002025 if (comma)
2026 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002027 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002028 arg = comma;
2029 comma = split(arg);
2030 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00002031 for (cp = daemon->hostmaster; *cp; cp++)
2032 if (*cp == '@')
2033 *cp = '.';
2034
Simon Kelley4f7b3042012-11-28 21:27:02 +00002035 if (comma)
2036 {
2037 arg = comma;
2038 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002039 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002040 if (comma)
2041 {
2042 arg = comma;
2043 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01002044 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002045 if (comma)
Simon Kelley407a1f32016-03-01 17:06:07 +00002046 daemon->soa_expiry = (u32)atoi(comma);
Simon Kelley4f7b3042012-11-28 21:27:02 +00002047 }
2048 }
2049 }
2050
2051 break;
2052
Simon Kelley2bb73af2013-04-24 17:38:19 +01002053 case 's': /* --domain */
2054 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01002055 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00002056 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01002057 else
Simon Kelley9009d742008-11-14 20:04:27 +00002058 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002059 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00002060 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01002061 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002062 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002063 else
2064 {
Simon Kelley9009d742008-11-14 20:04:27 +00002065 if (comma)
2066 {
Simon Kelley429798f2012-12-10 20:45:53 +00002067 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00002068 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002069
Simon Kelley48fd1c42013-04-25 09:49:38 +01002070 new->prefix = NULL;
Simon Kelley6b2b5642018-03-10 18:12:04 +00002071 new->indexed = 0;
2072
Simon Kelley9009d742008-11-14 20:04:27 +00002073 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00002074 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00002075 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002076 int msize;
2077
Simon Kelley28866e92011-02-14 20:19:14 +00002078 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002079 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002080 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002081 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00002082 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00002083 int mask = (1 << (32 - msize)) - 1;
2084 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00002085 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
2086 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00002087 if (arg)
2088 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002089 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002090 {
2091 if (!(new->prefix = canonicalise_opt(arg)) ||
2092 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2093 ret_err(_("bad prefix"));
2094 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002095 else if (strcmp(arg, "local") != 0 ||
2096 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002097 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00002098 else
2099 {
Simon Kelleyde73a492014-02-17 21:43:27 +00002100 /* generate the equivalent of
Simon Kelleyde73a492014-02-17 21:43:27 +00002101 local=/xxx.yyy.zzz.in-addr.arpa/ */
2102 struct server *serv = add_rev4(new->start, msize);
Olivier Gayotdc990582017-03-06 22:17:21 +00002103 if (!serv)
2104 ret_err(_("bad prefix"));
2105
Simon Kelleyde73a492014-02-17 21:43:27 +00002106 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002107
2108 /* local=/<domain>/ */
2109 serv = opt_malloc(sizeof(struct server));
2110 memset(serv, 0, sizeof(struct server));
2111 serv->domain = d;
2112 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2113 serv->next = daemon->servers;
2114 daemon->servers = serv;
Simon Kelley28866e92011-02-14 20:19:14 +00002115 }
2116 }
Simon Kelley9009d742008-11-14 20:04:27 +00002117 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002118 else if (inet_pton(AF_INET6, comma, &new->start6))
2119 {
2120 u64 mask = (1LLU << (128 - msize)) - 1LLU;
2121 u64 addrpart = addr6part(&new->start6);
2122 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002123
Simon Kelleyd74942a2012-02-07 20:51:56 +00002124 /* prefix==64 overflows the mask calculation above */
2125 if (msize == 64)
2126 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01002127
Simon Kelleyd74942a2012-02-07 20:51:56 +00002128 new->end6 = new->start6;
2129 setaddr6part(&new->start6, addrpart & ~mask);
2130 setaddr6part(&new->end6, addrpart | mask);
2131
2132 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002133 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002134 else if (arg)
2135 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002136 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01002137 {
2138 if (!(new->prefix = canonicalise_opt(arg)) ||
2139 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
2140 ret_err(_("bad prefix"));
2141 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01002142 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002143 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002144 else
2145 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01002146 /* generate the equivalent of
Simon Kelley48fd1c42013-04-25 09:49:38 +01002147 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyde73a492014-02-17 21:43:27 +00002148 struct server *serv = add_rev6(&new->start6, msize);
2149 serv->flags |= SERV_NO_ADDR;
Simon Kelley3ad3f3b2014-12-16 18:25:17 +00002150
2151 /* local=/<domain>/ */
2152 serv = opt_malloc(sizeof(struct server));
2153 memset(serv, 0, sizeof(struct server));
2154 serv->domain = d;
2155 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
2156 serv->next = daemon->servers;
2157 daemon->servers = serv;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002158 }
2159 }
2160 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002161 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002162 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002163 }
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002164 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00002165 {
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002166 char *prefstr;
Simon Kelleyd74942a2012-02-07 20:51:56 +00002167 arg = split(comma);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002168 prefstr = split(arg);
2169
Simon Kelleyd74942a2012-02-07 20:51:56 +00002170 if (inet_pton(AF_INET, comma, &new->start))
2171 {
2172 new->is6 = 0;
2173 if (!arg)
2174 new->end.s_addr = new->start.s_addr;
2175 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002176 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002177 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002178 else if (inet_pton(AF_INET6, comma, &new->start6))
2179 {
2180 new->is6 = 1;
2181 if (!arg)
2182 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2183 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002184 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00002185 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002186 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002187 ret_err(gen_err);
Simon Kelleyeec5c1e2013-10-25 10:37:30 +01002188
2189 if (option != 's' && prefstr)
2190 {
2191 if (!(new->prefix = canonicalise_opt(prefstr)) ||
2192 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2193 ret_err(_("bad prefix"));
2194 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00002195 }
Simon Kelley2307eac2012-02-13 10:13:13 +00002196
2197 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002198 if (option == 's')
2199 {
2200 new->next = daemon->cond_domain;
2201 daemon->cond_domain = new;
2202 }
2203 else
2204 {
Simon Kelley6b2b5642018-03-10 18:12:04 +00002205 char *star;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002206 new->next = daemon->synth_domains;
2207 daemon->synth_domains = new;
Simon Kelleydd33e982018-07-30 14:55:39 +01002208 if (new->prefix &&
2209 (star = strrchr(new->prefix, '*'))
2210 && *(star+1) == 0)
Simon Kelley6b2b5642018-03-10 18:12:04 +00002211 {
2212 *star = 0;
2213 new->indexed = 1;
2214 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002215 }
Simon Kelley9009d742008-11-14 20:04:27 +00002216 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01002217 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00002218 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01002219 else
2220 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00002221 }
2222 }
Simon Kelley849a8352006-06-09 21:02:31 +01002223 break;
2224
Simon Kelley1e505122016-01-25 21:29:23 +00002225 case LOPT_CPE_ID: /* --add-dns-client */
2226 if (arg)
Simon Kelley33702ab2015-12-28 23:17:15 +00002227 daemon->dns_client_id = opt_string_alloc(arg);
2228 break;
2229
Simon Kelleyc7f3bd22016-02-28 21:48:34 +00002230 case LOPT_ADD_MAC: /* --add-mac */
Simon Kelley1e505122016-01-25 21:29:23 +00002231 if (!arg)
2232 set_option_bool(OPT_ADD_MAC);
2233 else
2234 {
2235 unhide_metas(arg);
2236 if (strcmp(arg, "base64") == 0)
2237 set_option_bool(OPT_MAC_B64);
Simon Kelley9e4cf472016-02-17 20:26:32 +00002238 else if (strcmp(arg, "text") == 0)
2239 set_option_bool(OPT_MAC_HEX);
Simon Kelley22c0f4f2016-02-17 22:12:31 +00002240 else
2241 ret_err(gen_err);
Simon Kelley1e505122016-01-25 21:29:23 +00002242 }
2243 break;
2244
Simon Kelleyf2621c72007-04-29 19:47:21 +01002245 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00002246 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002247 break;
2248
Simon Kelleyf2621c72007-04-29 19:47:21 +01002249 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00002250 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002251 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002252 break;
Simon Kelley9e038942008-05-30 20:06:34 +01002253
Simon Kelley7622fc02009-06-04 20:32:05 +01002254#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01002255 case LOPT_SCRIPTUSR: /* --scriptuser */
2256 daemon->scriptuser = opt_string_alloc(arg);
2257 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002258#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002259
Simon Kelleyf2621c72007-04-29 19:47:21 +01002260 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002261 do {
Simon Kelley824af852008-02-12 20:43:05 +00002262 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002263 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002264 new->next = daemon->if_names;
2265 daemon->if_names = new;
2266 /* new->name may be NULL if someone does
2267 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00002268 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002269 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002270 arg = comma;
2271 } while (arg);
2272 break;
2273
Simon Kelley2937f8a2013-07-29 19:49:07 +01002274 case LOPT_TFTP: /* --enable-tftp */
2275 set_option_bool(OPT_TFTP);
2276 if (!arg)
2277 break;
2278 /* fall through */
2279
Simon Kelleyf2621c72007-04-29 19:47:21 +01002280 case 'I': /* --except-interface */
2281 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01002282 do {
Simon Kelley824af852008-02-12 20:43:05 +00002283 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002284 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002285 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002286 if (option == 'I')
2287 {
2288 new->next = daemon->if_except;
2289 daemon->if_except = new;
2290 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01002291 else if (option == LOPT_TFTP)
2292 {
2293 new->next = daemon->tftp_interfaces;
2294 daemon->tftp_interfaces = new;
2295 }
Simon Kelley849a8352006-06-09 21:02:31 +01002296 else
2297 {
2298 new->next = daemon->dhcp_except;
2299 daemon->dhcp_except = new;
2300 }
2301 arg = comma;
2302 } while (arg);
2303 break;
2304
Simon Kelleyf2621c72007-04-29 19:47:21 +01002305 case 'B': /* --bogus-nxdomain */
Glen Huang32fc6db2014-12-27 15:28:12 +00002306 case LOPT_IGNORE_ADDR: /* --ignore-address */
2307 {
Simon Kelley849a8352006-06-09 21:02:31 +01002308 struct in_addr addr;
2309 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002310 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002311 {
Simon Kelley824af852008-02-12 20:43:05 +00002312 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Glen Huang32fc6db2014-12-27 15:28:12 +00002313 if (option == 'B')
2314 {
2315 baddr->next = daemon->bogus_addr;
2316 daemon->bogus_addr = baddr;
2317 }
2318 else
2319 {
2320 baddr->next = daemon->ignore_addr;
2321 daemon->ignore_addr = baddr;
2322 }
Simon Kelley849a8352006-06-09 21:02:31 +01002323 baddr->addr = addr;
2324 }
2325 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002326 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01002327 break;
2328 }
2329
Simon Kelleyf2621c72007-04-29 19:47:21 +01002330 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00002331 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01002332 do {
Simon Kelley824af852008-02-12 20:43:05 +00002333 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002334 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002335 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002336 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002337 {
2338 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00002339 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002340#ifdef HAVE_SOCKADDR_SA_LEN
2341 new->addr.in.sin_len = sizeof(new->addr.in);
2342#endif
2343 }
Simon Kelley849a8352006-06-09 21:02:31 +01002344 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2345 {
2346 new->addr.sa.sa_family = AF_INET6;
2347 new->addr.in6.sin6_flowinfo = 0;
2348 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002349 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002350#ifdef HAVE_SOCKADDR_SA_LEN
2351 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2352#endif
2353 }
Simon Kelley849a8352006-06-09 21:02:31 +01002354 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002355 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002356
2357 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002358 if (option == 'a')
2359 {
2360 new->next = daemon->if_addrs;
2361 daemon->if_addrs = new;
2362 }
2363 else
2364 {
2365 new->next = daemon->auth_peers;
2366 daemon->auth_peers = new;
2367 }
Simon Kelley849a8352006-06-09 21:02:31 +01002368 arg = comma;
2369 } while (arg);
2370 break;
2371
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002372 case 'S': /* --server */
2373 case LOPT_LOCAL: /* --local */
2374 case 'A': /* --address */
2375 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002376 {
2377 struct server *serv, *newlist = NULL;
2378
2379 unhide_metas(arg);
2380
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002381 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002382 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002383 int rebind = !(*arg == '/');
2384 char *end = NULL;
2385 if (!rebind)
2386 arg++;
2387 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002388 {
2389 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002390 /* elide leading dots - they are implied in the search algorithm */
2391 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002392 /* # matches everything and becomes a zero length domain string */
2393 if (strcmp(arg, "#") == 0)
2394 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002395 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002396 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002397 serv = opt_malloc(sizeof(struct server));
2398 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002399 serv->next = newlist;
2400 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002401 serv->domain = domain;
2402 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002403 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002404 if (rebind)
2405 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002406 }
2407 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002408 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002409 }
2410 else
2411 {
Simon Kelley824af852008-02-12 20:43:05 +00002412 newlist = opt_malloc(sizeof(struct server));
2413 memset(newlist, 0, sizeof(struct server));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002414#ifdef HAVE_LOOP
2415 newlist->uid = rand32();
2416#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002417 }
2418
Simon Kelley7b1eae42014-02-20 13:43:28 +00002419 if (servers_only && option == 'S')
2420 newlist->flags |= SERV_FROM_FILE;
2421
Simon Kelley849a8352006-06-09 21:02:31 +01002422 if (option == 'A')
2423 {
2424 newlist->flags |= SERV_LITERAL_ADDRESS;
2425 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002426 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002427 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002428 else if (option == LOPT_NO_REBIND)
2429 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002430
2431 if (!arg || !*arg)
2432 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002433 if (!(newlist->flags & SERV_NO_REBIND))
2434 newlist->flags |= SERV_NO_ADDR; /* no server */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002435 }
2436
2437 else if (strcmp(arg, "#") == 0)
Simon Kelleyda8b6512018-09-03 23:18:36 +01002438 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002439 else
2440 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002441 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2442 if (err)
2443 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002444 }
2445
Simon Kelleyf2621c72007-04-29 19:47:21 +01002446 serv = newlist;
2447 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002448 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002449 serv->next->flags = serv->flags;
2450 serv->next->addr = serv->addr;
2451 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002452 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002453 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002454 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002455 serv->next = daemon->servers;
2456 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002457 break;
2458 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002459
Simon Kelleyde73a492014-02-17 21:43:27 +00002460 case LOPT_REV_SERV: /* --rev-server */
2461 {
2462 char *string;
2463 int size;
2464 struct server *serv;
2465 struct in_addr addr4;
Simon Kelleyde73a492014-02-17 21:43:27 +00002466 struct in6_addr addr6;
Simon Kelleyde73a492014-02-17 21:43:27 +00002467
2468 unhide_metas(arg);
2469 if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
2470 ret_err(gen_err);
2471
2472 if (inet_pton(AF_INET, arg, &addr4))
Olivier Gayotdc990582017-03-06 22:17:21 +00002473 {
2474 serv = add_rev4(addr4, size);
2475 if (!serv)
2476 ret_err(_("bad prefix"));
2477 }
Simon Kelleyde73a492014-02-17 21:43:27 +00002478 else if (inet_pton(AF_INET6, arg, &addr6))
2479 serv = add_rev6(&addr6, size);
Simon Kelleyde73a492014-02-17 21:43:27 +00002480 else
2481 ret_err(gen_err);
2482
2483 string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
2484
2485 if (string)
2486 ret_err(string);
Simon Kelley7b1eae42014-02-20 13:43:28 +00002487
2488 if (servers_only)
2489 serv->flags |= SERV_FROM_FILE;
2490
Simon Kelleyde73a492014-02-17 21:43:27 +00002491 break;
2492 }
2493
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002494 case LOPT_IPSET: /* --ipset */
2495#ifndef HAVE_IPSET
2496 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2497 break;
2498#else
2499 {
2500 struct ipsets ipsets_head;
2501 struct ipsets *ipsets = &ipsets_head;
2502 int size;
2503 char *end;
2504 char **sets, **sets_pos;
2505 memset(ipsets, 0, sizeof(struct ipsets));
2506 unhide_metas(arg);
2507 if (arg && *arg == '/')
2508 {
2509 arg++;
2510 while ((end = split_chr(arg, '/')))
2511 {
2512 char *domain = NULL;
2513 /* elide leading dots - they are implied in the search algorithm */
2514 while (*arg == '.')
2515 arg++;
2516 /* # matches everything and becomes a zero length domain string */
2517 if (strcmp(arg, "#") == 0 || !*arg)
2518 domain = "";
2519 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002520 ret_err(gen_err);
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002521 ipsets->next = opt_malloc(sizeof(struct ipsets));
2522 ipsets = ipsets->next;
2523 memset(ipsets, 0, sizeof(struct ipsets));
2524 ipsets->domain = domain;
2525 arg = end;
2526 }
2527 }
2528 else
2529 {
2530 ipsets->next = opt_malloc(sizeof(struct ipsets));
2531 ipsets = ipsets->next;
2532 memset(ipsets, 0, sizeof(struct ipsets));
2533 ipsets->domain = "";
2534 }
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002535
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002536 if (!arg || !*arg)
Simon Kelleya3bd7e72018-07-19 22:00:08 +01002537 ret_err(gen_err);
2538
2539 for (size = 2, end = arg; *end; ++end)
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002540 if (*end == ',')
2541 ++size;
2542
2543 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2544
2545 do {
2546 end = split(arg);
2547 *sets_pos++ = opt_string_alloc(arg);
2548 arg = end;
2549 } while (end);
2550 *sets_pos = 0;
2551 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2552 ipsets->next->sets = sets;
2553 ipsets->next = daemon->ipsets;
2554 daemon->ipsets = ipsets_head.next;
2555
2556 break;
2557 }
2558#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002559
Simon Kelleyf2621c72007-04-29 19:47:21 +01002560 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002561 {
2562 int size;
2563
2564 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002565 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002566 else
2567 {
2568 /* zero is OK, and means no caching. */
2569
2570 if (size < 0)
2571 size = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002572
2573 daemon->cachesize = size;
2574 }
2575 break;
2576 }
2577
Simon Kelleyf2621c72007-04-29 19:47:21 +01002578 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002579 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002580 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002581 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002582
Simon Kelley1a6bca82008-07-11 11:11:42 +01002583 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002584 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002585 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002586 break;
2587
Hans Dedecker926332a2016-01-23 10:48:12 +00002588 case LOPT_MAXPORT: /* --max-port */
2589 if (!atoi_check16(arg, &daemon->max_port))
2590 ret_err(gen_err);
2591 break;
2592
Simon Kelleyf2621c72007-04-29 19:47:21 +01002593 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002594 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002595 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002596 break;
2597
Simon Kelley25cf5e32015-01-09 15:53:03 +00002598 case 'q': /* --log-queries */
2599 set_option_bool(OPT_LOG);
2600 if (arg && strcmp(arg, "extra") == 0)
2601 set_option_bool(OPT_EXTRALOG);
2602 break;
2603
Simon Kelleyf2621c72007-04-29 19:47:21 +01002604 case LOPT_MAX_LOGS: /* --log-async */
2605 daemon->max_logs = LOG_MAX; /* default */
2606 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002607 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002608 else if (daemon->max_logs > 100)
2609 daemon->max_logs = 100;
2610 break;
2611
2612 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002613 {
2614 int i;
2615 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002616 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002617 daemon->edns_pktsz = (unsigned short)i;
2618 break;
2619 }
2620
Simon Kelleyf2621c72007-04-29 19:47:21 +01002621 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002622 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002623 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002624 /* if explicitly set to zero, use single OS ephemeral port
2625 and disable random ports */
2626 if (daemon->query_port == 0)
2627 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002628 break;
2629
Simon Kelley824af852008-02-12 20:43:05 +00002630 case 'T': /* --local-ttl */
2631 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002632 case LOPT_MAXTTL: /* --max-ttl */
RinSatsuki28de3872015-01-10 15:22:21 +00002633 case LOPT_MINCTTL: /* --min-cache-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002634 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002635 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley832e47b2016-02-24 21:24:45 +00002636 case LOPT_DHCPTTL: /* --dhcp-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002637 {
2638 int ttl;
2639 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002640 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002641 else if (option == LOPT_NEGTTL)
2642 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002643 else if (option == LOPT_MAXTTL)
2644 daemon->max_ttl = (unsigned long)ttl;
RinSatsuki28de3872015-01-10 15:22:21 +00002645 else if (option == LOPT_MINCTTL)
2646 {
2647 if (ttl > TTL_FLOOR_LIMIT)
2648 ttl = TTL_FLOOR_LIMIT;
2649 daemon->min_cache_ttl = (unsigned long)ttl;
2650 }
Simon Kelley1d860412012-09-20 20:48:04 +01002651 else if (option == LOPT_MAXCTTL)
2652 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002653 else if (option == LOPT_AUTHTTL)
2654 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley832e47b2016-02-24 21:24:45 +00002655 else if (option == LOPT_DHCPTTL)
2656 {
2657 daemon->dhcp_ttl = (unsigned long)ttl;
2658 daemon->use_dhcp_ttl = 1;
2659 }
Simon Kelley849a8352006-06-09 21:02:31 +01002660 else
2661 daemon->local_ttl = (unsigned long)ttl;
2662 break;
2663 }
2664
Simon Kelley7622fc02009-06-04 20:32:05 +01002665#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002666 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002667 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002668 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002669 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002670#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002671
Simon Kelley7622fc02009-06-04 20:32:05 +01002672#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002673 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002674 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002675 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002676 break;
2677
Simon Kelleybec366b2016-02-24 22:03:26 +00002678 case LOPT_TFTP_MTU: /* --tftp-mtu */
2679 if (!atoi_check(arg, &daemon->tftp_mtu))
2680 ret_err(gen_err);
2681 break;
2682
Simon Kelley824af852008-02-12 20:43:05 +00002683 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002684 comma = split(arg);
2685 if (comma)
2686 {
2687 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2688 new->interface = opt_string_alloc(comma);
2689 new->prefix = opt_string_alloc(arg);
2690 new->next = daemon->if_prefix;
2691 daemon->if_prefix = new;
2692 }
2693 else
2694 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002695 break;
2696
Simon Kelley824af852008-02-12 20:43:05 +00002697 case LOPT_TFTPPORTS: /* --tftp-port-range */
2698 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002699 !atoi_check16(arg, &daemon->start_tftp_port) ||
2700 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002701 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002702
2703 if (daemon->start_tftp_port > daemon->end_tftp_port)
2704 {
2705 int tmp = daemon->start_tftp_port;
2706 daemon->start_tftp_port = daemon->end_tftp_port;
2707 daemon->end_tftp_port = tmp;
2708 }
2709
2710 break;
Floris Bos60704f52017-04-09 22:22:49 +01002711
2712 case LOPT_APREF: /* --tftp-unique-root */
2713 if (!arg || strcasecmp(arg, "ip") == 0)
2714 set_option_bool(OPT_TFTP_APREF_IP);
2715 else if (strcasecmp(arg, "mac") == 0)
2716 set_option_bool(OPT_TFTP_APREF_MAC);
2717 else
2718 ret_err(gen_err);
2719 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002720#endif
Simon Kelley824af852008-02-12 20:43:05 +00002721
Simon Kelleyf2621c72007-04-29 19:47:21 +01002722 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002723 {
Simon Kelley22cd8602018-01-14 22:57:14 +00002724 struct dhcp_bridge *new;
2725
Simon Kelley316e2732010-01-22 20:16:09 +00002726 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002727 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002728
Simon Kelley22cd8602018-01-14 22:57:14 +00002729 for (new = daemon->bridges; new; new = new->next)
2730 if (strcmp(new->iface, arg) == 0)
2731 break;
2732
2733 if (!new)
2734 {
2735 new = opt_malloc(sizeof(struct dhcp_bridge));
2736 strcpy(new->iface, arg);
2737 new->alias = NULL;
2738 new->next = daemon->bridges;
2739 daemon->bridges = new;
2740 }
2741
Simon Kelley832af0b2007-01-21 20:01:28 +00002742 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002743 arg = comma;
2744 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002745 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002746 {
Simon Kelley824af852008-02-12 20:43:05 +00002747 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002748 b->next = new->alias;
2749 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002750 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002751 }
2752 } while (comma);
2753
2754 break;
2755 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002756
Simon Kelley7622fc02009-06-04 20:32:05 +01002757#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002758 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002759 {
2760 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002761 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002762 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002763
Simon Kelley52b92f42012-01-22 16:05:15 +00002764 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002765 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002766
Simon Kelley849a8352006-06-09 21:02:31 +01002767 while(1)
2768 {
2769 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002770 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2771 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2772 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002773 break;
2774
Simon Kelleyf2621c72007-04-29 19:47:21 +01002775 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002776 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002777 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002778 {
Simon Kelley824af852008-02-12 20:43:05 +00002779 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2780 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002781 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002782 /* ignore empty tag */
2783 if (tt->net)
2784 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002785 }
2786 else
2787 {
2788 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002789 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002790 else if (strstr(arg, "set:") == arg)
2791 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002792 else
Simon Kelley824af852008-02-12 20:43:05 +00002793 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002794 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002795 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002796 }
2797 else
2798 {
2799 a[0] = arg;
2800 break;
2801 }
2802 }
2803
Simon Kelley1f776932012-12-16 19:46:08 +00002804 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002805 if (!(a[k] = split(a[k-1])))
2806 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002807
Simon Kelley52b92f42012-01-22 16:05:15 +00002808 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002809 ret_err(_("bad dhcp-range"));
2810
2811 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002812 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002813 new->next = daemon->dhcp;
2814 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002815 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002816 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002817 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002818 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002819 new->flags |= CONTEXT_PROXY;
2820 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002821 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002822
2823 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2824 {
2825 struct in_addr tmp = new->start;
2826 new->start = new->end;
2827 new->end = tmp;
2828 }
2829
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002830 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002831 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002832 {
2833 new->flags |= CONTEXT_NETMASK;
2834 leasepos = 3;
2835 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002836 ret_err(_("inconsistent DHCP range"));
Simon Kelleyfa794662016-03-03 20:33:54 +00002837
Simon Kelley52b92f42012-01-22 16:05:15 +00002838
Simon Kelleyfa794662016-03-03 20:33:54 +00002839 if (k >= 4 && strchr(a[3], '.') &&
2840 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
2841 {
2842 new->flags |= CONTEXT_BRDCAST;
2843 leasepos = 4;
2844 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002845 }
Simon Kelley849a8352006-06-09 21:02:31 +01002846 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002847#ifdef HAVE_DHCP6
2848 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002849 {
Simon Kelley89500e32013-09-20 16:29:20 +01002850 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002851 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002852 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002853 new->next = daemon->dhcp6;
2854 daemon->dhcp6 = new;
2855
Simon Kelley30cd9662012-03-25 20:44:38 +01002856 for (leasepos = 1; leasepos < k; leasepos++)
2857 {
2858 if (strcmp(a[leasepos], "static") == 0)
2859 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2860 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002861 new->flags |= CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002862 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002863 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002864 else if (strcmp(a[leasepos], "ra-advrouter") == 0)
2865 new->flags |= CONTEXT_RA_ROUTER | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002866 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002867 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Neil Jerram2fd5bc92015-06-10 22:13:06 +01002868 else if (strcmp(a[leasepos], "off-link") == 0)
2869 new->flags |= CONTEXT_RA_OFF_LINK;
Simon Kelley30cd9662012-03-25 20:44:38 +01002870 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2871 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002872 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2873 {
2874 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2875 new->flags |= CONTEXT_TEMPLATE;
2876 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002877 else
2878 break;
2879 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002880
Simon Kelley52b92f42012-01-22 16:05:15 +00002881 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002882 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002883 {
2884 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002885 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002886 if (!(*cp >= '0' && *cp <= '9'))
2887 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002888 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002889 {
2890 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002891 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002892 }
2893 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002894
Simon Kelley6692a1a2013-08-20 14:41:31 +01002895 if (new->prefix != 64)
2896 {
Simon Kelley7ea3d3f2014-04-25 22:04:05 +01002897 if (new->flags & CONTEXT_RA)
Simon Kelley6692a1a2013-08-20 14:41:31 +01002898 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2899 else if (new->flags & CONTEXT_TEMPLATE)
2900 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2901 }
2902
2903 if (new->prefix < 64)
2904 ret_err(_("prefix length must be at least 64"));
2905
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002906 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2907 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002908
2909 /* dhcp-range=:: enables DHCP stateless on any interface */
2910 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2911 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002912
2913 if (new->flags & CONTEXT_TEMPLATE)
2914 {
2915 struct in6_addr zero;
2916 memset(&zero, 0, sizeof(zero));
2917 if (!is_same_net6(&zero, &new->start6, new->prefix))
2918 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2919 }
2920
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002921 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002922 {
2923 struct in6_addr tmp = new->start6;
2924 new->start6 = new->end6;
2925 new->end6 = tmp;
2926 }
Simon Kelley849a8352006-06-09 21:02:31 +01002927 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002928#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002929 else
2930 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002931
Simon Kelley30cd9662012-03-25 20:44:38 +01002932 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002933 {
Simon Kelleyfa794662016-03-03 20:33:54 +00002934 if (leasepos != k-1)
2935 ret_err(_("bad dhcp-range"));
2936
Simon Kelley849a8352006-06-09 21:02:31 +01002937 if (strcmp(a[leasepos], "infinite") == 0)
2938 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002939 else if (strcmp(a[leasepos], "deprecated") == 0)
2940 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002941 else
2942 {
2943 int fac = 1;
2944 if (strlen(a[leasepos]) > 0)
2945 {
2946 switch (a[leasepos][strlen(a[leasepos]) - 1])
2947 {
Simon Kelley42243212012-07-20 15:19:18 +01002948 case 'w':
2949 case 'W':
2950 fac *= 7;
2951 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002952 case 'd':
2953 case 'D':
2954 fac *= 24;
Simon Kelley87e00fe2018-02-16 21:27:35 +00002955 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002956 case 'h':
2957 case 'H':
2958 fac *= 60;
2959 /* fall through */
2960 case 'm':
2961 case 'M':
2962 fac *= 60;
2963 /* fall through */
2964 case 's':
2965 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002966 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002967 }
2968
Simon Kelleybe379862012-12-23 12:01:39 +00002969 for (cp = a[leasepos]; *cp; cp++)
2970 if (!(*cp >= '0' && *cp <= '9'))
2971 break;
2972
Simon Kelley54dae552013-02-05 17:55:10 +00002973 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00002974 ret_err(_("bad dhcp-range"));
2975
Simon Kelley849a8352006-06-09 21:02:31 +01002976 new->lease_time = atoi(a[leasepos]) * fac;
2977 /* Leases of a minute or less confuse
2978 some clients, notably Apple's */
2979 if (new->lease_time < 120)
2980 new->lease_time = 120;
2981 }
2982 }
2983 }
2984 break;
2985 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01002986
Simon Kelley5aabfc72007-08-29 11:24:47 +01002987 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01002988 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01002989 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002990 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01002991 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01002992 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01002993 struct in_addr in;
2994
Simon Kelley824af852008-02-12 20:43:05 +00002995 new = opt_malloc(sizeof(struct dhcp_config));
2996
Simon Kelley849a8352006-06-09 21:02:31 +01002997 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00002998 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
2999 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003000 new->netid = NULL;
3001
Simon Kelley849a8352006-06-09 21:02:31 +01003002 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01003003 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003004 if (!(a[k] = split(a[k-1])))
3005 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003006
3007 for (j = 0; j < k; j++)
3008 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
3009 {
3010 char *arg = a[j];
3011
3012 if ((arg[0] == 'i' || arg[0] == 'I') &&
3013 (arg[1] == 'd' || arg[1] == 'D') &&
3014 arg[2] == ':')
3015 {
3016 if (arg[3] == '*')
3017 new->flags |= CONFIG_NOCLID;
3018 else
3019 {
3020 int len;
3021 arg += 3; /* dump id: */
3022 if (strchr(arg, ':'))
3023 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
3024 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01003025 {
3026 unhide_metas(arg);
3027 len = (int) strlen(arg);
3028 }
3029
Simon Kelley28866e92011-02-14 20:19:14 +00003030 if (len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003031 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003032 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003033 {
3034 new->flags |= CONFIG_CLID;
3035 new->clid_len = len;
3036 memcpy(new->clid, arg, len);
3037 }
Simon Kelley849a8352006-06-09 21:02:31 +01003038 }
3039 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003040 /* dhcp-host has strange backwards-compat needs. */
3041 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01003042 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003043 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3044 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3045 newtag->net = opt_malloc(strlen(arg + 4) + 1);
3046 newlist->next = new->netid;
3047 new->netid = newlist;
3048 newlist->list = newtag;
3049 strcpy(newtag->net, arg+4);
3050 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01003051 }
Simon Kelley7de060b2011-08-26 17:24:52 +01003052 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003053 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00003054#ifdef HAVE_DHCP6
3055 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
3056 {
3057 arg[strlen(arg)-1] = 0;
3058 arg++;
3059
3060 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003061 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00003062
3063 for (i= 0; i < 8; i++)
3064 if (new->addr6.s6_addr[i] != 0)
3065 break;
3066
3067 /* set WILDCARD if network part all zeros */
3068 if (i == 8)
3069 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00003070
3071 new->flags |= CONFIG_ADDR6;
3072 }
3073#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01003074 else
Simon Kelley849a8352006-06-09 21:02:31 +01003075 {
Simon Kelley9009d742008-11-14 20:04:27 +00003076 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00003077 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
3078 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003079 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00003080 else
3081 {
3082
3083 newhw->next = new->hwaddr;
3084 new->hwaddr = newhw;
3085 }
Simon Kelley849a8352006-06-09 21:02:31 +01003086 }
3087 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003088 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01003089 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003090 struct dhcp_config *configs;
3091
Simon Kelley849a8352006-06-09 21:02:31 +01003092 new->addr = in;
3093 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003094
3095 /* If the same IP appears in more than one host config, then DISCOVER
3096 for one of the hosts will get the address, but REQUEST will be NAKed,
3097 since the address is reserved by the other one -> protocol loop. */
3098 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
3099 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
3100 {
3101 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
3102 return 0;
3103 }
Simon Kelley849a8352006-06-09 21:02:31 +01003104 }
3105 else
3106 {
3107 char *cp, *lastp = NULL, last = 0;
Simon Kelley76ff4402013-12-17 16:29:14 +00003108 int fac = 1, isdig = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003109
3110 if (strlen(a[j]) > 1)
3111 {
3112 lastp = a[j] + strlen(a[j]) - 1;
3113 last = *lastp;
3114 switch (last)
3115 {
Simon Kelley42243212012-07-20 15:19:18 +01003116 case 'w':
3117 case 'W':
3118 fac *= 7;
3119 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01003120 case 'd':
3121 case 'D':
3122 fac *= 24;
3123 /* fall through */
3124 case 'h':
3125 case 'H':
3126 fac *= 60;
3127 /* fall through */
3128 case 'm':
3129 case 'M':
3130 fac *= 60;
3131 /* fall through */
3132 case 's':
3133 case 'S':
3134 *lastp = 0;
3135 }
3136 }
3137
3138 for (cp = a[j]; *cp; cp++)
Simon Kelley76ff4402013-12-17 16:29:14 +00003139 if (isdigit((unsigned char)*cp))
3140 isdig = 1;
3141 else if (*cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01003142 break;
Simon Kelley76ff4402013-12-17 16:29:14 +00003143
Simon Kelley849a8352006-06-09 21:02:31 +01003144 if (*cp)
3145 {
3146 if (lastp)
3147 *lastp = last;
3148 if (strcmp(a[j], "infinite") == 0)
3149 {
3150 new->lease_time = 0xffffffff;
3151 new->flags |= CONFIG_TIME;
3152 }
3153 else if (strcmp(a[j], "ignore") == 0)
3154 new->flags |= CONFIG_DISABLE;
3155 else
3156 {
Simon Kelley1f15b812009-10-13 17:49:32 +01003157 if (!(new->hostname = canonicalise_opt(a[j])) ||
3158 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003159 ret_err(_("bad DHCP host name"));
3160
3161 new->flags |= CONFIG_NAME;
3162 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01003163 }
3164 }
Simon Kelley76ff4402013-12-17 16:29:14 +00003165 else if (isdig)
Simon Kelley849a8352006-06-09 21:02:31 +01003166 {
3167 new->lease_time = atoi(a[j]) * fac;
3168 /* Leases of a minute or less confuse
3169 some clients, notably Apple's */
3170 if (new->lease_time < 120)
3171 new->lease_time = 120;
3172 new->flags |= CONFIG_TIME;
3173 }
3174 }
3175
Simon Kelley5aabfc72007-08-29 11:24:47 +01003176 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003177 break;
3178 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003179
3180 case LOPT_TAG_IF: /* --tag-if */
3181 {
3182 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
3183
3184 new->tag = NULL;
3185 new->set = NULL;
3186 new->next = NULL;
3187
3188 /* preserve order */
3189 if (!daemon->tag_if)
3190 daemon->tag_if = new;
3191 else
3192 {
3193 struct tag_if *tmp;
3194 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
3195 tmp->next = new;
3196 }
3197
3198 while (arg)
3199 {
3200 size_t len;
3201
3202 comma = split(arg);
3203 len = strlen(arg);
3204
3205 if (len < 5)
3206 {
3207 new->set = NULL;
3208 break;
3209 }
3210 else
3211 {
3212 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
3213 newtag->net = opt_malloc(len - 3);
3214 strcpy(newtag->net, arg+4);
3215 unhide_metas(newtag->net);
3216
3217 if (strstr(arg, "set:") == arg)
3218 {
3219 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
3220 newlist->next = new->set;
3221 new->set = newlist;
3222 newlist->list = newtag;
3223 }
3224 else if (strstr(arg, "tag:") == arg)
3225 {
3226 newtag->next = new->tag;
3227 new->tag = newtag;
3228 }
3229 else
3230 {
3231 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00003232 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003233 break;
3234 }
3235 }
3236
3237 arg = comma;
3238 }
3239
3240 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003241 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003242
3243 break;
3244 }
3245
Simon Kelley849a8352006-06-09 21:02:31 +01003246
Simon Kelley73a08a22009-02-05 20:28:08 +00003247 case 'O': /* --dhcp-option */
3248 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00003249 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00003250 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003251 return parse_dhcp_opt(errstr, arg,
3252 option == LOPT_FORCE ? DHOPT_FORCE :
3253 (option == LOPT_MATCH ? DHOPT_MATCH :
3254 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
Simon Kelleyc8226202018-08-08 23:46:03 +01003255
3256 case LOPT_NAME_MATCH: /* --dhcp-name-match */
3257 {
3258 struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
3259 struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
3260 ssize_t len;
3261
3262 if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
3263 ret_err(gen_err);
3264
3265 new->wildcard = 0;
3266 new->netid = id;
3267 id->net = opt_string_alloc(set_prefix(arg));
3268
3269 if (comma[len-1] == '*')
3270 {
3271 comma[len-1] = 0;
3272 new->wildcard = 1;
3273 }
3274 new->name = opt_string_alloc(comma);
3275
3276 new->next = daemon->dhcp_name_match;
3277 daemon->dhcp_name_match = new;
3278
3279 break;
3280 }
3281
Simon Kelleyf2621c72007-04-29 19:47:21 +01003282 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01003283 {
3284 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003285 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01003286 {
Simon Kelley824af852008-02-12 20:43:05 +00003287 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01003288 newid->next = id;
3289 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003290 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003291 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01003292 arg = comma;
3293 };
3294
3295 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003296 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003297 else
3298 {
Simon Kelley7de060b2011-08-26 17:24:52 +01003299 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01003300 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003301 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003302 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003303 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003304 dhcp_next_server.s_addr = 0;
3305 if (comma)
3306 {
3307 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003308 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00003309 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003310 if (comma)
3311 {
3312 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003313 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
3314 {
3315 /*
3316 * The user may have specified the tftp hostname here.
3317 * save it so that it can be resolved/looked up during
3318 * actual dhcp_reply().
3319 */
3320
3321 tftp_sname = opt_string_alloc(comma);
3322 dhcp_next_server.s_addr = 0;
3323 }
Simon Kelley849a8352006-06-09 21:02:31 +01003324 }
3325 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003326
3327 new = opt_malloc(sizeof(struct dhcp_boot));
3328 new->file = dhcp_file;
3329 new->sname = dhcp_sname;
3330 new->tftp_sname = tftp_sname;
3331 new->next_server = dhcp_next_server;
3332 new->netid = id;
3333 new->next = daemon->boot_config;
3334 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01003335 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003336
Simon Kelley849a8352006-06-09 21:02:31 +01003337 break;
3338 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003339
Floris Bos503c6092017-04-09 23:07:13 +01003340 case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
3341 {
3342 struct dhcp_netid *id = NULL;
3343 while (is_tag_prefix(arg))
3344 {
3345 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
3346 newid->next = id;
3347 id = newid;
3348 comma = split(arg);
3349 newid->net = opt_string_alloc(arg+4);
3350 arg = comma;
3351 };
3352
3353 if (!arg)
3354 ret_err(gen_err);
3355 else
3356 {
3357 struct delay_config *new;
3358 int delay;
3359 if (!atoi_check(arg, &delay))
3360 ret_err(gen_err);
3361
3362 new = opt_malloc(sizeof(struct delay_config));
3363 new->delay = delay;
3364 new->netid = id;
3365 new->next = daemon->delay_conf;
3366 daemon->delay_conf = new;
3367 }
3368
3369 break;
3370 }
3371
Simon Kelley7622fc02009-06-04 20:32:05 +01003372 case LOPT_PXE_PROMT: /* --pxe-prompt */
3373 {
3374 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
3375 int timeout;
Floris Bos503c6092017-04-09 23:07:13 +01003376
Simon Kelley7622fc02009-06-04 20:32:05 +01003377 new->netid = NULL;
3378 new->opt = 10; /* PXE_MENU_PROMPT */
3379
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003380 while (is_tag_prefix(arg))
3381 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003382 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3383 comma = split(arg);
3384 nn->next = new->netid;
3385 new->netid = nn;
3386 nn->net = opt_string_alloc(arg+4);
3387 arg = comma;
3388 }
3389
3390 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003391 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003392 else
3393 {
3394 comma = split(arg);
3395 unhide_metas(arg);
3396 new->len = strlen(arg) + 1;
3397 new->val = opt_malloc(new->len);
3398 memcpy(new->val + 1, arg, new->len - 1);
3399
3400 new->u.vendor_class = (unsigned char *)"PXEClient";
3401 new->flags = DHOPT_VENDOR;
3402
3403 if (comma && atoi_check(comma, &timeout))
3404 *(new->val) = timeout;
3405 else
3406 *(new->val) = 255;
3407
3408 new->next = daemon->dhcp_opts;
3409 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003410 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01003411 }
3412
3413 break;
3414 }
3415
3416 case LOPT_PXE_SERV: /* --pxe-service */
3417 {
3418 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
3419 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
Simon Kelley68bea102016-05-11 22:15:06 +01003420 "IA32_EFI", "x86-64_EFI", "Xscale_EFI", "BC_EFI",
3421 "ARM32_EFI", "ARM64_EFI", NULL };
Simon Kelley7622fc02009-06-04 20:32:05 +01003422 static int boottype = 32768;
3423
3424 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00003425 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01003426 new->server.s_addr = 0;
3427
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003428 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01003429 {
3430 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
3431 comma = split(arg);
3432 nn->next = new->netid;
3433 new->netid = nn;
3434 nn->net = opt_string_alloc(arg+4);
3435 arg = comma;
3436 }
3437
3438 if (arg && (comma = split(arg)))
3439 {
3440 for (i = 0; CSA[i]; i++)
3441 if (strcasecmp(CSA[i], arg) == 0)
3442 break;
3443
3444 if (CSA[i] || atoi_check(arg, &i))
3445 {
3446 arg = comma;
3447 comma = split(arg);
3448
3449 new->CSA = i;
3450 new->menu = opt_string_alloc(arg);
3451
Simon Kelley316e2732010-01-22 20:16:09 +00003452 if (!comma)
3453 {
3454 new->type = 0; /* local boot */
3455 new->basename = NULL;
3456 }
3457 else
Simon Kelley7622fc02009-06-04 20:32:05 +01003458 {
3459 arg = comma;
3460 comma = split(arg);
3461 if (atoi_check(arg, &i))
3462 {
3463 new->type = i;
3464 new->basename = NULL;
3465 }
3466 else
3467 {
3468 new->type = boottype++;
3469 new->basename = opt_string_alloc(arg);
3470 }
3471
Simon Kelley751d6f42012-02-10 15:24:51 +00003472 if (comma)
3473 {
3474 if (!inet_pton(AF_INET, comma, &new->server))
3475 {
3476 new->server.s_addr = 0;
3477 new->sname = opt_string_alloc(comma);
3478 }
3479
3480 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003481 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003482
Simon Kelley316e2732010-01-22 20:16:09 +00003483 /* Order matters */
3484 new->next = NULL;
3485 if (!daemon->pxe_services)
3486 daemon->pxe_services = new;
3487 else
3488 {
3489 struct pxe_service *s;
3490 for (s = daemon->pxe_services; s->next; s = s->next);
3491 s->next = new;
3492 }
3493
3494 daemon->enable_pxe = 1;
3495 break;
3496
Simon Kelley7622fc02009-06-04 20:32:05 +01003497 }
3498 }
3499
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003500 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003501 }
3502
Simon Kelleyf2621c72007-04-29 19:47:21 +01003503 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003504 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003505 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003506 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003507 else
3508 {
Simon Kelley824af852008-02-12 20:43:05 +00003509 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003510 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003511 unhide_metas(comma);
3512 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003513 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003514 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003515 else
3516 {
3517 new->next = daemon->dhcp_macs;
3518 daemon->dhcp_macs = new;
3519 }
Simon Kelley849a8352006-06-09 21:02:31 +01003520 }
3521 }
3522 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003523
3524#ifdef OPTION6_PREFIX_CLASS
3525 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3526 {
3527 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3528
3529 if (!(comma = split(arg)) ||
3530 !atoi_check16(comma, &new->class))
3531 ret_err(gen_err);
3532
3533 new->tag.net = opt_string_alloc(set_prefix(arg));
3534 new->next = daemon->prefix_classes;
3535 daemon->prefix_classes = new;
3536
3537 break;
3538 }
3539#endif
3540
3541
Simon Kelleyf2621c72007-04-29 19:47:21 +01003542 case 'U': /* --dhcp-vendorclass */
3543 case 'j': /* --dhcp-userclass */
3544 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3545 case LOPT_REMOTE: /* --dhcp-remoteid */
3546 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003547 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003548 unsigned char *p;
3549 int dig = 0;
3550 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3551
3552 if (!(comma = split(arg)))
3553 ret_err(gen_err);
3554
3555 new->netid.net = opt_string_alloc(set_prefix(arg));
3556 /* check for hex string - must digits may include : must not have nothing else,
3557 only allowed for agent-options. */
3558
3559 arg = comma;
3560 if ((comma = split(arg)))
3561 {
3562 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3563 ret_err(gen_err);
3564 else
3565 new->enterprise = atoi(arg+11);
3566 }
3567 else
3568 comma = arg;
3569
3570 for (p = (unsigned char *)comma; *p; p++)
3571 if (isxdigit(*p))
3572 dig = 1;
3573 else if (*p != ':')
3574 break;
3575 unhide_metas(comma);
3576 if (option == 'U' || option == 'j' || *p || !dig)
3577 {
3578 new->len = strlen(comma);
3579 new->data = opt_malloc(new->len);
3580 memcpy(new->data, comma, new->len);
3581 }
3582 else
3583 {
3584 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3585 new->data = opt_malloc(new->len);
3586 memcpy(new->data, comma, new->len);
3587 }
3588
3589 switch (option)
3590 {
3591 case 'j':
3592 new->match_type = MATCH_USER;
3593 break;
3594 case 'U':
3595 new->match_type = MATCH_VENDOR;
3596 break;
3597 case LOPT_CIRCUIT:
3598 new->match_type = MATCH_CIRCUIT;
3599 break;
3600 case LOPT_REMOTE:
3601 new->match_type = MATCH_REMOTE;
3602 break;
3603 case LOPT_SUBSCR:
3604 new->match_type = MATCH_SUBSCRIBER;
3605 break;
3606 }
3607 new->next = daemon->dhcp_vendors;
3608 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003609
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003610 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003611 }
3612
Simon Kelley9e038942008-05-30 20:06:34 +01003613 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3614 if (!arg)
3615 {
3616 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3617 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3618 }
3619 else
3620 {
3621 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003622 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3623 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003624 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003625 if (!comma)
3626 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3627 }
3628 break;
3629
Simon Kelley824af852008-02-12 20:43:05 +00003630 case 'J': /* --dhcp-ignore */
3631 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3632 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003633 case '3': /* --bootp-dynamic */
3634 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003635 {
Simon Kelley824af852008-02-12 20:43:05 +00003636 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003637 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003638 if (option == 'J')
3639 {
3640 new->next = daemon->dhcp_ignore;
3641 daemon->dhcp_ignore = new;
3642 }
Simon Kelley824af852008-02-12 20:43:05 +00003643 else if (option == LOPT_BROADCAST)
3644 {
3645 new->next = daemon->force_broadcast;
3646 daemon->force_broadcast = new;
3647 }
Simon Kelley9009d742008-11-14 20:04:27 +00003648 else if (option == '3')
3649 {
3650 new->next = daemon->bootp_dynamic;
3651 daemon->bootp_dynamic = new;
3652 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003653 else if (option == LOPT_GEN_NAMES)
3654 {
3655 new->next = daemon->dhcp_gen_names;
3656 daemon->dhcp_gen_names = new;
3657 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003658 else
3659 {
3660 new->next = daemon->dhcp_ignore_names;
3661 daemon->dhcp_ignore_names = new;
3662 }
3663
3664 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003665 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003666 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003667 member->next = list;
3668 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003669 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003670 member->net = opt_string_alloc(arg+4);
3671 else
3672 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003673 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003674 }
Simon Kelley849a8352006-06-09 21:02:31 +01003675
3676 new->list = list;
3677 break;
3678 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003679
3680 case LOPT_PROXY: /* --dhcp-proxy */
3681 daemon->override = 1;
3682 while (arg) {
3683 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3684 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003685 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003686 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003687 new->next = daemon->override_relays;
3688 daemon->override_relays = new;
3689 arg = comma;
3690 }
3691 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003692
3693 case LOPT_RELAY: /* --dhcp-relay */
3694 {
3695 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3696 comma = split(arg);
3697 new->interface = opt_string_alloc(split(comma));
3698 new->iface_index = 0;
3699 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3700 {
3701 new->next = daemon->relay4;
3702 daemon->relay4 = new;
3703 }
3704#ifdef HAVE_DHCP6
3705 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3706 {
3707 new->next = daemon->relay6;
3708 daemon->relay6 = new;
3709 }
3710#endif
3711 else
3712 ret_err(_("Bad dhcp-relay"));
3713
3714 break;
3715 }
3716
Simon Kelley7622fc02009-06-04 20:32:05 +01003717#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003718
Simon Kelley8b372702012-03-09 17:45:10 +00003719#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003720 case LOPT_RA_PARAM: /* --ra-param */
3721 if ((comma = split(arg)))
3722 {
3723 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3724 new->lifetime = -1;
3725 new->prio = 0;
David Flamand005c46d2017-04-11 11:49:54 +01003726 new->mtu = 0;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003727 new->mtu_name = NULL;
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003728 new->name = opt_string_alloc(arg);
David Flamand005c46d2017-04-11 11:49:54 +01003729 if (strcasestr(comma, "mtu:") == comma)
3730 {
3731 arg = comma + 4;
3732 if (!(comma = split(comma)))
3733 goto err;
3734 if (!strcasecmp(arg, "off"))
3735 new->mtu = -1;
Vladislav Grishenko6ec5f5c2017-04-24 22:34:45 +01003736 else if (!atoi_check(arg, &new->mtu))
3737 new->mtu_name = opt_string_alloc(arg);
3738 else if (new->mtu < 1280)
David Flamand005c46d2017-04-11 11:49:54 +01003739 goto err;
3740 }
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003741 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3742 {
3743 if (*comma == 'l' || *comma == 'L')
3744 new->prio = 0x18;
3745 else
3746 new->prio = 0x08;
3747 comma = split(comma);
3748 }
3749 arg = split(comma);
3750 if (!atoi_check(comma, &new->interval) ||
3751 (arg && !atoi_check(arg, &new->lifetime)))
David Flamand005c46d2017-04-11 11:49:54 +01003752err:
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003753 ret_err(_("bad RA-params"));
3754
3755 new->next = daemon->ra_interfaces;
3756 daemon->ra_interfaces = new;
3757 }
3758 break;
3759
Simon Kelley8b372702012-03-09 17:45:10 +00003760 case LOPT_DUID: /* --dhcp-duid */
3761 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003762 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003763 else
3764 {
3765 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3766 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3767 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3768 }
3769 break;
3770#endif
3771
Simon Kelleyf2621c72007-04-29 19:47:21 +01003772 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003773 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003774 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003775 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003776 struct doctor *new = opt_malloc(sizeof(struct doctor));
3777 new->next = daemon->doctors;
3778 daemon->doctors = new;
3779 new->mask.s_addr = 0xffffffff;
3780 new->end.s_addr = 0;
3781
Simon Kelley849a8352006-06-09 21:02:31 +01003782 if ((a[0] = arg))
3783 for (k = 1; k < 3; k++)
3784 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003785 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003786 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003787 unhide_metas(a[k]);
3788 }
Simon Kelley849a8352006-06-09 21:02:31 +01003789
Simon Kelley73a08a22009-02-05 20:28:08 +00003790 dash = split_chr(a[0], '-');
3791
Simon Kelley849a8352006-06-09 21:02:31 +01003792 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003793 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
Simon Kelleya3bd7e72018-07-19 22:00:08 +01003794 (!(inet_pton(AF_INET, a[1], &new->out) > 0)) ||
3795 (k == 3 && !inet_pton(AF_INET, a[2], &new->mask)))
3796 ret_err(_("missing address in alias"));
Simon Kelley849a8352006-06-09 21:02:31 +01003797
Simon Kelley73a08a22009-02-05 20:28:08 +00003798 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003799 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003800 !is_same_net(new->in, new->end, new->mask) ||
3801 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003802 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003803
3804 break;
3805 }
3806
Simon Kelleyf2621c72007-04-29 19:47:21 +01003807 case LOPT_INTNAME: /* --interface-name */
3808 {
3809 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003810 char *domain = NULL;
3811
Simon Kelleyf2621c72007-04-29 19:47:21 +01003812 comma = split(arg);
3813
Simon Kelley1f15b812009-10-13 17:49:32 +01003814 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003815 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003816
Simon Kelley824af852008-02-12 20:43:05 +00003817 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003818 new->next = NULL;
Simon Kelley376d48c2013-11-13 13:04:30 +00003819 new->addr = NULL;
3820
Simon Kelleyf2621c72007-04-29 19:47:21 +01003821 /* Add to the end of the list, so that first name
3822 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003823 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003824 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003825 new->name = domain;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003826 new->family = 0;
3827 arg = split_chr(comma, '/');
3828 if (arg)
3829 {
3830 if (strcmp(arg, "4") == 0)
3831 new->family = AF_INET;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003832 else if (strcmp(arg, "6") == 0)
3833 new->family = AF_INET6;
Simon Kelleyf7029f52013-11-21 15:09:09 +00003834 else
3835 ret_err(gen_err);
3836 }
Simon Kelley824af852008-02-12 20:43:05 +00003837 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003838 break;
3839 }
Simon Kelley9009d742008-11-14 20:04:27 +00003840
3841 case LOPT_CNAME: /* --cname */
3842 {
3843 struct cname *new;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003844 char *alias, *target, *last, *pen;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003845 int ttl = -1;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003846
Simon Kelleya1d973f2016-12-22 22:09:50 +00003847 for (last = pen = NULL, comma = arg; comma; comma = split(comma))
Simon Kelley9009d742008-11-14 20:04:27 +00003848 {
Simon Kelleya1d973f2016-12-22 22:09:50 +00003849 pen = last;
3850 last = comma;
3851 }
3852
3853 if (!pen)
3854 ret_err(_("bad CNAME"));
3855
3856 if (pen != arg && atoi_check(last, &ttl))
3857 last = pen;
3858
3859 target = canonicalise_opt(last);
3860
3861 while (arg != last)
3862 {
Petr Menšík56f06232018-03-06 23:13:32 +00003863 int arglen = strlen(arg);
Simon Kelleya1d973f2016-12-22 22:09:50 +00003864 alias = canonicalise_opt(arg);
Simon Kelley56144132017-05-03 22:54:09 +01003865
3866 if (!alias || !target)
3867 ret_err(_("bad CNAME"));
Simon Kelleya1d973f2016-12-22 22:09:50 +00003868
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003869 for (new = daemon->cnames; new; new = new->next)
Simon Kelley56144132017-05-03 22:54:09 +01003870 if (hostname_isequal(new->alias, alias))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003871 ret_err(_("duplicate CNAME"));
3872 new = opt_malloc(sizeof(struct cname));
3873 new->next = daemon->cnames;
3874 daemon->cnames = new;
3875 new->alias = alias;
3876 new->target = target;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00003877 new->ttl = ttl;
Simon Kelleya1d973f2016-12-22 22:09:50 +00003878
Petr Menšík56f06232018-03-06 23:13:32 +00003879 for (arg += arglen+1; *arg && isspace(*arg); arg++);
Simon Kelley9009d742008-11-14 20:04:27 +00003880 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003881
Simon Kelley9009d742008-11-14 20:04:27 +00003882 break;
3883 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003884
3885 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003886 {
3887 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003888 char *dom, *target = NULL;
3889
Simon Kelleyf2621c72007-04-29 19:47:21 +01003890 comma = split(arg);
3891
Simon Kelley1f15b812009-10-13 17:49:32 +01003892 if (!(dom = canonicalise_opt(arg)) ||
3893 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003894 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003895 else
3896 {
3897 new = opt_malloc(sizeof(struct ptr_record));
3898 new->next = daemon->ptr;
3899 daemon->ptr = new;
3900 new->name = dom;
3901 new->ptr = target;
3902 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003903 break;
3904 }
3905
Simon Kelley1a6bca82008-07-11 11:11:42 +01003906 case LOPT_NAPTR: /* --naptr-record */
3907 {
3908 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3909 int k = 0;
3910 struct naptr *new;
3911 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003912 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003913
3914 if ((a[0] = arg))
3915 for (k = 1; k < 7; k++)
3916 if (!(a[k] = split(a[k-1])))
3917 break;
3918
3919
3920 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003921 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003922 !atoi_check16(a[1], &order) ||
3923 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003924 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003925 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003926 else
3927 {
3928 new = opt_malloc(sizeof(struct naptr));
3929 new->next = daemon->naptr;
3930 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003931 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003932 new->flags = opt_string_alloc(a[3]);
3933 new->services = opt_string_alloc(a[4]);
3934 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003935 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003936 new->order = order;
3937 new->pref = pref;
3938 }
3939 break;
3940 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003941
3942 case LOPT_RR: /* dns-rr */
3943 {
3944 struct txt_record *new;
Simon Kelley4caa86d2016-03-16 18:44:16 +00003945 size_t len = 0;
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003946 char *data;
3947 int val;
3948
3949 comma = split(arg);
3950 data = split(comma);
3951
3952 new = opt_malloc(sizeof(struct txt_record));
3953 new->next = daemon->rr;
3954 daemon->rr = new;
3955
3956 if (!atoi_check(comma, &val) ||
3957 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003958 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003959 ret_err(_("bad RR record"));
3960
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003961 new->class = val;
3962 new->len = 0;
3963
3964 if (data)
3965 {
Simon Kelley974a6d02018-08-23 23:01:16 +01003966 new->txt = opt_malloc(len);
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003967 new->len = len;
3968 memcpy(new->txt, data, len);
3969 }
3970
3971 break;
3972 }
3973
Simon Kelley974a6d02018-08-23 23:01:16 +01003974 case LOPT_CAA: /* --caa-record */
3975 {
3976 struct txt_record *new;
3977 char *tag, *value;
3978 int flags;
3979
3980 comma = split(arg);
3981 tag = split(comma);
3982 value = split(tag);
3983
3984 new = opt_malloc(sizeof(struct txt_record));
3985 new->next = daemon->rr;
3986 daemon->rr = new;
3987
3988 if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
3989 ret_err(_("bad CAA record"));
3990
3991 unhide_metas(tag);
3992 unhide_metas(value);
3993
3994 new->len = strlen(tag) + strlen(value) + 2;
3995 new->txt = opt_malloc(new->len);
3996 new->txt[0] = flags;
3997 new->txt[1] = strlen(tag);
3998 memcpy(&new->txt[2], tag, strlen(tag));
3999 memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
4000 new->class = T_CAA;
4001
4002 break;
4003 }
4004
Simon Kelleyf2621c72007-04-29 19:47:21 +01004005 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01004006 {
4007 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00004008 unsigned char *p, *cnt;
4009 size_t len;
4010
4011 comma = split(arg);
4012
Simon Kelley824af852008-02-12 20:43:05 +00004013 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004014 new->next = daemon->txt;
4015 daemon->txt = new;
4016 new->class = C_IN;
Simon Kelleyfec216d2014-03-27 20:54:34 +00004017 new->stat = 0;
4018
Simon Kelley1f15b812009-10-13 17:49:32 +01004019 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004020 ret_err(_("bad TXT record"));
4021
Simon Kelley28866e92011-02-14 20:19:14 +00004022 len = comma ? strlen(comma) : 0;
4023 len += (len/255) + 1; /* room for extra counts */
4024 new->txt = p = opt_malloc(len);
4025
4026 cnt = p++;
4027 *cnt = 0;
4028
4029 while (comma && *comma)
4030 {
4031 unsigned char c = (unsigned char)*comma++;
4032
4033 if (c == ',' || *cnt == 255)
4034 {
4035 if (c != ',')
4036 comma--;
4037 cnt = p++;
4038 *cnt = 0;
4039 }
4040 else
4041 {
4042 *p++ = unhide_meta(c);
4043 (*cnt)++;
4044 }
4045 }
4046
4047 new->len = p - new->txt;
4048
Simon Kelley849a8352006-06-09 21:02:31 +01004049 break;
4050 }
4051
Simon Kelleyf2621c72007-04-29 19:47:21 +01004052 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01004053 {
4054 int port = 1, priority = 0, weight = 0;
4055 char *name, *target = NULL;
4056 struct mx_srv_record *new;
4057
Simon Kelleyf2621c72007-04-29 19:47:21 +01004058 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01004059
Simon Kelley1f15b812009-10-13 17:49:32 +01004060 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004061 ret_err(_("bad SRV record"));
4062
Simon Kelley849a8352006-06-09 21:02:31 +01004063 if (comma)
4064 {
4065 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004066 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004067 if (!(target = canonicalise_opt(arg)))
4068 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00004069
Simon Kelley849a8352006-06-09 21:02:31 +01004070 if (comma)
4071 {
4072 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004073 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004074 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004075 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00004076
Simon Kelley849a8352006-06-09 21:02:31 +01004077 if (comma)
4078 {
4079 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01004080 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01004081 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004082 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00004083
Simon Kelley407a1f32016-03-01 17:06:07 +00004084 if (comma && !atoi_check16(comma, &weight))
4085 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01004086 }
4087 }
4088 }
4089
Simon Kelley824af852008-02-12 20:43:05 +00004090 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01004091 new->next = daemon->mxnames;
4092 daemon->mxnames = new;
4093 new->issrv = 1;
4094 new->name = name;
4095 new->target = target;
4096 new->srvport = port;
4097 new->priority = priority;
4098 new->weight = weight;
4099 break;
4100 }
Simon Kelley7622fc02009-06-04 20:32:05 +01004101
Simon Kelleye759d422012-03-16 13:18:57 +00004102 case LOPT_HOST_REC: /* --host-record */
4103 {
4104 struct host_record *new = opt_malloc(sizeof(struct host_record));
4105 memset(new, 0, sizeof(struct host_record));
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004106 new->ttl = -1;
4107
Simon Kelleye759d422012-03-16 13:18:57 +00004108 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004109 ret_err(_("Bad host-record"));
4110
4111 while (arg)
4112 {
4113 struct all_addr addr;
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004114 char *dig;
4115
4116 for (dig = arg; *dig != 0; dig++)
4117 if (*dig < '0' || *dig > '9')
4118 break;
4119 if (*dig == 0)
4120 new->ttl = atoi(arg);
4121 else if (inet_pton(AF_INET, arg, &addr))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004122 new->addr = addr.addr.addr4;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004123 else if (inet_pton(AF_INET6, arg, &addr))
4124 new->addr6 = addr.addr.addr6;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004125 else
4126 {
4127 int nomem;
4128 char *canon = canonicalise(arg, &nomem);
4129 struct name_list *nl = opt_malloc(sizeof(struct name_list));
4130 if (!canon)
4131 ret_err(_("Bad name in host-record"));
4132
4133 nl->name = canon;
4134 /* keep order, so that PTR record goes to first name */
4135 nl->next = NULL;
4136 if (!new->names)
4137 new->names = nl;
4138 else
4139 {
4140 struct name_list *tmp;
4141 for (tmp = new->names; tmp->next; tmp = tmp->next);
4142 tmp->next = nl;
4143 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004144 }
Simon Kelleye4807d82012-09-27 21:52:26 +01004145
4146 arg = comma;
4147 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004148 }
Simon Kelleye759d422012-03-16 13:18:57 +00004149
4150 /* Keep list order */
4151 if (!daemon->host_records_tail)
4152 daemon->host_records = new;
4153 else
4154 daemon->host_records_tail->next = new;
4155 new->next = NULL;
4156 daemon->host_records_tail = new;
4157 break;
4158 }
Simon Kelley0fc2f312014-01-08 10:26:58 +00004159
4160#ifdef HAVE_DNSSEC
Simon Kelleyf3e57872018-07-20 21:10:48 +01004161 case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
Simon Kelleyf6e62e22015-03-01 18:17:54 +00004162 daemon->timestamp_file = opt_string_alloc(arg);
4163 break;
4164
Simon Kelleyf3e57872018-07-20 21:10:48 +01004165 case LOPT_DNSSEC_CHECK: /* --dnssec-check-unsigned */
Simon Kelleya6918532018-04-15 16:20:52 +01004166 if (arg)
4167 {
4168 if (strcmp(arg, "no") == 0)
4169 set_option_bool(OPT_DNSSEC_IGN_NS);
4170 else
4171 ret_err(_("bad value for dnssec-check-unsigned"));
4172 }
4173 break;
4174
Simon Kelleyf3e57872018-07-20 21:10:48 +01004175 case LOPT_TRUST_ANCHOR: /* --trust-anchor */
Simon Kelley0fc2f312014-01-08 10:26:58 +00004176 {
Simon Kelleyee415862014-02-11 11:07:22 +00004177 struct ds_config *new = opt_malloc(sizeof(struct ds_config));
4178 char *cp, *cp1, *keyhex, *digest, *algo = NULL;
4179 int len;
Simon Kelleycbf13a22014-01-25 17:59:14 +00004180
4181 new->class = C_IN;
Simon Kelley0fc2f312014-01-08 10:26:58 +00004182
Simon Kelleycbf13a22014-01-25 17:59:14 +00004183 if ((comma = split(arg)) && (algo = split(comma)))
4184 {
4185 int class = 0;
4186 if (strcmp(comma, "IN") == 0)
4187 class = C_IN;
4188 else if (strcmp(comma, "CH") == 0)
4189 class = C_CHAOS;
4190 else if (strcmp(comma, "HS") == 0)
4191 class = C_HESIOD;
4192
4193 if (class != 0)
4194 {
4195 new->class = class;
4196 comma = algo;
4197 algo = split(comma);
4198 }
4199 }
4200
Simon Kelleyee415862014-02-11 11:07:22 +00004201 if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
4202 !atoi_check16(comma, &new->keytag) ||
4203 !atoi_check8(algo, &new->algo) ||
4204 !atoi_check8(digest, &new->digest_type) ||
Simon Kelleycbf13a22014-01-25 17:59:14 +00004205 !(new->name = canonicalise_opt(arg)))
Simon Kelleyee415862014-02-11 11:07:22 +00004206 ret_err(_("bad trust anchor"));
Simon Kelleycbf13a22014-01-25 17:59:14 +00004207
Simon Kelley0fc2f312014-01-08 10:26:58 +00004208 /* Upper bound on length */
Simon Kelleyee415862014-02-11 11:07:22 +00004209 len = (2*strlen(keyhex))+1;
4210 new->digest = opt_malloc(len);
4211 unhide_metas(keyhex);
4212 /* 4034: "Whitespace is allowed within digits" */
4213 for (cp = keyhex; *cp; )
4214 if (isspace(*cp))
4215 for (cp1 = cp; *cp1; cp1++)
4216 *cp1 = *(cp1+1);
4217 else
4218 cp++;
4219 if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
4220 ret_err(_("bad HEX in trust anchor"));
Simon Kelley0fc2f312014-01-08 10:26:58 +00004221
Simon Kelleyee415862014-02-11 11:07:22 +00004222 new->next = daemon->ds;
4223 daemon->ds = new;
4224
Simon Kelley0fc2f312014-01-08 10:26:58 +00004225 break;
4226 }
4227#endif
4228
Simon Kelley7622fc02009-06-04 20:32:05 +01004229 default:
Simon Kelley0fc2f312014-01-08 10:26:58 +00004230 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"));
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004231
Simon Kelley849a8352006-06-09 21:02:31 +01004232 }
Simon Kelley824af852008-02-12 20:43:05 +00004233
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004234 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004235}
4236
Simon Kelley28866e92011-02-14 20:19:14 +00004237static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01004238{
Simon Kelley824af852008-02-12 20:43:05 +00004239 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004240 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01004241
4242 while (fgets(buff, MAXDNAME, f))
4243 {
Simon Kelley7b1eae42014-02-20 13:43:28 +00004244 int white, i;
4245 volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
Simon Kelley13dee6f2017-02-28 16:51:58 +00004246 char *errmess, *p, *arg, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004247 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00004248
Simon Kelley824af852008-02-12 20:43:05 +00004249 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley7b1eae42014-02-20 13:43:28 +00004250 if (option != 0 || hard_opt == LOPT_REV_SERV)
Simon Kelley824af852008-02-12 20:43:05 +00004251 {
4252 if (setjmp(mem_jmp))
4253 continue;
4254 mem_recover = 1;
4255 }
4256
Simon Kelley13dee6f2017-02-28 16:51:58 +00004257 arg = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01004258 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00004259 errmess = NULL;
4260
Simon Kelley849a8352006-06-09 21:02:31 +01004261 /* Implement quotes, inside quotes we allow \\ \" \n and \t
4262 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004263 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01004264 {
4265 if (*p == '"')
4266 {
4267 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004268
Simon Kelley849a8352006-06-09 21:02:31 +01004269 for(; *p && *p != '"'; p++)
4270 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004271 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01004272 {
4273 if (p[1] == 't')
4274 p[1] = '\t';
4275 else if (p[1] == 'n')
4276 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01004277 else if (p[1] == 'b')
4278 p[1] = '\b';
4279 else if (p[1] == 'r')
4280 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00004281 else if (p[1] == 'e') /* escape */
4282 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01004283 memmove(p, p+1, strlen(p+1)+1);
4284 }
4285 *p = hide_meta(*p);
4286 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004287
4288 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01004289 {
4290 errmess = _("missing \"");
4291 goto oops;
4292 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004293
4294 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01004295 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01004296
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004297 if (isspace(*p))
4298 {
4299 *p = ' ';
4300 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01004301 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004302 else
4303 {
4304 if (white && *p == '#')
4305 {
4306 *p = 0;
4307 break;
4308 }
4309 white = 0;
4310 }
Simon Kelley849a8352006-06-09 21:02:31 +01004311 }
4312
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004313
4314 /* strip leading spaces */
4315 for (start = buff; *start && *start == ' '; start++);
4316
4317 /* strip trailing spaces */
4318 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
4319
4320 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01004321 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004322 else
4323 start[len] = 0;
4324
Simon Kelley611ebc52012-07-16 16:23:46 +01004325 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004326 arg = start;
4327 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01004328 {
4329 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004330 for (arg = p+1; *arg == ' '; arg++);
4331 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01004332 *p = 0;
4333 }
4334 else
4335 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00004336
Simon Kelley611ebc52012-07-16 16:23:46 +01004337 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004338 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01004339 for (option = 0, i = 0; opts[i].name; i++)
4340 if (strcmp(opts[i].name, start) == 0)
4341 {
4342 option = opts[i].val;
4343 break;
4344 }
4345
4346 if (!option)
4347 errmess = _("bad option");
4348 else if (opts[i].has_arg == 0 && arg)
4349 errmess = _("extraneous parameter");
4350 else if (opts[i].has_arg == 1 && !arg)
4351 errmess = _("missing parameter");
Simon Kelley7b1eae42014-02-20 13:43:28 +00004352 else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
4353 errmess = _("illegal option");
Simon Kelley5aabfc72007-08-29 11:24:47 +01004354 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004355
4356 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00004357 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004358 strcpy(daemon->namebuff, errmess);
4359
Simon Kelley9bafdc62018-08-21 22:53:38 +01004360 if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
Simon Kelleyf2621c72007-04-29 19:47:21 +01004361 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004362 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00004363 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004364 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004365 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004366 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01004367 }
Simon Kelley849a8352006-06-09 21:02:31 +01004368 }
4369
Simon Kelley8ef5ada2010-06-03 19:42:45 +01004370 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01004371 fclose(f);
4372}
4373
Simon Kelley4f7bb572018-03-08 18:47:08 +00004374#if defined(HAVE_DHCP) && defined(HAVE_INOTIFY)
Simon Kelley70d18732015-01-31 19:59:29 +00004375int option_read_dynfile(char *file, int flags)
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004376{
Simon Kelleyf9c86372015-02-03 21:52:48 +00004377 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
4378
Simon Kelley70d18732015-01-31 19:59:29 +00004379 if (flags & AH_DHCP_HST)
4380 return one_file(file, LOPT_BANK);
4381 else if (flags & AH_DHCP_OPT)
4382 return one_file(file, LOPT_OPTS);
Simon Kelleyf9c86372015-02-03 21:52:48 +00004383
Simon Kelley70d18732015-01-31 19:59:29 +00004384 return 0;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004385}
4386#endif
4387
Simon Kelley395eb712012-07-06 22:07:05 +01004388static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00004389{
4390 FILE *f;
4391 int nofile_ok = 0;
4392 static int read_stdin = 0;
4393 static struct fileread {
4394 dev_t dev;
4395 ino_t ino;
4396 struct fileread *next;
4397 } *filesread = NULL;
4398
4399 if (hard_opt == '7')
4400 {
4401 /* default conf-file reading */
4402 hard_opt = 0;
4403 nofile_ok = 1;
4404 }
4405
4406 if (hard_opt == 0 && strcmp(file, "-") == 0)
4407 {
4408 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01004409 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004410 read_stdin = 1;
4411 file = "stdin";
4412 f = stdin;
4413 }
4414 else
4415 {
4416 /* ignore repeated files. */
4417 struct stat statbuf;
4418
4419 if (hard_opt == 0 && stat(file, &statbuf) == 0)
4420 {
4421 struct fileread *r;
4422
4423 for (r = filesread; r; r = r->next)
4424 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01004425 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004426
4427 r = safe_malloc(sizeof(struct fileread));
4428 r->next = filesread;
4429 filesread = r;
4430 r->dev = statbuf.st_dev;
4431 r->ino = statbuf.st_ino;
4432 }
4433
4434 if (!(f = fopen(file, "r")))
4435 {
4436 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01004437 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00004438 else
4439 {
4440 char *str = _("cannot read %s: %s");
4441 if (hard_opt != 0)
4442 {
4443 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01004444 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00004445 }
4446 else
4447 die(str, file, EC_FILE);
4448 }
4449 }
4450 }
4451
4452 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01004453 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00004454}
4455
4456/* expand any name which is a directory */
4457struct hostsfile *expand_filelist(struct hostsfile *list)
4458{
Simon Kelley19c51cf2014-03-18 22:38:30 +00004459 unsigned int i;
Simon Kelley28866e92011-02-14 20:19:14 +00004460 struct hostsfile *ah;
4461
Simon Kelley19c51cf2014-03-18 22:38:30 +00004462 /* find largest used index */
4463 for (i = SRC_AH, ah = list; ah; ah = ah->next)
Simon Kelley28866e92011-02-14 20:19:14 +00004464 {
4465 if (i <= ah->index)
4466 i = ah->index + 1;
4467
4468 if (ah->flags & AH_DIR)
4469 ah->flags |= AH_INACTIVE;
4470 else
4471 ah->flags &= ~AH_INACTIVE;
4472 }
4473
4474 for (ah = list; ah; ah = ah->next)
4475 if (!(ah->flags & AH_INACTIVE))
4476 {
4477 struct stat buf;
4478 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
4479 {
4480 DIR *dir_stream;
4481 struct dirent *ent;
4482
4483 /* don't read this as a file */
4484 ah->flags |= AH_INACTIVE;
Simon Kelley5f4dc5c2015-01-20 20:51:02 +00004485
Simon Kelley28866e92011-02-14 20:19:14 +00004486 if (!(dir_stream = opendir(ah->fname)))
4487 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
4488 ah->fname, strerror(errno));
4489 else
4490 {
4491 while ((ent = readdir(dir_stream)))
4492 {
4493 size_t lendir = strlen(ah->fname);
4494 size_t lenfile = strlen(ent->d_name);
4495 struct hostsfile *ah1;
4496 char *path;
4497
4498 /* ignore emacs backups and dotfiles */
4499 if (lenfile == 0 ||
4500 ent->d_name[lenfile - 1] == '~' ||
4501 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
4502 ent->d_name[0] == '.')
4503 continue;
4504
4505 /* see if we have an existing record.
4506 dir is ah->fname
4507 file is ent->d_name
4508 path to match is ah1->fname */
4509
4510 for (ah1 = list; ah1; ah1 = ah1->next)
4511 {
4512 if (lendir < strlen(ah1->fname) &&
4513 strstr(ah1->fname, ah->fname) == ah1->fname &&
4514 ah1->fname[lendir] == '/' &&
4515 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
4516 {
4517 ah1->flags &= ~AH_INACTIVE;
4518 break;
4519 }
4520 }
4521
4522 /* make new record */
4523 if (!ah1)
4524 {
4525 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
4526 continue;
4527
4528 if (!(path = whine_malloc(lendir + lenfile + 2)))
4529 {
4530 free(ah1);
4531 continue;
4532 }
4533
4534 strcpy(path, ah->fname);
4535 strcat(path, "/");
4536 strcat(path, ent->d_name);
4537 ah1->fname = path;
4538 ah1->index = i++;
4539 ah1->flags = AH_DIR;
4540 ah1->next = list;
4541 list = ah1;
4542 }
4543
4544 /* inactivate record if not regular file */
4545 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
4546 ah1->flags |= AH_INACTIVE;
4547
4548 }
4549 closedir(dir_stream);
4550 }
4551 }
4552 }
4553
4554 return list;
4555}
4556
Simon Kelley7b1eae42014-02-20 13:43:28 +00004557void read_servers_file(void)
4558{
4559 FILE *f;
4560
4561 if (!(f = fopen(daemon->servers_file, "r")))
4562 {
4563 my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
4564 return;
4565 }
4566
4567 mark_servers(SERV_FROM_FILE);
4568 cleanup_servers();
4569
4570 read_file(daemon->servers_file, f, LOPT_REV_SERV);
4571}
4572
Simon Kelley28866e92011-02-14 20:19:14 +00004573
Simon Kelley7622fc02009-06-04 20:32:05 +01004574#ifdef HAVE_DHCP
Simon Kelley4f7bb572018-03-08 18:47:08 +00004575static void clear_dynamic_conf(void)
4576{
4577 struct dhcp_config *configs, *cp, **up;
4578
4579 /* remove existing... */
4580 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
4581 {
4582 cp = configs->next;
4583
4584 if (configs->flags & CONFIG_BANK)
4585 {
4586 struct hwaddr_config *mac, *tmp;
4587 struct dhcp_netid_list *list, *tmplist;
4588
4589 for (mac = configs->hwaddr; mac; mac = tmp)
4590 {
4591 tmp = mac->next;
4592 free(mac);
4593 }
4594
4595 if (configs->flags & CONFIG_CLID)
4596 free(configs->clid);
4597
4598 for (list = configs->netid; list; list = tmplist)
4599 {
4600 free(list->list);
4601 tmplist = list->next;
4602 free(list);
4603 }
4604
4605 if (configs->flags & CONFIG_NAME)
4606 free(configs->hostname);
4607
4608 *up = configs->next;
4609 free(configs);
4610 }
4611 else
4612 up = &configs->next;
4613 }
4614}
4615
4616static void clear_dynamic_opt(void)
4617{
4618 struct dhcp_opt *opts, *cp, **up;
4619 struct dhcp_netid *id, *next;
4620
4621 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
4622 {
4623 cp = opts->next;
4624
4625 if (opts->flags & DHOPT_BANK)
4626 {
4627 if ((opts->flags & DHOPT_VENDOR))
4628 free(opts->u.vendor_class);
4629 free(opts->val);
4630 for (id = opts->netid; id; id = next)
4631 {
4632 next = id->next;
4633 free(id->net);
4634 free(id);
4635 }
4636 *up = opts->next;
4637 free(opts);
4638 }
4639 else
4640 up = &opts->next;
4641 }
4642}
4643
Simon Kelley824af852008-02-12 20:43:05 +00004644void reread_dhcp(void)
4645{
Simon Kelley4f7bb572018-03-08 18:47:08 +00004646 struct hostsfile *hf;
Simon Kelley28866e92011-02-14 20:19:14 +00004647
Simon Kelley4f7bb572018-03-08 18:47:08 +00004648 /* Do these even if there is no daemon->dhcp_hosts_file or
4649 daemon->dhcp_opts_file since entries may have been created by the
4650 inotify dynamic file reading system. */
4651
4652 clear_dynamic_conf();
4653 clear_dynamic_opt();
4654
4655 if (daemon->dhcp_hosts_file)
Simon Kelley824af852008-02-12 20:43:05 +00004656 {
Simon Kelley28866e92011-02-14 20:19:14 +00004657 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
4658 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
Simon Kelley4f7bb572018-03-08 18:47:08 +00004659 if (!(hf->flags & AH_INACTIVE))
4660 {
4661 if (one_file(hf->fname, LOPT_BANK))
4662 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
4663 }
Simon Kelley824af852008-02-12 20:43:05 +00004664 }
4665
4666 if (daemon->dhcp_opts_file)
4667 {
Simon Kelley28866e92011-02-14 20:19:14 +00004668 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4669 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4670 if (!(hf->flags & AH_INACTIVE))
4671 {
Simon Kelley395eb712012-07-06 22:07:05 +01004672 if (one_file(hf->fname, LOPT_OPTS))
4673 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004674 }
Simon Kelley824af852008-02-12 20:43:05 +00004675 }
Simon Kelley4f7bb572018-03-08 18:47:08 +00004676
4677# ifdef HAVE_INOTIFY
4678 /* Setup notify and read pre-existing files. */
4679 set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
4680# endif
Simon Kelley824af852008-02-12 20:43:05 +00004681}
Simon Kelley7622fc02009-06-04 20:32:05 +01004682#endif
Simon Kelley4f7bb572018-03-08 18:47:08 +00004683
Simon Kelley5aabfc72007-08-29 11:24:47 +01004684void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004685{
Neil Jerram3bd4c472018-01-18 22:49:38 +00004686 size_t argbuf_size = MAXDNAME;
4687 char *argbuf = opt_malloc(argbuf_size);
Simon Kelley824af852008-02-12 20:43:05 +00004688 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004689 int option, conffile_opt = '7', testmode = 0;
Simon Kelley90cb2222015-07-05 21:59:10 +01004690 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004691
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004692 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004693
Simon Kelley824af852008-02-12 20:43:05 +00004694 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004695 memset(daemon, 0, sizeof(struct daemon));
4696 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004697
Simon Kelley3be34542004-09-11 19:12:13 +01004698 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004699 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004700 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004701 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004702 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4703 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004704 daemon->default_resolv.is_default = 1;
4705 daemon->default_resolv.name = RESOLVFILE;
4706 daemon->resolv_files = &daemon->default_resolv;
4707 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004708 daemon->runfile = RUNFILE;
4709 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004710 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004711 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004712 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004713 daemon->auth_ttl = AUTH_TTL;
4714 daemon->soa_refresh = SOA_REFRESH;
4715 daemon->soa_retry = SOA_RETRY;
4716 daemon->soa_expiry = SOA_EXPIRY;
Hans Dedecker926332a2016-01-23 10:48:12 +00004717 daemon->max_port = MAX_PORT;
Simon Kelleybaf553d2018-01-29 22:49:27 +00004718 daemon->min_port = MIN_PORT;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01004719
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004720#ifndef NO_ID
Simon Kelleyfec216d2014-03-27 20:54:34 +00004721 add_txt("version.bind", "dnsmasq-" VERSION, 0 );
4722 add_txt("authors.bind", "Simon Kelley", 0);
4723 add_txt("copyright.bind", COPYRIGHT, 0);
4724 add_txt("cachesize.bind", NULL, TXT_STAT_CACHESIZE);
4725 add_txt("insertions.bind", NULL, TXT_STAT_INSERTS);
4726 add_txt("evictions.bind", NULL, TXT_STAT_EVICTIONS);
4727 add_txt("misses.bind", NULL, TXT_STAT_MISSES);
4728 add_txt("hits.bind", NULL, TXT_STAT_HITS);
4729#ifdef HAVE_AUTH
4730 add_txt("auth.bind", NULL, TXT_STAT_AUTH);
4731#endif
4732 add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
Kevin Darbyshire-Bryant7ac9ae12016-09-09 20:52:08 +01004733#endif
Simon Kelley0a852542005-03-23 20:28:59 +00004734
Simon Kelley849a8352006-06-09 21:02:31 +01004735 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004736 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004737#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004738 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004739#else
Simon Kelley849a8352006-06-09 21:02:31 +01004740 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004741#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004742
4743 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004744 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004745 for (; optind < argc; optind++)
4746 {
4747 unsigned char *c = (unsigned char *)argv[optind];
4748 for (; *c != 0; c++)
4749 if (!isspace(*c))
4750 die(_("junk found in command line"), NULL, EC_BADCONF);
4751 }
Simon Kelley28866e92011-02-14 20:19:14 +00004752 break;
4753 }
4754
Simon Kelley849a8352006-06-09 21:02:31 +01004755 /* Copy optarg so that argv doesn't get changed */
4756 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004757 {
Neil Jerram3bd4c472018-01-18 22:49:38 +00004758 if (strlen(optarg) >= argbuf_size)
4759 {
4760 free(argbuf);
4761 argbuf_size = strlen(optarg) + 1;
4762 argbuf = opt_malloc(argbuf_size);
4763 }
Petr Menšík47b45b22018-08-15 18:17:00 +02004764 safe_strncpy(argbuf, optarg, argbuf_size);
Neil Jerram3bd4c472018-01-18 22:49:38 +00004765 arg = argbuf;
Simon Kelley849a8352006-06-09 21:02:31 +01004766 }
4767 else
4768 arg = NULL;
4769
4770 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004771 if (option == LOPT_TEST)
4772 testmode = 1;
4773 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004774 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004775#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004776 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004777 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004778#ifdef HAVE_DHCP6
4779 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4780 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004781#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004782 else
4783#endif
4784 do_usage();
4785
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004786 exit(0);
4787 }
Simon Kelley849a8352006-06-09 21:02:31 +01004788 else if (option == 'v')
4789 {
4790 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004791 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004792 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4793 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004794 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004795 exit(0);
4796 }
Simon Kelley849a8352006-06-09 21:02:31 +01004797 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004798 {
Simon Kelley28866e92011-02-14 20:19:14 +00004799 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004800 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004801 }
Simon Kelley849a8352006-06-09 21:02:31 +01004802 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004803 {
Simon Kelley26128d22004-11-14 16:43:54 +00004804#ifdef HAVE_GETOPT_LONG
Simon Kelley7b1eae42014-02-20 13:43:28 +00004805 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004806#else
Simon Kelley7b1eae42014-02-20 13:43:28 +00004807 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
Simon Kelley849a8352006-06-09 21:02:31 +01004808#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004809 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004810 }
4811 }
Simon Kelley849a8352006-06-09 21:02:31 +01004812
Neil Jerram3bd4c472018-01-18 22:49:38 +00004813 free(argbuf);
4814
Simon Kelley849a8352006-06-09 21:02:31 +01004815 if (conffile)
Chen Wei28b879a2015-02-17 22:07:35 +00004816 {
4817 one_file(conffile, conffile_opt);
Simon Kelley90cb2222015-07-05 21:59:10 +01004818 if (conffile_opt == 0)
4819 free(conffile);
Chen Wei28b879a2015-02-17 22:07:35 +00004820 }
Simon Kelley849a8352006-06-09 21:02:31 +01004821
Simon Kelley1a6bca82008-07-11 11:11:42 +01004822 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004823 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004824 {
4825 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004826 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley14ffa072016-04-25 16:36:44 +01004827 if (!(tmp->flags & SERV_HAS_SOURCE))
4828 {
4829 if (tmp->source_addr.sa.sa_family == AF_INET)
4830 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01004831 else if (tmp->source_addr.sa.sa_family == AF_INET6)
4832 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley14ffa072016-04-25 16:36:44 +01004833 }
4834 }
4835
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004836 if (daemon->host_records)
4837 {
4838 struct host_record *hr;
4839
4840 for (hr = daemon->host_records; hr; hr = hr->next)
4841 if (hr->ttl == -1)
4842 hr->ttl = daemon->local_ttl;
4843 }
4844
4845 if (daemon->cnames)
4846 {
Simon Kelley903df072017-01-19 17:22:00 +00004847 struct cname *cn, *cn2, *cn3;
4848
4849#define NOLOOP 1
4850#define TESTLOOP 2
4851
4852 /* Fill in TTL for CNAMES noe we have local_ttl.
4853 Also prepare to do loop detection. */
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004854 for (cn = daemon->cnames; cn; cn = cn->next)
Simon Kelley903df072017-01-19 17:22:00 +00004855 {
4856 if (cn->ttl == -1)
4857 cn->ttl = daemon->local_ttl;
4858 cn->flag = 0;
4859 cn->targetp = NULL;
4860 for (cn2 = daemon->cnames; cn2; cn2 = cn2->next)
4861 if (hostname_isequal(cn->target, cn2->alias))
4862 {
4863 cn->targetp = cn2;
4864 break;
4865 }
4866 }
4867
4868 /* Find any CNAME loops.*/
4869 for (cn = daemon->cnames; cn; cn = cn->next)
4870 {
4871 for (cn2 = cn->targetp; cn2; cn2 = cn2->targetp)
4872 {
4873 if (cn2->flag == NOLOOP)
4874 break;
4875
4876 if (cn2->flag == TESTLOOP)
4877 die(_("CNAME loop involving %s"), cn->alias, EC_BADCONF);
4878
4879 cn2->flag = TESTLOOP;
4880 }
4881
4882 for (cn3 = cn->targetp; cn3 != cn2; cn3 = cn3->targetp)
4883 cn3->flag = NOLOOP;
4884 }
Simon Kelleydf3d54f2016-02-24 21:03:38 +00004885 }
4886
Simon Kelley3be34542004-09-11 19:12:13 +01004887 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004888 {
4889 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004890 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004891 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004892 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004893 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004894 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004895 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004896
4897 /* create default, if not specified */
4898 if (daemon->authserver && !daemon->hostmaster)
4899 {
4900 strcpy(buff, "hostmaster.");
4901 strcat(buff, daemon->authserver);
4902 daemon->hostmaster = opt_string_alloc(buff);
4903 }
4904
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004905 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004906 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004907 {
Simon Kelley0a852542005-03-23 20:28:59 +00004908 struct mx_srv_record *mx;
4909
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004910 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004911 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004912
Simon Kelley0a852542005-03-23 20:28:59 +00004913 for (mx = daemon->mxnames; mx; mx = mx->next)
4914 if (!mx->issrv && hostname_isequal(mx->name, buff))
4915 break;
4916
Simon Kelley28866e92011-02-14 20:19:14 +00004917 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004918 {
Simon Kelley824af852008-02-12 20:43:05 +00004919 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004920 mx->next = daemon->mxnames;
4921 mx->issrv = 0;
4922 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004923 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004924 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004925 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004926
Simon Kelley3be34542004-09-11 19:12:13 +01004927 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004928 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004929
4930 for (mx = daemon->mxnames; mx; mx = mx->next)
4931 if (!mx->issrv && !mx->target)
4932 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004933 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004934
Simon Kelley28866e92011-02-14 20:19:14 +00004935 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004936 daemon->resolv_files &&
4937 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004938 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004939 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004940
Simon Kelley28866e92011-02-14 20:19:14 +00004941 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004942 {
4943 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004944 FILE *f;
4945
Simon Kelley28866e92011-02-14 20:19:14 +00004946 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004947 !daemon->resolv_files ||
4948 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004949 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004950
Simon Kelley3be34542004-09-11 19:12:13 +01004951 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004952 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004953
4954 while ((line = fgets(buff, MAXDNAME, f)))
4955 {
4956 char *token = strtok(line, " \t\n\r");
4957
4958 if (!token || strcmp(token, "search") != 0)
4959 continue;
4960
4961 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004962 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004963 break;
4964 }
Simon Kelley3be34542004-09-11 19:12:13 +01004965
Simon Kelleyde379512004-06-22 20:23:33 +01004966 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004967
Simon Kelley3be34542004-09-11 19:12:13 +01004968 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004969 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004970 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004971
4972 if (daemon->domain_suffix)
4973 {
4974 /* add domain for any srv record without one. */
4975 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004976
Simon Kelley3d8df262005-08-29 12:19:27 +01004977 for (srv = daemon->mxnames; srv; srv = srv->next)
4978 if (srv->issrv &&
4979 strchr(srv->name, '.') &&
4980 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4981 {
4982 strcpy(buff, srv->name);
4983 strcat(buff, ".");
4984 strcat(buff, daemon->domain_suffix);
4985 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004986 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004987 }
4988 }
Simon Kelley28866e92011-02-14 20:19:14 +00004989 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004990 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004991
Simon Kelleyc8a80482014-03-05 14:29:54 +00004992 /* If there's access-control config, then ignore --local-service, it's intended
4993 as a system default to keep otherwise unconfigured installations safe. */
4994 if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
4995 reset_option_bool(OPT_LOCAL_SERVICE);
4996
Simon Kelley7622fc02009-06-04 20:32:05 +01004997 if (testmode)
4998 {
4999 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
5000 exit(0);
5001 }
Simon Kelley849a8352006-06-09 21:02:31 +01005002}