blob: 329ad5a1628eac4216fc25bd91016bdfd0684a0d [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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 */
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +010067#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
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100133#define LOPT_PREF_CLSS 321
Simon Kelleyc6309242013-03-07 20:59:28 +0000134#endif
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100135#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
Simon Kelley6bd3a092013-10-11 10:25:56 +0100141
Simon Kelley16972692006-10-16 20:04:18 +0100142
Simon Kelley849a8352006-06-09 21:02:31 +0100143#ifdef HAVE_GETOPT_LONG
144static const struct option opts[] =
145#else
146static const struct myoption opts[] =
147#endif
148 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100149 { "version", 0, 0, 'v' },
150 { "no-hosts", 0, 0, 'h' },
151 { "no-poll", 0, 0, 'n' },
152 { "help", 0, 0, 'w' },
153 { "no-daemon", 0, 0, 'd' },
154 { "log-queries", 0, 0, 'q' },
155 { "user", 2, 0, 'u' },
156 { "group", 2, 0, 'g' },
157 { "resolv-file", 2, 0, 'r' },
158 { "mx-host", 1, 0, 'm' },
159 { "mx-target", 1, 0, 't' },
160 { "cache-size", 2, 0, 'c' },
161 { "port", 1, 0, 'p' },
162 { "dhcp-leasefile", 2, 0, 'l' },
163 { "dhcp-lease", 1, 0, 'l' },
164 { "dhcp-host", 1, 0, 'G' },
165 { "dhcp-range", 1, 0, 'F' },
166 { "dhcp-option", 1, 0, 'O' },
167 { "dhcp-boot", 1, 0, 'M' },
168 { "domain", 1, 0, 's' },
169 { "domain-suffix", 1, 0, 's' },
170 { "interface", 1, 0, 'i' },
171 { "listen-address", 1, 0, 'a' },
172 { "bogus-priv", 0, 0, 'b' },
173 { "bogus-nxdomain", 1, 0, 'B' },
174 { "selfmx", 0, 0, 'e' },
175 { "filterwin2k", 0, 0, 'f' },
176 { "pid-file", 2, 0, 'x' },
177 { "strict-order", 0, 0, 'o' },
178 { "server", 1, 0, 'S' },
179 { "local", 1, 0, LOPT_LOCAL },
180 { "address", 1, 0, 'A' },
181 { "conf-file", 2, 0, 'C' },
182 { "no-resolv", 0, 0, 'R' },
183 { "expand-hosts", 0, 0, 'E' },
184 { "localmx", 0, 0, 'L' },
185 { "local-ttl", 1, 0, 'T' },
186 { "no-negcache", 0, 0, 'N' },
187 { "addn-hosts", 1, 0, 'H' },
188 { "query-port", 1, 0, 'Q' },
189 { "except-interface", 1, 0, 'I' },
190 { "no-dhcp-interface", 1, 0, '2' },
191 { "domain-needed", 0, 0, 'D' },
192 { "dhcp-lease-max", 1, 0, 'X' },
193 { "bind-interfaces", 0, 0, 'z' },
194 { "read-ethers", 0, 0, 'Z' },
195 { "alias", 1, 0, 'V' },
196 { "dhcp-vendorclass", 1, 0, 'U' },
197 { "dhcp-userclass", 1, 0, 'j' },
198 { "dhcp-ignore", 1, 0, 'J' },
199 { "edns-packet-max", 1, 0, 'P' },
200 { "keep-in-foreground", 0, 0, 'k' },
201 { "dhcp-authoritative", 0, 0, 'K' },
202 { "srv-host", 1, 0, 'W' },
203 { "localise-queries", 0, 0, 'y' },
204 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100205 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100206 { "enable-dbus", 2, 0, '1' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100207 { "bootp-dynamic", 2, 0, '3' },
208 { "dhcp-mac", 1, 0, '4' },
209 { "no-ping", 0, 0, '5' },
210 { "dhcp-script", 1, 0, '6' },
211 { "conf-dir", 1, 0, '7' },
212 { "log-facility", 1, 0 ,'8' },
213 { "leasefile-ro", 0, 0, '9' },
214 { "dns-forward-max", 1, 0, '0' },
215 { "clear-on-reload", 0, 0, LOPT_RELOAD },
216 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley2937f8a2013-07-29 19:49:07 +0100217 { "enable-tftp", 2, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100218 { "tftp-secure", 0, 0, LOPT_SECURE },
219 { "tftp-unique-root", 0, 0, LOPT_APREF },
220 { "tftp-root", 1, 0, LOPT_PREFIX },
221 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelley61ce6002012-04-20 21:28:49 +0100222 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100223 { "ptr-record", 1, 0, LOPT_PTR },
224 { "naptr-record", 1, 0, LOPT_NAPTR },
225 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
226 { "dhcp-option-force", 1, 0, LOPT_FORCE },
227 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
228 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
229 { "log-async", 2, 0, LOPT_MAX_LOGS },
230 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
231 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
232 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
233 { "interface-name", 1, 0, LOPT_INTNAME },
234 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
235 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
236 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
237 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
238 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100239 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100240 { "all-servers", 0, 0, LOPT_NOLAST },
241 { "dhcp-match", 1, 0, LOPT_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100242 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100243 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100244 { "max-ttl", 1, 0, LOPT_MAXTTL },
Simon Kelley1d860412012-09-20 20:48:04 +0100245 { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100246 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
247 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
248 { "min-port", 1, 0, LOPT_MINPORT },
249 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
250 { "cname", 1, 0, LOPT_CNAME },
251 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
252 { "pxe-service", 1, 0, LOPT_PXE_SERV },
253 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100254 { "tag-if", 1, 0, LOPT_TAG_IF },
255 { "dhcp-proxy", 2, 0, LOPT_PROXY },
256 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
257 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley28866e92011-02-14 20:19:14 +0000258 { "add-mac", 0, 0, LOPT_ADD_MAC },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100259 { "add-subnet", 2, 0, LOPT_ADD_SBNET },
Simon Kelley28866e92011-02-14 20:19:14 +0000260 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100261 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
262 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000263 { "dhcp-client-update", 0, 0, LOPT_FQDN },
264 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000265 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000266 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000267 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100268 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000269 { "auth-zone", 1, 0, LOPT_AUTHZONE },
270 { "auth-server", 1, 0, LOPT_AUTHSERV },
271 { "auth-ttl", 1, 0, LOPT_AUTHTTL },
272 { "auth-soa", 1, 0, LOPT_AUTHSOA },
Simon Kelleye1ff4192012-12-09 17:08:47 +0000273 { "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
Simon Kelley49678762012-12-09 18:24:58 +0000274 { "auth-peer", 1, 0, LOPT_AUTHPEER },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000275 { "ipset", 1, 0, LOPT_IPSET },
Simon Kelley2bb73af2013-04-24 17:38:19 +0100276 { "synth-domain", 1, 0, LOPT_SYNTH },
Simon Kelleyc6309242013-03-07 20:59:28 +0000277#ifdef OPTION6_PREFIX_CLASS
278 { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
279#endif
Simon Kelleyff7eea22013-09-04 18:01:38 +0100280 { "dhcp-relay", 1, 0, LOPT_RELAY },
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100281 { "ra-param", 1, 0, LOPT_RA_PARAM },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100282 { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
283 { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
284 { "quiet-ra", 0, 0, LOPT_QUIET_RA },
Simon Kelley849a8352006-06-09 21:02:31 +0100285 { NULL, 0, 0, 0 }
286 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000287
Simon Kelley28866e92011-02-14 20:19:14 +0000288
289#define ARG_DUP OPT_LAST
290#define ARG_ONE OPT_LAST + 1
291#define ARG_USED_CL OPT_LAST + 2
292#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000293
Simon Kelley1a6bca82008-07-11 11:11:42 +0100294static struct {
295 int opt;
296 unsigned int rept;
297 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000298 char * const desc;
299 char * const arg;
300} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000301 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
302 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100303 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000304 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
305 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
306 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100307 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
308 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
309 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
310 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
311 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000312 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
313 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100314 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000315 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
316 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100317 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100318 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000319 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
320 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
321 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100322 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
323 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
324 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
325 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
326 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
327 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100328 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
329 { '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 +0000330 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100331 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000332 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100333 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
334 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
335 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
336 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
337 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
338 { 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 +0000339 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
340 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100341 { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000342 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100343 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000344 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
345 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
346 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000347 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000348 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
349 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
350 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
351 { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
352 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100353 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100354 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000355 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
356 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100357 { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000358 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
359 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100360 { '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 +0000361 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
362 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
363 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100364 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
365 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100366 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000367 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100368 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
369 { '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 +0000370 { 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 +0100371 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000372 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
373 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
374 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
375 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100376 { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
377 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000378 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100379 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100380 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100381 { 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 +0100382 { LOPT_TFTP, ARG_DUP, "[=<intr>[,<intr>]]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100383 { LOPT_PREFIX, ARG_DUP, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100384 { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
385 { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000386 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100387 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100388 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100389 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
390 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000391 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100392 { 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 +0100393 { 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 +0000394 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100395 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100396 { LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100397 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
399 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000400 { 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 +0000401 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
402 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Simon Kelleyff7eea22013-09-04 18:01:38 +0100403 { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<interface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
Simon Kelley9009d742008-11-14 20:04:27 +0000404 { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100405 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
406 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
407 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100408 { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
Simon Kelleyed4c0762013-10-08 20:46:34 +0100409 { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100410 { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
411 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
412 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000413 { 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 +0000414 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000415 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleye759d422012-03-16 13:18:57 +0000416 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100417 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000418 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
Simon Kelley86e3b9a2012-11-30 13:46:48 +0000419 { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
Simon Kelley333b2ce2013-01-07 21:46:03 +0000420 { LOPT_AUTHZONE, ARG_DUP, "<domain>,[<subnet>...]", gettext_noop("Domain to export to global DNS"), NULL },
Simon Kelley4f7b3042012-11-28 21:27:02 +0000421 { LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
422 { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
Simon Kelley49678762012-12-09 18:24:58 +0000423 { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
424 { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000425 { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
Simon Kelley48fd1c42013-04-25 09:49:38 +0100426 { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for sythesised names"), NULL },
Simon Kelleyc6309242013-03-07 20:59:28 +0000427#ifdef OPTION6_PREFIX_CLASS
428 { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
429#endif
Simon Kelleyc4cd95d2013-10-10 20:58:11 +0100430 { LOPT_RA_PARAM, ARG_DUP, "<interface>,[high,|low,]<interval>[,<lifetime>]", gettext_noop("Set priority, resend-interval and router-lifetime"), NULL },
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100431 { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
432 { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
433 { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100434 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000435};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000436
Simon Kelley3d8df262005-08-29 12:19:27 +0100437/* We hide metacharaters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100438 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 +0100439 following sequence so that they map to themselves: it is therefore possible to call
440 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000441 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100442 couple of other places.
443 Note that space is included here so that
444 --dhcp-option=3, string
445 has five characters, whilst
446 --dhcp-option=3," string"
447 has six.
448*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100449
Simon Kelleyf2621c72007-04-29 19:47:21 +0100450static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100451
452static char hide_meta(char c)
453{
454 unsigned int i;
455
456 for (i = 0; i < (sizeof(meta) - 1); i++)
457 if (c == meta[i])
458 return (char)i;
459
460 return c;
461}
462
463static char unhide_meta(char cr)
464{
465 unsigned int c = cr;
466
467 if (c < (sizeof(meta) - 1))
468 cr = meta[c];
469
470 return cr;
471}
472
473static void unhide_metas(char *cp)
474{
475 if (cp)
476 for(; *cp; cp++)
477 *cp = unhide_meta(*cp);
478}
479
Simon Kelley824af852008-02-12 20:43:05 +0000480static void *opt_malloc(size_t size)
481{
482 void *ret;
483
484 if (mem_recover)
485 {
486 ret = whine_malloc(size);
487 if (!ret)
488 longjmp(mem_jmp, 1);
489 }
490 else
491 ret = safe_malloc(size);
492
493 return ret;
494}
495
496static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100497{
498 char *ret = NULL;
499
500 if (cp && strlen(cp) != 0)
501 {
Simon Kelley824af852008-02-12 20:43:05 +0000502 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100503 strcpy(ret, cp);
504
505 /* restore hidden metachars */
506 unhide_metas(ret);
507 }
508
509 return ret;
510}
511
Simon Kelley3d8df262005-08-29 12:19:27 +0100512
Simon Kelleyf2621c72007-04-29 19:47:21 +0100513/* find next comma, split string with zero and eliminate spaces.
514 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000515
516static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100517{
518 char *comma, *p;
519
Simon Kelley73a08a22009-02-05 20:28:08 +0000520 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100521 return NULL;
522
523 p = comma;
524 *comma = ' ';
525
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100526 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100527
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100528 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100529 *p = 0;
530
531 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100532}
533
Simon Kelley73a08a22009-02-05 20:28:08 +0000534static char *split(char *s)
535{
536 return split_chr(s, ',');
537}
538
Simon Kelley1f15b812009-10-13 17:49:32 +0100539static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100540{
Simon Kelley1f15b812009-10-13 17:49:32 +0100541 char *ret;
542 int nomem;
543
Simon Kelley3d8df262005-08-29 12:19:27 +0100544 if (!s)
545 return 0;
546
547 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100548 if (!(ret = canonicalise(s, &nomem)) && nomem)
549 {
550 if (mem_recover)
551 longjmp(mem_jmp, 1);
552 else
553 die(_("could not get memory"), NULL, EC_NOMEM);
554 }
555
556 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100557}
558
559static int atoi_check(char *a, int *res)
560{
561 char *p;
562
563 if (!a)
564 return 0;
565
566 unhide_metas(a);
567
568 for (p = a; *p; p++)
569 if (*p < '0' || *p > '9')
570 return 0;
571
572 *res = atoi(a);
573 return 1;
574}
575
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100576static int atoi_check16(char *a, int *res)
577{
578 if (!(atoi_check(a, res)) ||
579 *res < 0 ||
580 *res > 0xffff)
581 return 0;
582
583 return 1;
584}
585
Simon Kelley5aabfc72007-08-29 11:24:47 +0100586static void add_txt(char *name, char *txt)
Simon Kelley0a852542005-03-23 20:28:59 +0000587{
588 size_t len = strlen(txt);
Simon Kelley824af852008-02-12 20:43:05 +0000589 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelley0a852542005-03-23 20:28:59 +0000590
Simon Kelley824af852008-02-12 20:43:05 +0000591 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000592 r->next = daemon->txt;
593 daemon->txt = r;
594 r->class = C_CHAOS;
Simon Kelley824af852008-02-12 20:43:05 +0000595 r->txt = opt_malloc(len+1);
Simon Kelley0a852542005-03-23 20:28:59 +0000596 r->len = len+1;
597 *(r->txt) = len;
598 memcpy((r->txt)+1, txt, len);
599}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000600
Simon Kelley849a8352006-06-09 21:02:31 +0100601static void do_usage(void)
602{
603 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000604 int i, j;
605
606 struct {
607 char handle;
608 int val;
609 } tab[] = {
610 { '$', CACHESIZ },
611 { '*', EDNS_PKTSZ },
612 { '&', MAXLEASES },
613 { '!', FTABSIZ },
614 { '#', TFTP_MAX_CONNECTIONS },
615 { '\0', 0 }
616 };
Simon Kelley849a8352006-06-09 21:02:31 +0100617
618 printf(_("Usage: dnsmasq [options]\n\n"));
619#ifndef HAVE_GETOPT_LONG
620 printf(_("Use short options only on the command line.\n"));
621#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100622 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100623
Simon Kelley1a6bca82008-07-11 11:11:42 +0100624 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100625 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100626 char *desc = usage[i].flagdesc;
627 char *eq = "=";
628
629 if (!desc || *desc == '[')
630 eq = "";
631
632 if (!desc)
633 desc = "";
634
635 for ( j = 0; opts[j].name; j++)
636 if (opts[j].val == usage[i].opt)
637 break;
638 if (usage[i].opt < 256)
639 sprintf(buff, "-%c, ", usage[i].opt);
640 else
641 sprintf(buff, " ");
642
643 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100644 printf("%-40.40s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100645
Simon Kelley849a8352006-06-09 21:02:31 +0100646 if (usage[i].arg)
647 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000648 strcpy(buff, usage[i].arg);
649 for (j = 0; tab[j].handle; j++)
650 if (tab[j].handle == *(usage[i].arg))
651 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100652 }
Simon Kelley849a8352006-06-09 21:02:31 +0100653 printf(_(usage[i].desc), buff);
654 printf("\n");
655 }
656}
657
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100658#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
659
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100660char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
661{
662 int source_port = 0, serv_port = NAMESERVER_PORT;
663 char *portno, *source;
664#ifdef HAVE_IPV6
665 int scope_index = 0;
666 char *scope_id;
667#endif
668
669 if ((source = split_chr(arg, '@')) && /* is there a source. */
670 (portno = split_chr(source, '#')) &&
671 !atoi_check16(portno, &source_port))
672 return _("bad port");
673
674 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
675 !atoi_check16(portno, &serv_port))
676 return _("bad port");
677
678#ifdef HAVE_IPV6
679 scope_id = split_chr(arg, '%');
680#endif
681
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100682 if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100683 {
684 addr->in.sin_port = htons(serv_port);
685 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
686#ifdef HAVE_SOCKADDR_SA_LEN
687 source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
688#endif
689 source_addr->in.sin_addr.s_addr = INADDR_ANY;
690 source_addr->in.sin_port = htons(daemon->query_port);
691
692 if (source)
693 {
694 if (flags)
695 *flags |= SERV_HAS_SOURCE;
696 source_addr->in.sin_port = htons(source_port);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +0100697 if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
Simon Kelleyfaafb3f2012-09-20 14:17:39 +0100698 {
699#if defined(SO_BINDTODEVICE)
700 source_addr->in.sin_addr.s_addr = INADDR_ANY;
701 strncpy(interface, source, IF_NAMESIZE - 1);
702#else
703 return _("interface binding not supported");
704#endif
705 }
706 }
707 }
708#ifdef HAVE_IPV6
709 else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
710 {
711 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
712 return _("bad interface name");
713
714 addr->in6.sin6_port = htons(serv_port);
715 addr->in6.sin6_scope_id = scope_index;
716 source_addr->in6.sin6_addr = in6addr_any;
717 source_addr->in6.sin6_port = htons(daemon->query_port);
718 source_addr->in6.sin6_scope_id = 0;
719 addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
720 addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
721#ifdef HAVE_SOCKADDR_SA_LEN
722 addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
723#endif
724 if (source)
725 {
726 if (flags)
727 *flags |= SERV_HAS_SOURCE;
728 source_addr->in6.sin6_port = htons(source_port);
729 if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
730 {
731#if defined(SO_BINDTODEVICE)
732 source_addr->in6.sin6_addr = in6addr_any;
733 strncpy(interface, source, IF_NAMESIZE - 1);
734#else
735 return _("interface binding not supported");
736#endif
737 }
738 }
739 }
740#endif
741 else
742 return _("bad address");
743
744 return NULL;
745}
746
Simon Kelleyb5a8dd12012-12-10 11:37:25 +0000747#ifdef HAVE_DHCP
748
749static int is_tag_prefix(char *arg)
750{
751 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
752 return 1;
753
754 return 0;
755}
756
757static char *set_prefix(char *arg)
758{
759 if (strstr(arg, "set:") == arg)
760 return arg+4;
761
762 return arg;
763}
764
Simon Kelley832af0b2007-01-21 20:01:28 +0000765/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100766static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000767{
Simon Kelley824af852008-02-12 20:43:05 +0000768 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000769 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000770 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100771 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100772 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000773 u16 opt_len = 0;
774 int is6 = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100775 int option_ok = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000776
777 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000778 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000779 new->netid = NULL;
780 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100781 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000782
Simon Kelleyf2621c72007-04-29 19:47:21 +0100783 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000784 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100785 comma = split(arg);
786
787 for (cp = arg; *cp; cp++)
788 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000789 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100790
791 if (!*cp)
792 {
793 new->opt = atoi(arg);
794 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100795 option_ok = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100796 break;
797 }
798
799 if (strstr(arg, "option:") == arg)
800 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100801 if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
802 {
803 opt_len = lookup_dhcp_len(AF_INET, new->opt);
804 /* option:<optname> must follow tag and vendor string. */
805 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
806 option_ok = 1;
807 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100808 break;
809 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000810#ifdef HAVE_DHCP6
811 else if (strstr(arg, "option6:") == arg)
812 {
813 for (cp = arg+8; *cp; cp++)
814 if (*cp < '0' || *cp > '9')
815 break;
816
817 if (!*cp)
818 {
819 new->opt = atoi(arg+8);
820 opt_len = 0;
Simon Kelleybd08ae62013-04-19 10:22:06 +0100821 option_ok = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000822 }
823 else
Simon Kelley40ef23b2012-03-13 21:59:28 +0000824 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100825 if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
826 {
827 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
828 if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
829 option_ok = 1;
830 }
Simon Kelley40ef23b2012-03-13 21:59:28 +0000831 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000832 /* option6:<opt>|<optname> must follow tag and vendor string. */
833 is6 = 1;
834 break;
835 }
836#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +0100837 else if (strstr(arg, "vendor:") == arg)
838 {
Simon Kelley73a08a22009-02-05 20:28:08 +0000839 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
840 new->flags |= DHOPT_VENDOR;
841 }
842 else if (strstr(arg, "encap:") == arg)
843 {
844 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100845 new->flags |= DHOPT_ENCAPSULATE;
846 }
Simon Kelley316e2732010-01-22 20:16:09 +0000847 else if (strstr(arg, "vi-encap:") == arg)
848 {
849 new->u.encap = atoi(arg+9);
850 new->flags |= DHOPT_RFC3925;
851 if (flags == DHOPT_MATCH)
852 {
Simon Kelleybd08ae62013-04-19 10:22:06 +0100853 option_ok = 1;
Simon Kelley316e2732010-01-22 20:16:09 +0000854 break;
855 }
856 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100857 else
858 {
Simon Kelley824af852008-02-12 20:43:05 +0000859 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100860 /* allow optional "net:" or "tag:" for consistency */
861 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +0000862 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100863 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100864 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100865 new->netid->next = np;
866 np = new->netid;
867 }
868
869 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +0000870 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000871
872#ifdef HAVE_DHCP6
873 if (is6)
874 {
875 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100876 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000877
878 if (opt_len == 0 &&
879 !(new->flags & DHOPT_RFC3925))
Simon Kelleybd08ae62013-04-19 10:22:06 +0100880 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000881 }
882 else
883#endif
884 if (opt_len == 0 &&
885 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelleybd08ae62013-04-19 10:22:06 +0100886 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelley40ef23b2012-03-13 21:59:28 +0000887
Simon Kelley316e2732010-01-22 20:16:09 +0000888 /* option may be missing with rfc3925 match */
Simon Kelleybd08ae62013-04-19 10:22:06 +0100889 if (!option_ok)
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100890 ret_err(_("bad dhcp-option"));
891
892 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +0000893 {
894 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100895 char c;
Simon Kelley28866e92011-02-14 20:19:14 +0000896 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000897 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000898 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100899 dots = 0;
900 for (cp = comma; (c = *cp); cp++)
901 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +0000902 {
903 addrs++;
904 is_dec = is_hex = 0;
905 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100906 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +0000907 {
908 digs++;
909 is_dec = is_addr = 0;
910 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100911 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000912 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000913 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000914 if (cp == comma) /* leading / means a pathname */
915 is_addr = 0;
916 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100917 else if (c == '.')
918 {
Simon Kelley23245c02012-07-18 16:21:11 +0100919 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100920 dots++;
921 }
922 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +0000923 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100924 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +0000925 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100926 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +0000927 {
928 is_addr = 0;
929 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100930 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +0000931 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100932 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +0000933 *cp = 0;
934 }
935 else
936 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100937 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000938 (c >='a' && c <= 'f') ||
939 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000940 {
941 is_hex = 0;
942 if (c != '[' && c != ']')
943 is_addr6 = 0;
944 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000945 }
Simon Kelley28866e92011-02-14 20:19:14 +0000946 else
947 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100948
Simon Kelley28866e92011-02-14 20:19:14 +0000949 if (!found_dig)
950 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000951
Simon Kelleyf2621c72007-04-29 19:47:21 +0100952 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +0100953 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100954 {
955 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000956
957 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100958 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000959
960 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100961 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100962 }
Simon Kelley28866e92011-02-14 20:19:14 +0000963 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000964 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
965 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +0100966
967 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
968 {
969 int val, fac = 1;
970
971 switch (comma[strlen(comma) - 1])
972 {
Simon Kelley42243212012-07-20 15:19:18 +0100973 case 'w':
974 case 'W':
975 fac *= 7;
976 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +0100977 case 'd':
978 case 'D':
979 fac *= 24;
980 /* fall though */
981 case 'h':
982 case 'H':
983 fac *= 60;
984 /* fall through */
985 case 'm':
986 case 'M':
987 fac *= 60;
988 /* fall through */
989 case 's':
990 case 'S':
991 comma[strlen(comma) - 1] = 0;
992 }
993
994 new->len = 4;
995 new->val = opt_malloc(4);
996 val = atoi(comma);
997 *((int *)new->val) = htonl(val * fac);
998 }
999 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001000 {
1001 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +00001002 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +00001003 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
1004 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +00001005 }
1006 else if (is_dec)
1007 {
1008 int i, val = atoi(comma);
1009 /* assume numeric arg is 1 byte except for
1010 options where it is known otherwise.
1011 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001012 if (opt_len != 0)
1013 new->len = opt_len;
1014 else if (val & 0xffff0000)
1015 new->len = 4;
1016 else if (val & 0xff00)
1017 new->len = 2;
1018 else
1019 new->len = 1;
1020
Simon Kelley832af0b2007-01-21 20:01:28 +00001021 if (lenchar == 'b')
1022 new->len = 1;
1023 else if (lenchar == 's')
1024 new->len = 2;
1025 else if (lenchar == 'i')
1026 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001027
Simon Kelley824af852008-02-12 20:43:05 +00001028 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001029 for (i=0; i<new->len; i++)
1030 new->val[i] = val>>((new->len - i - 1)*8);
1031 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001032 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +00001033 {
1034 struct in_addr in;
1035 unsigned char *op;
1036 char *slash;
1037 /* max length of address/subnet descriptor is five bytes,
1038 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +00001039 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +00001040 new->flags |= DHOPT_ADDR;
1041
Simon Kelley572b41e2011-02-18 18:11:18 +00001042 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
1043 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001044 {
Simon Kelley6b010842007-02-12 20:32:07 +00001045 *(op++) = 1; /* RFC 3361 "enc byte" */
1046 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +00001047 }
1048 while (addrs--)
1049 {
1050 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001051 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +00001052 slash = split_chr(cp, '/');
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001053 inet_pton(AF_INET, cp, &in);
Simon Kelley832af0b2007-01-21 20:01:28 +00001054 if (!slash)
1055 {
1056 memcpy(op, &in, INADDRSZ);
1057 op += INADDRSZ;
1058 }
1059 else
1060 {
1061 unsigned char *p = (unsigned char *)&in;
1062 int netsize = atoi(slash);
1063 *op++ = netsize;
1064 if (netsize > 0)
1065 *op++ = *p++;
1066 if (netsize > 8)
1067 *op++ = *p++;
1068 if (netsize > 16)
1069 *op++ = *p++;
1070 if (netsize > 24)
1071 *op++ = *p++;
1072 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1073 }
1074 }
1075 new->len = op - new->val;
1076 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001077 else if (is_addr6 && is6)
1078 {
1079 unsigned char *op;
1080 new->val = op = opt_malloc(16 * addrs);
1081 new->flags |= DHOPT_ADDR6;
1082 while (addrs--)
1083 {
1084 cp = comma;
1085 comma = split(cp);
1086
1087 /* check for [1234::7] */
1088 if (*cp == '[')
1089 cp++;
1090 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
1091 cp[strlen(cp)-1] = 0;
1092
1093 if (inet_pton(AF_INET6, cp, op))
1094 {
1095 op += IN6ADDRSZ;
1096 continue;
1097 }
1098
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001099 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001100 }
1101 new->len = op - new->val;
1102 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001103 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +00001104 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001105 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +00001106 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +00001107 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +00001108 {
1109 /* dns search, RFC 3397, or SIP, RFC 3361 */
1110 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +00001111 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001112 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +00001113 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001114
1115 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001116 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001117
1118 while (arg && *arg)
1119 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001120 char *in, *dom = NULL;
1121 size_t domlen = 1;
1122 /* Allow "." as an empty domain */
1123 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001124 {
Simon Kelleyc52e1892010-06-07 22:01:39 +01001125 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001126 ret_err(_("bad domain in dhcp-option"));
1127
Simon Kelleyc52e1892010-06-07 22:01:39 +01001128 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +00001129 }
Simon Kelleyc52e1892010-06-07 22:01:39 +01001130
1131 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +00001132 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +01001133 {
1134 memcpy(newp, m, header_size + len);
1135 free(m);
1136 }
Simon Kelley824af852008-02-12 20:43:05 +00001137 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +00001138 p = m + header_size;
1139 q = p + len;
1140
1141 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +01001142 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +00001143 {
1144 unsigned char *cp = q++;
1145 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001146 for (j = 0; *in && (*in != '.'); in++, j++)
1147 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +00001148 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +01001149 if (*in)
1150 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +00001151 }
1152 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001153 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001154
Simon Kelley832af0b2007-01-21 20:01:28 +00001155 /* Now tail-compress using earlier names. */
1156 newlen = q - p;
1157 for (tail = p + len; *tail; tail += (*tail) + 1)
1158 for (r = p; r - p < (int)len; r += (*r) + 1)
1159 if (strcmp((char *)r, (char *)tail) == 0)
1160 {
1161 PUTSHORT((r - p) | 0xc000, tail);
1162 newlen = tail - p;
1163 goto end;
1164 }
1165 end:
1166 len = newlen;
1167
1168 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001169 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001170 }
1171
1172 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001173 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001174 m[0] = 0;
1175 new->len = (int) len + header_size;
1176 new->val = m;
1177 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001178#ifdef HAVE_DHCP6
1179 else if (comma && (opt_len & OT_CSTRING))
1180 {
1181 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001182 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001183 unsigned char *p, *newp;
1184
Simon Kelley40ef23b2012-03-13 21:59:28 +00001185 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001186 if (comma[i] == ',')
1187 commas++;
1188
1189 newp = opt_malloc(strlen(comma)+(2*commas));
1190 p = newp;
1191 arg = comma;
1192 comma = split(arg);
1193
1194 while (arg && *arg)
1195 {
1196 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001197 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001198 PUTSHORT(len, p);
1199 memcpy(p, arg, len);
1200 p += len;
1201
1202 arg = comma;
1203 comma = split(arg);
1204 }
1205
1206 new->val = newp;
1207 new->len = p - newp;
1208 }
1209 else if (comma && (opt_len & OT_RFC1035_NAME))
1210 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001211 unsigned char *p = NULL, *newp, *end;
1212 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001213 arg = comma;
1214 comma = split(arg);
1215
1216 while (arg && *arg)
1217 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001218 char *dom = canonicalise_opt(arg);
1219 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001220 ret_err(_("bad domain in dhcp-option"));
1221
Simon Kelley18f0fb02012-03-31 21:18:55 +01001222 newp = opt_malloc(len + strlen(dom) + 2);
1223
1224 if (p)
1225 {
1226 memcpy(newp, p, len);
1227 free(p);
1228 }
1229
1230 p = newp;
1231 end = do_rfc1035_name(p + len, dom);
1232 *end++ = 0;
1233 len = end - p;
1234 free(dom);
1235
Simon Kelley4cb1b322012-02-06 14:30:41 +00001236 arg = comma;
1237 comma = split(arg);
1238 }
1239
Simon Kelley18f0fb02012-03-31 21:18:55 +01001240 new->val = p;
1241 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001242 }
1243#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001244 else
1245 {
1246 new->len = strlen(comma);
1247 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001248 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001249 new->flags |= DHOPT_STRING;
1250 }
1251 }
1252 }
1253
Simon Kelley4cb1b322012-02-06 14:30:41 +00001254 if (!is6 &&
1255 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001256 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001257 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001258 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001259
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001260 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001261 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001262 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1263 !new->netid ||
1264 new->netid->next)
1265 ret_err(_("illegal dhcp-match"));
1266
1267 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001268 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001269 new->next = daemon->dhcp_match6;
1270 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001271 }
1272 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001273 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001274 new->next = daemon->dhcp_match;
1275 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001276 }
Simon Kelley824af852008-02-12 20:43:05 +00001277 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001278 else if (is6)
1279 {
1280 new->next = daemon->dhcp_opts6;
1281 daemon->dhcp_opts6 = new;
1282 }
1283 else
1284 {
1285 new->next = daemon->dhcp_opts;
1286 daemon->dhcp_opts = new;
1287 }
1288
1289 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001290}
1291
Simon Kelley7622fc02009-06-04 20:32:05 +01001292#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001293
Simon Kelley28866e92011-02-14 20:19:14 +00001294void set_option_bool(unsigned int opt)
1295{
1296 if (opt < 32)
1297 daemon->options |= 1u << opt;
1298 else
1299 daemon->options2 |= 1u << (opt - 32);
1300}
1301
Simon Kelley2b5bae92012-06-26 16:55:23 +01001302void reset_option_bool(unsigned int opt)
1303{
1304 if (opt < 32)
1305 daemon->options &= ~(1u << opt);
1306 else
1307 daemon->options2 &= ~(1u << (opt - 32));
1308}
1309
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001310static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
Simon Kelley849a8352006-06-09 21:02:31 +01001311{
1312 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001313 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001314
Simon Kelley832af0b2007-01-21 20:01:28 +00001315 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001316 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001317
Simon Kelley1a6bca82008-07-11 11:11:42 +01001318 for (i=0; usage[i].opt != 0; i++)
1319 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001320 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001321 int rept = usage[i].rept;
1322
Simon Kelley28866e92011-02-14 20:19:14 +00001323 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001324 {
1325 /* command line */
1326 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001327 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001328 if (rept == ARG_ONE)
1329 usage[i].rept = ARG_USED_CL;
1330 }
1331 else
1332 {
1333 /* allow file to override command line */
1334 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001335 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001336 if (rept == ARG_USED_CL || rept == ARG_ONE)
1337 usage[i].rept = ARG_USED_FILE;
1338 }
1339
1340 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1341 {
Simon Kelley28866e92011-02-14 20:19:14 +00001342 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001343 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001344 }
1345
1346 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001347 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001348
Simon Kelley849a8352006-06-09 21:02:31 +01001349 switch (option)
1350 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001351 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001352 {
Simon Kelley824af852008-02-12 20:43:05 +00001353 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001354 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001355 {
Simon Kelley28866e92011-02-14 20:19:14 +00001356 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001357 free(file);
1358 }
Simon Kelley849a8352006-06-09 21:02:31 +01001359 break;
1360 }
1361
Simon Kelleyf2621c72007-04-29 19:47:21 +01001362 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001363 {
1364 DIR *dir_stream;
1365 struct dirent *ent;
1366 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001367 struct list {
1368 char *suffix;
1369 struct list *next;
1370 } *ignore_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001371
Simon Kelley1f15b812009-10-13 17:49:32 +01001372 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001373 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001374 break;
1375
Simon Kelley1f15b812009-10-13 17:49:32 +01001376 for (arg = comma; arg; arg = comma)
1377 {
1378 comma = split(arg);
1379 li = opt_malloc(sizeof(struct list));
1380 li->next = ignore_suffix;
1381 ignore_suffix = li;
1382 /* Have to copy: buffer is overwritten */
1383 li->suffix = opt_string_alloc(arg);
1384 };
1385
Simon Kelley849a8352006-06-09 21:02:31 +01001386 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001387 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001388
Simon Kelley849a8352006-06-09 21:02:31 +01001389 while ((ent = readdir(dir_stream)))
1390 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001391 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001392 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001393
1394 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001395 if (len == 0 ||
1396 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001397 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1398 ent->d_name[0] == '.')
1399 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001400
Simon Kelley1f15b812009-10-13 17:49:32 +01001401 for (li = ignore_suffix; li; li = li->next)
1402 {
1403 /* check for proscribed suffices */
1404 size_t ls = strlen(li->suffix);
1405 if (len > ls &&
1406 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1407 break;
1408 }
1409 if (li)
1410 continue;
1411
Simon Kelley824af852008-02-12 20:43:05 +00001412 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001413 strcpy(path, directory);
1414 strcat(path, "/");
1415 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001416
Simon Kelley39595cf2013-02-04 21:40:07 +00001417 /* files must be readable */
Simon Kelley849a8352006-06-09 21:02:31 +01001418 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001419 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001420
Simon Kelley39595cf2013-02-04 21:40:07 +00001421 /* only reg files allowed. */
1422 if (S_ISREG(buf.st_mode))
1423 one_file(path, 0);
1424
Simon Kelley849a8352006-06-09 21:02:31 +01001425 free(path);
1426 }
1427
1428 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001429 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001430 for(; ignore_suffix; ignore_suffix = li)
1431 {
1432 li = ignore_suffix->next;
1433 free(ignore_suffix->suffix);
1434 free(ignore_suffix);
1435 }
1436
Simon Kelley849a8352006-06-09 21:02:31 +01001437 break;
1438 }
1439
Simon Kelleyed4c0762013-10-08 20:46:34 +01001440 case LOPT_ADD_SBNET: /* --add-subnet */
1441 set_option_bool(OPT_CLIENT_SUBNET);
1442 if (arg)
1443 {
1444 comma = split(arg);
1445 if (!atoi_check(arg, &daemon->addr4_netmask) ||
1446 (comma && !atoi_check(comma, &daemon->addr6_netmask)))
1447 ret_err(gen_err);
1448 }
1449 break;
1450
Simon Kelleyad094272012-08-10 17:10:54 +01001451 case '1': /* --enable-dbus */
1452 set_option_bool(OPT_DBUS);
1453 if (arg)
1454 daemon->dbus_name = opt_string_alloc(arg);
1455 else
1456 daemon->dbus_name = DNSMASQ_SERVICE;
1457 break;
1458
Simon Kelleyf2621c72007-04-29 19:47:21 +01001459 case '8': /* --log-facility */
1460 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001461 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001462 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001463 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001464 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001465#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001466 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001467#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001468 for (i = 0; facilitynames[i].c_name; i++)
1469 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1470 break;
1471
1472 if (facilitynames[i].c_name)
1473 daemon->log_fac = facilitynames[i].c_val;
1474 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001475 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001476#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001477 }
1478 break;
1479
Simon Kelleyf2621c72007-04-29 19:47:21 +01001480 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001481 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001482 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001483
Simon Kelleyf2621c72007-04-29 19:47:21 +01001484 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001485 {
Simon Kelley824af852008-02-12 20:43:05 +00001486 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001487 struct resolvc *new, *list = daemon->resolv_files;
1488
1489 if (list && list->is_default)
1490 {
1491 /* replace default resolv file - possibly with nothing */
1492 if (name)
1493 {
1494 list->is_default = 0;
1495 list->name = name;
1496 }
1497 else
1498 list = NULL;
1499 }
1500 else if (name)
1501 {
Simon Kelley824af852008-02-12 20:43:05 +00001502 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001503 new->next = list;
1504 new->name = name;
1505 new->is_default = 0;
1506 new->mtime = 0;
1507 new->logged = 0;
1508 list = new;
1509 }
1510 daemon->resolv_files = list;
1511 break;
1512 }
1513
Simon Kelleyf2621c72007-04-29 19:47:21 +01001514 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001515 {
1516 int pref = 1;
1517 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001518 char *name, *target = NULL;
1519
Simon Kelleyf2621c72007-04-29 19:47:21 +01001520 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001521 {
1522 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001523 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001524 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001525 }
1526
Simon Kelley1f15b812009-10-13 17:49:32 +01001527 if (!(name = canonicalise_opt(arg)) ||
1528 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001529 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001530
Simon Kelley824af852008-02-12 20:43:05 +00001531 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001532 new->next = daemon->mxnames;
1533 daemon->mxnames = new;
1534 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001535 new->name = name;
1536 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001537 new->weight = pref;
1538 break;
1539 }
1540
Simon Kelleyf2621c72007-04-29 19:47:21 +01001541 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001542 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001543 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001544 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001545
1546#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001547 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001548 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001549 break;
1550
Simon Kelleyc72daea2012-01-05 21:33:27 +00001551 /* Sorry about the gross pre-processor abuse */
1552 case '6': /* --dhcp-script */
1553 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001554# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001555 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001556# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001557 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001558# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001559 if (option == LOPT_LUASCRIPT)
1560# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001561 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001562# else
1563 daemon->luascript = opt_string_alloc(arg);
1564# endif
1565 else
1566 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001567# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001568 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001569#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001570
Simon Kelley28866e92011-02-14 20:19:14 +00001571 case LOPT_DHCP_HOST: /* --dhcp-hostfile */
1572 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001573 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001574 {
Simon Kelley824af852008-02-12 20:43:05 +00001575 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley849a8352006-06-09 21:02:31 +01001576 static int hosts_index = 1;
Simon Kelley824af852008-02-12 20:43:05 +00001577 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001578 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001579 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001580 if (option == 'H')
1581 {
1582 new->next = daemon->addn_hosts;
1583 daemon->addn_hosts = new;
1584 }
1585 else if (option == LOPT_DHCP_HOST)
1586 {
1587 new->next = daemon->dhcp_hosts_file;
1588 daemon->dhcp_hosts_file = new;
1589 }
Simon Kelleye1ff4192012-12-09 17:08:47 +00001590 else if (option == LOPT_DHCP_OPTS)
Simon Kelley28866e92011-02-14 20:19:14 +00001591 {
1592 new->next = daemon->dhcp_opts_file;
1593 daemon->dhcp_opts_file = new;
1594 }
Simon Kelley849a8352006-06-09 21:02:31 +01001595 break;
1596 }
1597
Simon Kelleyf373a152013-09-23 12:47:47 +01001598
1599#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +00001600 case LOPT_AUTHSERV: /* --auth-server */
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001601 if (!(comma = split(arg)))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001602 ret_err(gen_err);
1603
Simon Kelley4f7b3042012-11-28 21:27:02 +00001604 daemon->authserver = opt_string_alloc(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001605 arg = comma;
1606 do {
1607 struct iname *new = opt_malloc(sizeof(struct iname));
1608 comma = split(arg);
1609 new->name = NULL;
1610 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001611 if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
Simon Kelley429798f2012-12-10 20:45:53 +00001612 new->addr.sa.sa_family = AF_INET;
1613#ifdef HAVE_IPV6
1614 else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1615 new->addr.sa.sa_family = AF_INET6;
1616#endif
1617 else
1618 new->name = opt_string_alloc(arg);
1619
1620 new->next = daemon->authinterface;
1621 daemon->authinterface = new;
1622
1623 arg = comma;
1624 } while (arg);
1625
Simon Kelley4f7b3042012-11-28 21:27:02 +00001626 break;
Simon Kelleye1ff4192012-12-09 17:08:47 +00001627
1628 case LOPT_AUTHSFS: /* --auth-sec-servers */
1629 {
1630 struct name_list *new;
1631
1632 do {
1633 comma = split(arg);
Simon Kelley429798f2012-12-10 20:45:53 +00001634 new = opt_malloc(sizeof(struct name_list));
Simon Kelleye1ff4192012-12-09 17:08:47 +00001635 new->name = opt_string_alloc(arg);
1636 new->next = daemon->secondary_forward_server;
1637 daemon->secondary_forward_server = new;
1638 arg = comma;
1639 } while (arg);
1640 break;
1641 }
1642
Simon Kelley4f7b3042012-11-28 21:27:02 +00001643 case LOPT_AUTHZONE: /* --auth-zone */
1644 {
1645 struct auth_zone *new;
1646
1647 comma = split(arg);
Simon Kelley1e14cc02012-12-29 17:27:59 +00001648
Simon Kelley429798f2012-12-10 20:45:53 +00001649 new = opt_malloc(sizeof(struct auth_zone));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001650 new->domain = opt_string_alloc(arg);
1651 new->subnet = NULL;
1652 new->next = daemon->auth_zones;
1653 daemon->auth_zones = new;
1654
1655 while ((arg = comma))
1656 {
1657 int prefixlen = 0;
1658 char *prefix;
Simon Kelley429798f2012-12-10 20:45:53 +00001659 struct subnet *subnet = opt_malloc(sizeof(struct subnet));
Simon Kelley4f7b3042012-11-28 21:27:02 +00001660
1661 subnet->next = new->subnet;
1662 new->subnet = subnet;
1663
1664 comma = split(arg);
1665 prefix = split_chr(arg, '/');
1666
1667 if (prefix && !atoi_check(prefix, &prefixlen))
1668 ret_err(gen_err);
1669
1670 if (inet_pton(AF_INET, arg, &subnet->addr4))
1671 {
1672 subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
1673 subnet->is6 = 0;
1674 }
1675#ifdef HAVE_IPV6
1676 else if (inet_pton(AF_INET6, arg, &subnet->addr6))
1677 {
1678 subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
1679 subnet->is6 = 1;
1680 }
1681#endif
1682 else
1683 ret_err(gen_err);
1684 }
1685 break;
1686 }
1687
1688 case LOPT_AUTHSOA: /* --auth-soa */
1689 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001690 daemon->soa_sn = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001691 if (comma)
1692 {
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001693 char *cp;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001694 arg = comma;
1695 comma = split(arg);
1696 daemon->hostmaster = opt_string_alloc(arg);
Simon Kelley86e3b9a2012-11-30 13:46:48 +00001697 for (cp = daemon->hostmaster; *cp; cp++)
1698 if (*cp == '@')
1699 *cp = '.';
1700
Simon Kelley4f7b3042012-11-28 21:27:02 +00001701 if (comma)
1702 {
1703 arg = comma;
1704 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001705 daemon->soa_refresh = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001706 if (comma)
1707 {
1708 arg = comma;
1709 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001710 daemon->soa_retry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001711 if (comma)
1712 {
1713 arg = comma;
1714 comma = split(arg);
Simon Kelley5c72bb92013-08-19 14:12:59 +01001715 daemon->soa_expiry = (u32)atoi(arg);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001716 }
1717 }
1718 }
1719 }
1720
1721 break;
Simon Kelleyf373a152013-09-23 12:47:47 +01001722#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +00001723
Simon Kelley2bb73af2013-04-24 17:38:19 +01001724 case 's': /* --domain */
1725 case LOPT_SYNTH: /* --synth-domain */
Simon Kelley849a8352006-06-09 21:02:31 +01001726 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00001727 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01001728 else
Simon Kelley9009d742008-11-14 20:04:27 +00001729 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001730 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00001731 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01001732 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001733 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001734 else
1735 {
Simon Kelley9009d742008-11-14 20:04:27 +00001736 if (comma)
1737 {
Simon Kelley429798f2012-12-10 20:45:53 +00001738 struct cond_domain *new = opt_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00001739 char *netpart;
Simon Kelley2bb73af2013-04-24 17:38:19 +01001740
Simon Kelley48fd1c42013-04-25 09:49:38 +01001741 new->prefix = NULL;
1742
Simon Kelley9009d742008-11-14 20:04:27 +00001743 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00001744 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00001745 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00001746 int msize;
1747
Simon Kelley28866e92011-02-14 20:19:14 +00001748 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001749 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001750 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001751 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00001752 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00001753 int mask = (1 << (32 - msize)) - 1;
1754 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001755 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
1756 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00001757 if (arg)
1758 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001759 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01001760 {
1761 if (!(new->prefix = canonicalise_opt(arg)) ||
1762 strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
1763 ret_err(_("bad prefix"));
1764 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01001765 else if (strcmp(arg, "local") != 0 ||
1766 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001767 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00001768 else
1769 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001770 /* generate the equivalent of
1771 local=/<domain>/
1772 local=/xxx.yyy.zzz.in-addr.arpa/ */
Simon Kelley28866e92011-02-14 20:19:14 +00001773 struct server *serv = opt_malloc(sizeof(struct server));
1774 in_addr_t a = ntohl(new->start.s_addr) >> 8;
1775 char *p;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001776
Simon Kelley28866e92011-02-14 20:19:14 +00001777 memset(serv, 0, sizeof(struct server));
1778 serv->domain = d;
1779 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1780 serv->next = daemon->servers;
1781 daemon->servers = serv;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001782
Simon Kelley28866e92011-02-14 20:19:14 +00001783 serv = opt_malloc(sizeof(struct server));
1784 memset(serv, 0, sizeof(struct server));
1785 p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
1786
1787 if (msize == 24)
1788 p += sprintf(p, "%d.", a & 0xff);
1789 a = a >> 8;
1790 if (msize != 8)
1791 p += sprintf(p, "%d.", a & 0xff);
1792 a = a >> 8;
1793 p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
Simon Kelley48fd1c42013-04-25 09:49:38 +01001794
Simon Kelley28866e92011-02-14 20:19:14 +00001795 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1796 serv->next = daemon->servers;
1797 daemon->servers = serv;
1798 }
1799 }
Simon Kelley9009d742008-11-14 20:04:27 +00001800 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00001801#ifdef HAVE_IPV6
1802 else if (inet_pton(AF_INET6, comma, &new->start6))
1803 {
1804 u64 mask = (1LLU << (128 - msize)) - 1LLU;
1805 u64 addrpart = addr6part(&new->start6);
1806 new->is6 = 1;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001807
Simon Kelleyd74942a2012-02-07 20:51:56 +00001808 /* prefix==64 overflows the mask calculation above */
1809 if (msize == 64)
1810 mask = (u64)-1LL;
Simon Kelley48fd1c42013-04-25 09:49:38 +01001811
Simon Kelleyd74942a2012-02-07 20:51:56 +00001812 new->end6 = new->start6;
1813 setaddr6part(&new->start6, addrpart & ~mask);
1814 setaddr6part(&new->end6, addrpart | mask);
1815
1816 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001817 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001818 else if (arg)
1819 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001820 if (option != 's')
Simon Kelleyb5a7ff42013-04-25 11:03:47 +01001821 {
1822 if (!(new->prefix = canonicalise_opt(arg)) ||
1823 strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
1824 ret_err(_("bad prefix"));
1825 }
Simon Kelley48fd1c42013-04-25 09:49:38 +01001826 else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001827 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001828 else
1829 {
Simon Kelley48fd1c42013-04-25 09:49:38 +01001830 /* generate the equivalent of
1831 local=/<domain>/
1832 local=/xxx.yyy.zzz.ip6.arpa/ */
Simon Kelleyd74942a2012-02-07 20:51:56 +00001833 struct server *serv = opt_malloc(sizeof(struct server));
Simon Kelleyd74942a2012-02-07 20:51:56 +00001834 char *p;
Simon Kelleyceae00d2012-02-09 21:28:14 +00001835
Simon Kelleyd74942a2012-02-07 20:51:56 +00001836 memset(serv, 0, sizeof(struct server));
1837 serv->domain = d;
1838 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1839 serv->next = daemon->servers;
1840 daemon->servers = serv;
1841
1842 serv = opt_malloc(sizeof(struct server));
1843 memset(serv, 0, sizeof(struct server));
1844 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
1845
1846 for (i = msize-1; i >= 0; i -= 4)
1847 {
1848 int dig = ((unsigned char *)&new->start6)[i>>3];
1849 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
1850 }
1851 p += sprintf(p, "ip6.arpa");
1852
1853 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1854 serv->next = daemon->servers;
1855 daemon->servers = serv;
1856 }
1857 }
1858 }
1859#endif
1860 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001861 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001862 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01001863 else
Simon Kelleyd74942a2012-02-07 20:51:56 +00001864 {
1865 arg = split(comma);
1866 if (inet_pton(AF_INET, comma, &new->start))
1867 {
1868 new->is6 = 0;
1869 if (!arg)
1870 new->end.s_addr = new->start.s_addr;
1871 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001872 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001873 }
1874#ifdef HAVE_IPV6
1875 else if (inet_pton(AF_INET6, comma, &new->start6))
1876 {
1877 new->is6 = 1;
1878 if (!arg)
1879 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
1880 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001881 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001882 }
1883#endif
1884 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001885 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001886 }
Simon Kelley2307eac2012-02-13 10:13:13 +00001887
1888 new->domain = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01001889 if (option == 's')
1890 {
1891 new->next = daemon->cond_domain;
1892 daemon->cond_domain = new;
1893 }
1894 else
1895 {
1896 new->next = daemon->synth_domains;
1897 daemon->synth_domains = new;
1898 }
Simon Kelley9009d742008-11-14 20:04:27 +00001899 }
Simon Kelley2bb73af2013-04-24 17:38:19 +01001900 else if (option == 's')
Simon Kelley9009d742008-11-14 20:04:27 +00001901 daemon->domain_suffix = d;
Simon Kelley2bb73af2013-04-24 17:38:19 +01001902 else
1903 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001904 }
1905 }
Simon Kelley849a8352006-06-09 21:02:31 +01001906 break;
1907
Simon Kelleyf2621c72007-04-29 19:47:21 +01001908 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00001909 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001910 break;
1911
Simon Kelleyf2621c72007-04-29 19:47:21 +01001912 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00001913 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001914 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01001915 break;
Simon Kelley9e038942008-05-30 20:06:34 +01001916
Simon Kelley7622fc02009-06-04 20:32:05 +01001917#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01001918 case LOPT_SCRIPTUSR: /* --scriptuser */
1919 daemon->scriptuser = opt_string_alloc(arg);
1920 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001921#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001922
Simon Kelleyf2621c72007-04-29 19:47:21 +01001923 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01001924 do {
Simon Kelley824af852008-02-12 20:43:05 +00001925 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001926 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001927 new->next = daemon->if_names;
1928 daemon->if_names = new;
1929 /* new->name may be NULL if someone does
1930 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00001931 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01001932 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001933 arg = comma;
1934 } while (arg);
1935 break;
1936
Simon Kelley2937f8a2013-07-29 19:49:07 +01001937 case LOPT_TFTP: /* --enable-tftp */
1938 set_option_bool(OPT_TFTP);
1939 if (!arg)
1940 break;
1941 /* fall through */
1942
Simon Kelleyf2621c72007-04-29 19:47:21 +01001943 case 'I': /* --except-interface */
1944 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01001945 do {
Simon Kelley824af852008-02-12 20:43:05 +00001946 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001947 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001948 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001949 if (option == 'I')
1950 {
1951 new->next = daemon->if_except;
1952 daemon->if_except = new;
1953 }
Simon Kelley2937f8a2013-07-29 19:49:07 +01001954 else if (option == LOPT_TFTP)
1955 {
1956 new->next = daemon->tftp_interfaces;
1957 daemon->tftp_interfaces = new;
1958 }
Simon Kelley849a8352006-06-09 21:02:31 +01001959 else
1960 {
1961 new->next = daemon->dhcp_except;
1962 daemon->dhcp_except = new;
1963 }
1964 arg = comma;
1965 } while (arg);
1966 break;
1967
Simon Kelleyf2621c72007-04-29 19:47:21 +01001968 case 'B': /* --bogus-nxdomain */
Simon Kelley849a8352006-06-09 21:02:31 +01001969 {
1970 struct in_addr addr;
1971 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001972 if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01001973 {
Simon Kelley824af852008-02-12 20:43:05 +00001974 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Simon Kelley849a8352006-06-09 21:02:31 +01001975 baddr->next = daemon->bogus_addr;
1976 daemon->bogus_addr = baddr;
1977 baddr->addr = addr;
1978 }
1979 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001980 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01001981 break;
1982 }
1983
Simon Kelleyf2621c72007-04-29 19:47:21 +01001984 case 'a': /* --listen-address */
Simon Kelley49678762012-12-09 18:24:58 +00001985 case LOPT_AUTHPEER: /* --auth-peer */
Simon Kelley849a8352006-06-09 21:02:31 +01001986 do {
Simon Kelley824af852008-02-12 20:43:05 +00001987 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001988 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001989 unhide_metas(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01001990 if (arg && (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01001991 {
1992 new->addr.sa.sa_family = AF_INET;
Simon Kelley49678762012-12-09 18:24:58 +00001993 new->addr.in.sin_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001994#ifdef HAVE_SOCKADDR_SA_LEN
1995 new->addr.in.sin_len = sizeof(new->addr.in);
1996#endif
1997 }
1998#ifdef HAVE_IPV6
1999 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
2000 {
2001 new->addr.sa.sa_family = AF_INET6;
2002 new->addr.in6.sin6_flowinfo = 0;
2003 new->addr.in6.sin6_scope_id = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002004 new->addr.in6.sin6_port = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002005#ifdef HAVE_SOCKADDR_SA_LEN
2006 new->addr.in6.sin6_len = sizeof(new->addr.in6);
2007#endif
2008 }
2009#endif
2010 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002011 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01002012
2013 new->used = 0;
Simon Kelley49678762012-12-09 18:24:58 +00002014 if (option == 'a')
2015 {
2016 new->next = daemon->if_addrs;
2017 daemon->if_addrs = new;
2018 }
2019 else
2020 {
2021 new->next = daemon->auth_peers;
2022 daemon->auth_peers = new;
2023 }
Simon Kelley849a8352006-06-09 21:02:31 +01002024 arg = comma;
2025 } while (arg);
2026 break;
2027
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002028 case 'S': /* --server */
2029 case LOPT_LOCAL: /* --local */
2030 case 'A': /* --address */
2031 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01002032 {
2033 struct server *serv, *newlist = NULL;
2034
2035 unhide_metas(arg);
2036
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002037 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01002038 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002039 int rebind = !(*arg == '/');
2040 char *end = NULL;
2041 if (!rebind)
2042 arg++;
2043 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01002044 {
2045 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002046 /* elide leading dots - they are implied in the search algorithm */
2047 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01002048 /* # matches everything and becomes a zero length domain string */
2049 if (strcmp(arg, "#") == 0)
2050 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01002051 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002052 option = '?';
Simon Kelley824af852008-02-12 20:43:05 +00002053 serv = opt_malloc(sizeof(struct server));
2054 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002055 serv->next = newlist;
2056 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01002057 serv->domain = domain;
2058 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00002059 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002060 if (rebind)
2061 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002062 }
2063 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002064 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002065 }
2066 else
2067 {
Simon Kelley824af852008-02-12 20:43:05 +00002068 newlist = opt_malloc(sizeof(struct server));
2069 memset(newlist, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01002070 }
2071
2072 if (option == 'A')
2073 {
2074 newlist->flags |= SERV_LITERAL_ADDRESS;
2075 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002076 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002077 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002078 else if (option == LOPT_NO_REBIND)
2079 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01002080
2081 if (!arg || !*arg)
2082 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002083 if (!(newlist->flags & SERV_NO_REBIND))
2084 newlist->flags |= SERV_NO_ADDR; /* no server */
2085 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002086 ret_err(gen_err);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002087 }
2088
2089 else if (strcmp(arg, "#") == 0)
2090 {
2091 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01002092 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002093 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002094 }
2095 else
2096 {
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002097 char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2098 if (err)
2099 ret_err(err);
Simon Kelley849a8352006-06-09 21:02:31 +01002100 }
2101
Simon Kelleyf2621c72007-04-29 19:47:21 +01002102 serv = newlist;
2103 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01002104 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002105 serv->next->flags = serv->flags;
2106 serv->next->addr = serv->addr;
2107 serv->next->source_addr = serv->source_addr;
Simon Kelleyfaafb3f2012-09-20 14:17:39 +01002108 strcpy(serv->next->interface, serv->interface);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002109 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01002110 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002111 serv->next = daemon->servers;
2112 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01002113 break;
2114 }
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +00002115
2116 case LOPT_IPSET: /* --ipset */
2117#ifndef HAVE_IPSET
2118 ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
2119 break;
2120#else
2121 {
2122 struct ipsets ipsets_head;
2123 struct ipsets *ipsets = &ipsets_head;
2124 int size;
2125 char *end;
2126 char **sets, **sets_pos;
2127 memset(ipsets, 0, sizeof(struct ipsets));
2128 unhide_metas(arg);
2129 if (arg && *arg == '/')
2130 {
2131 arg++;
2132 while ((end = split_chr(arg, '/')))
2133 {
2134 char *domain = NULL;
2135 /* elide leading dots - they are implied in the search algorithm */
2136 while (*arg == '.')
2137 arg++;
2138 /* # matches everything and becomes a zero length domain string */
2139 if (strcmp(arg, "#") == 0 || !*arg)
2140 domain = "";
2141 else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg)))
2142 option = '?';
2143 ipsets->next = opt_malloc(sizeof(struct ipsets));
2144 ipsets = ipsets->next;
2145 memset(ipsets, 0, sizeof(struct ipsets));
2146 ipsets->domain = domain;
2147 arg = end;
2148 }
2149 }
2150 else
2151 {
2152 ipsets->next = opt_malloc(sizeof(struct ipsets));
2153 ipsets = ipsets->next;
2154 memset(ipsets, 0, sizeof(struct ipsets));
2155 ipsets->domain = "";
2156 }
2157 if (!arg || !*arg)
2158 {
2159 option = '?';
2160 break;
2161 }
2162 size = 2;
2163 for (end = arg; *end; ++end)
2164 if (*end == ',')
2165 ++size;
2166
2167 sets = sets_pos = opt_malloc(sizeof(char *) * size);
2168
2169 do {
2170 end = split(arg);
2171 *sets_pos++ = opt_string_alloc(arg);
2172 arg = end;
2173 } while (end);
2174 *sets_pos = 0;
2175 for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next)
2176 ipsets->next->sets = sets;
2177 ipsets->next = daemon->ipsets;
2178 daemon->ipsets = ipsets_head.next;
2179
2180 break;
2181 }
2182#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002183
Simon Kelleyf2621c72007-04-29 19:47:21 +01002184 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01002185 {
2186 int size;
2187
2188 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002189 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002190 else
2191 {
2192 /* zero is OK, and means no caching. */
2193
2194 if (size < 0)
2195 size = 0;
2196 else if (size > 10000)
2197 size = 10000;
2198
2199 daemon->cachesize = size;
2200 }
2201 break;
2202 }
2203
Simon Kelleyf2621c72007-04-29 19:47:21 +01002204 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002205 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002206 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002207 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01002208
Simon Kelley1a6bca82008-07-11 11:11:42 +01002209 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002210 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002211 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002212 break;
2213
Simon Kelleyf2621c72007-04-29 19:47:21 +01002214 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01002215 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002216 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01002217 break;
2218
Simon Kelleyf2621c72007-04-29 19:47:21 +01002219 case LOPT_MAX_LOGS: /* --log-async */
2220 daemon->max_logs = LOG_MAX; /* default */
2221 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002222 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002223 else if (daemon->max_logs > 100)
2224 daemon->max_logs = 100;
2225 break;
2226
2227 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002228 {
2229 int i;
2230 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002231 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002232 daemon->edns_pktsz = (unsigned short)i;
2233 break;
2234 }
2235
Simon Kelleyf2621c72007-04-29 19:47:21 +01002236 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002237 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002238 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01002239 /* if explicitly set to zero, use single OS ephemeral port
2240 and disable random ports */
2241 if (daemon->query_port == 0)
2242 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01002243 break;
2244
Simon Kelley824af852008-02-12 20:43:05 +00002245 case 'T': /* --local-ttl */
2246 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002247 case LOPT_MAXTTL: /* --max-ttl */
Simon Kelley1d860412012-09-20 20:48:04 +01002248 case LOPT_MAXCTTL: /* --max-cache-ttl */
Simon Kelley4f7b3042012-11-28 21:27:02 +00002249 case LOPT_AUTHTTL: /* --auth-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01002250 {
2251 int ttl;
2252 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002253 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00002254 else if (option == LOPT_NEGTTL)
2255 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002256 else if (option == LOPT_MAXTTL)
2257 daemon->max_ttl = (unsigned long)ttl;
Simon Kelley1d860412012-09-20 20:48:04 +01002258 else if (option == LOPT_MAXCTTL)
2259 daemon->max_cache_ttl = (unsigned long)ttl;
Simon Kelley4f7b3042012-11-28 21:27:02 +00002260 else if (option == LOPT_AUTHTTL)
2261 daemon->auth_ttl = (unsigned long)ttl;
Simon Kelley849a8352006-06-09 21:02:31 +01002262 else
2263 daemon->local_ttl = (unsigned long)ttl;
2264 break;
2265 }
2266
Simon Kelley7622fc02009-06-04 20:32:05 +01002267#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002268 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01002269 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002270 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002271 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002272#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002273
Simon Kelley7622fc02009-06-04 20:32:05 +01002274#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002275 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00002276 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002277 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00002278 break;
2279
Simon Kelley824af852008-02-12 20:43:05 +00002280 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002281 comma = split(arg);
2282 if (comma)
2283 {
2284 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
2285 new->interface = opt_string_alloc(comma);
2286 new->prefix = opt_string_alloc(arg);
2287 new->next = daemon->if_prefix;
2288 daemon->if_prefix = new;
2289 }
2290 else
2291 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002292 break;
2293
Simon Kelley824af852008-02-12 20:43:05 +00002294 case LOPT_TFTPPORTS: /* --tftp-port-range */
2295 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002296 !atoi_check16(arg, &daemon->start_tftp_port) ||
2297 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002298 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00002299
2300 if (daemon->start_tftp_port > daemon->end_tftp_port)
2301 {
2302 int tmp = daemon->start_tftp_port;
2303 daemon->start_tftp_port = daemon->end_tftp_port;
2304 daemon->end_tftp_port = tmp;
2305 }
2306
2307 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002308#endif
Simon Kelley824af852008-02-12 20:43:05 +00002309
Simon Kelleyf2621c72007-04-29 19:47:21 +01002310 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00002311 {
Simon Kelley824af852008-02-12 20:43:05 +00002312 struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley316e2732010-01-22 20:16:09 +00002313 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002314 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00002315
Simon Kelley316e2732010-01-22 20:16:09 +00002316 strcpy(new->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002317 new->alias = NULL;
2318 new->next = daemon->bridges;
2319 daemon->bridges = new;
2320
2321 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002322 arg = comma;
2323 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00002324 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00002325 {
Simon Kelley824af852008-02-12 20:43:05 +00002326 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00002327 b->next = new->alias;
2328 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00002329 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00002330 }
2331 } while (comma);
2332
2333 break;
2334 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002335
Simon Kelley7622fc02009-06-04 20:32:05 +01002336#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002337 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002338 {
2339 int k, leasepos = 2;
Simon Kelley8445f5d2012-12-17 21:54:08 +00002340 char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002341 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002342
Simon Kelley52b92f42012-01-22 16:05:15 +00002343 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002344 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002345
Simon Kelley849a8352006-06-09 21:02:31 +01002346 if (!arg)
2347 {
2348 option = '?';
2349 break;
2350 }
2351
2352 while(1)
2353 {
2354 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002355 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2356 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2357 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002358 break;
2359
Simon Kelleyf2621c72007-04-29 19:47:21 +01002360 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002361 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002362 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002363 {
Simon Kelley824af852008-02-12 20:43:05 +00002364 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2365 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002366 tt->next = new->filter;
Simon Kelley0c387192013-09-05 10:21:12 +01002367 /* ignore empty tag */
2368 if (tt->net)
2369 new->filter = tt;
Simon Kelley849a8352006-06-09 21:02:31 +01002370 }
2371 else
2372 {
2373 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002374 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002375 else if (strstr(arg, "set:") == arg)
2376 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002377 else
Simon Kelley824af852008-02-12 20:43:05 +00002378 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002379 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002380 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002381 }
2382 else
2383 {
2384 a[0] = arg;
2385 break;
2386 }
2387 }
2388
Simon Kelley1f776932012-12-16 19:46:08 +00002389 for (k = 1; k < 8; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002390 if (!(a[k] = split(a[k-1])))
2391 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002392
Simon Kelley52b92f42012-01-22 16:05:15 +00002393 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002394 ret_err(_("bad dhcp-range"));
2395
2396 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002397 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002398 new->next = daemon->dhcp;
2399 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002400 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002401 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002402 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002403 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002404 new->flags |= CONTEXT_PROXY;
2405 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002406 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002407
2408 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2409 {
2410 struct in_addr tmp = new->start;
2411 new->start = new->end;
2412 new->end = tmp;
2413 }
2414
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002415 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002416 (inet_pton(AF_INET, a[2], &new->netmask) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002417 {
2418 new->flags |= CONTEXT_NETMASK;
2419 leasepos = 3;
2420 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002421 ret_err(_("inconsistent DHCP range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002422 }
2423
2424 if (k >= 4 && strchr(a[3], '.') &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002425 (inet_pton(AF_INET, a[3], &new->broadcast) > 0))
Simon Kelley52b92f42012-01-22 16:05:15 +00002426 {
2427 new->flags |= CONTEXT_BRDCAST;
2428 leasepos = 4;
2429 }
Simon Kelley849a8352006-06-09 21:02:31 +01002430 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002431#ifdef HAVE_DHCP6
2432 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002433 {
Simon Kelley89500e32013-09-20 16:29:20 +01002434 new->flags |= CONTEXT_V6;
Simon Kelley52b92f42012-01-22 16:05:15 +00002435 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002436 new->end6 = new->start6;
Simon Kelley6692a1a2013-08-20 14:41:31 +01002437 new->next = daemon->dhcp6;
2438 daemon->dhcp6 = new;
2439
Simon Kelley30cd9662012-03-25 20:44:38 +01002440 for (leasepos = 1; leasepos < k; leasepos++)
2441 {
2442 if (strcmp(a[leasepos], "static") == 0)
2443 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2444 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
Simon Kelley1f776932012-12-16 19:46:08 +00002445 new->flags |= CONTEXT_RA_ONLY | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002446 else if (strcmp(a[leasepos], "ra-names") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002447 new->flags |= CONTEXT_RA_NAME | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002448 else if (strcmp(a[leasepos], "ra-stateless") == 0)
Simon Kelley1f776932012-12-16 19:46:08 +00002449 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP | CONTEXT_RA;
Simon Kelley30cd9662012-03-25 20:44:38 +01002450 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2451 new->flags |= CONTEXT_DHCP;
Simon Kelley1f776932012-12-16 19:46:08 +00002452 else if (strstr(a[leasepos], "constructor:") == a[leasepos])
2453 {
2454 new->template_interface = opt_string_alloc(a[leasepos] + 12);
2455 new->flags |= CONTEXT_TEMPLATE;
2456 }
Simon Kelley921360c2013-05-31 14:07:22 +01002457 else if (strstr(a[leasepos], "constructor-noauth:") == a[leasepos])
2458 {
2459 new->template_interface = opt_string_alloc(a[leasepos] + 19);
2460 new->flags |= CONTEXT_TEMPLATE | CONTEXT_NOAUTH;
2461 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002462 else
2463 break;
2464 }
Simon Kelley6692a1a2013-08-20 14:41:31 +01002465
Simon Kelley52b92f42012-01-22 16:05:15 +00002466 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002467 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002468 {
2469 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002470 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002471 if (!(*cp >= '0' && *cp <= '9'))
2472 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002473 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002474 {
2475 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002476 leasepos++;
Simon Kelley52b92f42012-01-22 16:05:15 +00002477 }
2478 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002479
Simon Kelley6692a1a2013-08-20 14:41:31 +01002480 if (new->prefix != 64)
2481 {
2482 if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
2483 ret_err(_("prefix length must be exactly 64 for RA subnets"));
2484 else if (new->flags & CONTEXT_TEMPLATE)
2485 ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2486 }
2487
2488 if (new->prefix < 64)
2489 ret_err(_("prefix length must be at least 64"));
2490
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002491 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2492 ret_err(_("inconsistent DHCPv6 range"));
Simon Kelley6692a1a2013-08-20 14:41:31 +01002493
2494 /* dhcp-range=:: enables DHCP stateless on any interface */
2495 if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2496 new->prefix = 0;
Simon Kelley66409192013-08-01 20:19:32 +01002497
2498 if (new->flags & CONTEXT_TEMPLATE)
2499 {
2500 struct in6_addr zero;
2501 memset(&zero, 0, sizeof(zero));
2502 if (!is_same_net6(&zero, &new->start6, new->prefix))
2503 ret_err(_("prefix must be zero with \"constructor:\" argument"));
2504 }
2505
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002506 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002507 {
2508 struct in6_addr tmp = new->start6;
2509 new->start6 = new->end6;
2510 new->end6 = tmp;
2511 }
Simon Kelley849a8352006-06-09 21:02:31 +01002512 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002513#endif
Simon Kelleyd9ee9c02013-04-12 11:17:55 +01002514 else
2515 ret_err(_("bad dhcp-range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002516
Simon Kelley30cd9662012-03-25 20:44:38 +01002517 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002518 {
2519 if (strcmp(a[leasepos], "infinite") == 0)
2520 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002521 else if (strcmp(a[leasepos], "deprecated") == 0)
2522 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002523 else
2524 {
2525 int fac = 1;
2526 if (strlen(a[leasepos]) > 0)
2527 {
2528 switch (a[leasepos][strlen(a[leasepos]) - 1])
2529 {
Simon Kelley42243212012-07-20 15:19:18 +01002530 case 'w':
2531 case 'W':
2532 fac *= 7;
2533 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002534 case 'd':
2535 case 'D':
2536 fac *= 24;
2537 /* fall though */
2538 case 'h':
2539 case 'H':
2540 fac *= 60;
2541 /* fall through */
2542 case 'm':
2543 case 'M':
2544 fac *= 60;
2545 /* fall through */
2546 case 's':
2547 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002548 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002549 }
2550
Simon Kelleybe379862012-12-23 12:01:39 +00002551 for (cp = a[leasepos]; *cp; cp++)
2552 if (!(*cp >= '0' && *cp <= '9'))
2553 break;
2554
Simon Kelley54dae552013-02-05 17:55:10 +00002555 if (*cp || (leasepos+1 < k))
Simon Kelleybe379862012-12-23 12:01:39 +00002556 ret_err(_("bad dhcp-range"));
2557
Simon Kelley849a8352006-06-09 21:02:31 +01002558 new->lease_time = atoi(a[leasepos]) * fac;
2559 /* Leases of a minute or less confuse
2560 some clients, notably Apple's */
2561 if (new->lease_time < 120)
2562 new->lease_time = 120;
2563 }
2564 }
2565 }
2566 break;
2567 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01002568
Simon Kelley5aabfc72007-08-29 11:24:47 +01002569 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01002570 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01002571 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002572 int j, k = 0;
Simon Kelley3e8ed782013-05-29 14:31:33 +01002573 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01002574 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01002575 struct in_addr in;
2576
Simon Kelley824af852008-02-12 20:43:05 +00002577 new = opt_malloc(sizeof(struct dhcp_config));
2578
Simon Kelley849a8352006-06-09 21:02:31 +01002579 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00002580 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
2581 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002582 new->netid = NULL;
2583
Simon Kelley849a8352006-06-09 21:02:31 +01002584 if ((a[0] = arg))
Simon Kelley3e8ed782013-05-29 14:31:33 +01002585 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002586 if (!(a[k] = split(a[k-1])))
2587 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002588
2589 for (j = 0; j < k; j++)
2590 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
2591 {
2592 char *arg = a[j];
2593
2594 if ((arg[0] == 'i' || arg[0] == 'I') &&
2595 (arg[1] == 'd' || arg[1] == 'D') &&
2596 arg[2] == ':')
2597 {
2598 if (arg[3] == '*')
2599 new->flags |= CONFIG_NOCLID;
2600 else
2601 {
2602 int len;
2603 arg += 3; /* dump id: */
2604 if (strchr(arg, ':'))
2605 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
2606 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01002607 {
2608 unhide_metas(arg);
2609 len = (int) strlen(arg);
2610 }
2611
Simon Kelley28866e92011-02-14 20:19:14 +00002612 if (len == -1)
Simon Kelley9f7f3b12012-05-28 21:39:57 +01002613
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002614 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00002615 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01002616 {
2617 new->flags |= CONFIG_CLID;
2618 new->clid_len = len;
2619 memcpy(new->clid, arg, len);
2620 }
Simon Kelley849a8352006-06-09 21:02:31 +01002621 }
2622 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002623 /* dhcp-host has strange backwards-compat needs. */
2624 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01002625 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002626 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2627 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2628 newtag->net = opt_malloc(strlen(arg + 4) + 1);
2629 newlist->next = new->netid;
2630 new->netid = newlist;
2631 newlist->list = newtag;
2632 strcpy(newtag->net, arg+4);
2633 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01002634 }
Simon Kelley7de060b2011-08-26 17:24:52 +01002635 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002636 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00002637#ifdef HAVE_DHCP6
2638 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
2639 {
2640 arg[strlen(arg)-1] = 0;
2641 arg++;
2642
2643 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002644 ret_err(_("bad IPv6 address"));
Simon Kelley30393102013-01-17 16:34:16 +00002645
2646 for (i= 0; i < 8; i++)
2647 if (new->addr6.s6_addr[i] != 0)
2648 break;
2649
2650 /* set WILDCARD if network part all zeros */
2651 if (i == 8)
2652 new->flags |= CONFIG_WILDCARD;
Simon Kelley4cb1b322012-02-06 14:30:41 +00002653
2654 new->flags |= CONFIG_ADDR6;
2655 }
2656#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01002657 else
Simon Kelley849a8352006-06-09 21:02:31 +01002658 {
Simon Kelley9009d742008-11-14 20:04:27 +00002659 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00002660 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
2661 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002662 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00002663 else
2664 {
2665
2666 newhw->next = new->hwaddr;
2667 new->hwaddr = newhw;
2668 }
Simon Kelley849a8352006-06-09 21:02:31 +01002669 }
2670 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002671 else if (strchr(a[j], '.') && (inet_pton(AF_INET, a[j], &in) > 0))
Simon Kelley849a8352006-06-09 21:02:31 +01002672 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002673 struct dhcp_config *configs;
2674
Simon Kelley849a8352006-06-09 21:02:31 +01002675 new->addr = in;
2676 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002677
2678 /* If the same IP appears in more than one host config, then DISCOVER
2679 for one of the hosts will get the address, but REQUEST will be NAKed,
2680 since the address is reserved by the other one -> protocol loop. */
2681 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
2682 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
2683 {
2684 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
2685 return 0;
2686 }
Simon Kelley849a8352006-06-09 21:02:31 +01002687 }
2688 else
2689 {
2690 char *cp, *lastp = NULL, last = 0;
2691 int fac = 1;
2692
2693 if (strlen(a[j]) > 1)
2694 {
2695 lastp = a[j] + strlen(a[j]) - 1;
2696 last = *lastp;
2697 switch (last)
2698 {
Simon Kelley42243212012-07-20 15:19:18 +01002699 case 'w':
2700 case 'W':
2701 fac *= 7;
2702 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002703 case 'd':
2704 case 'D':
2705 fac *= 24;
2706 /* fall through */
2707 case 'h':
2708 case 'H':
2709 fac *= 60;
2710 /* fall through */
2711 case 'm':
2712 case 'M':
2713 fac *= 60;
2714 /* fall through */
2715 case 's':
2716 case 'S':
2717 *lastp = 0;
2718 }
2719 }
2720
2721 for (cp = a[j]; *cp; cp++)
Simon Kelley572b41e2011-02-18 18:11:18 +00002722 if (!isdigit((unsigned char)*cp) && *cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01002723 break;
2724
2725 if (*cp)
2726 {
2727 if (lastp)
2728 *lastp = last;
2729 if (strcmp(a[j], "infinite") == 0)
2730 {
2731 new->lease_time = 0xffffffff;
2732 new->flags |= CONFIG_TIME;
2733 }
2734 else if (strcmp(a[j], "ignore") == 0)
2735 new->flags |= CONFIG_DISABLE;
2736 else
2737 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002738 if (!(new->hostname = canonicalise_opt(a[j])) ||
2739 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002740 ret_err(_("bad DHCP host name"));
2741
2742 new->flags |= CONFIG_NAME;
2743 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01002744 }
2745 }
2746 else
2747 {
2748 new->lease_time = atoi(a[j]) * fac;
2749 /* Leases of a minute or less confuse
2750 some clients, notably Apple's */
2751 if (new->lease_time < 120)
2752 new->lease_time = 120;
2753 new->flags |= CONFIG_TIME;
2754 }
2755 }
2756
Simon Kelley5aabfc72007-08-29 11:24:47 +01002757 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01002758 break;
2759 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002760
2761 case LOPT_TAG_IF: /* --tag-if */
2762 {
2763 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
2764
2765 new->tag = NULL;
2766 new->set = NULL;
2767 new->next = NULL;
2768
2769 /* preserve order */
2770 if (!daemon->tag_if)
2771 daemon->tag_if = new;
2772 else
2773 {
2774 struct tag_if *tmp;
2775 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
2776 tmp->next = new;
2777 }
2778
2779 while (arg)
2780 {
2781 size_t len;
2782
2783 comma = split(arg);
2784 len = strlen(arg);
2785
2786 if (len < 5)
2787 {
2788 new->set = NULL;
2789 break;
2790 }
2791 else
2792 {
2793 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2794 newtag->net = opt_malloc(len - 3);
2795 strcpy(newtag->net, arg+4);
2796 unhide_metas(newtag->net);
2797
2798 if (strstr(arg, "set:") == arg)
2799 {
2800 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2801 newlist->next = new->set;
2802 new->set = newlist;
2803 newlist->list = newtag;
2804 }
2805 else if (strstr(arg, "tag:") == arg)
2806 {
2807 newtag->next = new->tag;
2808 new->tag = newtag;
2809 }
2810 else
2811 {
2812 new->set = NULL;
Simon Kelley4dc9c652013-02-04 21:43:52 +00002813 free(newtag);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002814 break;
2815 }
2816 }
2817
2818 arg = comma;
2819 }
2820
2821 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002822 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002823
2824 break;
2825 }
2826
Simon Kelley849a8352006-06-09 21:02:31 +01002827
Simon Kelley73a08a22009-02-05 20:28:08 +00002828 case 'O': /* --dhcp-option */
2829 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00002830 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00002831 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002832 return parse_dhcp_opt(errstr, arg,
2833 option == LOPT_FORCE ? DHOPT_FORCE :
2834 (option == LOPT_MATCH ? DHOPT_MATCH :
2835 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
2836
Simon Kelleyf2621c72007-04-29 19:47:21 +01002837 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01002838 {
2839 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002840 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002841 {
Simon Kelley824af852008-02-12 20:43:05 +00002842 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01002843 newid->next = id;
2844 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002845 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002846 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002847 arg = comma;
2848 };
2849
2850 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002851 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002852 else
2853 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002854 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01002855 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002856 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002857 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002858 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002859 dhcp_next_server.s_addr = 0;
2860 if (comma)
2861 {
2862 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002863 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002864 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002865 if (comma)
2866 {
2867 unhide_metas(comma);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002868 if (!(inet_pton(AF_INET, comma, &dhcp_next_server) > 0))
2869 {
2870 /*
2871 * The user may have specified the tftp hostname here.
2872 * save it so that it can be resolved/looked up during
2873 * actual dhcp_reply().
2874 */
2875
2876 tftp_sname = opt_string_alloc(comma);
2877 dhcp_next_server.s_addr = 0;
2878 }
Simon Kelley849a8352006-06-09 21:02:31 +01002879 }
2880 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002881
2882 new = opt_malloc(sizeof(struct dhcp_boot));
2883 new->file = dhcp_file;
2884 new->sname = dhcp_sname;
2885 new->tftp_sname = tftp_sname;
2886 new->next_server = dhcp_next_server;
2887 new->netid = id;
2888 new->next = daemon->boot_config;
2889 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01002890 }
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01002891
Simon Kelley849a8352006-06-09 21:02:31 +01002892 break;
2893 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002894
2895 case LOPT_PXE_PROMT: /* --pxe-prompt */
2896 {
2897 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
2898 int timeout;
2899
2900 new->netid = NULL;
2901 new->opt = 10; /* PXE_MENU_PROMPT */
2902
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002903 while (is_tag_prefix(arg))
2904 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002905 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2906 comma = split(arg);
2907 nn->next = new->netid;
2908 new->netid = nn;
2909 nn->net = opt_string_alloc(arg+4);
2910 arg = comma;
2911 }
2912
2913 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002914 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01002915 else
2916 {
2917 comma = split(arg);
2918 unhide_metas(arg);
2919 new->len = strlen(arg) + 1;
2920 new->val = opt_malloc(new->len);
2921 memcpy(new->val + 1, arg, new->len - 1);
2922
2923 new->u.vendor_class = (unsigned char *)"PXEClient";
2924 new->flags = DHOPT_VENDOR;
2925
2926 if (comma && atoi_check(comma, &timeout))
2927 *(new->val) = timeout;
2928 else
2929 *(new->val) = 255;
2930
2931 new->next = daemon->dhcp_opts;
2932 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01002933 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01002934 }
2935
2936 break;
2937 }
2938
2939 case LOPT_PXE_SERV: /* --pxe-service */
2940 {
2941 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
2942 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
2943 "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };
2944 static int boottype = 32768;
2945
2946 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00002947 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01002948 new->server.s_addr = 0;
2949
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002950 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01002951 {
2952 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2953 comma = split(arg);
2954 nn->next = new->netid;
2955 new->netid = nn;
2956 nn->net = opt_string_alloc(arg+4);
2957 arg = comma;
2958 }
2959
2960 if (arg && (comma = split(arg)))
2961 {
2962 for (i = 0; CSA[i]; i++)
2963 if (strcasecmp(CSA[i], arg) == 0)
2964 break;
2965
2966 if (CSA[i] || atoi_check(arg, &i))
2967 {
2968 arg = comma;
2969 comma = split(arg);
2970
2971 new->CSA = i;
2972 new->menu = opt_string_alloc(arg);
2973
Simon Kelley316e2732010-01-22 20:16:09 +00002974 if (!comma)
2975 {
2976 new->type = 0; /* local boot */
2977 new->basename = NULL;
2978 }
2979 else
Simon Kelley7622fc02009-06-04 20:32:05 +01002980 {
2981 arg = comma;
2982 comma = split(arg);
2983 if (atoi_check(arg, &i))
2984 {
2985 new->type = i;
2986 new->basename = NULL;
2987 }
2988 else
2989 {
2990 new->type = boottype++;
2991 new->basename = opt_string_alloc(arg);
2992 }
2993
Simon Kelley751d6f42012-02-10 15:24:51 +00002994 if (comma)
2995 {
2996 if (!inet_pton(AF_INET, comma, &new->server))
2997 {
2998 new->server.s_addr = 0;
2999 new->sname = opt_string_alloc(comma);
3000 }
3001
3002 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003003 }
Simon Kelley751d6f42012-02-10 15:24:51 +00003004
Simon Kelley316e2732010-01-22 20:16:09 +00003005 /* Order matters */
3006 new->next = NULL;
3007 if (!daemon->pxe_services)
3008 daemon->pxe_services = new;
3009 else
3010 {
3011 struct pxe_service *s;
3012 for (s = daemon->pxe_services; s->next; s = s->next);
3013 s->next = new;
3014 }
3015
3016 daemon->enable_pxe = 1;
3017 break;
3018
Simon Kelley7622fc02009-06-04 20:32:05 +01003019 }
3020 }
3021
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003022 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01003023 }
3024
Simon Kelleyf2621c72007-04-29 19:47:21 +01003025 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01003026 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003027 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003028 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01003029 else
3030 {
Simon Kelley824af852008-02-12 20:43:05 +00003031 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003032 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003033 unhide_metas(comma);
3034 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00003035 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003036 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00003037 else
3038 {
3039 new->next = daemon->dhcp_macs;
3040 daemon->dhcp_macs = new;
3041 }
Simon Kelley849a8352006-06-09 21:02:31 +01003042 }
3043 }
3044 break;
Simon Kelleyc6309242013-03-07 20:59:28 +00003045
3046#ifdef OPTION6_PREFIX_CLASS
3047 case LOPT_PREF_CLSS: /* --dhcp-prefix-class */
3048 {
3049 struct prefix_class *new = opt_malloc(sizeof(struct prefix_class));
3050
3051 if (!(comma = split(arg)) ||
3052 !atoi_check16(comma, &new->class))
3053 ret_err(gen_err);
3054
3055 new->tag.net = opt_string_alloc(set_prefix(arg));
3056 new->next = daemon->prefix_classes;
3057 daemon->prefix_classes = new;
3058
3059 break;
3060 }
3061#endif
3062
3063
Simon Kelleyf2621c72007-04-29 19:47:21 +01003064 case 'U': /* --dhcp-vendorclass */
3065 case 'j': /* --dhcp-userclass */
3066 case LOPT_CIRCUIT: /* --dhcp-circuitid */
3067 case LOPT_REMOTE: /* --dhcp-remoteid */
3068 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01003069 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003070 unsigned char *p;
3071 int dig = 0;
3072 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
3073
3074 if (!(comma = split(arg)))
3075 ret_err(gen_err);
3076
3077 new->netid.net = opt_string_alloc(set_prefix(arg));
3078 /* check for hex string - must digits may include : must not have nothing else,
3079 only allowed for agent-options. */
3080
3081 arg = comma;
3082 if ((comma = split(arg)))
3083 {
3084 if (option != 'U' || strstr(arg, "enterprise:") != arg)
3085 ret_err(gen_err);
3086 else
3087 new->enterprise = atoi(arg+11);
3088 }
3089 else
3090 comma = arg;
3091
3092 for (p = (unsigned char *)comma; *p; p++)
3093 if (isxdigit(*p))
3094 dig = 1;
3095 else if (*p != ':')
3096 break;
3097 unhide_metas(comma);
3098 if (option == 'U' || option == 'j' || *p || !dig)
3099 {
3100 new->len = strlen(comma);
3101 new->data = opt_malloc(new->len);
3102 memcpy(new->data, comma, new->len);
3103 }
3104 else
3105 {
3106 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
3107 new->data = opt_malloc(new->len);
3108 memcpy(new->data, comma, new->len);
3109 }
3110
3111 switch (option)
3112 {
3113 case 'j':
3114 new->match_type = MATCH_USER;
3115 break;
3116 case 'U':
3117 new->match_type = MATCH_VENDOR;
3118 break;
3119 case LOPT_CIRCUIT:
3120 new->match_type = MATCH_CIRCUIT;
3121 break;
3122 case LOPT_REMOTE:
3123 new->match_type = MATCH_REMOTE;
3124 break;
3125 case LOPT_SUBSCR:
3126 new->match_type = MATCH_SUBSCRIBER;
3127 break;
3128 }
3129 new->next = daemon->dhcp_vendors;
3130 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00003131
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003132 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003133 }
3134
Simon Kelley9e038942008-05-30 20:06:34 +01003135 case LOPT_ALTPORT: /* --dhcp-alternate-port */
3136 if (!arg)
3137 {
3138 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
3139 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
3140 }
3141 else
3142 {
3143 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003144 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
3145 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003146 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01003147 if (!comma)
3148 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
3149 }
3150 break;
3151
Simon Kelley824af852008-02-12 20:43:05 +00003152 case 'J': /* --dhcp-ignore */
3153 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
3154 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003155 case '3': /* --bootp-dynamic */
3156 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01003157 {
Simon Kelley824af852008-02-12 20:43:05 +00003158 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01003159 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003160 if (option == 'J')
3161 {
3162 new->next = daemon->dhcp_ignore;
3163 daemon->dhcp_ignore = new;
3164 }
Simon Kelley824af852008-02-12 20:43:05 +00003165 else if (option == LOPT_BROADCAST)
3166 {
3167 new->next = daemon->force_broadcast;
3168 daemon->force_broadcast = new;
3169 }
Simon Kelley9009d742008-11-14 20:04:27 +00003170 else if (option == '3')
3171 {
3172 new->next = daemon->bootp_dynamic;
3173 daemon->bootp_dynamic = new;
3174 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003175 else if (option == LOPT_GEN_NAMES)
3176 {
3177 new->next = daemon->dhcp_gen_names;
3178 daemon->dhcp_gen_names = new;
3179 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003180 else
3181 {
3182 new->next = daemon->dhcp_ignore_names;
3183 daemon->dhcp_ignore_names = new;
3184 }
3185
3186 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00003187 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003188 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003189 member->next = list;
3190 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003191 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00003192 member->net = opt_string_alloc(arg+4);
3193 else
3194 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003195 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00003196 }
Simon Kelley849a8352006-06-09 21:02:31 +01003197
3198 new->list = list;
3199 break;
3200 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003201
3202 case LOPT_PROXY: /* --dhcp-proxy */
3203 daemon->override = 1;
3204 while (arg) {
3205 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
3206 comma = split(arg);
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003207 if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003208 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003209 new->next = daemon->override_relays;
3210 daemon->override_relays = new;
3211 arg = comma;
3212 }
3213 break;
Simon Kelleyff7eea22013-09-04 18:01:38 +01003214
3215 case LOPT_RELAY: /* --dhcp-relay */
3216 {
3217 struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
3218 comma = split(arg);
3219 new->interface = opt_string_alloc(split(comma));
3220 new->iface_index = 0;
3221 if (inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server))
3222 {
3223 new->next = daemon->relay4;
3224 daemon->relay4 = new;
3225 }
3226#ifdef HAVE_DHCP6
3227 else if (inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server))
3228 {
3229 new->next = daemon->relay6;
3230 daemon->relay6 = new;
3231 }
3232#endif
3233 else
3234 ret_err(_("Bad dhcp-relay"));
3235
3236 break;
3237 }
3238
Simon Kelley7622fc02009-06-04 20:32:05 +01003239#endif
Simon Kelley849a8352006-06-09 21:02:31 +01003240
Simon Kelley8b372702012-03-09 17:45:10 +00003241#ifdef HAVE_DHCP6
Simon Kelleyc4cd95d2013-10-10 20:58:11 +01003242 case LOPT_RA_PARAM: /* --ra-param */
3243 if ((comma = split(arg)))
3244 {
3245 struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
3246 new->lifetime = -1;
3247 new->prio = 0;
3248 new->name = opt_string_alloc(arg);
3249 if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
3250 {
3251 if (*comma == 'l' || *comma == 'L')
3252 new->prio = 0x18;
3253 else
3254 new->prio = 0x08;
3255 comma = split(comma);
3256 }
3257 arg = split(comma);
3258 if (!atoi_check(comma, &new->interval) ||
3259 (arg && !atoi_check(arg, &new->lifetime)))
3260 ret_err(_("bad RA-params"));
3261
3262 new->next = daemon->ra_interfaces;
3263 daemon->ra_interfaces = new;
3264 }
3265 break;
3266
Simon Kelley8b372702012-03-09 17:45:10 +00003267 case LOPT_DUID: /* --dhcp-duid */
3268 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003269 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00003270 else
3271 {
3272 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
3273 daemon->duid_config = opt_malloc(daemon->duid_config_len);
3274 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
3275 }
3276 break;
3277#endif
3278
Simon Kelleyf2621c72007-04-29 19:47:21 +01003279 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01003280 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003281 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01003282 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00003283 struct doctor *new = opt_malloc(sizeof(struct doctor));
3284 new->next = daemon->doctors;
3285 daemon->doctors = new;
3286 new->mask.s_addr = 0xffffffff;
3287 new->end.s_addr = 0;
3288
Simon Kelley849a8352006-06-09 21:02:31 +01003289 if ((a[0] = arg))
3290 for (k = 1; k < 3; k++)
3291 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01003292 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01003293 break;
Simon Kelley849a8352006-06-09 21:02:31 +01003294 unhide_metas(a[k]);
3295 }
Simon Kelley849a8352006-06-09 21:02:31 +01003296
Simon Kelley73a08a22009-02-05 20:28:08 +00003297 dash = split_chr(a[0], '-');
3298
Simon Kelley849a8352006-06-09 21:02:31 +01003299 if ((k < 2) ||
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003300 (!(inet_pton(AF_INET, a[0], &new->in) > 0)) ||
3301 (!(inet_pton(AF_INET, a[1], &new->out) > 0)))
Simon Kelley73a08a22009-02-05 20:28:08 +00003302 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01003303
3304 if (k == 3)
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003305 inet_pton(AF_INET, a[2], &new->mask);
Simon Kelley849a8352006-06-09 21:02:31 +01003306
Simon Kelley73a08a22009-02-05 20:28:08 +00003307 if (dash &&
Simon Kelleyddd9a6b2013-04-29 17:00:21 +01003308 (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00003309 !is_same_net(new->in, new->end, new->mask) ||
3310 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003311 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01003312
3313 break;
3314 }
3315
Simon Kelleyf2621c72007-04-29 19:47:21 +01003316 case LOPT_INTNAME: /* --interface-name */
3317 {
3318 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01003319 char *domain = NULL;
3320
Simon Kelleyf2621c72007-04-29 19:47:21 +01003321 comma = split(arg);
3322
Simon Kelley1f15b812009-10-13 17:49:32 +01003323 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003324 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003325
Simon Kelley824af852008-02-12 20:43:05 +00003326 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003327 new->next = NULL;
Simon Kelley115ac3e2013-05-20 11:28:32 +01003328 new->addr4 = NULL;
3329#ifdef HAVE_IPV6
3330 new->addr6 = NULL;
3331#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01003332 /* Add to the end of the list, so that first name
3333 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00003334 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01003335 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003336 new->name = domain;
Simon Kelley824af852008-02-12 20:43:05 +00003337 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003338 break;
3339 }
Simon Kelley9009d742008-11-14 20:04:27 +00003340
3341 case LOPT_CNAME: /* --cname */
3342 {
3343 struct cname *new;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003344 char *alias;
3345 char *target;
3346
Simon Kelley9009d742008-11-14 20:04:27 +00003347 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003348 ret_err(gen_err);
3349
3350 alias = canonicalise_opt(arg);
3351 target = canonicalise_opt(comma);
3352
3353 if (!alias || !target)
3354 ret_err(_("bad CNAME"));
Simon Kelley9009d742008-11-14 20:04:27 +00003355 else
3356 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003357 for (new = daemon->cnames; new; new = new->next)
3358 if (hostname_isequal(new->alias, arg))
3359 ret_err(_("duplicate CNAME"));
3360 new = opt_malloc(sizeof(struct cname));
3361 new->next = daemon->cnames;
3362 daemon->cnames = new;
3363 new->alias = alias;
3364 new->target = target;
Simon Kelley9009d742008-11-14 20:04:27 +00003365 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003366
Simon Kelley9009d742008-11-14 20:04:27 +00003367 break;
3368 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003369
3370 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00003371 {
3372 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003373 char *dom, *target = NULL;
3374
Simon Kelleyf2621c72007-04-29 19:47:21 +01003375 comma = split(arg);
3376
Simon Kelley1f15b812009-10-13 17:49:32 +01003377 if (!(dom = canonicalise_opt(arg)) ||
3378 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003379 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01003380 else
3381 {
3382 new = opt_malloc(sizeof(struct ptr_record));
3383 new->next = daemon->ptr;
3384 daemon->ptr = new;
3385 new->name = dom;
3386 new->ptr = target;
3387 }
Simon Kelley832af0b2007-01-21 20:01:28 +00003388 break;
3389 }
3390
Simon Kelley1a6bca82008-07-11 11:11:42 +01003391 case LOPT_NAPTR: /* --naptr-record */
3392 {
3393 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3394 int k = 0;
3395 struct naptr *new;
3396 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01003397 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003398
3399 if ((a[0] = arg))
3400 for (k = 1; k < 7; k++)
3401 if (!(a[k] = split(a[k-1])))
3402 break;
3403
3404
3405 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003406 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003407 !atoi_check16(a[1], &order) ||
3408 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01003409 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003410 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01003411 else
3412 {
3413 new = opt_malloc(sizeof(struct naptr));
3414 new->next = daemon->naptr;
3415 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01003416 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003417 new->flags = opt_string_alloc(a[3]);
3418 new->services = opt_string_alloc(a[4]);
3419 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01003420 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01003421 new->order = order;
3422 new->pref = pref;
3423 }
3424 break;
3425 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003426
3427 case LOPT_RR: /* dns-rr */
3428 {
3429 struct txt_record *new;
3430 size_t len;
3431 char *data;
3432 int val;
3433
3434 comma = split(arg);
3435 data = split(comma);
3436
3437 new = opt_malloc(sizeof(struct txt_record));
3438 new->next = daemon->rr;
3439 daemon->rr = new;
3440
3441 if (!atoi_check(comma, &val) ||
3442 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01003443 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003444 ret_err(_("bad RR record"));
3445
Simon Kelley9f7f3b12012-05-28 21:39:57 +01003446 new->class = val;
3447 new->len = 0;
3448
3449 if (data)
3450 {
3451 new->txt=opt_malloc(len);
3452 new->len = len;
3453 memcpy(new->txt, data, len);
3454 }
3455
3456 break;
3457 }
3458
Simon Kelleyf2621c72007-04-29 19:47:21 +01003459 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003460 {
3461 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003462 unsigned char *p, *cnt;
3463 size_t len;
3464
3465 comma = split(arg);
3466
Simon Kelley824af852008-02-12 20:43:05 +00003467 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003468 new->next = daemon->txt;
3469 daemon->txt = new;
3470 new->class = C_IN;
Simon Kelley849a8352006-06-09 21:02:31 +01003471
Simon Kelley1f15b812009-10-13 17:49:32 +01003472 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003473 ret_err(_("bad TXT record"));
3474
Simon Kelley28866e92011-02-14 20:19:14 +00003475 len = comma ? strlen(comma) : 0;
3476 len += (len/255) + 1; /* room for extra counts */
3477 new->txt = p = opt_malloc(len);
3478
3479 cnt = p++;
3480 *cnt = 0;
3481
3482 while (comma && *comma)
3483 {
3484 unsigned char c = (unsigned char)*comma++;
3485
3486 if (c == ',' || *cnt == 255)
3487 {
3488 if (c != ',')
3489 comma--;
3490 cnt = p++;
3491 *cnt = 0;
3492 }
3493 else
3494 {
3495 *p++ = unhide_meta(c);
3496 (*cnt)++;
3497 }
3498 }
3499
3500 new->len = p - new->txt;
3501
Simon Kelley849a8352006-06-09 21:02:31 +01003502 break;
3503 }
3504
Simon Kelleyf2621c72007-04-29 19:47:21 +01003505 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003506 {
3507 int port = 1, priority = 0, weight = 0;
3508 char *name, *target = NULL;
3509 struct mx_srv_record *new;
3510
Simon Kelleyf2621c72007-04-29 19:47:21 +01003511 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003512
Simon Kelley1f15b812009-10-13 17:49:32 +01003513 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003514 ret_err(_("bad SRV record"));
3515
Simon Kelley849a8352006-06-09 21:02:31 +01003516 if (comma)
3517 {
3518 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003519 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003520 if (!(target = canonicalise_opt(arg)))
3521 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00003522
Simon Kelley849a8352006-06-09 21:02:31 +01003523 if (comma)
3524 {
3525 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003526 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003527 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003528 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00003529
Simon Kelley849a8352006-06-09 21:02:31 +01003530 if (comma)
3531 {
3532 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003533 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003534 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003535 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00003536
Simon Kelley849a8352006-06-09 21:02:31 +01003537 if (comma)
3538 {
3539 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003540 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003541 if (!atoi_check16(arg, &weight))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003542 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01003543 }
3544 }
3545 }
3546 }
3547
Simon Kelley824af852008-02-12 20:43:05 +00003548 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003549 new->next = daemon->mxnames;
3550 daemon->mxnames = new;
3551 new->issrv = 1;
3552 new->name = name;
3553 new->target = target;
3554 new->srvport = port;
3555 new->priority = priority;
3556 new->weight = weight;
3557 break;
3558 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003559
Simon Kelleye759d422012-03-16 13:18:57 +00003560 case LOPT_HOST_REC: /* --host-record */
3561 {
3562 struct host_record *new = opt_malloc(sizeof(struct host_record));
3563 memset(new, 0, sizeof(struct host_record));
3564
3565 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003566 ret_err(_("Bad host-record"));
3567
3568 while (arg)
3569 {
3570 struct all_addr addr;
3571 if (inet_pton(AF_INET, arg, &addr))
3572 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00003573#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003574 else if (inet_pton(AF_INET6, arg, &addr))
3575 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00003576#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003577 else
3578 {
3579 int nomem;
3580 char *canon = canonicalise(arg, &nomem);
3581 struct name_list *nl = opt_malloc(sizeof(struct name_list));
3582 if (!canon)
3583 ret_err(_("Bad name in host-record"));
3584
3585 nl->name = canon;
3586 /* keep order, so that PTR record goes to first name */
3587 nl->next = NULL;
3588 if (!new->names)
3589 new->names = nl;
3590 else
3591 {
3592 struct name_list *tmp;
3593 for (tmp = new->names; tmp->next; tmp = tmp->next);
3594 tmp->next = nl;
3595 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003596 }
Simon Kelleye4807d82012-09-27 21:52:26 +01003597
3598 arg = comma;
3599 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003600 }
Simon Kelleye759d422012-03-16 13:18:57 +00003601
3602 /* Keep list order */
3603 if (!daemon->host_records_tail)
3604 daemon->host_records = new;
3605 else
3606 daemon->host_records_tail->next = new;
3607 new->next = NULL;
3608 daemon->host_records_tail = new;
3609 break;
3610 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003611
Simon Kelley7622fc02009-06-04 20:32:05 +01003612 default:
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003613 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
3614
Simon Kelley849a8352006-06-09 21:02:31 +01003615 }
Simon Kelley824af852008-02-12 20:43:05 +00003616
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003617 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01003618}
3619
Simon Kelley28866e92011-02-14 20:19:14 +00003620static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01003621{
Simon Kelley824af852008-02-12 20:43:05 +00003622 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003623 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01003624
3625 while (fgets(buff, MAXDNAME, f))
3626 {
Simon Kelley611ebc52012-07-16 16:23:46 +01003627 int white, i, option = hard_opt;
3628 char *errmess, *p, *arg = NULL, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003629 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00003630
Simon Kelley824af852008-02-12 20:43:05 +00003631 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley611ebc52012-07-16 16:23:46 +01003632 if (option != 0)
Simon Kelley824af852008-02-12 20:43:05 +00003633 {
3634 if (setjmp(mem_jmp))
3635 continue;
3636 mem_recover = 1;
3637 }
3638
Simon Kelley849a8352006-06-09 21:02:31 +01003639 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00003640 errmess = NULL;
3641
Simon Kelley849a8352006-06-09 21:02:31 +01003642 /* Implement quotes, inside quotes we allow \\ \" \n and \t
3643 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003644 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01003645 {
3646 if (*p == '"')
3647 {
3648 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003649
Simon Kelley849a8352006-06-09 21:02:31 +01003650 for(; *p && *p != '"'; p++)
3651 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003652 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01003653 {
3654 if (p[1] == 't')
3655 p[1] = '\t';
3656 else if (p[1] == 'n')
3657 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01003658 else if (p[1] == 'b')
3659 p[1] = '\b';
3660 else if (p[1] == 'r')
3661 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00003662 else if (p[1] == 'e') /* escape */
3663 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01003664 memmove(p, p+1, strlen(p+1)+1);
3665 }
3666 *p = hide_meta(*p);
3667 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003668
3669 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003670 {
3671 errmess = _("missing \"");
3672 goto oops;
3673 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003674
3675 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01003676 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003677
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003678 if (isspace(*p))
3679 {
3680 *p = ' ';
3681 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01003682 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003683 else
3684 {
3685 if (white && *p == '#')
3686 {
3687 *p = 0;
3688 break;
3689 }
3690 white = 0;
3691 }
Simon Kelley849a8352006-06-09 21:02:31 +01003692 }
3693
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003694
3695 /* strip leading spaces */
3696 for (start = buff; *start && *start == ' '; start++);
3697
3698 /* strip trailing spaces */
3699 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
3700
3701 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01003702 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003703 else
3704 start[len] = 0;
3705
Simon Kelley611ebc52012-07-16 16:23:46 +01003706 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003707 arg = start;
3708 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01003709 {
3710 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003711 for (arg = p+1; *arg == ' '; arg++);
3712 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01003713 *p = 0;
3714 }
3715 else
3716 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003717
Simon Kelley611ebc52012-07-16 16:23:46 +01003718 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01003719 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003720 for (option = 0, i = 0; opts[i].name; i++)
3721 if (strcmp(opts[i].name, start) == 0)
3722 {
3723 option = opts[i].val;
3724 break;
3725 }
3726
3727 if (!option)
3728 errmess = _("bad option");
3729 else if (opts[i].has_arg == 0 && arg)
3730 errmess = _("extraneous parameter");
3731 else if (opts[i].has_arg == 1 && !arg)
3732 errmess = _("missing parameter");
3733 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003734
3735 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00003736 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003737 strcpy(daemon->namebuff, errmess);
3738
3739 if (errmess || !one_opt(option, arg, buff, _("error"), 0))
Simon Kelleyf2621c72007-04-29 19:47:21 +01003740 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003741 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00003742 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003743 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01003744 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003745 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003746 }
Simon Kelley849a8352006-06-09 21:02:31 +01003747 }
3748
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003749 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003750 fclose(f);
3751}
3752
Simon Kelley395eb712012-07-06 22:07:05 +01003753static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00003754{
3755 FILE *f;
3756 int nofile_ok = 0;
3757 static int read_stdin = 0;
3758 static struct fileread {
3759 dev_t dev;
3760 ino_t ino;
3761 struct fileread *next;
3762 } *filesread = NULL;
3763
3764 if (hard_opt == '7')
3765 {
3766 /* default conf-file reading */
3767 hard_opt = 0;
3768 nofile_ok = 1;
3769 }
3770
3771 if (hard_opt == 0 && strcmp(file, "-") == 0)
3772 {
3773 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01003774 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003775 read_stdin = 1;
3776 file = "stdin";
3777 f = stdin;
3778 }
3779 else
3780 {
3781 /* ignore repeated files. */
3782 struct stat statbuf;
3783
3784 if (hard_opt == 0 && stat(file, &statbuf) == 0)
3785 {
3786 struct fileread *r;
3787
3788 for (r = filesread; r; r = r->next)
3789 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01003790 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003791
3792 r = safe_malloc(sizeof(struct fileread));
3793 r->next = filesread;
3794 filesread = r;
3795 r->dev = statbuf.st_dev;
3796 r->ino = statbuf.st_ino;
3797 }
3798
3799 if (!(f = fopen(file, "r")))
3800 {
3801 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01003802 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00003803 else
3804 {
3805 char *str = _("cannot read %s: %s");
3806 if (hard_opt != 0)
3807 {
3808 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01003809 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00003810 }
3811 else
3812 die(str, file, EC_FILE);
3813 }
3814 }
3815 }
3816
3817 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01003818 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003819}
3820
3821/* expand any name which is a directory */
3822struct hostsfile *expand_filelist(struct hostsfile *list)
3823{
3824 int i;
3825 struct hostsfile *ah;
3826
3827 for (i = 0, ah = list; ah; ah = ah->next)
3828 {
3829 if (i <= ah->index)
3830 i = ah->index + 1;
3831
3832 if (ah->flags & AH_DIR)
3833 ah->flags |= AH_INACTIVE;
3834 else
3835 ah->flags &= ~AH_INACTIVE;
3836 }
3837
3838 for (ah = list; ah; ah = ah->next)
3839 if (!(ah->flags & AH_INACTIVE))
3840 {
3841 struct stat buf;
3842 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
3843 {
3844 DIR *dir_stream;
3845 struct dirent *ent;
3846
3847 /* don't read this as a file */
3848 ah->flags |= AH_INACTIVE;
3849
3850 if (!(dir_stream = opendir(ah->fname)))
3851 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
3852 ah->fname, strerror(errno));
3853 else
3854 {
3855 while ((ent = readdir(dir_stream)))
3856 {
3857 size_t lendir = strlen(ah->fname);
3858 size_t lenfile = strlen(ent->d_name);
3859 struct hostsfile *ah1;
3860 char *path;
3861
3862 /* ignore emacs backups and dotfiles */
3863 if (lenfile == 0 ||
3864 ent->d_name[lenfile - 1] == '~' ||
3865 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
3866 ent->d_name[0] == '.')
3867 continue;
3868
3869 /* see if we have an existing record.
3870 dir is ah->fname
3871 file is ent->d_name
3872 path to match is ah1->fname */
3873
3874 for (ah1 = list; ah1; ah1 = ah1->next)
3875 {
3876 if (lendir < strlen(ah1->fname) &&
3877 strstr(ah1->fname, ah->fname) == ah1->fname &&
3878 ah1->fname[lendir] == '/' &&
3879 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
3880 {
3881 ah1->flags &= ~AH_INACTIVE;
3882 break;
3883 }
3884 }
3885
3886 /* make new record */
3887 if (!ah1)
3888 {
3889 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
3890 continue;
3891
3892 if (!(path = whine_malloc(lendir + lenfile + 2)))
3893 {
3894 free(ah1);
3895 continue;
3896 }
3897
3898 strcpy(path, ah->fname);
3899 strcat(path, "/");
3900 strcat(path, ent->d_name);
3901 ah1->fname = path;
3902 ah1->index = i++;
3903 ah1->flags = AH_DIR;
3904 ah1->next = list;
3905 list = ah1;
3906 }
3907
3908 /* inactivate record if not regular file */
3909 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
3910 ah1->flags |= AH_INACTIVE;
3911
3912 }
3913 closedir(dir_stream);
3914 }
3915 }
3916 }
3917
3918 return list;
3919}
3920
3921
Simon Kelley7622fc02009-06-04 20:32:05 +01003922#ifdef HAVE_DHCP
Simon Kelley824af852008-02-12 20:43:05 +00003923void reread_dhcp(void)
3924{
Simon Kelley28866e92011-02-14 20:19:14 +00003925 struct hostsfile *hf;
3926
Simon Kelley824af852008-02-12 20:43:05 +00003927 if (daemon->dhcp_hosts_file)
3928 {
3929 struct dhcp_config *configs, *cp, **up;
Simon Kelley28866e92011-02-14 20:19:14 +00003930
Simon Kelley824af852008-02-12 20:43:05 +00003931 /* remove existing... */
3932 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
3933 {
3934 cp = configs->next;
3935
3936 if (configs->flags & CONFIG_BANK)
3937 {
Simon Kelley9009d742008-11-14 20:04:27 +00003938 struct hwaddr_config *mac, *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003939 struct dhcp_netid_list *list, *tmplist;
Simon Kelley9009d742008-11-14 20:04:27 +00003940
3941 for (mac = configs->hwaddr; mac; mac = tmp)
3942 {
3943 tmp = mac->next;
3944 free(mac);
3945 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003946
Simon Kelley824af852008-02-12 20:43:05 +00003947 if (configs->flags & CONFIG_CLID)
3948 free(configs->clid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003949
3950 for (list = configs->netid; list; list = tmplist)
3951 {
3952 free(list->list);
3953 tmplist = list->next;
3954 free(list);
3955 }
3956
Simon Kelley824af852008-02-12 20:43:05 +00003957 if (configs->flags & CONFIG_NAME)
3958 free(configs->hostname);
3959
3960 *up = configs->next;
3961 free(configs);
3962 }
3963 else
3964 up = &configs->next;
3965 }
3966
Simon Kelley28866e92011-02-14 20:19:14 +00003967 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
3968 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
3969 if (!(hf->flags & AH_INACTIVE))
3970 {
Simon Kelley395eb712012-07-06 22:07:05 +01003971 if (one_file(hf->fname, LOPT_BANK))
3972 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00003973 }
Simon Kelley824af852008-02-12 20:43:05 +00003974 }
3975
3976 if (daemon->dhcp_opts_file)
3977 {
3978 struct dhcp_opt *opts, *cp, **up;
3979 struct dhcp_netid *id, *next;
3980
3981 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
3982 {
3983 cp = opts->next;
3984
3985 if (opts->flags & DHOPT_BANK)
3986 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003987 if ((opts->flags & DHOPT_VENDOR))
3988 free(opts->u.vendor_class);
Simon Kelley824af852008-02-12 20:43:05 +00003989 free(opts->val);
3990 for (id = opts->netid; id; id = next)
3991 {
3992 next = id->next;
3993 free(id->net);
3994 free(id);
3995 }
3996 *up = opts->next;
3997 free(opts);
3998 }
3999 else
4000 up = &opts->next;
4001 }
4002
Simon Kelley28866e92011-02-14 20:19:14 +00004003 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
4004 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
4005 if (!(hf->flags & AH_INACTIVE))
4006 {
Simon Kelley395eb712012-07-06 22:07:05 +01004007 if (one_file(hf->fname, LOPT_OPTS))
4008 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00004009 }
Simon Kelley824af852008-02-12 20:43:05 +00004010 }
4011}
Simon Kelley7622fc02009-06-04 20:32:05 +01004012#endif
Simon Kelley824af852008-02-12 20:43:05 +00004013
Simon Kelley5aabfc72007-08-29 11:24:47 +01004014void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004015{
Simon Kelley824af852008-02-12 20:43:05 +00004016 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00004017 int option, conffile_opt = '7', testmode = 0;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004018 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01004019
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004020 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004021
Simon Kelley824af852008-02-12 20:43:05 +00004022 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01004023 memset(daemon, 0, sizeof(struct daemon));
4024 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004025
Simon Kelley3be34542004-09-11 19:12:13 +01004026 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01004027 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01004028 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01004029 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01004030 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
4031 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01004032 daemon->default_resolv.is_default = 1;
4033 daemon->default_resolv.name = RESOLVFILE;
4034 daemon->resolv_files = &daemon->default_resolv;
4035 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01004036 daemon->runfile = RUNFILE;
4037 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00004038 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01004039 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01004040 daemon->log_fac = -1;
Simon Kelley4f7b3042012-11-28 21:27:02 +00004041 daemon->auth_ttl = AUTH_TTL;
4042 daemon->soa_refresh = SOA_REFRESH;
4043 daemon->soa_retry = SOA_RETRY;
4044 daemon->soa_expiry = SOA_EXPIRY;
Simon Kelley5aabfc72007-08-29 11:24:47 +01004045 add_txt("version.bind", "dnsmasq-" VERSION );
4046 add_txt("authors.bind", "Simon Kelley");
4047 add_txt("copyright.bind", COPYRIGHT);
Simon Kelley0a852542005-03-23 20:28:59 +00004048
Simon Kelley849a8352006-06-09 21:02:31 +01004049 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004050 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004051#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01004052 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004053#else
Simon Kelley849a8352006-06-09 21:02:31 +01004054 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004055#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004056
4057 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00004058 {
Simon Kelley572b41e2011-02-18 18:11:18 +00004059 for (; optind < argc; optind++)
4060 {
4061 unsigned char *c = (unsigned char *)argv[optind];
4062 for (; *c != 0; c++)
4063 if (!isspace(*c))
4064 die(_("junk found in command line"), NULL, EC_BADCONF);
4065 }
Simon Kelley28866e92011-02-14 20:19:14 +00004066 break;
4067 }
4068
Simon Kelley849a8352006-06-09 21:02:31 +01004069 /* Copy optarg so that argv doesn't get changed */
4070 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004071 {
Simon Kelley849a8352006-06-09 21:02:31 +01004072 strncpy(buff, optarg, MAXDNAME);
4073 buff[MAXDNAME-1] = 0;
4074 arg = buff;
4075 }
4076 else
4077 arg = NULL;
4078
4079 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01004080 if (option == LOPT_TEST)
4081 testmode = 1;
4082 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01004083 {
Simon Kelley7622fc02009-06-04 20:32:05 +01004084#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00004085 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01004086 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00004087#ifdef HAVE_DHCP6
4088 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
4089 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01004090#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00004091 else
4092#endif
4093 do_usage();
4094
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004095 exit(0);
4096 }
Simon Kelley849a8352006-06-09 21:02:31 +01004097 else if (option == 'v')
4098 {
4099 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00004100 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00004101 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
4102 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00004103 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004104 exit(0);
4105 }
Simon Kelley849a8352006-06-09 21:02:31 +01004106 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004107 {
Simon Kelley28866e92011-02-14 20:19:14 +00004108 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00004109 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004110 }
Simon Kelley849a8352006-06-09 21:02:31 +01004111 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004112 {
Simon Kelley26128d22004-11-14 16:43:54 +00004113#ifdef HAVE_GETOPT_LONG
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004114 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
Simon Kelley849a8352006-06-09 21:02:31 +01004115#else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004116 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
Simon Kelley849a8352006-06-09 21:02:31 +01004117#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01004118 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004119 }
4120 }
Simon Kelley849a8352006-06-09 21:02:31 +01004121
4122 if (conffile)
Simon Kelley28866e92011-02-14 20:19:14 +00004123 one_file(conffile, conffile_opt);
Simon Kelley849a8352006-06-09 21:02:31 +01004124
Simon Kelley1a6bca82008-07-11 11:11:42 +01004125 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01004126 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004127 {
4128 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004129 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004130 if (!(tmp->flags & SERV_HAS_SOURCE))
4131 {
4132 if (tmp->source_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004133 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004134#ifdef HAVE_IPV6
4135 else if (tmp->source_addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004136 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01004137#endif
4138 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004139 }
4140
Simon Kelley3be34542004-09-11 19:12:13 +01004141 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004142 {
4143 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01004144 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004145 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01004146 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004147#ifdef HAVE_IPV6
4148 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01004149 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004150#endif /* IPv6 */
4151 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00004152
4153 /* create default, if not specified */
4154 if (daemon->authserver && !daemon->hostmaster)
4155 {
4156 strcpy(buff, "hostmaster.");
4157 strcat(buff, daemon->authserver);
4158 daemon->hostmaster = opt_string_alloc(buff);
4159 }
4160
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004161 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00004162 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004163 {
Simon Kelley0a852542005-03-23 20:28:59 +00004164 struct mx_srv_record *mx;
4165
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004166 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004167 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004168
Simon Kelley0a852542005-03-23 20:28:59 +00004169 for (mx = daemon->mxnames; mx; mx = mx->next)
4170 if (!mx->issrv && hostname_isequal(mx->name, buff))
4171 break;
4172
Simon Kelley28866e92011-02-14 20:19:14 +00004173 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01004174 {
Simon Kelley824af852008-02-12 20:43:05 +00004175 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01004176 mx->next = daemon->mxnames;
4177 mx->issrv = 0;
4178 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00004179 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01004180 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004181 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004182
Simon Kelley3be34542004-09-11 19:12:13 +01004183 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00004184 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00004185
4186 for (mx = daemon->mxnames; mx; mx = mx->next)
4187 if (!mx->issrv && !mx->target)
4188 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00004189 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00004190
Simon Kelley28866e92011-02-14 20:19:14 +00004191 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01004192 daemon->resolv_files &&
4193 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00004194 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004195 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004196
Simon Kelley28866e92011-02-14 20:19:14 +00004197 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01004198 {
4199 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01004200 FILE *f;
4201
Simon Kelley28866e92011-02-14 20:19:14 +00004202 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01004203 !daemon->resolv_files ||
4204 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004205 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01004206
Simon Kelley3be34542004-09-11 19:12:13 +01004207 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01004208 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01004209
4210 while ((line = fgets(buff, MAXDNAME, f)))
4211 {
4212 char *token = strtok(line, " \t\n\r");
4213
4214 if (!token || strcmp(token, "search") != 0)
4215 continue;
4216
4217 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01004218 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01004219 break;
4220 }
Simon Kelley3be34542004-09-11 19:12:13 +01004221
Simon Kelleyde379512004-06-22 20:23:33 +01004222 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00004223
Simon Kelley3be34542004-09-11 19:12:13 +01004224 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01004225 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01004226 }
Simon Kelley3d8df262005-08-29 12:19:27 +01004227
4228 if (daemon->domain_suffix)
4229 {
4230 /* add domain for any srv record without one. */
4231 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01004232
Simon Kelley3d8df262005-08-29 12:19:27 +01004233 for (srv = daemon->mxnames; srv; srv = srv->next)
4234 if (srv->issrv &&
4235 strchr(srv->name, '.') &&
4236 strchr(srv->name, '.') == strrchr(srv->name, '.'))
4237 {
4238 strcpy(buff, srv->name);
4239 strcat(buff, ".");
4240 strcat(buff, daemon->domain_suffix);
4241 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00004242 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01004243 }
4244 }
Simon Kelley28866e92011-02-14 20:19:14 +00004245 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00004246 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01004247
4248 if (testmode)
4249 {
4250 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
4251 exit(0);
4252 }
Simon Kelley849a8352006-06-09 21:02:31 +01004253}