blob: 22c08e7777a42d0be9eecb28a994f53dd871b5be [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley849a8352006-06-09 21:02:31 +010017/* define this to get facilitynames */
18#define SYSLOG_NAMES
Simon Kelley9e4abcb2004-01-22 19:47:41 +000019#include "dnsmasq.h"
Simon Kelley824af852008-02-12 20:43:05 +000020#include <setjmp.h>
21
Simon Kelley7622fc02009-06-04 20:32:05 +010022static volatile int mem_recover = 0;
23static jmp_buf mem_jmp;
Simon Kelley395eb712012-07-06 22:07:05 +010024static int one_file(char *file, int hard_opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010025
Simon Kelley824af852008-02-12 20:43:05 +000026/* Solaris headers don't have facility names. */
27#ifdef HAVE_SOLARIS_NETWORK
28static const struct {
29 char *c_name;
30 unsigned int c_val;
31} facilitynames[] = {
32 { "kern", LOG_KERN },
33 { "user", LOG_USER },
34 { "mail", LOG_MAIL },
35 { "daemon", LOG_DAEMON },
36 { "auth", LOG_AUTH },
37 { "syslog", LOG_SYSLOG },
38 { "lpr", LOG_LPR },
39 { "news", LOG_NEWS },
40 { "uucp", LOG_UUCP },
Simon Kelley824af852008-02-12 20:43:05 +000041 { "audit", LOG_AUDIT },
Simon Kelley824af852008-02-12 20:43:05 +000042 { "cron", LOG_CRON },
43 { "local0", LOG_LOCAL0 },
44 { "local1", LOG_LOCAL1 },
45 { "local2", LOG_LOCAL2 },
46 { "local3", LOG_LOCAL3 },
47 { "local4", LOG_LOCAL4 },
48 { "local5", LOG_LOCAL5 },
49 { "local6", LOG_LOCAL6 },
50 { "local7", LOG_LOCAL7 },
51 { NULL, 0 }
52};
53#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000054
Simon Kelley849a8352006-06-09 21:02:31 +010055#ifndef HAVE_GETOPT_LONG
Simon Kelley9e4abcb2004-01-22 19:47:41 +000056struct myoption {
57 const char *name;
58 int has_arg;
59 int *flag;
60 int val;
61};
Simon Kelley849a8352006-06-09 21:02:31 +010062#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000063
Simon Kelley9009d742008-11-14 20:04:27 +000064#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
Simon Kelley9e4abcb2004-01-22 19:47:41 +000065
Simon Kelley16972692006-10-16 20:04:18 +010066/* options which don't have a one-char version */
Simon Kelley832af0b2007-01-21 20:01:28 +000067#define LOPT_RELOAD 256
68#define LOPT_NO_NAMES 257
69#define LOPT_TFTP 258
70#define LOPT_SECURE 259
71#define LOPT_PREFIX 260
72#define LOPT_PTR 261
73#define LOPT_BRIDGE 262
74#define LOPT_TFTP_MAX 263
Simon Kelley6b010842007-02-12 20:32:07 +000075#define LOPT_FORCE 264
76#define LOPT_NOBLOCK 265
Simon Kelleyf2621c72007-04-29 19:47:21 +010077#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
Simon Kelley5aabfc72007-08-29 11:24:47 +010083#define LOPT_BANK 272
84#define LOPT_DHCP_HOST 273
85#define LOPT_APREF 274
Simon Kelley824af852008-02-12 20:43:05 +000086#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
Simon Kelley9e038942008-05-30 20:06:34 +010095#define LOPT_ALTPORT 284
96#define LOPT_SCRIPTUSR 285
Simon Kelley1a6bca82008-07-11 11:11:42 +010097#define LOPT_LOCAL 286
98#define LOPT_NAPTR 287
99#define LOPT_MINPORT 288
Simon Kelley9009d742008-11-14 20:04:27 +0000100#define LOPT_DHCP_FQDN 289
101#define LOPT_CNAME 290
Simon Kelley7622fc02009-06-04 20:32:05 +0100102#define LOPT_PXE_PROMT 291
103#define LOPT_PXE_SERV 292
104#define LOPT_TEST 293
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100105#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
Simon Kelley28866e92011-02-14 20:19:14 +0000111#define LOPT_ADD_MAC 300
112#define LOPT_DNSSEC 301
Simon Kelley7de060b2011-08-26 17:24:52 +0100113#define LOPT_INCR_ADDR 302
114#define LOPT_CONNTRACK 303
Simon Kelleyc72daea2012-01-05 21:33:27 +0000115#define LOPT_FQDN 304
116#define LOPT_LUASCRIPT 305
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000117#define LOPT_RA 306
Simon Kelley8b372702012-03-09 17:45:10 +0000118#define LOPT_DUID 307
Simon Kelleye759d422012-03-16 13:18:57 +0000119#define LOPT_HOST_REC 308
Simon Kelley61ce6002012-04-20 21:28:49 +0100120#define LOPT_TFTP_LC 309
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100121#define LOPT_RR 310
Simon Kelley54dd3932012-06-20 11:23:38 +0100122#define LOPT_CLVERBIND 311
Simon Kelley16972692006-10-16 20:04:18 +0100123
Simon Kelley849a8352006-06-09 21:02:31 +0100124#ifdef HAVE_GETOPT_LONG
125static const struct option opts[] =
126#else
127static const struct myoption opts[] =
128#endif
129 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100130 { "version", 0, 0, 'v' },
131 { "no-hosts", 0, 0, 'h' },
132 { "no-poll", 0, 0, 'n' },
133 { "help", 0, 0, 'w' },
134 { "no-daemon", 0, 0, 'd' },
135 { "log-queries", 0, 0, 'q' },
136 { "user", 2, 0, 'u' },
137 { "group", 2, 0, 'g' },
138 { "resolv-file", 2, 0, 'r' },
139 { "mx-host", 1, 0, 'm' },
140 { "mx-target", 1, 0, 't' },
141 { "cache-size", 2, 0, 'c' },
142 { "port", 1, 0, 'p' },
143 { "dhcp-leasefile", 2, 0, 'l' },
144 { "dhcp-lease", 1, 0, 'l' },
145 { "dhcp-host", 1, 0, 'G' },
146 { "dhcp-range", 1, 0, 'F' },
147 { "dhcp-option", 1, 0, 'O' },
148 { "dhcp-boot", 1, 0, 'M' },
149 { "domain", 1, 0, 's' },
150 { "domain-suffix", 1, 0, 's' },
151 { "interface", 1, 0, 'i' },
152 { "listen-address", 1, 0, 'a' },
153 { "bogus-priv", 0, 0, 'b' },
154 { "bogus-nxdomain", 1, 0, 'B' },
155 { "selfmx", 0, 0, 'e' },
156 { "filterwin2k", 0, 0, 'f' },
157 { "pid-file", 2, 0, 'x' },
158 { "strict-order", 0, 0, 'o' },
159 { "server", 1, 0, 'S' },
160 { "local", 1, 0, LOPT_LOCAL },
161 { "address", 1, 0, 'A' },
162 { "conf-file", 2, 0, 'C' },
163 { "no-resolv", 0, 0, 'R' },
164 { "expand-hosts", 0, 0, 'E' },
165 { "localmx", 0, 0, 'L' },
166 { "local-ttl", 1, 0, 'T' },
167 { "no-negcache", 0, 0, 'N' },
168 { "addn-hosts", 1, 0, 'H' },
169 { "query-port", 1, 0, 'Q' },
170 { "except-interface", 1, 0, 'I' },
171 { "no-dhcp-interface", 1, 0, '2' },
172 { "domain-needed", 0, 0, 'D' },
173 { "dhcp-lease-max", 1, 0, 'X' },
174 { "bind-interfaces", 0, 0, 'z' },
175 { "read-ethers", 0, 0, 'Z' },
176 { "alias", 1, 0, 'V' },
177 { "dhcp-vendorclass", 1, 0, 'U' },
178 { "dhcp-userclass", 1, 0, 'j' },
179 { "dhcp-ignore", 1, 0, 'J' },
180 { "edns-packet-max", 1, 0, 'P' },
181 { "keep-in-foreground", 0, 0, 'k' },
182 { "dhcp-authoritative", 0, 0, 'K' },
183 { "srv-host", 1, 0, 'W' },
184 { "localise-queries", 0, 0, 'y' },
185 { "txt-record", 1, 0, 'Y' },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100186 { "dns-rr", 1, 0, LOPT_RR },
Simon Kelleyad094272012-08-10 17:10:54 +0100187 { "enable-dbus", 2, 0, '1' },
Simon Kelley7622fc02009-06-04 20:32:05 +0100188 { "bootp-dynamic", 2, 0, '3' },
189 { "dhcp-mac", 1, 0, '4' },
190 { "no-ping", 0, 0, '5' },
191 { "dhcp-script", 1, 0, '6' },
192 { "conf-dir", 1, 0, '7' },
193 { "log-facility", 1, 0 ,'8' },
194 { "leasefile-ro", 0, 0, '9' },
195 { "dns-forward-max", 1, 0, '0' },
196 { "clear-on-reload", 0, 0, LOPT_RELOAD },
197 { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100198 { "enable-tftp", 0, 0, LOPT_TFTP },
Simon Kelley7622fc02009-06-04 20:32:05 +0100199 { "tftp-secure", 0, 0, LOPT_SECURE },
200 { "tftp-unique-root", 0, 0, LOPT_APREF },
201 { "tftp-root", 1, 0, LOPT_PREFIX },
202 { "tftp-max", 1, 0, LOPT_TFTP_MAX },
Simon Kelley61ce6002012-04-20 21:28:49 +0100203 { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
Simon Kelley7622fc02009-06-04 20:32:05 +0100204 { "ptr-record", 1, 0, LOPT_PTR },
205 { "naptr-record", 1, 0, LOPT_NAPTR },
206 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
207 { "dhcp-option-force", 1, 0, LOPT_FORCE },
208 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
209 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
210 { "log-async", 2, 0, LOPT_MAX_LOGS },
211 { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
212 { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
213 { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
214 { "interface-name", 1, 0, LOPT_INTNAME },
215 { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
216 { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
217 { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
218 { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
219 { "stop-dns-rebind", 0, 0, LOPT_REBIND },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100220 { "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
Simon Kelley7622fc02009-06-04 20:32:05 +0100221 { "all-servers", 0, 0, LOPT_NOLAST },
222 { "dhcp-match", 1, 0, LOPT_MATCH },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100223 { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
Simon Kelley7622fc02009-06-04 20:32:05 +0100224 { "neg-ttl", 1, 0, LOPT_NEGTTL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100225 { "max-ttl", 1, 0, LOPT_MAXTTL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100226 { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
227 { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
228 { "min-port", 1, 0, LOPT_MINPORT },
229 { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
230 { "cname", 1, 0, LOPT_CNAME },
231 { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
232 { "pxe-service", 1, 0, LOPT_PXE_SERV },
233 { "test", 0, 0, LOPT_TEST },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100234 { "tag-if", 1, 0, LOPT_TAG_IF },
235 { "dhcp-proxy", 2, 0, LOPT_PROXY },
236 { "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
237 { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
Simon Kelley28866e92011-02-14 20:19:14 +0000238 { "add-mac", 0, 0, LOPT_ADD_MAC },
239 { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
Simon Kelley7de060b2011-08-26 17:24:52 +0100240 { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
241 { "conntrack", 0, 0, LOPT_CONNTRACK },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000242 { "dhcp-client-update", 0, 0, LOPT_FQDN },
243 { "dhcp-luascript", 1, 0, LOPT_LUASCRIPT },
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000244 { "enable-ra", 0, 0, LOPT_RA },
Simon Kelley8b372702012-03-09 17:45:10 +0000245 { "dhcp-duid", 1, 0, LOPT_DUID },
Simon Kelleye759d422012-03-16 13:18:57 +0000246 { "host-record", 1, 0, LOPT_HOST_REC },
Simon Kelley54dd3932012-06-20 11:23:38 +0100247 { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
Simon Kelley849a8352006-06-09 21:02:31 +0100248 { NULL, 0, 0, 0 }
249 };
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000250
Simon Kelley28866e92011-02-14 20:19:14 +0000251
252#define ARG_DUP OPT_LAST
253#define ARG_ONE OPT_LAST + 1
254#define ARG_USED_CL OPT_LAST + 2
255#define ARG_USED_FILE OPT_LAST + 3
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000256
Simon Kelley1a6bca82008-07-11 11:11:42 +0100257static struct {
258 int opt;
259 unsigned int rept;
260 char * const flagdesc;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000261 char * const desc;
262 char * const arg;
263} usage[] = {
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000264 { 'a', ARG_DUP, "<ipaddr>", gettext_noop("Specify local address(es) to listen on."), NULL },
265 { 'A', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100266 { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000267 { 'B', ARG_DUP, "<ipaddr>", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
268 { 'c', ARG_ONE, "<integer>", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
269 { 'C', ARG_DUP, "<path>", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100270 { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
271 { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL },
272 { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
273 { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
274 { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000275 { 'F', ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
276 { 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100277 { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000278 { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
279 { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100280 { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100281 { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000282 { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
283 { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
284 { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100285 { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
286 { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
287 { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
288 { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
289 { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
290 { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100291 { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
292 { '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 +0000293 { 'l', ARG_ONE, "<path>", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100294 { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000295 { 'm', ARG_DUP, "<host_name>,<target>,<pref>", gettext_noop("Specify an MX record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100296 { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
297 { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
298 { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
299 { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
300 { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
301 { 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 +0000302 { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
303 { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100304 { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000305 { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100306 { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000307 { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
308 { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
309 { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000310 { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000311 { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
312 { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
313 { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
314 { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
315 { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100316 { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100317 { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000318 { 'V', ARG_DUP, "<ipaddr>,<ipaddr>,<netmask>", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
319 { 'W', ARG_DUP, "<name>,<target>,...", gettext_noop("Specify a SRV record."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100320 { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000321 { 'x', ARG_ONE, "<path>", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
322 { 'X', ARG_ONE, "<integer>", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100323 { '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 +0000324 { 'Y', ARG_DUP, "<name>,<txt>[,<txt]", gettext_noop("Specify TXT DNS record."), NULL },
325 { LOPT_PTR, ARG_DUP, "<name>,<target>", gettext_noop("Specify PTR DNS record."), NULL },
326 { LOPT_INTNAME, ARG_DUP, "<name>,<interface>", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100327 { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
328 { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
Simon Kelleyad094272012-08-10 17:10:54 +0100329 { '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000330 { '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100331 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
332 { '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 +0000333 { 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 +0100334 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000335 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
336 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
337 { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change scripts as this user."), NULL },
338 { '7', ARG_DUP, "<path>", gettext_noop("Read configuration from all the files in this directory."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100339 { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
340 { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000341 { '0', ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100342 { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100343 { LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100344 { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100345 { LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL },
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100346 { 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 +0100347 { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
348 { 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 +0000349 { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100350 { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
Simon Kelley61ce6002012-04-20 21:28:49 +0100351 { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100352 { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
353 { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
Simon Kelley8ecfaa42012-01-07 15:29:48 +0000354 { LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100355 { 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 +0100356 { 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 +0000357 { LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100358 { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100359 { 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 +0100360 { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
Simon Kelley1a6bca82008-07-11 11:11:42 +0100361 { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
362 { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000363 { 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 +0000364 { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
365 { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
Simon Kelley9009d742008-11-14 20:04:27 +0000366 { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
Simon Kelley7622fc02009-06-04 20:32:05 +0100367 { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
368 { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
369 { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
Simon Kelley7de060b2011-08-26 17:24:52 +0100370 { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
371 { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
372 { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
373 { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
Simon Kelleyc72daea2012-01-05 21:33:27 +0000374 { 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 +0000375 { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
Simon Kelley8b372702012-03-09 17:45:10 +0000376 { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
Simon Kelleye759d422012-03-16 13:18:57 +0000377 { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
Simon Kelley9f7f3b12012-05-28 21:39:57 +0100378 { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
Simon Kelley54dd3932012-06-20 11:23:38 +0100379 { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL},
Simon Kelley1a6bca82008-07-11 11:11:42 +0100380 { 0, 0, NULL, NULL, NULL }
Simon Kelleyb8187c82005-11-26 21:46:27 +0000381};
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000382
Simon Kelley3d8df262005-08-29 12:19:27 +0100383/* We hide metacharaters in quoted strings by mapping them into the ASCII control
Simon Kelleyf2621c72007-04-29 19:47:21 +0100384 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 +0100385 following sequence so that they map to themselves: it is therefore possible to call
386 unhide_metas repeatedly on string without breaking things.
Simon Kelley824af852008-02-12 20:43:05 +0000387 The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a
Simon Kelleyf2621c72007-04-29 19:47:21 +0100388 couple of other places.
389 Note that space is included here so that
390 --dhcp-option=3, string
391 has five characters, whilst
392 --dhcp-option=3," string"
393 has six.
394*/
Simon Kelley3d8df262005-08-29 12:19:27 +0100395
Simon Kelleyf2621c72007-04-29 19:47:21 +0100396static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
Simon Kelley3d8df262005-08-29 12:19:27 +0100397
398static char hide_meta(char c)
399{
400 unsigned int i;
401
402 for (i = 0; i < (sizeof(meta) - 1); i++)
403 if (c == meta[i])
404 return (char)i;
405
406 return c;
407}
408
409static char unhide_meta(char cr)
410{
411 unsigned int c = cr;
412
413 if (c < (sizeof(meta) - 1))
414 cr = meta[c];
415
416 return cr;
417}
418
419static void unhide_metas(char *cp)
420{
421 if (cp)
422 for(; *cp; cp++)
423 *cp = unhide_meta(*cp);
424}
425
Simon Kelley824af852008-02-12 20:43:05 +0000426static void *opt_malloc(size_t size)
427{
428 void *ret;
429
430 if (mem_recover)
431 {
432 ret = whine_malloc(size);
433 if (!ret)
434 longjmp(mem_jmp, 1);
435 }
436 else
437 ret = safe_malloc(size);
438
439 return ret;
440}
441
442static char *opt_string_alloc(char *cp)
Simon Kelley3d8df262005-08-29 12:19:27 +0100443{
444 char *ret = NULL;
445
446 if (cp && strlen(cp) != 0)
447 {
Simon Kelley824af852008-02-12 20:43:05 +0000448 ret = opt_malloc(strlen(cp)+1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100449 strcpy(ret, cp);
450
451 /* restore hidden metachars */
452 unhide_metas(ret);
453 }
454
455 return ret;
456}
457
Simon Kelley3d8df262005-08-29 12:19:27 +0100458
Simon Kelleyf2621c72007-04-29 19:47:21 +0100459/* find next comma, split string with zero and eliminate spaces.
460 return start of string following comma */
Simon Kelley73a08a22009-02-05 20:28:08 +0000461
462static char *split_chr(char *s, char c)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100463{
464 char *comma, *p;
465
Simon Kelley73a08a22009-02-05 20:28:08 +0000466 if (!s || !(comma = strchr(s, c)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100467 return NULL;
468
469 p = comma;
470 *comma = ' ';
471
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100472 for (; *comma == ' '; comma++);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100473
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100474 for (; (p >= s) && *p == ' '; p--)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100475 *p = 0;
476
477 return comma;
Simon Kelley3d8df262005-08-29 12:19:27 +0100478}
479
Simon Kelley73a08a22009-02-05 20:28:08 +0000480static char *split(char *s)
481{
482 return split_chr(s, ',');
483}
484
Simon Kelley1f15b812009-10-13 17:49:32 +0100485static char *canonicalise_opt(char *s)
Simon Kelley3d8df262005-08-29 12:19:27 +0100486{
Simon Kelley1f15b812009-10-13 17:49:32 +0100487 char *ret;
488 int nomem;
489
Simon Kelley3d8df262005-08-29 12:19:27 +0100490 if (!s)
491 return 0;
492
493 unhide_metas(s);
Simon Kelley1f15b812009-10-13 17:49:32 +0100494 if (!(ret = canonicalise(s, &nomem)) && nomem)
495 {
496 if (mem_recover)
497 longjmp(mem_jmp, 1);
498 else
499 die(_("could not get memory"), NULL, EC_NOMEM);
500 }
501
502 return ret;
Simon Kelley3d8df262005-08-29 12:19:27 +0100503}
504
505static int atoi_check(char *a, int *res)
506{
507 char *p;
508
509 if (!a)
510 return 0;
511
512 unhide_metas(a);
513
514 for (p = a; *p; p++)
515 if (*p < '0' || *p > '9')
516 return 0;
517
518 *res = atoi(a);
519 return 1;
520}
521
Simon Kelley1ad24ae2008-07-20 20:22:50 +0100522static int atoi_check16(char *a, int *res)
523{
524 if (!(atoi_check(a, res)) ||
525 *res < 0 ||
526 *res > 0xffff)
527 return 0;
528
529 return 1;
530}
531
Simon Kelley5aabfc72007-08-29 11:24:47 +0100532static void add_txt(char *name, char *txt)
Simon Kelley0a852542005-03-23 20:28:59 +0000533{
534 size_t len = strlen(txt);
Simon Kelley824af852008-02-12 20:43:05 +0000535 struct txt_record *r = opt_malloc(sizeof(struct txt_record));
Simon Kelley0a852542005-03-23 20:28:59 +0000536
Simon Kelley824af852008-02-12 20:43:05 +0000537 r->name = opt_string_alloc(name);
Simon Kelley0a852542005-03-23 20:28:59 +0000538 r->next = daemon->txt;
539 daemon->txt = r;
540 r->class = C_CHAOS;
Simon Kelley824af852008-02-12 20:43:05 +0000541 r->txt = opt_malloc(len+1);
Simon Kelley0a852542005-03-23 20:28:59 +0000542 r->len = len+1;
543 *(r->txt) = len;
544 memcpy((r->txt)+1, txt, len);
545}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000546
Simon Kelley849a8352006-06-09 21:02:31 +0100547static void do_usage(void)
548{
549 char buff[100];
Simon Kelley832af0b2007-01-21 20:01:28 +0000550 int i, j;
551
552 struct {
553 char handle;
554 int val;
555 } tab[] = {
556 { '$', CACHESIZ },
557 { '*', EDNS_PKTSZ },
558 { '&', MAXLEASES },
559 { '!', FTABSIZ },
560 { '#', TFTP_MAX_CONNECTIONS },
561 { '\0', 0 }
562 };
Simon Kelley849a8352006-06-09 21:02:31 +0100563
564 printf(_("Usage: dnsmasq [options]\n\n"));
565#ifndef HAVE_GETOPT_LONG
566 printf(_("Use short options only on the command line.\n"));
567#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100568 printf(_("Valid options are:\n"));
Simon Kelley849a8352006-06-09 21:02:31 +0100569
Simon Kelley1a6bca82008-07-11 11:11:42 +0100570 for (i = 0; usage[i].opt != 0; i++)
Simon Kelley849a8352006-06-09 21:02:31 +0100571 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100572 char *desc = usage[i].flagdesc;
573 char *eq = "=";
574
575 if (!desc || *desc == '[')
576 eq = "";
577
578 if (!desc)
579 desc = "";
580
581 for ( j = 0; opts[j].name; j++)
582 if (opts[j].val == usage[i].opt)
583 break;
584 if (usage[i].opt < 256)
585 sprintf(buff, "-%c, ", usage[i].opt);
586 else
587 sprintf(buff, " ");
588
589 sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100590 printf("%-40.40s", buff);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100591
Simon Kelley849a8352006-06-09 21:02:31 +0100592 if (usage[i].arg)
593 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000594 strcpy(buff, usage[i].arg);
595 for (j = 0; tab[j].handle; j++)
596 if (tab[j].handle == *(usage[i].arg))
597 sprintf(buff, "%d", tab[j].val);
Simon Kelley849a8352006-06-09 21:02:31 +0100598 }
Simon Kelley849a8352006-06-09 21:02:31 +0100599 printf(_(usage[i].desc), buff);
600 printf("\n");
601 }
602}
603
Simon Kelleyc740e4f2012-08-09 16:19:01 +0100604#define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
605
Simon Kelley7622fc02009-06-04 20:32:05 +0100606#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +0000607
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100608static int is_tag_prefix(char *arg)
609{
610 if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
611 return 1;
612
613 return 0;
614}
615
616static char *set_prefix(char *arg)
617{
618 if (strstr(arg, "set:") == arg)
619 return arg+4;
620
621 return arg;
622}
623
Simon Kelley832af0b2007-01-21 20:01:28 +0000624/* This is too insanely large to keep in-line in the switch */
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100625static int parse_dhcp_opt(char *errstr, char *arg, int flags)
Simon Kelley832af0b2007-01-21 20:01:28 +0000626{
Simon Kelley824af852008-02-12 20:43:05 +0000627 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
Simon Kelley832af0b2007-01-21 20:01:28 +0000628 char lenchar = 0, *cp;
Simon Kelley40ef23b2012-03-13 21:59:28 +0000629 int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100630 char *comma = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100631 struct dhcp_netid *np = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000632 u16 opt_len = 0;
633 int is6 = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000634
635 new->len = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000636 new->flags = flags;
Simon Kelley832af0b2007-01-21 20:01:28 +0000637 new->netid = NULL;
638 new->val = NULL;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100639 new->opt = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000640
Simon Kelleyf2621c72007-04-29 19:47:21 +0100641 while (arg)
Simon Kelley832af0b2007-01-21 20:01:28 +0000642 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100643 comma = split(arg);
644
645 for (cp = arg; *cp; cp++)
646 if (*cp < '0' || *cp > '9')
Simon Kelley832af0b2007-01-21 20:01:28 +0000647 break;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100648
649 if (!*cp)
650 {
651 new->opt = atoi(arg);
652 opt_len = 0;
653 break;
654 }
655
656 if (strstr(arg, "option:") == arg)
657 {
Simon Kelley40ef23b2012-03-13 21:59:28 +0000658 new->opt = lookup_dhcp_opt(AF_INET, arg+7);
659 opt_len = lookup_dhcp_len(AF_INET, new->opt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100660 /* option:<optname> must follow tag and vendor string. */
661 break;
662 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000663#ifdef HAVE_DHCP6
664 else if (strstr(arg, "option6:") == arg)
665 {
666 for (cp = arg+8; *cp; cp++)
667 if (*cp < '0' || *cp > '9')
668 break;
669
670 if (!*cp)
671 {
672 new->opt = atoi(arg+8);
673 opt_len = 0;
674 }
675 else
Simon Kelley40ef23b2012-03-13 21:59:28 +0000676 {
677 new->opt = lookup_dhcp_opt(AF_INET6, arg+8);
678 opt_len = lookup_dhcp_len(AF_INET6, new->opt);
679 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000680 /* option6:<opt>|<optname> must follow tag and vendor string. */
681 is6 = 1;
682 break;
683 }
684#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +0100685 else if (strstr(arg, "vendor:") == arg)
686 {
Simon Kelley73a08a22009-02-05 20:28:08 +0000687 new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
688 new->flags |= DHOPT_VENDOR;
689 }
690 else if (strstr(arg, "encap:") == arg)
691 {
692 new->u.encap = atoi(arg+6);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100693 new->flags |= DHOPT_ENCAPSULATE;
694 }
Simon Kelley316e2732010-01-22 20:16:09 +0000695 else if (strstr(arg, "vi-encap:") == arg)
696 {
697 new->u.encap = atoi(arg+9);
698 new->flags |= DHOPT_RFC3925;
699 if (flags == DHOPT_MATCH)
700 {
701 new->opt = 1; /* avoid error below */
702 break;
703 }
704 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100705 else
706 {
Simon Kelley824af852008-02-12 20:43:05 +0000707 new->netid = opt_malloc(sizeof (struct dhcp_netid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100708 /* allow optional "net:" or "tag:" for consistency */
709 if (is_tag_prefix(arg))
Simon Kelley824af852008-02-12 20:43:05 +0000710 new->netid->net = opt_string_alloc(arg+4);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100711 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100712 new->netid->net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100713 new->netid->next = np;
714 np = new->netid;
715 }
716
717 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +0000718 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000719
720#ifdef HAVE_DHCP6
721 if (is6)
722 {
723 if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100724 ret_err(_("unsupported encapsulation for IPv6 option"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000725
726 if (opt_len == 0 &&
727 !(new->flags & DHOPT_RFC3925))
Simon Kelley40ef23b2012-03-13 21:59:28 +0000728 opt_len = lookup_dhcp_len(AF_INET6 ,new->opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000729 }
730 else
731#endif
732 if (opt_len == 0 &&
733 !(new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE | DHOPT_RFC3925)))
Simon Kelley40ef23b2012-03-13 21:59:28 +0000734 opt_len = lookup_dhcp_len(AF_INET ,new->opt);
735
Simon Kelley316e2732010-01-22 20:16:09 +0000736 /* option may be missing with rfc3925 match */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100737 if (new->opt == 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100738 ret_err(_("bad dhcp-option"));
739
740 if (comma)
Simon Kelley832af0b2007-01-21 20:01:28 +0000741 {
742 /* characterise the value */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100743 char c;
Simon Kelley28866e92011-02-14 20:19:14 +0000744 int found_dig = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000745 is_addr = is_addr6 = is_hex = is_dec = is_string = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000746 addrs = digs = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100747 dots = 0;
748 for (cp = comma; (c = *cp); cp++)
749 if (c == ',')
Simon Kelley832af0b2007-01-21 20:01:28 +0000750 {
751 addrs++;
752 is_dec = is_hex = 0;
753 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100754 else if (c == ':')
Simon Kelley832af0b2007-01-21 20:01:28 +0000755 {
756 digs++;
757 is_dec = is_addr = 0;
758 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100759 else if (c == '/')
Simon Kelley832af0b2007-01-21 20:01:28 +0000760 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000761 is_addr6 = is_dec = is_hex = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000762 if (cp == comma) /* leading / means a pathname */
763 is_addr = 0;
764 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100765 else if (c == '.')
766 {
Simon Kelley23245c02012-07-18 16:21:11 +0100767 is_addr6 = is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100768 dots++;
769 }
770 else if (c == '-')
Simon Kelley4cb1b322012-02-06 14:30:41 +0000771 is_hex = is_addr = is_addr6 = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100772 else if (c == ' ')
Simon Kelley832af0b2007-01-21 20:01:28 +0000773 is_dec = is_hex = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100774 else if (!(c >='0' && c <= '9'))
Simon Kelley832af0b2007-01-21 20:01:28 +0000775 {
776 is_addr = 0;
777 if (cp[1] == 0 && is_dec &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100778 (c == 'b' || c == 's' || c == 'i'))
Simon Kelley832af0b2007-01-21 20:01:28 +0000779 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100780 lenchar = c;
Simon Kelley832af0b2007-01-21 20:01:28 +0000781 *cp = 0;
782 }
783 else
784 is_dec = 0;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100785 if (!((c >='A' && c <= 'F') ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000786 (c >='a' && c <= 'f') ||
787 (c == '*' && (flags & DHOPT_MATCH))))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000788 {
789 is_hex = 0;
790 if (c != '[' && c != ']')
791 is_addr6 = 0;
792 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000793 }
Simon Kelley28866e92011-02-14 20:19:14 +0000794 else
795 found_dig = 1;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100796
Simon Kelley28866e92011-02-14 20:19:14 +0000797 if (!found_dig)
798 is_dec = is_addr = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000799
Simon Kelleyf2621c72007-04-29 19:47:21 +0100800 /* We know that some options take addresses */
Simon Kelley7622fc02009-06-04 20:32:05 +0100801 if (opt_len & OT_ADDR_LIST)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100802 {
803 is_string = is_dec = is_hex = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000804
805 if (!is6 && (!is_addr || dots == 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100806 ret_err(_("bad IP address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000807
808 if (is6 && !is_addr6)
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100809 ret_err(_("bad IPv6 address"));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100810 }
Simon Kelley28866e92011-02-14 20:19:14 +0000811 /* or names */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000812 else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
813 is_addr6 = is_addr = is_dec = is_hex = 0;
Simon Kelley23245c02012-07-18 16:21:11 +0100814
815 if (found_dig && (opt_len & OT_TIME) && strlen(comma) > 0)
816 {
817 int val, fac = 1;
818
819 switch (comma[strlen(comma) - 1])
820 {
Simon Kelley42243212012-07-20 15:19:18 +0100821 case 'w':
822 case 'W':
823 fac *= 7;
824 /* fall through */
Simon Kelley23245c02012-07-18 16:21:11 +0100825 case 'd':
826 case 'D':
827 fac *= 24;
828 /* fall though */
829 case 'h':
830 case 'H':
831 fac *= 60;
832 /* fall through */
833 case 'm':
834 case 'M':
835 fac *= 60;
836 /* fall through */
837 case 's':
838 case 'S':
839 comma[strlen(comma) - 1] = 0;
840 }
841
842 new->len = 4;
843 new->val = opt_malloc(4);
844 val = atoi(comma);
845 *((int *)new->val) = htonl(val * fac);
846 }
847 else if (is_hex && digs > 1)
Simon Kelley832af0b2007-01-21 20:01:28 +0000848 {
849 new->len = digs;
Simon Kelley824af852008-02-12 20:43:05 +0000850 new->val = opt_malloc(new->len);
Simon Kelley73a08a22009-02-05 20:28:08 +0000851 parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
852 new->flags |= DHOPT_HEX;
Simon Kelley832af0b2007-01-21 20:01:28 +0000853 }
854 else if (is_dec)
855 {
856 int i, val = atoi(comma);
857 /* assume numeric arg is 1 byte except for
858 options where it is known otherwise.
859 For vendor class option, we have to hack. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100860 if (opt_len != 0)
861 new->len = opt_len;
862 else if (val & 0xffff0000)
863 new->len = 4;
864 else if (val & 0xff00)
865 new->len = 2;
866 else
867 new->len = 1;
868
Simon Kelley832af0b2007-01-21 20:01:28 +0000869 if (lenchar == 'b')
870 new->len = 1;
871 else if (lenchar == 's')
872 new->len = 2;
873 else if (lenchar == 'i')
874 new->len = 4;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100875
Simon Kelley824af852008-02-12 20:43:05 +0000876 new->val = opt_malloc(new->len);
Simon Kelley832af0b2007-01-21 20:01:28 +0000877 for (i=0; i<new->len; i++)
878 new->val[i] = val>>((new->len - i - 1)*8);
879 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000880 else if (is_addr && !is6)
Simon Kelley832af0b2007-01-21 20:01:28 +0000881 {
882 struct in_addr in;
883 unsigned char *op;
884 char *slash;
885 /* max length of address/subnet descriptor is five bytes,
886 add one for the option 120 enc byte too */
Simon Kelley824af852008-02-12 20:43:05 +0000887 new->val = op = opt_malloc((5 * addrs) + 1);
Simon Kelley6b010842007-02-12 20:32:07 +0000888 new->flags |= DHOPT_ADDR;
889
Simon Kelley572b41e2011-02-18 18:11:18 +0000890 if (!(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
891 new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +0000892 {
Simon Kelley6b010842007-02-12 20:32:07 +0000893 *(op++) = 1; /* RFC 3361 "enc byte" */
894 new->flags &= ~DHOPT_ADDR;
Simon Kelley832af0b2007-01-21 20:01:28 +0000895 }
896 while (addrs--)
897 {
898 cp = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100899 comma = split(cp);
Simon Kelley73a08a22009-02-05 20:28:08 +0000900 slash = split_chr(cp, '/');
Simon Kelley832af0b2007-01-21 20:01:28 +0000901 in.s_addr = inet_addr(cp);
902 if (!slash)
903 {
904 memcpy(op, &in, INADDRSZ);
905 op += INADDRSZ;
906 }
907 else
908 {
909 unsigned char *p = (unsigned char *)&in;
910 int netsize = atoi(slash);
911 *op++ = netsize;
912 if (netsize > 0)
913 *op++ = *p++;
914 if (netsize > 8)
915 *op++ = *p++;
916 if (netsize > 16)
917 *op++ = *p++;
918 if (netsize > 24)
919 *op++ = *p++;
920 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
921 }
922 }
923 new->len = op - new->val;
924 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000925 else if (is_addr6 && is6)
926 {
927 unsigned char *op;
928 new->val = op = opt_malloc(16 * addrs);
929 new->flags |= DHOPT_ADDR6;
930 while (addrs--)
931 {
932 cp = comma;
933 comma = split(cp);
934
935 /* check for [1234::7] */
936 if (*cp == '[')
937 cp++;
938 if (strlen(cp) > 1 && cp[strlen(cp)-1] == ']')
939 cp[strlen(cp)-1] = 0;
940
941 if (inet_pton(AF_INET6, cp, op))
942 {
943 op += IN6ADDRSZ;
944 continue;
945 }
946
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100947 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000948 }
949 new->len = op - new->val;
950 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100951 else if (is_string)
Simon Kelley832af0b2007-01-21 20:01:28 +0000952 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000953 /* text arg */
Simon Kelley572b41e2011-02-18 18:11:18 +0000954 if ((new->opt == OPTION_DOMAIN_SEARCH || new->opt == OPTION_SIP_SERVER) &&
Simon Kelley4cb1b322012-02-06 14:30:41 +0000955 !is6 && !(new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000956 {
957 /* dns search, RFC 3397, or SIP, RFC 3361 */
958 unsigned char *q, *r, *tail;
Simon Kelley824af852008-02-12 20:43:05 +0000959 unsigned char *p, *m = NULL, *newp;
Simon Kelley832af0b2007-01-21 20:01:28 +0000960 size_t newlen, len = 0;
Simon Kelley572b41e2011-02-18 18:11:18 +0000961 int header_size = (new->opt == OPTION_DOMAIN_SEARCH) ? 0 : 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000962
963 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100964 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +0000965
966 while (arg && *arg)
967 {
Simon Kelleyc52e1892010-06-07 22:01:39 +0100968 char *in, *dom = NULL;
969 size_t domlen = 1;
970 /* Allow "." as an empty domain */
971 if (strcmp (arg, ".") != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000972 {
Simon Kelleyc52e1892010-06-07 22:01:39 +0100973 if (!(dom = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +0100974 ret_err(_("bad domain in dhcp-option"));
975
Simon Kelleyc52e1892010-06-07 22:01:39 +0100976 domlen = strlen(dom) + 2;
Simon Kelley832af0b2007-01-21 20:01:28 +0000977 }
Simon Kelleyc52e1892010-06-07 22:01:39 +0100978
979 newp = opt_malloc(len + domlen + header_size);
Simon Kelley824af852008-02-12 20:43:05 +0000980 if (m)
Simon Kelleyc52e1892010-06-07 22:01:39 +0100981 {
982 memcpy(newp, m, header_size + len);
983 free(m);
984 }
Simon Kelley824af852008-02-12 20:43:05 +0000985 m = newp;
Simon Kelley832af0b2007-01-21 20:01:28 +0000986 p = m + header_size;
987 q = p + len;
988
989 /* add string on the end in RFC1035 format */
Simon Kelleyc52e1892010-06-07 22:01:39 +0100990 for (in = dom; in && *in;)
Simon Kelley832af0b2007-01-21 20:01:28 +0000991 {
992 unsigned char *cp = q++;
993 int j;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100994 for (j = 0; *in && (*in != '.'); in++, j++)
995 *q++ = *in;
Simon Kelley832af0b2007-01-21 20:01:28 +0000996 *cp = j;
Simon Kelleyc52e1892010-06-07 22:01:39 +0100997 if (*in)
998 in++;
Simon Kelley832af0b2007-01-21 20:01:28 +0000999 }
1000 *q++ = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001001 free(dom);
Simon Kelleyc52e1892010-06-07 22:01:39 +01001002
Simon Kelley832af0b2007-01-21 20:01:28 +00001003 /* Now tail-compress using earlier names. */
1004 newlen = q - p;
1005 for (tail = p + len; *tail; tail += (*tail) + 1)
1006 for (r = p; r - p < (int)len; r += (*r) + 1)
1007 if (strcmp((char *)r, (char *)tail) == 0)
1008 {
1009 PUTSHORT((r - p) | 0xc000, tail);
1010 newlen = tail - p;
1011 goto end;
1012 }
1013 end:
1014 len = newlen;
1015
1016 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001017 comma = split(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001018 }
1019
1020 /* RFC 3361, enc byte is zero for names */
Simon Kelley572b41e2011-02-18 18:11:18 +00001021 if (new->opt == OPTION_SIP_SERVER)
Simon Kelley832af0b2007-01-21 20:01:28 +00001022 m[0] = 0;
1023 new->len = (int) len + header_size;
1024 new->val = m;
1025 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001026#ifdef HAVE_DHCP6
1027 else if (comma && (opt_len & OT_CSTRING))
1028 {
1029 /* length fields are two bytes so need 16 bits for each string */
Simon Kelley40ef23b2012-03-13 21:59:28 +00001030 int i, commas = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001031 unsigned char *p, *newp;
1032
Simon Kelley40ef23b2012-03-13 21:59:28 +00001033 for (i = 0; comma[i]; i++)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001034 if (comma[i] == ',')
1035 commas++;
1036
1037 newp = opt_malloc(strlen(comma)+(2*commas));
1038 p = newp;
1039 arg = comma;
1040 comma = split(arg);
1041
1042 while (arg && *arg)
1043 {
1044 u16 len = strlen(arg);
Simon Kelley18f0fb02012-03-31 21:18:55 +01001045 unhide_metas(arg);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001046 PUTSHORT(len, p);
1047 memcpy(p, arg, len);
1048 p += len;
1049
1050 arg = comma;
1051 comma = split(arg);
1052 }
1053
1054 new->val = newp;
1055 new->len = p - newp;
1056 }
1057 else if (comma && (opt_len & OT_RFC1035_NAME))
1058 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001059 unsigned char *p = NULL, *newp, *end;
1060 int len = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001061 arg = comma;
1062 comma = split(arg);
1063
1064 while (arg && *arg)
1065 {
Simon Kelley18f0fb02012-03-31 21:18:55 +01001066 char *dom = canonicalise_opt(arg);
1067 if (!dom)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001068 ret_err(_("bad domain in dhcp-option"));
1069
Simon Kelley18f0fb02012-03-31 21:18:55 +01001070 newp = opt_malloc(len + strlen(dom) + 2);
1071
1072 if (p)
1073 {
1074 memcpy(newp, p, len);
1075 free(p);
1076 }
1077
1078 p = newp;
1079 end = do_rfc1035_name(p + len, dom);
1080 *end++ = 0;
1081 len = end - p;
1082 free(dom);
1083
Simon Kelley4cb1b322012-02-06 14:30:41 +00001084 arg = comma;
1085 comma = split(arg);
1086 }
1087
Simon Kelley18f0fb02012-03-31 21:18:55 +01001088 new->val = p;
1089 new->len = len;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001090 }
1091#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001092 else
1093 {
1094 new->len = strlen(comma);
1095 /* keep terminating zero on string */
Simon Kelley824af852008-02-12 20:43:05 +00001096 new->val = (unsigned char *)opt_string_alloc(comma);
Simon Kelley832af0b2007-01-21 20:01:28 +00001097 new->flags |= DHOPT_STRING;
1098 }
1099 }
1100 }
1101
Simon Kelley4cb1b322012-02-06 14:30:41 +00001102 if (!is6 &&
1103 ((new->len > 255) ||
Simon Kelley316e2732010-01-22 20:16:09 +00001104 (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +00001105 (new->len > 250 && (new->flags & DHOPT_RFC3925))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001106 ret_err(_("dhcp-option too long"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001107
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001108 if (flags == DHOPT_MATCH)
Simon Kelley824af852008-02-12 20:43:05 +00001109 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001110 if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
1111 !new->netid ||
1112 new->netid->next)
1113 ret_err(_("illegal dhcp-match"));
1114
1115 if (is6)
Simon Kelley73a08a22009-02-05 20:28:08 +00001116 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001117 new->next = daemon->dhcp_match6;
1118 daemon->dhcp_match6 = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001119 }
1120 else
Simon Kelley73a08a22009-02-05 20:28:08 +00001121 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001122 new->next = daemon->dhcp_match;
1123 daemon->dhcp_match = new;
Simon Kelley73a08a22009-02-05 20:28:08 +00001124 }
Simon Kelley824af852008-02-12 20:43:05 +00001125 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001126 else if (is6)
1127 {
1128 new->next = daemon->dhcp_opts6;
1129 daemon->dhcp_opts6 = new;
1130 }
1131 else
1132 {
1133 new->next = daemon->dhcp_opts;
1134 daemon->dhcp_opts = new;
1135 }
1136
1137 return 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001138}
1139
Simon Kelley7622fc02009-06-04 20:32:05 +01001140#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001141
Simon Kelley28866e92011-02-14 20:19:14 +00001142void set_option_bool(unsigned int opt)
1143{
1144 if (opt < 32)
1145 daemon->options |= 1u << opt;
1146 else
1147 daemon->options2 |= 1u << (opt - 32);
1148}
1149
Simon Kelley2b5bae92012-06-26 16:55:23 +01001150void reset_option_bool(unsigned int opt)
1151{
1152 if (opt < 32)
1153 daemon->options &= ~(1u << opt);
1154 else
1155 daemon->options2 &= ~(1u << (opt - 32));
1156}
1157
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001158static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
Simon Kelley849a8352006-06-09 21:02:31 +01001159{
1160 int i;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001161 char *comma;
Simon Kelley849a8352006-06-09 21:02:31 +01001162
Simon Kelley832af0b2007-01-21 20:01:28 +00001163 if (option == '?')
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001164 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001165
Simon Kelley1a6bca82008-07-11 11:11:42 +01001166 for (i=0; usage[i].opt != 0; i++)
1167 if (usage[i].opt == option)
Simon Kelley849a8352006-06-09 21:02:31 +01001168 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001169 int rept = usage[i].rept;
1170
Simon Kelley28866e92011-02-14 20:19:14 +00001171 if (command_line)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001172 {
1173 /* command line */
1174 if (rept == ARG_USED_CL)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001175 ret_err(_("illegal repeated flag"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001176 if (rept == ARG_ONE)
1177 usage[i].rept = ARG_USED_CL;
1178 }
1179 else
1180 {
1181 /* allow file to override command line */
1182 if (rept == ARG_USED_FILE)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001183 ret_err(_("illegal repeated keyword"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01001184 if (rept == ARG_USED_CL || rept == ARG_ONE)
1185 usage[i].rept = ARG_USED_FILE;
1186 }
1187
1188 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL)
1189 {
Simon Kelley28866e92011-02-14 20:19:14 +00001190 set_option_bool(rept);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001191 return 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001192 }
1193
1194 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001195 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001196
Simon Kelley849a8352006-06-09 21:02:31 +01001197 switch (option)
1198 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001199 case 'C': /* --conf-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001200 {
Simon Kelley824af852008-02-12 20:43:05 +00001201 char *file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001202 if (file)
Simon Kelley9009d742008-11-14 20:04:27 +00001203 {
Simon Kelley28866e92011-02-14 20:19:14 +00001204 one_file(file, 0);
Simon Kelley9009d742008-11-14 20:04:27 +00001205 free(file);
1206 }
Simon Kelley849a8352006-06-09 21:02:31 +01001207 break;
1208 }
1209
Simon Kelleyf2621c72007-04-29 19:47:21 +01001210 case '7': /* --conf-dir */
Simon Kelley849a8352006-06-09 21:02:31 +01001211 {
1212 DIR *dir_stream;
1213 struct dirent *ent;
1214 char *directory, *path;
Simon Kelley1f15b812009-10-13 17:49:32 +01001215 struct list {
1216 char *suffix;
1217 struct list *next;
1218 } *ignore_suffix = NULL, *li;
Simon Kelley849a8352006-06-09 21:02:31 +01001219
Simon Kelley1f15b812009-10-13 17:49:32 +01001220 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001221 if (!(directory = opt_string_alloc(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001222 break;
1223
Simon Kelley1f15b812009-10-13 17:49:32 +01001224 for (arg = comma; arg; arg = comma)
1225 {
1226 comma = split(arg);
1227 li = opt_malloc(sizeof(struct list));
1228 li->next = ignore_suffix;
1229 ignore_suffix = li;
1230 /* Have to copy: buffer is overwritten */
1231 li->suffix = opt_string_alloc(arg);
1232 };
1233
Simon Kelley849a8352006-06-09 21:02:31 +01001234 if (!(dir_stream = opendir(directory)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001235 die(_("cannot access directory %s: %s"), directory, EC_FILE);
Simon Kelley1f15b812009-10-13 17:49:32 +01001236
Simon Kelley849a8352006-06-09 21:02:31 +01001237 while ((ent = readdir(dir_stream)))
1238 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001239 size_t len = strlen(ent->d_name);
Simon Kelley849a8352006-06-09 21:02:31 +01001240 struct stat buf;
Simon Kelley1f15b812009-10-13 17:49:32 +01001241
1242 /* ignore emacs backups and dotfiles */
Simon Kelley7622fc02009-06-04 20:32:05 +01001243 if (len == 0 ||
1244 ent->d_name[len - 1] == '~' ||
Simon Kelley849a8352006-06-09 21:02:31 +01001245 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
1246 ent->d_name[0] == '.')
1247 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01001248
Simon Kelley1f15b812009-10-13 17:49:32 +01001249 for (li = ignore_suffix; li; li = li->next)
1250 {
1251 /* check for proscribed suffices */
1252 size_t ls = strlen(li->suffix);
1253 if (len > ls &&
1254 strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
1255 break;
1256 }
1257 if (li)
1258 continue;
1259
Simon Kelley824af852008-02-12 20:43:05 +00001260 path = opt_malloc(strlen(directory) + len + 2);
Simon Kelley849a8352006-06-09 21:02:31 +01001261 strcpy(path, directory);
1262 strcat(path, "/");
1263 strcat(path, ent->d_name);
Simon Kelley7622fc02009-06-04 20:32:05 +01001264
Simon Kelley849a8352006-06-09 21:02:31 +01001265 if (stat(path, &buf) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001266 die(_("cannot access %s: %s"), path, EC_FILE);
Simon Kelley849a8352006-06-09 21:02:31 +01001267 /* only reg files allowed. */
1268 if (!S_ISREG(buf.st_mode))
1269 continue;
1270
Simon Kelley28866e92011-02-14 20:19:14 +00001271 /* files must be readable */
1272 one_file(path, 0);
Simon Kelley849a8352006-06-09 21:02:31 +01001273 free(path);
1274 }
1275
1276 closedir(dir_stream);
Simon Kelley9009d742008-11-14 20:04:27 +00001277 free(directory);
Simon Kelley1f15b812009-10-13 17:49:32 +01001278 for(; ignore_suffix; ignore_suffix = li)
1279 {
1280 li = ignore_suffix->next;
1281 free(ignore_suffix->suffix);
1282 free(ignore_suffix);
1283 }
1284
Simon Kelley849a8352006-06-09 21:02:31 +01001285 break;
1286 }
1287
Simon Kelleyad094272012-08-10 17:10:54 +01001288 case '1': /* --enable-dbus */
1289 set_option_bool(OPT_DBUS);
1290 if (arg)
1291 daemon->dbus_name = opt_string_alloc(arg);
1292 else
1293 daemon->dbus_name = DNSMASQ_SERVICE;
1294 break;
1295
Simon Kelleyf2621c72007-04-29 19:47:21 +01001296 case '8': /* --log-facility */
1297 /* may be a filename */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001298 if (strchr(arg, '/') || strcmp (arg, "-") == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001299 daemon->log_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001300 else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001301 {
Simon Kelley572b41e2011-02-18 18:11:18 +00001302#ifdef __ANDROID__
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001303 ret_err(_("setting log facility is not possible under Android"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001304#else
Simon Kelleyf2621c72007-04-29 19:47:21 +01001305 for (i = 0; facilitynames[i].c_name; i++)
1306 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
1307 break;
1308
1309 if (facilitynames[i].c_name)
1310 daemon->log_fac = facilitynames[i].c_val;
1311 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001312 ret_err(_("bad log facility"));
Simon Kelley572b41e2011-02-18 18:11:18 +00001313#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001314 }
1315 break;
1316
Simon Kelleyf2621c72007-04-29 19:47:21 +01001317 case 'x': /* --pid-file */
Simon Kelley824af852008-02-12 20:43:05 +00001318 daemon->runfile = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001319 break;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001320
Simon Kelleyf2621c72007-04-29 19:47:21 +01001321 case 'r': /* --resolv-file */
Simon Kelley849a8352006-06-09 21:02:31 +01001322 {
Simon Kelley824af852008-02-12 20:43:05 +00001323 char *name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001324 struct resolvc *new, *list = daemon->resolv_files;
1325
1326 if (list && list->is_default)
1327 {
1328 /* replace default resolv file - possibly with nothing */
1329 if (name)
1330 {
1331 list->is_default = 0;
1332 list->name = name;
1333 }
1334 else
1335 list = NULL;
1336 }
1337 else if (name)
1338 {
Simon Kelley824af852008-02-12 20:43:05 +00001339 new = opt_malloc(sizeof(struct resolvc));
Simon Kelley849a8352006-06-09 21:02:31 +01001340 new->next = list;
1341 new->name = name;
1342 new->is_default = 0;
1343 new->mtime = 0;
1344 new->logged = 0;
1345 list = new;
1346 }
1347 daemon->resolv_files = list;
1348 break;
1349 }
1350
Simon Kelleyf2621c72007-04-29 19:47:21 +01001351 case 'm': /* --mx-host */
Simon Kelley849a8352006-06-09 21:02:31 +01001352 {
1353 int pref = 1;
1354 struct mx_srv_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01001355 char *name, *target = NULL;
1356
Simon Kelleyf2621c72007-04-29 19:47:21 +01001357 if ((comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001358 {
1359 char *prefstr;
Simon Kelley1f15b812009-10-13 17:49:32 +01001360 if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001361 ret_err(_("bad MX preference"));
Simon Kelley849a8352006-06-09 21:02:31 +01001362 }
1363
Simon Kelley1f15b812009-10-13 17:49:32 +01001364 if (!(name = canonicalise_opt(arg)) ||
1365 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001366 ret_err(_("bad MX name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001367
Simon Kelley824af852008-02-12 20:43:05 +00001368 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01001369 new->next = daemon->mxnames;
1370 daemon->mxnames = new;
1371 new->issrv = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +01001372 new->name = name;
1373 new->target = target; /* may be NULL */
Simon Kelley849a8352006-06-09 21:02:31 +01001374 new->weight = pref;
1375 break;
1376 }
1377
Simon Kelleyf2621c72007-04-29 19:47:21 +01001378 case 't': /* --mx-target */
Simon Kelley1f15b812009-10-13 17:49:32 +01001379 if (!(daemon->mxtarget = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001380 ret_err(_("bad MX target"));
Simon Kelley849a8352006-06-09 21:02:31 +01001381 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001382
1383#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001384 case 'l': /* --dhcp-leasefile */
Simon Kelley824af852008-02-12 20:43:05 +00001385 daemon->lease_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001386 break;
1387
Simon Kelleyc72daea2012-01-05 21:33:27 +00001388 /* Sorry about the gross pre-processor abuse */
1389 case '6': /* --dhcp-script */
1390 case LOPT_LUASCRIPT: /* --dhcp-luascript */
Simon Kelley1f15b812009-10-13 17:49:32 +01001391# if defined(NO_FORK)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001392 ret_err(_("cannot run scripts under uClinux"));
Simon Kelley1f15b812009-10-13 17:49:32 +01001393# elif !defined(HAVE_SCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001394 ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
Simon Kelley7622fc02009-06-04 20:32:05 +01001395# else
Simon Kelleyc72daea2012-01-05 21:33:27 +00001396 if (option == LOPT_LUASCRIPT)
1397# if !defined(HAVE_LUASCRIPT)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001398 ret_err(_("recompile with HAVE_LUASCRIPT defined to enable Lua scripts"));
Simon Kelleyc72daea2012-01-05 21:33:27 +00001399# else
1400 daemon->luascript = opt_string_alloc(arg);
1401# endif
1402 else
1403 daemon->lease_change_command = opt_string_alloc(arg);
Simon Kelley7622fc02009-06-04 20:32:05 +01001404# endif
Simon Kelley849a8352006-06-09 21:02:31 +01001405 break;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001406#endif /* HAVE_DHCP */
Simon Kelley7622fc02009-06-04 20:32:05 +01001407
Simon Kelley28866e92011-02-14 20:19:14 +00001408 case LOPT_DHCP_HOST: /* --dhcp-hostfile */
1409 case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001410 case 'H': /* --addn-hosts */
Simon Kelley849a8352006-06-09 21:02:31 +01001411 {
Simon Kelley824af852008-02-12 20:43:05 +00001412 struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
Simon Kelley849a8352006-06-09 21:02:31 +01001413 static int hosts_index = 1;
Simon Kelley824af852008-02-12 20:43:05 +00001414 new->fname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001415 new->index = hosts_index++;
Simon Kelley7622fc02009-06-04 20:32:05 +01001416 new->flags = 0;
Simon Kelley28866e92011-02-14 20:19:14 +00001417 if (option == 'H')
1418 {
1419 new->next = daemon->addn_hosts;
1420 daemon->addn_hosts = new;
1421 }
1422 else if (option == LOPT_DHCP_HOST)
1423 {
1424 new->next = daemon->dhcp_hosts_file;
1425 daemon->dhcp_hosts_file = new;
1426 }
1427 else if (option == LOPT_DHCP_OPTS)
1428 {
1429 new->next = daemon->dhcp_opts_file;
1430 daemon->dhcp_opts_file = new;
1431 }
Simon Kelley849a8352006-06-09 21:02:31 +01001432 break;
1433 }
1434
Simon Kelleyf2621c72007-04-29 19:47:21 +01001435 case 's': /* --domain */
Simon Kelley849a8352006-06-09 21:02:31 +01001436 if (strcmp (arg, "#") == 0)
Simon Kelley28866e92011-02-14 20:19:14 +00001437 set_option_bool(OPT_RESOLV_DOMAIN);
Simon Kelley849a8352006-06-09 21:02:31 +01001438 else
Simon Kelley9009d742008-11-14 20:04:27 +00001439 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001440 char *d;
Simon Kelley9009d742008-11-14 20:04:27 +00001441 comma = split(arg);
Simon Kelley1f15b812009-10-13 17:49:32 +01001442 if (!(d = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001443 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001444 else
1445 {
Simon Kelley9009d742008-11-14 20:04:27 +00001446 if (comma)
1447 {
1448 struct cond_domain *new = safe_malloc(sizeof(struct cond_domain));
Simon Kelley28866e92011-02-14 20:19:14 +00001449 char *netpart;
1450
Simon Kelley9009d742008-11-14 20:04:27 +00001451 unhide_metas(comma);
Simon Kelley28866e92011-02-14 20:19:14 +00001452 if ((netpart = split_chr(comma, '/')))
Simon Kelley9009d742008-11-14 20:04:27 +00001453 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00001454 int msize;
1455
Simon Kelley28866e92011-02-14 20:19:14 +00001456 arg = split(netpart);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001457 if (!atoi_check(netpart, &msize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001458 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001459 else if (inet_pton(AF_INET, comma, &new->start))
Simon Kelley9009d742008-11-14 20:04:27 +00001460 {
Simon Kelleyd74942a2012-02-07 20:51:56 +00001461 int mask = (1 << (32 - msize)) - 1;
1462 new->is6 = 0;
Simon Kelley9009d742008-11-14 20:04:27 +00001463 new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
1464 new->end.s_addr = new->start.s_addr | htonl(mask);
Simon Kelley28866e92011-02-14 20:19:14 +00001465 if (arg)
1466 {
1467 /* generate the equivalent of
1468 local=/<domain>/
1469 local=/xxx.yyy.zzz.in-addr.arpa/ */
1470
1471 if (strcmp(arg, "local") != 0 ||
1472 (msize != 8 && msize != 16 && msize != 24))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001473 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00001474 else
1475 {
1476 struct server *serv = opt_malloc(sizeof(struct server));
1477 in_addr_t a = ntohl(new->start.s_addr) >> 8;
1478 char *p;
1479
1480 memset(serv, 0, sizeof(struct server));
1481 serv->domain = d;
1482 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1483 serv->next = daemon->servers;
1484 daemon->servers = serv;
1485
1486 serv = opt_malloc(sizeof(struct server));
1487 memset(serv, 0, sizeof(struct server));
1488 p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
1489
1490 if (msize == 24)
1491 p += sprintf(p, "%d.", a & 0xff);
1492 a = a >> 8;
1493 if (msize != 8)
1494 p += sprintf(p, "%d.", a & 0xff);
1495 a = a >> 8;
1496 p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
1497
1498 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1499 serv->next = daemon->servers;
1500 daemon->servers = serv;
1501 }
1502 }
Simon Kelley9009d742008-11-14 20:04:27 +00001503 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00001504#ifdef HAVE_IPV6
1505 else if (inet_pton(AF_INET6, comma, &new->start6))
1506 {
1507 u64 mask = (1LLU << (128 - msize)) - 1LLU;
1508 u64 addrpart = addr6part(&new->start6);
1509 new->is6 = 1;
1510
1511 /* prefix==64 overflows the mask calculation above */
1512 if (msize == 64)
1513 mask = (u64)-1LL;
1514
1515 new->end6 = new->start6;
1516 setaddr6part(&new->start6, addrpart & ~mask);
1517 setaddr6part(&new->end6, addrpart | mask);
1518
1519 if (msize < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001520 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001521 else if (arg)
1522 {
1523 /* generate the equivalent of
1524 local=/<domain>/
1525 local=/xxx.yyy.zzz.ip6.arpa/ */
1526
Simon Kelleyceae00d2012-02-09 21:28:14 +00001527 if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001528 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001529 else
1530 {
1531 struct server *serv = opt_malloc(sizeof(struct server));
Simon Kelleyd74942a2012-02-07 20:51:56 +00001532 char *p;
Simon Kelleyceae00d2012-02-09 21:28:14 +00001533
Simon Kelleyd74942a2012-02-07 20:51:56 +00001534 memset(serv, 0, sizeof(struct server));
1535 serv->domain = d;
1536 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1537 serv->next = daemon->servers;
1538 daemon->servers = serv;
1539
1540 serv = opt_malloc(sizeof(struct server));
1541 memset(serv, 0, sizeof(struct server));
1542 p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
1543
1544 for (i = msize-1; i >= 0; i -= 4)
1545 {
1546 int dig = ((unsigned char *)&new->start6)[i>>3];
1547 p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
1548 }
1549 p += sprintf(p, "ip6.arpa");
1550
1551 serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
1552 serv->next = daemon->servers;
1553 daemon->servers = serv;
1554 }
1555 }
1556 }
1557#endif
1558 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001559 ret_err(gen_err);
Simon Kelley9009d742008-11-14 20:04:27 +00001560 }
Simon Kelleyd74942a2012-02-07 20:51:56 +00001561 else
1562 {
1563 arg = split(comma);
1564 if (inet_pton(AF_INET, comma, &new->start))
1565 {
1566 new->is6 = 0;
1567 if (!arg)
1568 new->end.s_addr = new->start.s_addr;
1569 else if (!inet_pton(AF_INET, arg, &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001570 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001571 }
1572#ifdef HAVE_IPV6
1573 else if (inet_pton(AF_INET6, comma, &new->start6))
1574 {
1575 new->is6 = 1;
1576 if (!arg)
1577 memcpy(&new->end6, &new->start6, IN6ADDRSZ);
1578 else if (!inet_pton(AF_INET6, arg, &new->end6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001579 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001580 }
1581#endif
1582 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001583 ret_err(gen_err);
Simon Kelleyd74942a2012-02-07 20:51:56 +00001584 }
Simon Kelley2307eac2012-02-13 10:13:13 +00001585
1586 new->domain = d;
1587 new->next = daemon->cond_domain;
1588 daemon->cond_domain = new;
Simon Kelley9009d742008-11-14 20:04:27 +00001589 }
1590 else
1591 daemon->domain_suffix = d;
1592 }
1593 }
Simon Kelley849a8352006-06-09 21:02:31 +01001594 break;
1595
Simon Kelleyf2621c72007-04-29 19:47:21 +01001596 case 'u': /* --user */
Simon Kelley824af852008-02-12 20:43:05 +00001597 daemon->username = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001598 break;
1599
Simon Kelleyf2621c72007-04-29 19:47:21 +01001600 case 'g': /* --group */
Simon Kelley824af852008-02-12 20:43:05 +00001601 daemon->groupname = opt_string_alloc(arg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001602 daemon->group_set = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01001603 break;
Simon Kelley9e038942008-05-30 20:06:34 +01001604
Simon Kelley7622fc02009-06-04 20:32:05 +01001605#ifdef HAVE_DHCP
Simon Kelley9e038942008-05-30 20:06:34 +01001606 case LOPT_SCRIPTUSR: /* --scriptuser */
1607 daemon->scriptuser = opt_string_alloc(arg);
1608 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001609#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001610
Simon Kelleyf2621c72007-04-29 19:47:21 +01001611 case 'i': /* --interface */
Simon Kelley849a8352006-06-09 21:02:31 +01001612 do {
Simon Kelley824af852008-02-12 20:43:05 +00001613 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001614 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001615 new->next = daemon->if_names;
1616 daemon->if_names = new;
1617 /* new->name may be NULL if someone does
1618 "interface=" to disable all interfaces except loop. */
Simon Kelley824af852008-02-12 20:43:05 +00001619 new->name = opt_string_alloc(arg);
Simon Kelley4ce4f372012-06-14 11:50:45 +01001620 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001621 arg = comma;
1622 } while (arg);
1623 break;
1624
Simon Kelleyf2621c72007-04-29 19:47:21 +01001625 case 'I': /* --except-interface */
1626 case '2': /* --no-dhcp-interface */
Simon Kelley849a8352006-06-09 21:02:31 +01001627 do {
Simon Kelley824af852008-02-12 20:43:05 +00001628 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001629 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00001630 new->name = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001631 if (option == 'I')
1632 {
1633 new->next = daemon->if_except;
1634 daemon->if_except = new;
1635 }
1636 else
1637 {
1638 new->next = daemon->dhcp_except;
1639 daemon->dhcp_except = new;
1640 }
1641 arg = comma;
1642 } while (arg);
1643 break;
1644
Simon Kelleyf2621c72007-04-29 19:47:21 +01001645 case 'B': /* --bogus-nxdomain */
Simon Kelley849a8352006-06-09 21:02:31 +01001646 {
1647 struct in_addr addr;
1648 unhide_metas(arg);
1649 if (arg && (addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
1650 {
Simon Kelley824af852008-02-12 20:43:05 +00001651 struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
Simon Kelley849a8352006-06-09 21:02:31 +01001652 baddr->next = daemon->bogus_addr;
1653 daemon->bogus_addr = baddr;
1654 baddr->addr = addr;
1655 }
1656 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001657 ret_err(gen_err); /* error */
Simon Kelley849a8352006-06-09 21:02:31 +01001658 break;
1659 }
1660
Simon Kelleyf2621c72007-04-29 19:47:21 +01001661 case 'a': /* --listen-address */
Simon Kelley849a8352006-06-09 21:02:31 +01001662 do {
Simon Kelley824af852008-02-12 20:43:05 +00001663 struct iname *new = opt_malloc(sizeof(struct iname));
Simon Kelleyf2621c72007-04-29 19:47:21 +01001664 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01001665 unhide_metas(arg);
1666 new->next = daemon->if_addrs;
1667 if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
1668 {
1669 new->addr.sa.sa_family = AF_INET;
1670#ifdef HAVE_SOCKADDR_SA_LEN
1671 new->addr.in.sin_len = sizeof(new->addr.in);
1672#endif
1673 }
1674#ifdef HAVE_IPV6
1675 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
1676 {
1677 new->addr.sa.sa_family = AF_INET6;
1678 new->addr.in6.sin6_flowinfo = 0;
1679 new->addr.in6.sin6_scope_id = 0;
1680#ifdef HAVE_SOCKADDR_SA_LEN
1681 new->addr.in6.sin6_len = sizeof(new->addr.in6);
1682#endif
1683 }
1684#endif
1685 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001686 ret_err(gen_err);
Simon Kelley4ce4f372012-06-14 11:50:45 +01001687
1688 new->used = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001689 daemon->if_addrs = new;
1690 arg = comma;
1691 } while (arg);
1692 break;
1693
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001694 case 'S': /* --server */
1695 case LOPT_LOCAL: /* --local */
1696 case 'A': /* --address */
1697 case LOPT_NO_REBIND: /* --rebind-domain-ok */
Simon Kelley849a8352006-06-09 21:02:31 +01001698 {
1699 struct server *serv, *newlist = NULL;
1700
1701 unhide_metas(arg);
1702
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001703 if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
Simon Kelley849a8352006-06-09 21:02:31 +01001704 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001705 int rebind = !(*arg == '/');
1706 char *end = NULL;
1707 if (!rebind)
1708 arg++;
1709 while (rebind || (end = split_chr(arg, '/')))
Simon Kelley849a8352006-06-09 21:02:31 +01001710 {
1711 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001712 /* elide leading dots - they are implied in the search algorithm */
1713 while (*arg == '.') arg++;
Simon Kelley849a8352006-06-09 21:02:31 +01001714 /* # matches everything and becomes a zero length domain string */
1715 if (strcmp(arg, "#") == 0)
1716 domain = "";
Simon Kelley1f15b812009-10-13 17:49:32 +01001717 else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01001718 option = '?';
Simon Kelley824af852008-02-12 20:43:05 +00001719 serv = opt_malloc(sizeof(struct server));
1720 memset(serv, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01001721 serv->next = newlist;
1722 newlist = serv;
Simon Kelley849a8352006-06-09 21:02:31 +01001723 serv->domain = domain;
1724 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
Simon Kelley73a08a22009-02-05 20:28:08 +00001725 arg = end;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001726 if (rebind)
1727 break;
Simon Kelley849a8352006-06-09 21:02:31 +01001728 }
1729 if (!newlist)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001730 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001731 }
1732 else
1733 {
Simon Kelley824af852008-02-12 20:43:05 +00001734 newlist = opt_malloc(sizeof(struct server));
1735 memset(newlist, 0, sizeof(struct server));
Simon Kelley849a8352006-06-09 21:02:31 +01001736 }
1737
1738 if (option == 'A')
1739 {
1740 newlist->flags |= SERV_LITERAL_ADDRESS;
1741 if (!(newlist->flags & SERV_TYPE))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001742 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001743 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001744 else if (option == LOPT_NO_REBIND)
1745 newlist->flags |= SERV_NO_REBIND;
Simon Kelley849a8352006-06-09 21:02:31 +01001746
1747 if (!arg || !*arg)
1748 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001749 if (!(newlist->flags & SERV_NO_REBIND))
1750 newlist->flags |= SERV_NO_ADDR; /* no server */
1751 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001752 ret_err(gen_err);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001753 }
1754
1755 else if (strcmp(arg, "#") == 0)
1756 {
1757 newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
Simon Kelley849a8352006-06-09 21:02:31 +01001758 if (newlist->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001759 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001760 }
1761 else
1762 {
1763 int source_port = 0, serv_port = NAMESERVER_PORT;
1764 char *portno, *source;
Simon Kelley7de060b2011-08-26 17:24:52 +01001765#ifdef HAVE_IPV6
1766 int scope_index = 0;
1767 char *scope_id;
1768#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001769
Simon Kelley73a08a22009-02-05 20:28:08 +00001770 if ((source = split_chr(arg, '@')) && /* is there a source. */
1771 (portno = split_chr(source, '#')) &&
1772 !atoi_check16(portno, &source_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001773 ret_err(_("bad port"));
Simon Kelley73a08a22009-02-05 20:28:08 +00001774
1775 if ((portno = split_chr(arg, '#')) && /* is there a port no. */
1776 !atoi_check16(portno, &serv_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001777 ret_err(_("bad port"));
Simon Kelley849a8352006-06-09 21:02:31 +01001778
Simon Kelley7de060b2011-08-26 17:24:52 +01001779#ifdef HAVE_IPV6
1780 scope_id = split_chr(arg, '%');
1781#endif
1782
Simon Kelley849a8352006-06-09 21:02:31 +01001783 if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
1784 {
1785 newlist->addr.in.sin_port = htons(serv_port);
1786 newlist->source_addr.in.sin_port = htons(source_port);
1787 newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
1788#ifdef HAVE_SOCKADDR_SA_LEN
1789 newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
1790#endif
1791 if (source)
1792 {
Simon Kelley824af852008-02-12 20:43:05 +00001793 newlist->flags |= SERV_HAS_SOURCE;
Simon Kelley73a08a22009-02-05 20:28:08 +00001794 if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
Simon Kelley824af852008-02-12 20:43:05 +00001795 {
1796#if defined(SO_BINDTODEVICE)
1797 newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
Simon Kelley316e2732010-01-22 20:16:09 +00001798 strncpy(newlist->interface, source, IF_NAMESIZE - 1);
Simon Kelley824af852008-02-12 20:43:05 +00001799#else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001800 ret_err(_("interface binding not supported"));
Simon Kelley824af852008-02-12 20:43:05 +00001801#endif
1802 }
Simon Kelley849a8352006-06-09 21:02:31 +01001803 }
1804 else
1805 newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001806 }
Simon Kelley849a8352006-06-09 21:02:31 +01001807#ifdef HAVE_IPV6
1808 else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
1809 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001810 if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001811 ret_err(_("bad interface name"));
Simon Kelley7de060b2011-08-26 17:24:52 +01001812
Simon Kelley849a8352006-06-09 21:02:31 +01001813 newlist->addr.in6.sin6_port = htons(serv_port);
Simon Kelley7de060b2011-08-26 17:24:52 +01001814 newlist->addr.in6.sin6_scope_id = scope_index;
Simon Kelley849a8352006-06-09 21:02:31 +01001815 newlist->source_addr.in6.sin6_port = htons(source_port);
Simon Kelley7de060b2011-08-26 17:24:52 +01001816 newlist->source_addr.in6.sin6_scope_id = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001817 newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
Simon Kelley7de060b2011-08-26 17:24:52 +01001818 newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01001819#ifdef HAVE_SOCKADDR_SA_LEN
1820 newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
1821#endif
1822 if (source)
1823 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001824 newlist->flags |= SERV_HAS_SOURCE;
1825 if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
Simon Kelley824af852008-02-12 20:43:05 +00001826 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001827#if defined(SO_BINDTODEVICE)
Simon Kelley824af852008-02-12 20:43:05 +00001828 newlist->source_addr.in6.sin6_addr = in6addr_any;
Simon Kelley316e2732010-01-22 20:16:09 +00001829 strncpy(newlist->interface, source, IF_NAMESIZE - 1);
Simon Kelley824af852008-02-12 20:43:05 +00001830#else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001831 ret_err(_("interface binding not supported"));
Simon Kelley824af852008-02-12 20:43:05 +00001832#endif
1833 }
Simon Kelley849a8352006-06-09 21:02:31 +01001834 }
1835 else
1836 newlist->source_addr.in6.sin6_addr = in6addr_any;
1837 }
1838#endif
1839 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001840 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001841 }
1842
Simon Kelleyf2621c72007-04-29 19:47:21 +01001843 serv = newlist;
1844 while (serv->next)
Simon Kelley849a8352006-06-09 21:02:31 +01001845 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001846 serv->next->flags = serv->flags;
1847 serv->next->addr = serv->addr;
1848 serv->next->source_addr = serv->source_addr;
1849 serv = serv->next;
Simon Kelley849a8352006-06-09 21:02:31 +01001850 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001851 serv->next = daemon->servers;
1852 daemon->servers = newlist;
Simon Kelley849a8352006-06-09 21:02:31 +01001853 break;
1854 }
1855
Simon Kelleyf2621c72007-04-29 19:47:21 +01001856 case 'c': /* --cache-size */
Simon Kelley849a8352006-06-09 21:02:31 +01001857 {
1858 int size;
1859
1860 if (!atoi_check(arg, &size))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001861 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001862 else
1863 {
1864 /* zero is OK, and means no caching. */
1865
1866 if (size < 0)
1867 size = 0;
1868 else if (size > 10000)
1869 size = 10000;
1870
1871 daemon->cachesize = size;
1872 }
1873 break;
1874 }
1875
Simon Kelleyf2621c72007-04-29 19:47:21 +01001876 case 'p': /* --port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01001877 if (!atoi_check16(arg, &daemon->port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001878 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001879 break;
Simon Kelley208b65c2006-08-05 21:41:37 +01001880
Simon Kelley1a6bca82008-07-11 11:11:42 +01001881 case LOPT_MINPORT: /* --min-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01001882 if (!atoi_check16(arg, &daemon->min_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001883 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001884 break;
1885
Simon Kelleyf2621c72007-04-29 19:47:21 +01001886 case '0': /* --dns-forward-max */
Simon Kelley208b65c2006-08-05 21:41:37 +01001887 if (!atoi_check(arg, &daemon->ftabsize))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001888 ret_err(gen_err);
Simon Kelley208b65c2006-08-05 21:41:37 +01001889 break;
1890
Simon Kelleyf2621c72007-04-29 19:47:21 +01001891 case LOPT_MAX_LOGS: /* --log-async */
1892 daemon->max_logs = LOG_MAX; /* default */
1893 if (arg && !atoi_check(arg, &daemon->max_logs))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001894 ret_err(gen_err);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001895 else if (daemon->max_logs > 100)
1896 daemon->max_logs = 100;
1897 break;
1898
1899 case 'P': /* --edns-packet-max */
Simon Kelley849a8352006-06-09 21:02:31 +01001900 {
1901 int i;
1902 if (!atoi_check(arg, &i))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001903 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001904 daemon->edns_pktsz = (unsigned short)i;
1905 break;
1906 }
1907
Simon Kelleyf2621c72007-04-29 19:47:21 +01001908 case 'Q': /* --query-port */
Simon Kelley1ad24ae2008-07-20 20:22:50 +01001909 if (!atoi_check16(arg, &daemon->query_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001910 ret_err(gen_err);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001911 /* if explicitly set to zero, use single OS ephemeral port
1912 and disable random ports */
1913 if (daemon->query_port == 0)
1914 daemon->osport = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01001915 break;
1916
Simon Kelley824af852008-02-12 20:43:05 +00001917 case 'T': /* --local-ttl */
1918 case LOPT_NEGTTL: /* --neg-ttl */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001919 case LOPT_MAXTTL: /* --max-ttl */
Simon Kelley849a8352006-06-09 21:02:31 +01001920 {
1921 int ttl;
1922 if (!atoi_check(arg, &ttl))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001923 ret_err(gen_err);
Simon Kelley824af852008-02-12 20:43:05 +00001924 else if (option == LOPT_NEGTTL)
1925 daemon->neg_ttl = (unsigned long)ttl;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001926 else if (option == LOPT_MAXTTL)
1927 daemon->max_ttl = (unsigned long)ttl;
Simon Kelley849a8352006-06-09 21:02:31 +01001928 else
1929 daemon->local_ttl = (unsigned long)ttl;
1930 break;
1931 }
1932
Simon Kelley7622fc02009-06-04 20:32:05 +01001933#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001934 case 'X': /* --dhcp-lease-max */
Simon Kelley849a8352006-06-09 21:02:31 +01001935 if (!atoi_check(arg, &daemon->dhcp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001936 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01001937 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001938#endif
Simon Kelley849a8352006-06-09 21:02:31 +01001939
Simon Kelley7622fc02009-06-04 20:32:05 +01001940#ifdef HAVE_TFTP
Simon Kelleyf2621c72007-04-29 19:47:21 +01001941 case LOPT_TFTP_MAX: /* --tftp-max */
Simon Kelley832af0b2007-01-21 20:01:28 +00001942 if (!atoi_check(arg, &daemon->tftp_max))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001943 ret_err(gen_err);
Simon Kelley832af0b2007-01-21 20:01:28 +00001944 break;
1945
Simon Kelley824af852008-02-12 20:43:05 +00001946 case LOPT_PREFIX: /* --tftp-prefix */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001947 comma = split(arg);
1948 if (comma)
1949 {
1950 struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
1951 new->interface = opt_string_alloc(comma);
1952 new->prefix = opt_string_alloc(arg);
1953 new->next = daemon->if_prefix;
1954 daemon->if_prefix = new;
1955 }
1956 else
1957 daemon->tftp_prefix = opt_string_alloc(arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001958 break;
1959
Simon Kelley824af852008-02-12 20:43:05 +00001960 case LOPT_TFTPPORTS: /* --tftp-port-range */
1961 if (!(comma = split(arg)) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01001962 !atoi_check16(arg, &daemon->start_tftp_port) ||
1963 !atoi_check16(comma, &daemon->end_tftp_port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001964 ret_err(_("bad port range"));
Simon Kelley824af852008-02-12 20:43:05 +00001965
1966 if (daemon->start_tftp_port > daemon->end_tftp_port)
1967 {
1968 int tmp = daemon->start_tftp_port;
1969 daemon->start_tftp_port = daemon->end_tftp_port;
1970 daemon->end_tftp_port = tmp;
1971 }
1972
1973 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01001974#endif
Simon Kelley824af852008-02-12 20:43:05 +00001975
Simon Kelleyf2621c72007-04-29 19:47:21 +01001976 case LOPT_BRIDGE: /* --bridge-interface */
Simon Kelley832af0b2007-01-21 20:01:28 +00001977 {
Simon Kelley824af852008-02-12 20:43:05 +00001978 struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley316e2732010-01-22 20:16:09 +00001979 if (!(comma = split(arg)) || strlen(arg) > IF_NAMESIZE - 1 )
Simon Kelleyc4a7f902012-07-12 20:52:12 +01001980 ret_err(_("bad bridge-interface"));
Simon Kelley832af0b2007-01-21 20:01:28 +00001981
Simon Kelley316e2732010-01-22 20:16:09 +00001982 strcpy(new->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001983 new->alias = NULL;
1984 new->next = daemon->bridges;
1985 daemon->bridges = new;
1986
1987 do {
Simon Kelleyf2621c72007-04-29 19:47:21 +01001988 arg = comma;
1989 comma = split(arg);
Simon Kelley316e2732010-01-22 20:16:09 +00001990 if (strlen(arg) != 0 && strlen(arg) <= IF_NAMESIZE - 1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001991 {
Simon Kelley824af852008-02-12 20:43:05 +00001992 struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge));
Simon Kelley832af0b2007-01-21 20:01:28 +00001993 b->next = new->alias;
1994 new->alias = b;
Simon Kelley316e2732010-01-22 20:16:09 +00001995 strcpy(b->iface, arg);
Simon Kelley832af0b2007-01-21 20:01:28 +00001996 }
1997 } while (comma);
1998
1999 break;
2000 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002001
Simon Kelley7622fc02009-06-04 20:32:05 +01002002#ifdef HAVE_DHCP
Simon Kelleyf2621c72007-04-29 19:47:21 +01002003 case 'F': /* --dhcp-range */
Simon Kelley849a8352006-06-09 21:02:31 +01002004 {
2005 int k, leasepos = 2;
Simon Kelley30cd9662012-03-25 20:44:38 +01002006 char *cp, *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley824af852008-02-12 20:43:05 +00002007 struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
Simon Kelley849a8352006-06-09 21:02:31 +01002008
Simon Kelley52b92f42012-01-22 16:05:15 +00002009 memset (new, 0, sizeof(*new));
Simon Kelley849a8352006-06-09 21:02:31 +01002010 new->lease_time = DEFLEASE;
Simon Kelley52b92f42012-01-22 16:05:15 +00002011
Simon Kelley849a8352006-06-09 21:02:31 +01002012 if (!arg)
2013 {
2014 option = '?';
2015 break;
2016 }
2017
2018 while(1)
2019 {
2020 for (cp = arg; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002021 if (!(*cp == ' ' || *cp == '.' || *cp == ':' ||
2022 (*cp >= 'a' && *cp <= 'f') || (*cp >= 'A' && *cp <= 'F') ||
2023 (*cp >='0' && *cp <= '9')))
Simon Kelley849a8352006-06-09 21:02:31 +01002024 break;
2025
Simon Kelleyf2621c72007-04-29 19:47:21 +01002026 if (*cp != ',' && (comma = split(arg)))
Simon Kelley849a8352006-06-09 21:02:31 +01002027 {
Simon Kelley8bc4cec2012-07-03 21:04:11 +01002028 if (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002029 {
Simon Kelley824af852008-02-12 20:43:05 +00002030 struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2031 tt->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002032 tt->next = new->filter;
2033 new->filter = tt;
2034 }
2035 else
2036 {
2037 if (new->netid.net)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002038 ret_err(_("only one tag allowed"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002039 else if (strstr(arg, "set:") == arg)
2040 new->netid.net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002041 else
Simon Kelley824af852008-02-12 20:43:05 +00002042 new->netid.net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002043 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002044 arg = comma;
Simon Kelley849a8352006-06-09 21:02:31 +01002045 }
2046 else
2047 {
2048 a[0] = arg;
2049 break;
2050 }
2051 }
2052
Simon Kelley30cd9662012-03-25 20:44:38 +01002053 for (k = 1; k < 7; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002054 if (!(a[k] = split(a[k-1])))
2055 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002056
Simon Kelley52b92f42012-01-22 16:05:15 +00002057 if (k < 2)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002058 ret_err(_("bad dhcp-range"));
2059
2060 if (inet_pton(AF_INET, a[0], &new->start))
Simon Kelley849a8352006-06-09 21:02:31 +01002061 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002062 new->next = daemon->dhcp;
2063 daemon->dhcp = new;
Simon Kelley30cd9662012-03-25 20:44:38 +01002064 new->end = new->start;
Simon Kelley52b92f42012-01-22 16:05:15 +00002065 if (strcmp(a[1], "static") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002066 new->flags |= CONTEXT_STATIC;
Simon Kelley52b92f42012-01-22 16:05:15 +00002067 else if (strcmp(a[1], "proxy") == 0)
Simon Kelley30cd9662012-03-25 20:44:38 +01002068 new->flags |= CONTEXT_PROXY;
2069 else if (!inet_pton(AF_INET, a[1], &new->end))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002070 ret_err(_("bad dhcp-range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002071
2072 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2073 {
2074 struct in_addr tmp = new->start;
2075 new->start = new->end;
2076 new->end = tmp;
2077 }
2078
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002079 if (k >= 3 && strchr(a[2], '.') &&
Simon Kelley52b92f42012-01-22 16:05:15 +00002080 ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
2081 {
2082 new->flags |= CONTEXT_NETMASK;
2083 leasepos = 3;
2084 if (!is_same_net(new->start, new->end, new->netmask))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002085 ret_err(_("inconsistent DHCP range"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002086 }
2087
2088 if (k >= 4 && strchr(a[3], '.') &&
2089 ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
2090 {
2091 new->flags |= CONTEXT_BRDCAST;
2092 leasepos = 4;
2093 }
Simon Kelley849a8352006-06-09 21:02:31 +01002094 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002095#ifdef HAVE_DHCP6
2096 else if (inet_pton(AF_INET6, a[0], &new->start6))
Simon Kelley7622fc02009-06-04 20:32:05 +01002097 {
Simon Kelley52b92f42012-01-22 16:05:15 +00002098 new->prefix = 64; /* default */
Simon Kelley30cd9662012-03-25 20:44:38 +01002099 new->end6 = new->start6;
2100
2101 for (leasepos = 1; leasepos < k; leasepos++)
2102 {
2103 if (strcmp(a[leasepos], "static") == 0)
2104 new->flags |= CONTEXT_STATIC | CONTEXT_DHCP;
2105 else if (strcmp(a[leasepos], "ra-only") == 0 || strcmp(a[leasepos], "slaac") == 0 )
2106 new->flags |= CONTEXT_RA_ONLY;
2107 else if (strcmp(a[leasepos], "ra-names") == 0)
2108 new->flags |= CONTEXT_RA_NAME;
2109 else if (strcmp(a[leasepos], "ra-stateless") == 0)
2110 new->flags |= CONTEXT_RA_STATELESS | CONTEXT_DHCP;
2111 else if (leasepos == 1 && inet_pton(AF_INET6, a[leasepos], &new->end6))
2112 new->flags |= CONTEXT_DHCP;
2113 else
2114 break;
2115 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00002116
Simon Kelley30cd9662012-03-25 20:44:38 +01002117 if (new->flags & CONTEXT_DHCP)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00002118 {
2119 new->next = daemon->dhcp6;
2120 daemon->dhcp6 = new;
2121 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002122 else
2123 {
2124 new->next = daemon->ra_contexts;
2125 daemon->ra_contexts = new;
2126 }
2127
Simon Kelley52b92f42012-01-22 16:05:15 +00002128 /* bare integer < 128 is prefix value */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002129 if (leasepos < k)
Simon Kelley52b92f42012-01-22 16:05:15 +00002130 {
2131 int pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002132 for (cp = a[leasepos]; *cp; cp++)
Simon Kelley52b92f42012-01-22 16:05:15 +00002133 if (!(*cp >= '0' && *cp <= '9'))
2134 break;
Simon Kelley30cd9662012-03-25 20:44:38 +01002135 if (!*cp && (pref = atoi(a[leasepos])) <= 128)
Simon Kelley52b92f42012-01-22 16:05:15 +00002136 {
2137 new->prefix = pref;
Simon Kelley30cd9662012-03-25 20:44:38 +01002138 leasepos++;
2139 if ((new->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) &&
2140 new->prefix != 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002141 ret_err(_("prefix must be exactly 64 for RA subnets"));
Simon Kelley801ca9a2012-03-06 19:30:17 +00002142 else if (new->prefix < 64)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002143 ret_err(_("prefix must be at least 64"));
Simon Kelley52b92f42012-01-22 16:05:15 +00002144 }
2145 }
Simon Kelley30cd9662012-03-25 20:44:38 +01002146
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002147 if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2148 ret_err(_("inconsistent DHCPv6 range"));
2149 if (addr6part(&new->start6) > addr6part(&new->end6))
Simon Kelley52b92f42012-01-22 16:05:15 +00002150 {
2151 struct in6_addr tmp = new->start6;
2152 new->start6 = new->end6;
2153 new->end6 = tmp;
2154 }
Simon Kelley849a8352006-06-09 21:02:31 +01002155 }
Simon Kelley52b92f42012-01-22 16:05:15 +00002156#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002157
Simon Kelley30cd9662012-03-25 20:44:38 +01002158 if (leasepos < k)
Simon Kelley849a8352006-06-09 21:02:31 +01002159 {
2160 if (strcmp(a[leasepos], "infinite") == 0)
2161 new->lease_time = 0xffffffff;
Simon Kelleyc8257542012-03-28 21:15:41 +01002162 else if (strcmp(a[leasepos], "deprecated") == 0)
2163 new->flags |= CONTEXT_DEPRECATE;
Simon Kelley849a8352006-06-09 21:02:31 +01002164 else
2165 {
2166 int fac = 1;
2167 if (strlen(a[leasepos]) > 0)
2168 {
2169 switch (a[leasepos][strlen(a[leasepos]) - 1])
2170 {
Simon Kelley42243212012-07-20 15:19:18 +01002171 case 'w':
2172 case 'W':
2173 fac *= 7;
2174 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002175 case 'd':
2176 case 'D':
2177 fac *= 24;
2178 /* fall though */
2179 case 'h':
2180 case 'H':
2181 fac *= 60;
2182 /* fall through */
2183 case 'm':
2184 case 'M':
2185 fac *= 60;
2186 /* fall through */
2187 case 's':
2188 case 'S':
Simon Kelleyf2621c72007-04-29 19:47:21 +01002189 a[leasepos][strlen(a[leasepos]) - 1] = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002190 }
2191
2192 new->lease_time = atoi(a[leasepos]) * fac;
2193 /* Leases of a minute or less confuse
2194 some clients, notably Apple's */
2195 if (new->lease_time < 120)
2196 new->lease_time = 120;
2197 }
2198 }
2199 }
2200 break;
2201 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01002202
Simon Kelley5aabfc72007-08-29 11:24:47 +01002203 case LOPT_BANK:
Simon Kelleyf2621c72007-04-29 19:47:21 +01002204 case 'G': /* --dhcp-host */
Simon Kelley849a8352006-06-09 21:02:31 +01002205 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002206 int j, k = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01002207 char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
Simon Kelley5aabfc72007-08-29 11:24:47 +01002208 struct dhcp_config *new;
Simon Kelley849a8352006-06-09 21:02:31 +01002209 struct in_addr in;
2210
Simon Kelley824af852008-02-12 20:43:05 +00002211 new = opt_malloc(sizeof(struct dhcp_config));
2212
Simon Kelley849a8352006-06-09 21:02:31 +01002213 new->next = daemon->dhcp_conf;
Simon Kelley9009d742008-11-14 20:04:27 +00002214 new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
2215 new->hwaddr = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002216 new->netid = NULL;
2217
Simon Kelley849a8352006-06-09 21:02:31 +01002218 if ((a[0] = arg))
2219 for (k = 1; k < 6; k++)
Simon Kelleyf2621c72007-04-29 19:47:21 +01002220 if (!(a[k] = split(a[k-1])))
2221 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002222
2223 for (j = 0; j < k; j++)
2224 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
2225 {
2226 char *arg = a[j];
2227
2228 if ((arg[0] == 'i' || arg[0] == 'I') &&
2229 (arg[1] == 'd' || arg[1] == 'D') &&
2230 arg[2] == ':')
2231 {
2232 if (arg[3] == '*')
2233 new->flags |= CONFIG_NOCLID;
2234 else
2235 {
2236 int len;
2237 arg += 3; /* dump id: */
2238 if (strchr(arg, ':'))
2239 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
2240 else
Simon Kelley5aabfc72007-08-29 11:24:47 +01002241 {
2242 unhide_metas(arg);
2243 len = (int) strlen(arg);
2244 }
2245
Simon Kelley28866e92011-02-14 20:19:14 +00002246 if (len == -1)
Simon Kelley9f7f3b12012-05-28 21:39:57 +01002247
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002248 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00002249 else if ((new->clid = opt_malloc(len)))
Simon Kelley5aabfc72007-08-29 11:24:47 +01002250 {
2251 new->flags |= CONFIG_CLID;
2252 new->clid_len = len;
2253 memcpy(new->clid, arg, len);
2254 }
Simon Kelley849a8352006-06-09 21:02:31 +01002255 }
2256 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002257 /* dhcp-host has strange backwards-compat needs. */
2258 else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
Simon Kelley849a8352006-06-09 21:02:31 +01002259 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002260 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2261 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2262 newtag->net = opt_malloc(strlen(arg + 4) + 1);
2263 newlist->next = new->netid;
2264 new->netid = newlist;
2265 newlist->list = newtag;
2266 strcpy(newtag->net, arg+4);
2267 unhide_metas(newtag->net);
Simon Kelley849a8352006-06-09 21:02:31 +01002268 }
Simon Kelley7de060b2011-08-26 17:24:52 +01002269 else if (strstr(arg, "tag:") == arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002270 ret_err(_("cannot match tags in --dhcp-host"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00002271#ifdef HAVE_DHCP6
2272 else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
2273 {
2274 arg[strlen(arg)-1] = 0;
2275 arg++;
2276
2277 if (!inet_pton(AF_INET6, arg, &new->addr6))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002278 ret_err(_("bad IPv6 address"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00002279
2280 new->flags |= CONFIG_ADDR6;
2281 }
2282#endif
Simon Kelley7de060b2011-08-26 17:24:52 +01002283 else
Simon Kelley849a8352006-06-09 21:02:31 +01002284 {
Simon Kelley9009d742008-11-14 20:04:27 +00002285 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
Simon Kelley28866e92011-02-14 20:19:14 +00002286 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
2287 &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002288 ret_err(_("bad hex constant"));
Simon Kelley28866e92011-02-14 20:19:14 +00002289 else
2290 {
2291
2292 newhw->next = new->hwaddr;
2293 new->hwaddr = newhw;
2294 }
Simon Kelley849a8352006-06-09 21:02:31 +01002295 }
2296 }
2297 else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
2298 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002299 struct dhcp_config *configs;
2300
Simon Kelley849a8352006-06-09 21:02:31 +01002301 new->addr = in;
2302 new->flags |= CONFIG_ADDR;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002303
2304 /* If the same IP appears in more than one host config, then DISCOVER
2305 for one of the hosts will get the address, but REQUEST will be NAKed,
2306 since the address is reserved by the other one -> protocol loop. */
2307 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
2308 if ((configs->flags & CONFIG_ADDR) && configs->addr.s_addr == in.s_addr)
2309 {
2310 sprintf(errstr, _("duplicate dhcp-host IP address %s"), inet_ntoa(in));
2311 return 0;
2312 }
Simon Kelley849a8352006-06-09 21:02:31 +01002313 }
2314 else
2315 {
2316 char *cp, *lastp = NULL, last = 0;
2317 int fac = 1;
2318
2319 if (strlen(a[j]) > 1)
2320 {
2321 lastp = a[j] + strlen(a[j]) - 1;
2322 last = *lastp;
2323 switch (last)
2324 {
Simon Kelley42243212012-07-20 15:19:18 +01002325 case 'w':
2326 case 'W':
2327 fac *= 7;
2328 /* fall through */
Simon Kelley849a8352006-06-09 21:02:31 +01002329 case 'd':
2330 case 'D':
2331 fac *= 24;
2332 /* fall through */
2333 case 'h':
2334 case 'H':
2335 fac *= 60;
2336 /* fall through */
2337 case 'm':
2338 case 'M':
2339 fac *= 60;
2340 /* fall through */
2341 case 's':
2342 case 'S':
2343 *lastp = 0;
2344 }
2345 }
2346
2347 for (cp = a[j]; *cp; cp++)
Simon Kelley572b41e2011-02-18 18:11:18 +00002348 if (!isdigit((unsigned char)*cp) && *cp != ' ')
Simon Kelley849a8352006-06-09 21:02:31 +01002349 break;
2350
2351 if (*cp)
2352 {
2353 if (lastp)
2354 *lastp = last;
2355 if (strcmp(a[j], "infinite") == 0)
2356 {
2357 new->lease_time = 0xffffffff;
2358 new->flags |= CONFIG_TIME;
2359 }
2360 else if (strcmp(a[j], "ignore") == 0)
2361 new->flags |= CONFIG_DISABLE;
2362 else
2363 {
Simon Kelley1f15b812009-10-13 17:49:32 +01002364 if (!(new->hostname = canonicalise_opt(a[j])) ||
2365 !legal_hostname(new->hostname))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002366 ret_err(_("bad DHCP host name"));
2367
2368 new->flags |= CONFIG_NAME;
2369 new->domain = strip_hostname(new->hostname);
Simon Kelley849a8352006-06-09 21:02:31 +01002370 }
2371 }
2372 else
2373 {
2374 new->lease_time = atoi(a[j]) * fac;
2375 /* Leases of a minute or less confuse
2376 some clients, notably Apple's */
2377 if (new->lease_time < 120)
2378 new->lease_time = 120;
2379 new->flags |= CONFIG_TIME;
2380 }
2381 }
2382
Simon Kelley5aabfc72007-08-29 11:24:47 +01002383 daemon->dhcp_conf = new;
Simon Kelley849a8352006-06-09 21:02:31 +01002384 break;
2385 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002386
2387 case LOPT_TAG_IF: /* --tag-if */
2388 {
2389 struct tag_if *new = opt_malloc(sizeof(struct tag_if));
2390
2391 new->tag = NULL;
2392 new->set = NULL;
2393 new->next = NULL;
2394
2395 /* preserve order */
2396 if (!daemon->tag_if)
2397 daemon->tag_if = new;
2398 else
2399 {
2400 struct tag_if *tmp;
2401 for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
2402 tmp->next = new;
2403 }
2404
2405 while (arg)
2406 {
2407 size_t len;
2408
2409 comma = split(arg);
2410 len = strlen(arg);
2411
2412 if (len < 5)
2413 {
2414 new->set = NULL;
2415 break;
2416 }
2417 else
2418 {
2419 struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2420 newtag->net = opt_malloc(len - 3);
2421 strcpy(newtag->net, arg+4);
2422 unhide_metas(newtag->net);
2423
2424 if (strstr(arg, "set:") == arg)
2425 {
2426 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2427 newlist->next = new->set;
2428 new->set = newlist;
2429 newlist->list = newtag;
2430 }
2431 else if (strstr(arg, "tag:") == arg)
2432 {
2433 newtag->next = new->tag;
2434 new->tag = newtag;
2435 }
2436 else
2437 {
2438 new->set = NULL;
2439 break;
2440 }
2441 }
2442
2443 arg = comma;
2444 }
2445
2446 if (!new->set)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002447 ret_err(_("bad tag-if"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002448
2449 break;
2450 }
2451
Simon Kelley849a8352006-06-09 21:02:31 +01002452
Simon Kelley73a08a22009-02-05 20:28:08 +00002453 case 'O': /* --dhcp-option */
2454 case LOPT_FORCE: /* --dhcp-option-force */
Simon Kelley824af852008-02-12 20:43:05 +00002455 case LOPT_OPTS:
Simon Kelley73a08a22009-02-05 20:28:08 +00002456 case LOPT_MATCH: /* --dhcp-match */
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002457 return parse_dhcp_opt(errstr, arg,
2458 option == LOPT_FORCE ? DHOPT_FORCE :
2459 (option == LOPT_MATCH ? DHOPT_MATCH :
2460 (option == LOPT_OPTS ? DHOPT_BANK : 0)));
2461
Simon Kelleyf2621c72007-04-29 19:47:21 +01002462 case 'M': /* --dhcp-boot */
Simon Kelley849a8352006-06-09 21:02:31 +01002463 {
2464 struct dhcp_netid *id = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002465 while (is_tag_prefix(arg))
Simon Kelley849a8352006-06-09 21:02:31 +01002466 {
Simon Kelley824af852008-02-12 20:43:05 +00002467 struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelley849a8352006-06-09 21:02:31 +01002468 newid->next = id;
2469 id = newid;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002470 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002471 newid->net = opt_string_alloc(arg+4);
Simon Kelley849a8352006-06-09 21:02:31 +01002472 arg = comma;
2473 };
2474
2475 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002476 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002477 else
2478 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002479 char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +01002480 struct in_addr dhcp_next_server;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002481 struct dhcp_boot *new;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002482 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002483 dhcp_file = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002484 dhcp_next_server.s_addr = 0;
2485 if (comma)
2486 {
2487 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01002488 comma = split(arg);
Simon Kelley824af852008-02-12 20:43:05 +00002489 dhcp_sname = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002490 if (comma)
2491 {
2492 unhide_metas(comma);
Simon Kelley7de060b2011-08-26 17:24:52 +01002493 if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1) {
2494
2495 /*
2496 * The user may have specified the tftp hostname here.
2497 * save it so that it can be resolved/looked up during
2498 * actual dhcp_reply().
2499 */
2500
2501 tftp_sname = opt_string_alloc(comma);
2502 dhcp_next_server.s_addr = 0;
2503 }
Simon Kelley849a8352006-06-09 21:02:31 +01002504 }
2505 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002506
2507 new = opt_malloc(sizeof(struct dhcp_boot));
2508 new->file = dhcp_file;
2509 new->sname = dhcp_sname;
2510 new->tftp_sname = tftp_sname;
2511 new->next_server = dhcp_next_server;
2512 new->netid = id;
2513 new->next = daemon->boot_config;
2514 daemon->boot_config = new;
Simon Kelley849a8352006-06-09 21:02:31 +01002515 }
2516
Simon Kelley849a8352006-06-09 21:02:31 +01002517 break;
2518 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002519
2520 case LOPT_PXE_PROMT: /* --pxe-prompt */
2521 {
2522 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
2523 int timeout;
2524
2525 new->netid = NULL;
2526 new->opt = 10; /* PXE_MENU_PROMPT */
2527
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002528 while (is_tag_prefix(arg))
2529 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002530 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2531 comma = split(arg);
2532 nn->next = new->netid;
2533 new->netid = nn;
2534 nn->net = opt_string_alloc(arg+4);
2535 arg = comma;
2536 }
2537
2538 if (!arg)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002539 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01002540 else
2541 {
2542 comma = split(arg);
2543 unhide_metas(arg);
2544 new->len = strlen(arg) + 1;
2545 new->val = opt_malloc(new->len);
2546 memcpy(new->val + 1, arg, new->len - 1);
2547
2548 new->u.vendor_class = (unsigned char *)"PXEClient";
2549 new->flags = DHOPT_VENDOR;
2550
2551 if (comma && atoi_check(comma, &timeout))
2552 *(new->val) = timeout;
2553 else
2554 *(new->val) = 255;
2555
2556 new->next = daemon->dhcp_opts;
2557 daemon->dhcp_opts = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01002558 daemon->enable_pxe = 1;
Simon Kelley7622fc02009-06-04 20:32:05 +01002559 }
2560
2561 break;
2562 }
2563
2564 case LOPT_PXE_SERV: /* --pxe-service */
2565 {
2566 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
2567 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
2568 "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };
2569 static int boottype = 32768;
2570
2571 new->netid = NULL;
Simon Kelley751d6f42012-02-10 15:24:51 +00002572 new->sname = NULL;
Simon Kelley7622fc02009-06-04 20:32:05 +01002573 new->server.s_addr = 0;
2574
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002575 while (is_tag_prefix(arg))
Simon Kelley7622fc02009-06-04 20:32:05 +01002576 {
2577 struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2578 comma = split(arg);
2579 nn->next = new->netid;
2580 new->netid = nn;
2581 nn->net = opt_string_alloc(arg+4);
2582 arg = comma;
2583 }
2584
2585 if (arg && (comma = split(arg)))
2586 {
2587 for (i = 0; CSA[i]; i++)
2588 if (strcasecmp(CSA[i], arg) == 0)
2589 break;
2590
2591 if (CSA[i] || atoi_check(arg, &i))
2592 {
2593 arg = comma;
2594 comma = split(arg);
2595
2596 new->CSA = i;
2597 new->menu = opt_string_alloc(arg);
2598
Simon Kelley316e2732010-01-22 20:16:09 +00002599 if (!comma)
2600 {
2601 new->type = 0; /* local boot */
2602 new->basename = NULL;
2603 }
2604 else
Simon Kelley7622fc02009-06-04 20:32:05 +01002605 {
2606 arg = comma;
2607 comma = split(arg);
2608 if (atoi_check(arg, &i))
2609 {
2610 new->type = i;
2611 new->basename = NULL;
2612 }
2613 else
2614 {
2615 new->type = boottype++;
2616 new->basename = opt_string_alloc(arg);
2617 }
2618
Simon Kelley751d6f42012-02-10 15:24:51 +00002619 if (comma)
2620 {
2621 if (!inet_pton(AF_INET, comma, &new->server))
2622 {
2623 new->server.s_addr = 0;
2624 new->sname = opt_string_alloc(comma);
2625 }
2626
2627 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002628 }
Simon Kelley751d6f42012-02-10 15:24:51 +00002629
Simon Kelley316e2732010-01-22 20:16:09 +00002630 /* Order matters */
2631 new->next = NULL;
2632 if (!daemon->pxe_services)
2633 daemon->pxe_services = new;
2634 else
2635 {
2636 struct pxe_service *s;
2637 for (s = daemon->pxe_services; s->next; s = s->next);
2638 s->next = new;
2639 }
2640
2641 daemon->enable_pxe = 1;
2642 break;
2643
Simon Kelley7622fc02009-06-04 20:32:05 +01002644 }
2645 }
2646
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002647 ret_err(gen_err);
Simon Kelley7622fc02009-06-04 20:32:05 +01002648 }
2649
Simon Kelleyf2621c72007-04-29 19:47:21 +01002650 case '4': /* --dhcp-mac */
Simon Kelley849a8352006-06-09 21:02:31 +01002651 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002652 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002653 ret_err(gen_err);
Simon Kelley849a8352006-06-09 21:02:31 +01002654 else
2655 {
Simon Kelley824af852008-02-12 20:43:05 +00002656 struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002657 new->netid.net = opt_string_alloc(set_prefix(arg));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002658 unhide_metas(comma);
2659 new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
Simon Kelley28866e92011-02-14 20:19:14 +00002660 if (new->hwaddr_len == -1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002661 ret_err(gen_err);
Simon Kelley28866e92011-02-14 20:19:14 +00002662 else
2663 {
2664 new->next = daemon->dhcp_macs;
2665 daemon->dhcp_macs = new;
2666 }
Simon Kelley849a8352006-06-09 21:02:31 +01002667 }
2668 }
2669 break;
2670
Simon Kelleyf2621c72007-04-29 19:47:21 +01002671 case 'U': /* --dhcp-vendorclass */
2672 case 'j': /* --dhcp-userclass */
2673 case LOPT_CIRCUIT: /* --dhcp-circuitid */
2674 case LOPT_REMOTE: /* --dhcp-remoteid */
2675 case LOPT_SUBSCR: /* --dhcp-subscrid */
Simon Kelley849a8352006-06-09 21:02:31 +01002676 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002677 unsigned char *p;
2678 int dig = 0;
2679 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
2680
2681 if (!(comma = split(arg)))
2682 ret_err(gen_err);
2683
2684 new->netid.net = opt_string_alloc(set_prefix(arg));
2685 /* check for hex string - must digits may include : must not have nothing else,
2686 only allowed for agent-options. */
2687
2688 arg = comma;
2689 if ((comma = split(arg)))
2690 {
2691 if (option != 'U' || strstr(arg, "enterprise:") != arg)
2692 ret_err(gen_err);
2693 else
2694 new->enterprise = atoi(arg+11);
2695 }
2696 else
2697 comma = arg;
2698
2699 for (p = (unsigned char *)comma; *p; p++)
2700 if (isxdigit(*p))
2701 dig = 1;
2702 else if (*p != ':')
2703 break;
2704 unhide_metas(comma);
2705 if (option == 'U' || option == 'j' || *p || !dig)
2706 {
2707 new->len = strlen(comma);
2708 new->data = opt_malloc(new->len);
2709 memcpy(new->data, comma, new->len);
2710 }
2711 else
2712 {
2713 new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
2714 new->data = opt_malloc(new->len);
2715 memcpy(new->data, comma, new->len);
2716 }
2717
2718 switch (option)
2719 {
2720 case 'j':
2721 new->match_type = MATCH_USER;
2722 break;
2723 case 'U':
2724 new->match_type = MATCH_VENDOR;
2725 break;
2726 case LOPT_CIRCUIT:
2727 new->match_type = MATCH_CIRCUIT;
2728 break;
2729 case LOPT_REMOTE:
2730 new->match_type = MATCH_REMOTE;
2731 break;
2732 case LOPT_SUBSCR:
2733 new->match_type = MATCH_SUBSCRIBER;
2734 break;
2735 }
2736 new->next = daemon->dhcp_vendors;
2737 daemon->dhcp_vendors = new;
Simon Kelleya5c72ab2012-02-10 13:42:47 +00002738
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002739 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002740 }
2741
Simon Kelley9e038942008-05-30 20:06:34 +01002742 case LOPT_ALTPORT: /* --dhcp-alternate-port */
2743 if (!arg)
2744 {
2745 daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
2746 daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
2747 }
2748 else
2749 {
2750 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002751 if (!atoi_check16(arg, &daemon->dhcp_server_port) ||
2752 (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002753 ret_err(_("invalid port number"));
Simon Kelley9e038942008-05-30 20:06:34 +01002754 if (!comma)
2755 daemon->dhcp_client_port = daemon->dhcp_server_port+1;
2756 }
2757 break;
2758
Simon Kelley824af852008-02-12 20:43:05 +00002759 case 'J': /* --dhcp-ignore */
2760 case LOPT_NO_NAMES: /* --dhcp-ignore-names */
2761 case LOPT_BROADCAST: /* --dhcp-broadcast */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002762 case '3': /* --bootp-dynamic */
2763 case LOPT_GEN_NAMES: /* --dhcp-generate-names */
Simon Kelley849a8352006-06-09 21:02:31 +01002764 {
Simon Kelley824af852008-02-12 20:43:05 +00002765 struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
Simon Kelley849a8352006-06-09 21:02:31 +01002766 struct dhcp_netid *list = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00002767 if (option == 'J')
2768 {
2769 new->next = daemon->dhcp_ignore;
2770 daemon->dhcp_ignore = new;
2771 }
Simon Kelley824af852008-02-12 20:43:05 +00002772 else if (option == LOPT_BROADCAST)
2773 {
2774 new->next = daemon->force_broadcast;
2775 daemon->force_broadcast = new;
2776 }
Simon Kelley9009d742008-11-14 20:04:27 +00002777 else if (option == '3')
2778 {
2779 new->next = daemon->bootp_dynamic;
2780 daemon->bootp_dynamic = new;
2781 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002782 else if (option == LOPT_GEN_NAMES)
2783 {
2784 new->next = daemon->dhcp_gen_names;
2785 daemon->dhcp_gen_names = new;
2786 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002787 else
2788 {
2789 new->next = daemon->dhcp_ignore_names;
2790 daemon->dhcp_ignore_names = new;
2791 }
2792
2793 while (arg) {
Simon Kelley824af852008-02-12 20:43:05 +00002794 struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002795 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002796 member->next = list;
2797 list = member;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002798 if (is_tag_prefix(arg))
Simon Kelley9009d742008-11-14 20:04:27 +00002799 member->net = opt_string_alloc(arg+4);
2800 else
2801 member->net = opt_string_alloc(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01002802 arg = comma;
Simon Kelley832af0b2007-01-21 20:01:28 +00002803 }
Simon Kelley849a8352006-06-09 21:02:31 +01002804
2805 new->list = list;
2806 break;
2807 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002808
2809 case LOPT_PROXY: /* --dhcp-proxy */
2810 daemon->override = 1;
2811 while (arg) {
2812 struct addr_list *new = opt_malloc(sizeof(struct addr_list));
2813 comma = split(arg);
2814 if ((new->addr.s_addr = inet_addr(arg)) == (in_addr_t)-1)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002815 ret_err(_("bad dhcp-proxy address"));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002816 new->next = daemon->override_relays;
2817 daemon->override_relays = new;
2818 arg = comma;
2819 }
2820 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002821#endif
Simon Kelley849a8352006-06-09 21:02:31 +01002822
Simon Kelley8b372702012-03-09 17:45:10 +00002823#ifdef HAVE_DHCP6
2824 case LOPT_DUID: /* --dhcp-duid */
2825 if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002826 ret_err(_("bad DUID"));
Simon Kelley8b372702012-03-09 17:45:10 +00002827 else
2828 {
2829 daemon->duid_config_len = parse_hex(comma,(unsigned char *)comma, strlen(comma), NULL, NULL);
2830 daemon->duid_config = opt_malloc(daemon->duid_config_len);
2831 memcpy(daemon->duid_config, comma, daemon->duid_config_len);
2832 }
2833 break;
2834#endif
2835
Simon Kelleyf2621c72007-04-29 19:47:21 +01002836 case 'V': /* --alias */
Simon Kelley849a8352006-06-09 21:02:31 +01002837 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002838 char *dash, *a[3] = { NULL, NULL, NULL };
Simon Kelleyf2621c72007-04-29 19:47:21 +01002839 int k = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00002840 struct doctor *new = opt_malloc(sizeof(struct doctor));
2841 new->next = daemon->doctors;
2842 daemon->doctors = new;
2843 new->mask.s_addr = 0xffffffff;
2844 new->end.s_addr = 0;
2845
Simon Kelley849a8352006-06-09 21:02:31 +01002846 if ((a[0] = arg))
2847 for (k = 1; k < 3; k++)
2848 {
Simon Kelleyf2621c72007-04-29 19:47:21 +01002849 if (!(a[k] = split(a[k-1])))
Simon Kelley849a8352006-06-09 21:02:31 +01002850 break;
Simon Kelley849a8352006-06-09 21:02:31 +01002851 unhide_metas(a[k]);
2852 }
Simon Kelley849a8352006-06-09 21:02:31 +01002853
Simon Kelley73a08a22009-02-05 20:28:08 +00002854 dash = split_chr(a[0], '-');
2855
Simon Kelley849a8352006-06-09 21:02:31 +01002856 if ((k < 2) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00002857 ((new->in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
2858 ((new->out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
2859 option = '?';
Simon Kelley849a8352006-06-09 21:02:31 +01002860
2861 if (k == 3)
Simon Kelley73a08a22009-02-05 20:28:08 +00002862 new->mask.s_addr = inet_addr(a[2]);
Simon Kelley849a8352006-06-09 21:02:31 +01002863
Simon Kelley73a08a22009-02-05 20:28:08 +00002864 if (dash &&
2865 ((new->end.s_addr = inet_addr(dash)) == (in_addr_t)-1 ||
2866 !is_same_net(new->in, new->end, new->mask) ||
2867 ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002868 ret_err(_("invalid alias range"));
Simon Kelley849a8352006-06-09 21:02:31 +01002869
2870 break;
2871 }
2872
Simon Kelleyf2621c72007-04-29 19:47:21 +01002873 case LOPT_INTNAME: /* --interface-name */
2874 {
2875 struct interface_name *new, **up;
Simon Kelley1f15b812009-10-13 17:49:32 +01002876 char *domain = NULL;
2877
Simon Kelleyf2621c72007-04-29 19:47:21 +01002878 comma = split(arg);
2879
Simon Kelley1f15b812009-10-13 17:49:32 +01002880 if (!comma || !(domain = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002881 ret_err(_("bad interface name"));
Simon Kelley1f15b812009-10-13 17:49:32 +01002882
Simon Kelley824af852008-02-12 20:43:05 +00002883 new = opt_malloc(sizeof(struct interface_name));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002884 new->next = NULL;
2885 /* Add to the end of the list, so that first name
2886 of an interface is used for PTR lookups. */
Simon Kelley824af852008-02-12 20:43:05 +00002887 for (up = &daemon->int_names; *up; up = &((*up)->next));
Simon Kelleyf2621c72007-04-29 19:47:21 +01002888 *up = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01002889 new->name = domain;
Simon Kelley824af852008-02-12 20:43:05 +00002890 new->intr = opt_string_alloc(comma);
Simon Kelleyf2621c72007-04-29 19:47:21 +01002891 break;
2892 }
Simon Kelley9009d742008-11-14 20:04:27 +00002893
2894 case LOPT_CNAME: /* --cname */
2895 {
2896 struct cname *new;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002897 char *alias;
2898 char *target;
2899
Simon Kelley9009d742008-11-14 20:04:27 +00002900 if (!(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002901 ret_err(gen_err);
2902
2903 alias = canonicalise_opt(arg);
2904 target = canonicalise_opt(comma);
2905
2906 if (!alias || !target)
2907 ret_err(_("bad CNAME"));
Simon Kelley9009d742008-11-14 20:04:27 +00002908 else
2909 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002910 for (new = daemon->cnames; new; new = new->next)
2911 if (hostname_isequal(new->alias, arg))
2912 ret_err(_("duplicate CNAME"));
2913 new = opt_malloc(sizeof(struct cname));
2914 new->next = daemon->cnames;
2915 daemon->cnames = new;
2916 new->alias = alias;
2917 new->target = target;
Simon Kelley9009d742008-11-14 20:04:27 +00002918 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002919
Simon Kelley9009d742008-11-14 20:04:27 +00002920 break;
2921 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01002922
2923 case LOPT_PTR: /* --ptr-record */
Simon Kelley832af0b2007-01-21 20:01:28 +00002924 {
2925 struct ptr_record *new;
Simon Kelley1f15b812009-10-13 17:49:32 +01002926 char *dom, *target = NULL;
2927
Simon Kelleyf2621c72007-04-29 19:47:21 +01002928 comma = split(arg);
2929
Simon Kelley1f15b812009-10-13 17:49:32 +01002930 if (!(dom = canonicalise_opt(arg)) ||
2931 (comma && !(target = canonicalise_opt(comma))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002932 ret_err(_("bad PTR record"));
Simon Kelley1f15b812009-10-13 17:49:32 +01002933 else
2934 {
2935 new = opt_malloc(sizeof(struct ptr_record));
2936 new->next = daemon->ptr;
2937 daemon->ptr = new;
2938 new->name = dom;
2939 new->ptr = target;
2940 }
Simon Kelley832af0b2007-01-21 20:01:28 +00002941 break;
2942 }
2943
Simon Kelley1a6bca82008-07-11 11:11:42 +01002944 case LOPT_NAPTR: /* --naptr-record */
2945 {
2946 char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
2947 int k = 0;
2948 struct naptr *new;
2949 int order, pref;
Simon Kelley1f15b812009-10-13 17:49:32 +01002950 char *name, *replace = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01002951
2952 if ((a[0] = arg))
2953 for (k = 1; k < 7; k++)
2954 if (!(a[k] = split(a[k-1])))
2955 break;
2956
2957
2958 if (k < 6 ||
Simon Kelley1f15b812009-10-13 17:49:32 +01002959 !(name = canonicalise_opt(a[0])) ||
Simon Kelley1ad24ae2008-07-20 20:22:50 +01002960 !atoi_check16(a[1], &order) ||
2961 !atoi_check16(a[2], &pref) ||
Simon Kelley1f15b812009-10-13 17:49:32 +01002962 (k == 7 && !(replace = canonicalise_opt(a[6]))))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002963 ret_err(_("bad NAPTR record"));
Simon Kelley1a6bca82008-07-11 11:11:42 +01002964 else
2965 {
2966 new = opt_malloc(sizeof(struct naptr));
2967 new->next = daemon->naptr;
2968 daemon->naptr = new;
Simon Kelley1f15b812009-10-13 17:49:32 +01002969 new->name = name;
Simon Kelley1a6bca82008-07-11 11:11:42 +01002970 new->flags = opt_string_alloc(a[3]);
2971 new->services = opt_string_alloc(a[4]);
2972 new->regexp = opt_string_alloc(a[5]);
Simon Kelley1f15b812009-10-13 17:49:32 +01002973 new->replace = replace;
Simon Kelley1a6bca82008-07-11 11:11:42 +01002974 new->order = order;
2975 new->pref = pref;
2976 }
2977 break;
2978 }
Simon Kelley9f7f3b12012-05-28 21:39:57 +01002979
2980 case LOPT_RR: /* dns-rr */
2981 {
2982 struct txt_record *new;
2983 size_t len;
2984 char *data;
2985 int val;
2986
2987 comma = split(arg);
2988 data = split(comma);
2989
2990 new = opt_malloc(sizeof(struct txt_record));
2991 new->next = daemon->rr;
2992 daemon->rr = new;
2993
2994 if (!atoi_check(comma, &val) ||
2995 !(new->name = canonicalise_opt(arg)) ||
Simon Kelley51931b82012-05-29 17:06:02 +01002996 (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01002997 ret_err(_("bad RR record"));
2998
Simon Kelley9f7f3b12012-05-28 21:39:57 +01002999 new->class = val;
3000 new->len = 0;
3001
3002 if (data)
3003 {
3004 new->txt=opt_malloc(len);
3005 new->len = len;
3006 memcpy(new->txt, data, len);
3007 }
3008
3009 break;
3010 }
3011
Simon Kelleyf2621c72007-04-29 19:47:21 +01003012 case 'Y': /* --txt-record */
Simon Kelley849a8352006-06-09 21:02:31 +01003013 {
3014 struct txt_record *new;
Simon Kelley28866e92011-02-14 20:19:14 +00003015 unsigned char *p, *cnt;
3016 size_t len;
3017
3018 comma = split(arg);
3019
Simon Kelley824af852008-02-12 20:43:05 +00003020 new = opt_malloc(sizeof(struct txt_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003021 new->next = daemon->txt;
3022 daemon->txt = new;
3023 new->class = C_IN;
Simon Kelley849a8352006-06-09 21:02:31 +01003024
Simon Kelley1f15b812009-10-13 17:49:32 +01003025 if (!(new->name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003026 ret_err(_("bad TXT record"));
3027
Simon Kelley28866e92011-02-14 20:19:14 +00003028 len = comma ? strlen(comma) : 0;
3029 len += (len/255) + 1; /* room for extra counts */
3030 new->txt = p = opt_malloc(len);
3031
3032 cnt = p++;
3033 *cnt = 0;
3034
3035 while (comma && *comma)
3036 {
3037 unsigned char c = (unsigned char)*comma++;
3038
3039 if (c == ',' || *cnt == 255)
3040 {
3041 if (c != ',')
3042 comma--;
3043 cnt = p++;
3044 *cnt = 0;
3045 }
3046 else
3047 {
3048 *p++ = unhide_meta(c);
3049 (*cnt)++;
3050 }
3051 }
3052
3053 new->len = p - new->txt;
3054
Simon Kelley849a8352006-06-09 21:02:31 +01003055 break;
3056 }
3057
Simon Kelleyf2621c72007-04-29 19:47:21 +01003058 case 'W': /* --srv-host */
Simon Kelley849a8352006-06-09 21:02:31 +01003059 {
3060 int port = 1, priority = 0, weight = 0;
3061 char *name, *target = NULL;
3062 struct mx_srv_record *new;
3063
Simon Kelleyf2621c72007-04-29 19:47:21 +01003064 comma = split(arg);
Simon Kelley849a8352006-06-09 21:02:31 +01003065
Simon Kelley1f15b812009-10-13 17:49:32 +01003066 if (!(name = canonicalise_opt(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003067 ret_err(_("bad SRV record"));
3068
Simon Kelley849a8352006-06-09 21:02:31 +01003069 if (comma)
3070 {
3071 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003072 comma = split(arg);
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003073 if (!(target = canonicalise_opt(arg)))
3074 ret_err(_("bad SRV target"));
Simon Kelley824af852008-02-12 20:43:05 +00003075
Simon Kelley849a8352006-06-09 21:02:31 +01003076 if (comma)
3077 {
3078 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003079 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003080 if (!atoi_check16(arg, &port))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003081 ret_err(_("invalid port number"));
Simon Kelley824af852008-02-12 20:43:05 +00003082
Simon Kelley849a8352006-06-09 21:02:31 +01003083 if (comma)
3084 {
3085 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003086 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003087 if (!atoi_check16(arg, &priority))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003088 ret_err(_("invalid priority"));
Simon Kelley824af852008-02-12 20:43:05 +00003089
Simon Kelley849a8352006-06-09 21:02:31 +01003090 if (comma)
3091 {
3092 arg = comma;
Simon Kelleyf2621c72007-04-29 19:47:21 +01003093 comma = split(arg);
Simon Kelley1ad24ae2008-07-20 20:22:50 +01003094 if (!atoi_check16(arg, &weight))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003095 ret_err(_("invalid weight"));
Simon Kelley849a8352006-06-09 21:02:31 +01003096 }
3097 }
3098 }
3099 }
3100
Simon Kelley824af852008-02-12 20:43:05 +00003101 new = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley849a8352006-06-09 21:02:31 +01003102 new->next = daemon->mxnames;
3103 daemon->mxnames = new;
3104 new->issrv = 1;
3105 new->name = name;
3106 new->target = target;
3107 new->srvport = port;
3108 new->priority = priority;
3109 new->weight = weight;
3110 break;
3111 }
Simon Kelley7622fc02009-06-04 20:32:05 +01003112
Simon Kelleye759d422012-03-16 13:18:57 +00003113 case LOPT_HOST_REC: /* --host-record */
3114 {
3115 struct host_record *new = opt_malloc(sizeof(struct host_record));
3116 memset(new, 0, sizeof(struct host_record));
3117
3118 if (!arg || !(comma = split(arg)))
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003119 ret_err(_("Bad host-record"));
3120
3121 while (arg)
3122 {
3123 struct all_addr addr;
3124 if (inet_pton(AF_INET, arg, &addr))
3125 new->addr = addr.addr.addr4;
Simon Kelleye759d422012-03-16 13:18:57 +00003126#ifdef HAVE_IPV6
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003127 else if (inet_pton(AF_INET6, arg, &addr))
3128 new->addr6 = addr.addr.addr6;
Simon Kelleye759d422012-03-16 13:18:57 +00003129#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003130 else
3131 {
3132 int nomem;
3133 char *canon = canonicalise(arg, &nomem);
3134 struct name_list *nl = opt_malloc(sizeof(struct name_list));
3135 if (!canon)
3136 ret_err(_("Bad name in host-record"));
3137
3138 nl->name = canon;
3139 /* keep order, so that PTR record goes to first name */
3140 nl->next = NULL;
3141 if (!new->names)
3142 new->names = nl;
3143 else
3144 {
3145 struct name_list *tmp;
3146 for (tmp = new->names; tmp->next; tmp = tmp->next);
3147 tmp->next = nl;
3148 }
3149
3150 arg = comma;
3151 comma = split(arg);
3152 }
3153 }
Simon Kelleye759d422012-03-16 13:18:57 +00003154
3155 /* Keep list order */
3156 if (!daemon->host_records_tail)
3157 daemon->host_records = new;
3158 else
3159 daemon->host_records_tail->next = new;
3160 new->next = NULL;
3161 daemon->host_records_tail = new;
3162 break;
3163 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003164
Simon Kelley7622fc02009-06-04 20:32:05 +01003165 default:
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003166 ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)"));
3167
Simon Kelley849a8352006-06-09 21:02:31 +01003168 }
Simon Kelley824af852008-02-12 20:43:05 +00003169
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003170 return 1;
Simon Kelley849a8352006-06-09 21:02:31 +01003171}
3172
Simon Kelley28866e92011-02-14 20:19:14 +00003173static void read_file(char *file, FILE *f, int hard_opt)
Simon Kelley849a8352006-06-09 21:02:31 +01003174{
Simon Kelley824af852008-02-12 20:43:05 +00003175 volatile int lineno = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003176 char *buff = daemon->namebuff;
Simon Kelley849a8352006-06-09 21:02:31 +01003177
3178 while (fgets(buff, MAXDNAME, f))
3179 {
Simon Kelley611ebc52012-07-16 16:23:46 +01003180 int white, i, option = hard_opt;
3181 char *errmess, *p, *arg = NULL, *start;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003182 size_t len;
Simon Kelley832af0b2007-01-21 20:01:28 +00003183
Simon Kelley824af852008-02-12 20:43:05 +00003184 /* Memory allocation failure longjmps here if mem_recover == 1 */
Simon Kelley611ebc52012-07-16 16:23:46 +01003185 if (option != 0)
Simon Kelley824af852008-02-12 20:43:05 +00003186 {
3187 if (setjmp(mem_jmp))
3188 continue;
3189 mem_recover = 1;
3190 }
3191
Simon Kelley849a8352006-06-09 21:02:31 +01003192 lineno++;
Simon Kelley824af852008-02-12 20:43:05 +00003193 errmess = NULL;
3194
Simon Kelley849a8352006-06-09 21:02:31 +01003195 /* Implement quotes, inside quotes we allow \\ \" \n and \t
3196 metacharacters get hidden also strip comments */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003197 for (white = 1, p = buff; *p; p++)
Simon Kelley849a8352006-06-09 21:02:31 +01003198 {
3199 if (*p == '"')
3200 {
3201 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003202
Simon Kelley849a8352006-06-09 21:02:31 +01003203 for(; *p && *p != '"'; p++)
3204 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003205 if (*p == '\\' && strchr("\"tnebr\\", p[1]))
Simon Kelley849a8352006-06-09 21:02:31 +01003206 {
3207 if (p[1] == 't')
3208 p[1] = '\t';
3209 else if (p[1] == 'n')
3210 p[1] = '\n';
Simon Kelley849a8352006-06-09 21:02:31 +01003211 else if (p[1] == 'b')
3212 p[1] = '\b';
3213 else if (p[1] == 'r')
3214 p[1] = '\r';
Simon Kelley6b010842007-02-12 20:32:07 +00003215 else if (p[1] == 'e') /* escape */
3216 p[1] = '\033';
Simon Kelley849a8352006-06-09 21:02:31 +01003217 memmove(p, p+1, strlen(p+1)+1);
3218 }
3219 *p = hide_meta(*p);
3220 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003221
3222 if (*p == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +01003223 {
3224 errmess = _("missing \"");
3225 goto oops;
3226 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003227
3228 memmove(p, p+1, strlen(p+1)+1);
Simon Kelley849a8352006-06-09 21:02:31 +01003229 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01003230
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003231 if (isspace(*p))
3232 {
3233 *p = ' ';
3234 white = 1;
Simon Kelley849a8352006-06-09 21:02:31 +01003235 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003236 else
3237 {
3238 if (white && *p == '#')
3239 {
3240 *p = 0;
3241 break;
3242 }
3243 white = 0;
3244 }
Simon Kelley849a8352006-06-09 21:02:31 +01003245 }
3246
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003247
3248 /* strip leading spaces */
3249 for (start = buff; *start && *start == ' '; start++);
3250
3251 /* strip trailing spaces */
3252 for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
3253
3254 if (len == 0)
Simon Kelley849a8352006-06-09 21:02:31 +01003255 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003256 else
3257 start[len] = 0;
3258
Simon Kelley611ebc52012-07-16 16:23:46 +01003259 if (option != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003260 arg = start;
3261 else if ((p=strchr(start, '=')))
Simon Kelley849a8352006-06-09 21:02:31 +01003262 {
3263 /* allow spaces around "=" */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003264 for (arg = p+1; *arg == ' '; arg++);
3265 for (; p >= start && (*p == ' ' || *p == '='); p--)
Simon Kelley849a8352006-06-09 21:02:31 +01003266 *p = 0;
3267 }
3268 else
3269 arg = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +00003270
Simon Kelley611ebc52012-07-16 16:23:46 +01003271 if (option == 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +01003272 {
Simon Kelley5aabfc72007-08-29 11:24:47 +01003273 for (option = 0, i = 0; opts[i].name; i++)
3274 if (strcmp(opts[i].name, start) == 0)
3275 {
3276 option = opts[i].val;
3277 break;
3278 }
3279
3280 if (!option)
3281 errmess = _("bad option");
3282 else if (opts[i].has_arg == 0 && arg)
3283 errmess = _("extraneous parameter");
3284 else if (opts[i].has_arg == 1 && !arg)
3285 errmess = _("missing parameter");
3286 }
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003287
3288 oops:
Simon Kelley832af0b2007-01-21 20:01:28 +00003289 if (errmess)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003290 strcpy(daemon->namebuff, errmess);
3291
3292 if (errmess || !one_opt(option, arg, buff, _("error"), 0))
Simon Kelleyf2621c72007-04-29 19:47:21 +01003293 {
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003294 sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
Simon Kelley824af852008-02-12 20:43:05 +00003295 if (hard_opt != 0)
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003296 my_syslog(LOG_ERR, "%s", daemon->namebuff);
Simon Kelley5aabfc72007-08-29 11:24:47 +01003297 else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003298 die("%s", daemon->namebuff, EC_BADCONF);
Simon Kelleyf2621c72007-04-29 19:47:21 +01003299 }
Simon Kelley849a8352006-06-09 21:02:31 +01003300 }
3301
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003302 mem_recover = 0;
Simon Kelley849a8352006-06-09 21:02:31 +01003303 fclose(f);
3304}
3305
Simon Kelley395eb712012-07-06 22:07:05 +01003306static int one_file(char *file, int hard_opt)
Simon Kelley28866e92011-02-14 20:19:14 +00003307{
3308 FILE *f;
3309 int nofile_ok = 0;
3310 static int read_stdin = 0;
3311 static struct fileread {
3312 dev_t dev;
3313 ino_t ino;
3314 struct fileread *next;
3315 } *filesread = NULL;
3316
3317 if (hard_opt == '7')
3318 {
3319 /* default conf-file reading */
3320 hard_opt = 0;
3321 nofile_ok = 1;
3322 }
3323
3324 if (hard_opt == 0 && strcmp(file, "-") == 0)
3325 {
3326 if (read_stdin == 1)
Simon Kelley395eb712012-07-06 22:07:05 +01003327 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003328 read_stdin = 1;
3329 file = "stdin";
3330 f = stdin;
3331 }
3332 else
3333 {
3334 /* ignore repeated files. */
3335 struct stat statbuf;
3336
3337 if (hard_opt == 0 && stat(file, &statbuf) == 0)
3338 {
3339 struct fileread *r;
3340
3341 for (r = filesread; r; r = r->next)
3342 if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
Simon Kelley395eb712012-07-06 22:07:05 +01003343 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003344
3345 r = safe_malloc(sizeof(struct fileread));
3346 r->next = filesread;
3347 filesread = r;
3348 r->dev = statbuf.st_dev;
3349 r->ino = statbuf.st_ino;
3350 }
3351
3352 if (!(f = fopen(file, "r")))
3353 {
3354 if (errno == ENOENT && nofile_ok)
Simon Kelley395eb712012-07-06 22:07:05 +01003355 return 1; /* No conffile, all done. */
Simon Kelley28866e92011-02-14 20:19:14 +00003356 else
3357 {
3358 char *str = _("cannot read %s: %s");
3359 if (hard_opt != 0)
3360 {
3361 my_syslog(LOG_ERR, str, file, strerror(errno));
Simon Kelley395eb712012-07-06 22:07:05 +01003362 return 0;
Simon Kelley28866e92011-02-14 20:19:14 +00003363 }
3364 else
3365 die(str, file, EC_FILE);
3366 }
3367 }
3368 }
3369
3370 read_file(file, f, hard_opt);
Simon Kelley395eb712012-07-06 22:07:05 +01003371 return 1;
Simon Kelley28866e92011-02-14 20:19:14 +00003372}
3373
3374/* expand any name which is a directory */
3375struct hostsfile *expand_filelist(struct hostsfile *list)
3376{
3377 int i;
3378 struct hostsfile *ah;
3379
3380 for (i = 0, ah = list; ah; ah = ah->next)
3381 {
3382 if (i <= ah->index)
3383 i = ah->index + 1;
3384
3385 if (ah->flags & AH_DIR)
3386 ah->flags |= AH_INACTIVE;
3387 else
3388 ah->flags &= ~AH_INACTIVE;
3389 }
3390
3391 for (ah = list; ah; ah = ah->next)
3392 if (!(ah->flags & AH_INACTIVE))
3393 {
3394 struct stat buf;
3395 if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
3396 {
3397 DIR *dir_stream;
3398 struct dirent *ent;
3399
3400 /* don't read this as a file */
3401 ah->flags |= AH_INACTIVE;
3402
3403 if (!(dir_stream = opendir(ah->fname)))
3404 my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
3405 ah->fname, strerror(errno));
3406 else
3407 {
3408 while ((ent = readdir(dir_stream)))
3409 {
3410 size_t lendir = strlen(ah->fname);
3411 size_t lenfile = strlen(ent->d_name);
3412 struct hostsfile *ah1;
3413 char *path;
3414
3415 /* ignore emacs backups and dotfiles */
3416 if (lenfile == 0 ||
3417 ent->d_name[lenfile - 1] == '~' ||
3418 (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
3419 ent->d_name[0] == '.')
3420 continue;
3421
3422 /* see if we have an existing record.
3423 dir is ah->fname
3424 file is ent->d_name
3425 path to match is ah1->fname */
3426
3427 for (ah1 = list; ah1; ah1 = ah1->next)
3428 {
3429 if (lendir < strlen(ah1->fname) &&
3430 strstr(ah1->fname, ah->fname) == ah1->fname &&
3431 ah1->fname[lendir] == '/' &&
3432 strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
3433 {
3434 ah1->flags &= ~AH_INACTIVE;
3435 break;
3436 }
3437 }
3438
3439 /* make new record */
3440 if (!ah1)
3441 {
3442 if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
3443 continue;
3444
3445 if (!(path = whine_malloc(lendir + lenfile + 2)))
3446 {
3447 free(ah1);
3448 continue;
3449 }
3450
3451 strcpy(path, ah->fname);
3452 strcat(path, "/");
3453 strcat(path, ent->d_name);
3454 ah1->fname = path;
3455 ah1->index = i++;
3456 ah1->flags = AH_DIR;
3457 ah1->next = list;
3458 list = ah1;
3459 }
3460
3461 /* inactivate record if not regular file */
3462 if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
3463 ah1->flags |= AH_INACTIVE;
3464
3465 }
3466 closedir(dir_stream);
3467 }
3468 }
3469 }
3470
3471 return list;
3472}
3473
3474
Simon Kelley7622fc02009-06-04 20:32:05 +01003475#ifdef HAVE_DHCP
Simon Kelley824af852008-02-12 20:43:05 +00003476void reread_dhcp(void)
3477{
Simon Kelley28866e92011-02-14 20:19:14 +00003478 struct hostsfile *hf;
3479
Simon Kelley824af852008-02-12 20:43:05 +00003480 if (daemon->dhcp_hosts_file)
3481 {
3482 struct dhcp_config *configs, *cp, **up;
Simon Kelley28866e92011-02-14 20:19:14 +00003483
Simon Kelley824af852008-02-12 20:43:05 +00003484 /* remove existing... */
3485 for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
3486 {
3487 cp = configs->next;
3488
3489 if (configs->flags & CONFIG_BANK)
3490 {
Simon Kelley9009d742008-11-14 20:04:27 +00003491 struct hwaddr_config *mac, *tmp;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003492 struct dhcp_netid_list *list, *tmplist;
Simon Kelley9009d742008-11-14 20:04:27 +00003493
3494 for (mac = configs->hwaddr; mac; mac = tmp)
3495 {
3496 tmp = mac->next;
3497 free(mac);
3498 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003499
Simon Kelley824af852008-02-12 20:43:05 +00003500 if (configs->flags & CONFIG_CLID)
3501 free(configs->clid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01003502
3503 for (list = configs->netid; list; list = tmplist)
3504 {
3505 free(list->list);
3506 tmplist = list->next;
3507 free(list);
3508 }
3509
Simon Kelley824af852008-02-12 20:43:05 +00003510 if (configs->flags & CONFIG_NAME)
3511 free(configs->hostname);
3512
3513 *up = configs->next;
3514 free(configs);
3515 }
3516 else
3517 up = &configs->next;
3518 }
3519
Simon Kelley28866e92011-02-14 20:19:14 +00003520 daemon->dhcp_hosts_file = expand_filelist(daemon->dhcp_hosts_file);
3521 for (hf = daemon->dhcp_hosts_file; hf; hf = hf->next)
3522 if (!(hf->flags & AH_INACTIVE))
3523 {
Simon Kelley395eb712012-07-06 22:07:05 +01003524 if (one_file(hf->fname, LOPT_BANK))
3525 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00003526 }
Simon Kelley824af852008-02-12 20:43:05 +00003527 }
3528
3529 if (daemon->dhcp_opts_file)
3530 {
3531 struct dhcp_opt *opts, *cp, **up;
3532 struct dhcp_netid *id, *next;
3533
3534 for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
3535 {
3536 cp = opts->next;
3537
3538 if (opts->flags & DHOPT_BANK)
3539 {
Simon Kelley73a08a22009-02-05 20:28:08 +00003540 if ((opts->flags & DHOPT_VENDOR))
3541 free(opts->u.vendor_class);
Simon Kelley824af852008-02-12 20:43:05 +00003542 free(opts->val);
3543 for (id = opts->netid; id; id = next)
3544 {
3545 next = id->next;
3546 free(id->net);
3547 free(id);
3548 }
3549 *up = opts->next;
3550 free(opts);
3551 }
3552 else
3553 up = &opts->next;
3554 }
3555
Simon Kelley28866e92011-02-14 20:19:14 +00003556 daemon->dhcp_opts_file = expand_filelist(daemon->dhcp_opts_file);
3557 for (hf = daemon->dhcp_opts_file; hf; hf = hf->next)
3558 if (!(hf->flags & AH_INACTIVE))
3559 {
Simon Kelley395eb712012-07-06 22:07:05 +01003560 if (one_file(hf->fname, LOPT_OPTS))
3561 my_syslog(MS_DHCP | LOG_INFO, _("read %s"), hf->fname);
Simon Kelley28866e92011-02-14 20:19:14 +00003562 }
Simon Kelley824af852008-02-12 20:43:05 +00003563 }
3564}
Simon Kelley7622fc02009-06-04 20:32:05 +01003565#endif
Simon Kelley824af852008-02-12 20:43:05 +00003566
Simon Kelley5aabfc72007-08-29 11:24:47 +01003567void read_opts(int argc, char **argv, char *compile_opts)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003568{
Simon Kelley824af852008-02-12 20:43:05 +00003569 char *buff = opt_malloc(MAXDNAME);
Simon Kelley28866e92011-02-14 20:19:14 +00003570 int option, conffile_opt = '7', testmode = 0;
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003571 char *arg, *conffile = CONFFILE;
Simon Kelley849a8352006-06-09 21:02:31 +01003572
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003573 opterr = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +01003574
Simon Kelley824af852008-02-12 20:43:05 +00003575 daemon = opt_malloc(sizeof(struct daemon));
Simon Kelley3be34542004-09-11 19:12:13 +01003576 memset(daemon, 0, sizeof(struct daemon));
3577 daemon->namebuff = buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003578
Simon Kelley3be34542004-09-11 19:12:13 +01003579 /* Set defaults - everything else is zero or NULL */
Simon Kelley3be34542004-09-11 19:12:13 +01003580 daemon->cachesize = CACHESIZ;
Simon Kelley208b65c2006-08-05 21:41:37 +01003581 daemon->ftabsize = FTABSIZ;
Simon Kelley3be34542004-09-11 19:12:13 +01003582 daemon->port = NAMESERVER_PORT;
Simon Kelley9e038942008-05-30 20:06:34 +01003583 daemon->dhcp_client_port = DHCP_CLIENT_PORT;
3584 daemon->dhcp_server_port = DHCP_SERVER_PORT;
Simon Kelley3be34542004-09-11 19:12:13 +01003585 daemon->default_resolv.is_default = 1;
3586 daemon->default_resolv.name = RESOLVFILE;
3587 daemon->resolv_files = &daemon->default_resolv;
3588 daemon->username = CHUSER;
Simon Kelley3be34542004-09-11 19:12:13 +01003589 daemon->runfile = RUNFILE;
3590 daemon->dhcp_max = MAXLEASES;
Simon Kelley832af0b2007-01-21 20:01:28 +00003591 daemon->tftp_max = TFTP_MAX_CONNECTIONS;
Simon Kelley3be34542004-09-11 19:12:13 +01003592 daemon->edns_pktsz = EDNS_PKTSZ;
Simon Kelley849a8352006-06-09 21:02:31 +01003593 daemon->log_fac = -1;
Simon Kelley5aabfc72007-08-29 11:24:47 +01003594 add_txt("version.bind", "dnsmasq-" VERSION );
3595 add_txt("authors.bind", "Simon Kelley");
3596 add_txt("copyright.bind", COPYRIGHT);
Simon Kelley0a852542005-03-23 20:28:59 +00003597
Simon Kelley849a8352006-06-09 21:02:31 +01003598 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003599 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003600#ifdef HAVE_GETOPT_LONG
Simon Kelley849a8352006-06-09 21:02:31 +01003601 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003602#else
Simon Kelley849a8352006-06-09 21:02:31 +01003603 option = getopt(argc, argv, OPTSTRING);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003604#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003605
3606 if (option == -1)
Simon Kelley28866e92011-02-14 20:19:14 +00003607 {
Simon Kelley572b41e2011-02-18 18:11:18 +00003608 for (; optind < argc; optind++)
3609 {
3610 unsigned char *c = (unsigned char *)argv[optind];
3611 for (; *c != 0; c++)
3612 if (!isspace(*c))
3613 die(_("junk found in command line"), NULL, EC_BADCONF);
3614 }
Simon Kelley28866e92011-02-14 20:19:14 +00003615 break;
3616 }
3617
Simon Kelley849a8352006-06-09 21:02:31 +01003618 /* Copy optarg so that argv doesn't get changed */
3619 if (optarg)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003620 {
Simon Kelley849a8352006-06-09 21:02:31 +01003621 strncpy(buff, optarg, MAXDNAME);
3622 buff[MAXDNAME-1] = 0;
3623 arg = buff;
3624 }
3625 else
3626 arg = NULL;
3627
3628 /* command-line only stuff */
Simon Kelley7622fc02009-06-04 20:32:05 +01003629 if (option == LOPT_TEST)
3630 testmode = 1;
3631 else if (option == 'w')
Simon Kelley849a8352006-06-09 21:02:31 +01003632 {
Simon Kelley7622fc02009-06-04 20:32:05 +01003633#ifdef HAVE_DHCP
Simon Kelley4cb1b322012-02-06 14:30:41 +00003634 if (argc == 3 && strcmp(argv[2], "dhcp") == 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01003635 display_opts();
Simon Kelley4cb1b322012-02-06 14:30:41 +00003636#ifdef HAVE_DHCP6
3637 else if (argc == 3 && strcmp(argv[2], "dhcp6") == 0)
3638 display_opts6();
Simon Kelley7622fc02009-06-04 20:32:05 +01003639#endif
Simon Kelley4cb1b322012-02-06 14:30:41 +00003640 else
3641#endif
3642 do_usage();
3643
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003644 exit(0);
3645 }
Simon Kelley849a8352006-06-09 21:02:31 +01003646 else if (option == 'v')
3647 {
3648 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
Simon Kelleyc72daea2012-01-05 21:33:27 +00003649 printf(_("Compile time options: %s\n\n"), compile_opts);
Simon Kelleyb8187c82005-11-26 21:46:27 +00003650 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
3651 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
Simon Kelley824af852008-02-12 20:43:05 +00003652 printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003653 exit(0);
3654 }
Simon Kelley849a8352006-06-09 21:02:31 +01003655 else if (option == 'C')
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003656 {
Simon Kelley28866e92011-02-14 20:19:14 +00003657 conffile_opt = 0; /* file must exist */
Simon Kelley824af852008-02-12 20:43:05 +00003658 conffile = opt_string_alloc(arg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003659 }
Simon Kelley849a8352006-06-09 21:02:31 +01003660 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003661 {
Simon Kelley26128d22004-11-14 16:43:54 +00003662#ifdef HAVE_GETOPT_LONG
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003663 if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
Simon Kelley849a8352006-06-09 21:02:31 +01003664#else
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003665 if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
Simon Kelley849a8352006-06-09 21:02:31 +01003666#endif
Simon Kelleyc4a7f902012-07-12 20:52:12 +01003667 die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003668 }
3669 }
Simon Kelley849a8352006-06-09 21:02:31 +01003670
3671 if (conffile)
Simon Kelley28866e92011-02-14 20:19:14 +00003672 one_file(conffile, conffile_opt);
Simon Kelley849a8352006-06-09 21:02:31 +01003673
Simon Kelley1a6bca82008-07-11 11:11:42 +01003674 /* port might not be known when the address is parsed - fill in here */
Simon Kelley3be34542004-09-11 19:12:13 +01003675 if (daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003676 {
3677 struct server *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01003678 for (tmp = daemon->servers; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003679 if (!(tmp->flags & SERV_HAS_SOURCE))
3680 {
3681 if (tmp->source_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01003682 tmp->source_addr.in.sin_port = htons(daemon->query_port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003683#ifdef HAVE_IPV6
3684 else if (tmp->source_addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01003685 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5aabfc72007-08-29 11:24:47 +01003686#endif
3687 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003688 }
3689
Simon Kelley3be34542004-09-11 19:12:13 +01003690 if (daemon->if_addrs)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003691 {
3692 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +01003693 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003694 if (tmp->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +01003695 tmp->addr.in.sin_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003696#ifdef HAVE_IPV6
3697 else if (tmp->addr.sa.sa_family == AF_INET6)
Simon Kelley3be34542004-09-11 19:12:13 +01003698 tmp->addr.in6.sin6_port = htons(daemon->port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003699#endif /* IPv6 */
3700 }
3701
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00003702 /* only one of these need be specified: the other defaults to the host-name */
Simon Kelley28866e92011-02-14 20:19:14 +00003703 if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003704 {
Simon Kelley0a852542005-03-23 20:28:59 +00003705 struct mx_srv_record *mx;
3706
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003707 if (gethostname(buff, MAXDNAME) == -1)
Simon Kelley5aabfc72007-08-29 11:24:47 +01003708 die(_("cannot get host-name: %s"), NULL, EC_MISC);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00003709
Simon Kelley0a852542005-03-23 20:28:59 +00003710 for (mx = daemon->mxnames; mx; mx = mx->next)
3711 if (!mx->issrv && hostname_isequal(mx->name, buff))
3712 break;
3713
Simon Kelley28866e92011-02-14 20:19:14 +00003714 if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
Simon Kelleyde379512004-06-22 20:23:33 +01003715 {
Simon Kelley824af852008-02-12 20:43:05 +00003716 mx = opt_malloc(sizeof(struct mx_srv_record));
Simon Kelley91dccd02005-03-31 17:48:32 +01003717 mx->next = daemon->mxnames;
3718 mx->issrv = 0;
3719 mx->target = NULL;
Simon Kelley824af852008-02-12 20:43:05 +00003720 mx->name = opt_string_alloc(buff);
Simon Kelley91dccd02005-03-31 17:48:32 +01003721 daemon->mxnames = mx;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00003722 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003723
Simon Kelley3be34542004-09-11 19:12:13 +01003724 if (!daemon->mxtarget)
Simon Kelley824af852008-02-12 20:43:05 +00003725 daemon->mxtarget = opt_string_alloc(buff);
Simon Kelley0a852542005-03-23 20:28:59 +00003726
3727 for (mx = daemon->mxnames; mx; mx = mx->next)
3728 if (!mx->issrv && !mx->target)
3729 mx->target = daemon->mxtarget;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00003730 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00003731
Simon Kelley28866e92011-02-14 20:19:14 +00003732 if (!option_bool(OPT_NO_RESOLV) &&
Simon Kelley208b65c2006-08-05 21:41:37 +01003733 daemon->resolv_files &&
3734 daemon->resolv_files->next &&
Simon Kelley28866e92011-02-14 20:19:14 +00003735 option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003736 die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01003737
Simon Kelley28866e92011-02-14 20:19:14 +00003738 if (option_bool(OPT_RESOLV_DOMAIN))
Simon Kelleyde379512004-06-22 20:23:33 +01003739 {
3740 char *line;
Simon Kelley849a8352006-06-09 21:02:31 +01003741 FILE *f;
3742
Simon Kelley28866e92011-02-14 20:19:14 +00003743 if (option_bool(OPT_NO_RESOLV) ||
Simon Kelley208b65c2006-08-05 21:41:37 +01003744 !daemon->resolv_files ||
3745 (daemon->resolv_files)->next)
Simon Kelley5aabfc72007-08-29 11:24:47 +01003746 die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
Simon Kelleyde379512004-06-22 20:23:33 +01003747
Simon Kelley3be34542004-09-11 19:12:13 +01003748 if (!(f = fopen((daemon->resolv_files)->name, "r")))
Simon Kelley5aabfc72007-08-29 11:24:47 +01003749 die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
Simon Kelleyde379512004-06-22 20:23:33 +01003750
3751 while ((line = fgets(buff, MAXDNAME, f)))
3752 {
3753 char *token = strtok(line, " \t\n\r");
3754
3755 if (!token || strcmp(token, "search") != 0)
3756 continue;
3757
3758 if ((token = strtok(NULL, " \t\n\r")) &&
Simon Kelley1f15b812009-10-13 17:49:32 +01003759 (daemon->domain_suffix = canonicalise_opt(token)))
Simon Kelleyde379512004-06-22 20:23:33 +01003760 break;
3761 }
Simon Kelley3be34542004-09-11 19:12:13 +01003762
Simon Kelleyde379512004-06-22 20:23:33 +01003763 fclose(f);
Simon Kelley8a911cc2004-03-16 18:35:52 +00003764
Simon Kelley3be34542004-09-11 19:12:13 +01003765 if (!daemon->domain_suffix)
Simon Kelley5aabfc72007-08-29 11:24:47 +01003766 die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
Simon Kelleyde379512004-06-22 20:23:33 +01003767 }
Simon Kelley3d8df262005-08-29 12:19:27 +01003768
3769 if (daemon->domain_suffix)
3770 {
3771 /* add domain for any srv record without one. */
3772 struct mx_srv_record *srv;
Simon Kelleyde379512004-06-22 20:23:33 +01003773
Simon Kelley3d8df262005-08-29 12:19:27 +01003774 for (srv = daemon->mxnames; srv; srv = srv->next)
3775 if (srv->issrv &&
3776 strchr(srv->name, '.') &&
3777 strchr(srv->name, '.') == strrchr(srv->name, '.'))
3778 {
3779 strcpy(buff, srv->name);
3780 strcat(buff, ".");
3781 strcat(buff, daemon->domain_suffix);
3782 free(srv->name);
Simon Kelley824af852008-02-12 20:43:05 +00003783 srv->name = opt_string_alloc(buff);
Simon Kelley3d8df262005-08-29 12:19:27 +01003784 }
3785 }
Simon Kelley28866e92011-02-14 20:19:14 +00003786 else if (option_bool(OPT_DHCP_FQDN))
Simon Kelley9009d742008-11-14 20:04:27 +00003787 die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
Simon Kelley7622fc02009-06-04 20:32:05 +01003788
3789 if (testmode)
3790 {
3791 fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
3792 exit(0);
3793 }
Simon Kelley849a8352006-06-09 21:02:31 +01003794}