]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/option.c
import of dnsmasq-2.34.tar.gz
[people/ms/dnsmasq.git] / src / option.c
CommitLineData
cdeda28f 1/* dnsmasq is Copyright (c) 2000 - 2006 Simon Kelley
9e4abcb5
SK
2
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
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
849a8357
SK
13/* define this to get facilitynames */
14#define SYSLOG_NAMES
15
9e4abcb5
SK
16#include "dnsmasq.h"
17
849a8357 18#ifndef HAVE_GETOPT_LONG
9e4abcb5
SK
19struct myoption {
20 const char *name;
21 int has_arg;
22 int *flag;
23 int val;
24};
849a8357 25#endif
9e4abcb5 26
208b65c5 27#define OPTSTRING "9531yZDNLERKzowefnbvhdkqr: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:"
9e4abcb5 28
1697269c
SK
29/* options which don't have a one-char version */
30#define LOPT_RELOAD 256
31
32
849a8357
SK
33#ifdef HAVE_GETOPT_LONG
34static const struct option opts[] =
35#else
36static const struct myoption opts[] =
37#endif
38 {
39 {"version", 0, 0, 'v'},
40 {"no-hosts", 0, 0, 'h'},
41 {"no-poll", 0, 0, 'n'},
42 {"help", 0, 0, 'w'},
43 {"no-daemon", 0, 0, 'd'},
44 {"log-queries", 0, 0, 'q'},
45 {"user", 1, 0, 'u'},
46 {"group", 1, 0, 'g'},
47 {"resolv-file", 1, 0, 'r'},
48 {"mx-host", 1, 0, 'm'},
49 {"mx-target", 1, 0, 't'},
50 {"cache-size", 1, 0, 'c'},
51 {"port", 1, 0, 'p'},
52 {"dhcp-leasefile", 1, 0, 'l'},
53 {"dhcp-lease", 1, 0, 'l' },
54 {"dhcp-host", 1, 0, 'G'},
55 {"dhcp-range", 1, 0, 'F'},
56 {"dhcp-option", 1, 0, 'O'},
57 {"dhcp-boot", 1, 0, 'M'},
58 {"domain", 1, 0, 's'},
59 {"domain-suffix", 1, 0, 's'},
60 {"interface", 1, 0, 'i'},
61 {"listen-address", 1, 0, 'a'},
62 {"bogus-priv", 0, 0, 'b'},
63 {"bogus-nxdomain", 1, 0, 'B'},
64 {"selfmx", 0, 0, 'e'},
65 {"filterwin2k", 0, 0, 'f'},
66 {"pid-file", 1, 0, 'x'},
67 {"strict-order", 0, 0, 'o'},
68 {"server", 1, 0, 'S'},
69 {"local", 1, 0, 'S' },
70 {"address", 1, 0, 'A' },
71 {"conf-file", 1, 0, 'C'},
72 {"no-resolv", 0, 0, 'R'},
73 {"expand-hosts", 0, 0, 'E'},
74 {"localmx", 0, 0, 'L'},
75 {"local-ttl", 1, 0, 'T'},
76 {"no-negcache", 0, 0, 'N'},
77 {"addn-hosts", 1, 0, 'H'},
78 {"query-port", 1, 0, 'Q'},
79 {"except-interface", 1, 0, 'I'},
80 {"no-dhcp-interface", 1, 0, '2'},
81 {"domain-needed", 0, 0, 'D'},
82 {"dhcp-lease-max", 1, 0, 'X' },
83 {"bind-interfaces", 0, 0, 'z'},
84 {"read-ethers", 0, 0, 'Z' },
85 {"alias", 1, 0, 'V' },
86 {"dhcp-vendorclass", 1, 0, 'U'},
87 {"dhcp-userclass", 1, 0, 'j'},
88 {"dhcp-ignore", 1, 0, 'J'},
89 {"edns-packet-max", 1, 0, 'P'},
90 {"keep-in-foreground", 0, 0, 'k'},
91 {"dhcp-authoritative", 0, 0, 'K'},
92 {"srv-host", 1, 0, 'W'},
93 {"localise-queries", 0, 0, 'y'},
94 {"txt-record", 1, 0, 'Y'},
95 {"enable-dbus", 0, 0, '1'},
96 {"bootp-dynamic", 0, 0, '3'},
97 {"dhcp-mac", 1, 0, '4'},
98 {"no-ping", 0, 0, '5'},
99 {"dhcp-script", 1, 0, '6'},
100 {"conf-dir", 1, 0, '7'},
101 {"log-facility", 1, 0 ,'8'},
208b65c5
SK
102 {"leasefile-ro", 0, 0, '9'},
103 {"dns-forward-max", 1, 0, '0'},
1697269c 104 {"clear-on-reload", 0, 0, LOPT_RELOAD },
849a8357
SK
105 { NULL, 0, 0, 0 }
106 };
9e4abcb5
SK
107
108struct optflags {
1697269c 109 int c;
9e4abcb5
SK
110 unsigned int flag;
111};
112
3d8df260 113static const struct optflags optmap[] = {
1697269c
SK
114 { 'b', OPT_BOGUSPRIV },
115 { 'f', OPT_FILTER },
116 { 'q', OPT_LOG },
117 { 'e', OPT_SELFMX },
118 { 'h', OPT_NO_HOSTS },
119 { 'n', OPT_NO_POLL },
120 { 'd', OPT_DEBUG },
121 { 'k', OPT_NO_FORK },
122 { 'K', OPT_AUTHORITATIVE },
123 { 'o', OPT_ORDER },
124 { 'R', OPT_NO_RESOLV },
125 { 'E', OPT_EXPAND },
126 { 'L', OPT_LOCALMX },
127 { 'N', OPT_NO_NEG },
128 { 'D', OPT_NODOTS_LOCAL },
129 { 'z', OPT_NOWILD },
130 { 'Z', OPT_ETHERS },
131 { 'y', OPT_LOCALISE },
132 { '1', OPT_DBUS },
133 { '3', OPT_BOOTP_DYNAMIC },
134 { '5', OPT_NO_PING },
135 { '9', OPT_LEASE_RO },
136 { LOPT_RELOAD, OPT_RELOAD },
137 { 'v', 0},
138 { 'w', 0},
9e4abcb5
SK
139 { 0, 0 }
140};
141
b8187c80
SK
142static const struct {
143 char * const flag;
144 char * const desc;
145 char * const arg;
146} usage[] = {
147 { "-a, --listen-address=ipaddr", gettext_noop("Specify local address(es) to listen on."), NULL },
148 { "-A, --address=/domain/ipaddr", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
149 { "-b, --bogus-priv", gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
150 { "-B, --bogus-nxdomain=ipaddr", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL },
151 { "-c, --cache-size=cachesize", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
152 { "-C, --conf-file=path", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
153 { "-d, --no-daemon", gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
154 { "-D, --domain-needed", gettext_noop("Do NOT forward queries with no domain part."), NULL },
155 { "-e, --selfmx", gettext_noop("Return self-pointing MX records for local hosts."), NULL },
156 { "-E, --expand-hosts", gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
157 { "-f, --filterwin2k", gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
158 { "-F, --dhcp-range=ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
159 { "-g, --group=groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
160 { "-G, --dhcp-host=<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
161 { "-h, --no-hosts", gettext_noop("Do NOT load %s file."), HOSTSFILE },
162 { "-H, --addn-hosts=path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
163 { "-i, --interface=interface", gettext_noop("Specify interface(s) to listen on."), NULL },
164 { "-I, --except-interface=int", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
165 { "-j, --dhcp-userclass=<id>,<class>", gettext_noop("Map DHCP user class to option set."), NULL },
166 { "-J, --dhcp-ignore=<id>", gettext_noop("Don't do DHCP for hosts in option set."), NULL },
167 { "-k, --keep-in-foreground", gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
168 { "-K, --dhcp-authoritative", gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
169 { "-l, --dhcp-leasefile=path", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
170 { "-L, --localmx", gettext_noop("Return MX records for local hosts."), NULL },
171 { "-m, --mx-host=host_name,target,pref", gettext_noop("Specify an MX record."), NULL },
172 { "-M, --dhcp-boot=<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
173 { "-n, --no-poll", gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
174 { "-N, --no-negcache", gettext_noop("Do NOT cache failed search results."), NULL },
175 { "-o, --strict-order", gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
176 { "-O, --dhcp-option=<optspec>", gettext_noop("Set extra options to be set to DHCP clients."), NULL },
177 { "-p, --port=number", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
178 { "-P, --edns-packet-max=<size>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
179 { "-q, --log-queries", gettext_noop("Log queries."), NULL },
180 { "-Q, --query-port=number", gettext_noop("Force the originating port for upstream queries."), NULL },
181 { "-R, --no-resolv", gettext_noop("Do NOT read resolv.conf."), NULL },
182 { "-r, --resolv-file=path", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
183 { "-S, --server=/domain/ipaddr", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
184 { " --local=/domain/", gettext_noop("Never forward queries to specified domains."), NULL },
185 { "-s, --domain=domain", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
186 { "-t, --mx-target=host_name", gettext_noop("Specify default target in an MX record."), NULL },
187 { "-T, --local-ttl=time", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
188 { "-u, --user=username", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
189 { "-U, --dhcp-vendorclass=<id>,<class>", gettext_noop("Map DHCP vendor class to option set."), NULL },
190 { "-v, --version", gettext_noop("Display dnsmasq version and copyright information."), NULL },
191 { "-V, --alias=addr,addr,mask", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
192 { "-W, --srv-host=name,target,...", gettext_noop("Specify a SRV record."), NULL },
193 { "-w, --help", gettext_noop("Display this message."), NULL },
194 { "-x, --pid-file=path", gettext_noop("Specify path of PID file. (defaults to %s)."), RUNFILE },
195 { "-X, --dhcp-lease-max=number", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
196 { "-y, --localise-queries", gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
197 { "-Y --txt-record=name,txt....", gettext_noop("Specify TXT DNS record."), NULL },
198 { "-z, --bind-interfaces", gettext_noop("Bind only to interfaces in use."), NULL },
199 { "-Z, --read-ethers", gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
200 { "-1, --enable-dbus", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
201 { "-2, --no-dhcp-interface=interface", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
202 { "-3, --bootp-dynamic", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
cdeda28f 203 { "-4, --dhcp-mac=<id>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
5e9e0efb 204 { "-5, --no-ping", gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
7cebd20f 205 { "-6, --dhcp-script=path", gettext_noop("Script to run on DHCP lease creation and destruction."), NULL },
849a8357 206 { "-7, --conf-dir=path", gettext_noop("Read configuration from all the files in this directory."), NULL },
208b65c5
SK
207 { "-8, --log-facility=facilty", gettext_noop("Log to this syslog facility. (defaults to DAEMON)"), NULL },
208 { "-9, --leasefile-ro", gettext_noop("Read leases at startup, but never write the lease file."), NULL },
209 { "-0, --dns-forward-max=<queries>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
1697269c 210 { " --clear-on-reload", gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
b8187c80
SK
211 { NULL, NULL, NULL }
212};
9e4abcb5 213
3d8df260
SK
214/* We hide metacharaters in quoted strings by mapping them into the ASCII control
215 character space. Note that the \0, \t \a \b \r and \n characters are carefully placed in the
216 following sequence so that they map to themselves: it is therefore possible to call
217 unhide_metas repeatedly on string without breaking things.
218 The transformation gets undone by opt_canonicalise, atoi_check and safe_string_alloc, and a
219 couple of other places. */
220
b8187c80 221static const char meta[] = "\000123456\a\b\t\n78\r90abcdefABCDEF:,.";
3d8df260 222
849a8357
SK
223static void one_file(struct daemon *daemon, char *file, int nest);
224
3d8df260
SK
225static char hide_meta(char c)
226{
227 unsigned int i;
228
229 for (i = 0; i < (sizeof(meta) - 1); i++)
230 if (c == meta[i])
231 return (char)i;
232
233 return c;
234}
235
236static char unhide_meta(char cr)
237{
238 unsigned int c = cr;
239
240 if (c < (sizeof(meta) - 1))
241 cr = meta[c];
242
243 return cr;
244}
245
246static void unhide_metas(char *cp)
247{
248 if (cp)
249 for(; *cp; cp++)
250 *cp = unhide_meta(*cp);
251}
252
253static char *safe_string_alloc(char *cp)
254{
255 char *ret = NULL;
256
257 if (cp && strlen(cp) != 0)
258 {
259 ret = safe_malloc(strlen(cp)+1);
260 strcpy(ret, cp);
261
262 /* restore hidden metachars */
263 unhide_metas(ret);
264 }
265
266 return ret;
267}
268
269static char *safe_strchr(char *s, int c)
270{
271 if (!s)
272 return NULL;
273
274 return strchr(s, c);
275}
276
277static int canonicalise_opt(char *s)
278{
279 if (!s)
280 return 0;
281
282 unhide_metas(s);
283 return canonicalise(s);
284}
285
286static int atoi_check(char *a, int *res)
287{
288 char *p;
289
290 if (!a)
291 return 0;
292
293 unhide_metas(a);
294
295 for (p = a; *p; p++)
296 if (*p < '0' || *p > '9')
297 return 0;
298
299 *res = atoi(a);
300 return 1;
301}
302
0a852541
SK
303static void add_txt(struct daemon *daemon, char *name, char *txt)
304{
305 size_t len = strlen(txt);
306 struct txt_record *r = safe_malloc(sizeof(struct txt_record));
307
308 r->name = safe_string_alloc(name);
309 r->next = daemon->txt;
310 daemon->txt = r;
311 r->class = C_CHAOS;
312 r->txt = safe_malloc(len+1);
313 r->len = len+1;
314 *(r->txt) = len;
315 memcpy((r->txt)+1, txt, len);
316}
9e4abcb5 317
849a8357 318static void do_usage(void)
9e4abcb5 319{
849a8357
SK
320 char buff[100];
321 int i;
0a852541 322
849a8357
SK
323 printf(_("Usage: dnsmasq [options]\n\n"));
324#ifndef HAVE_GETOPT_LONG
325 printf(_("Use short options only on the command line.\n"));
326#endif
327 printf(_("Valid options are :\n"));
3be34541 328
849a8357 329 for (i = 0; usage[i].flag; i++)
9e4abcb5 330 {
849a8357 331 if (usage[i].arg)
0a852541 332 {
849a8357
SK
333 if (strcmp(usage[i].arg, "$") == 0)
334 sprintf(buff, "%d", CACHESIZ);
335 else if (strcmp(usage[i].arg, "*") == 0)
336 sprintf(buff, "%d", EDNS_PKTSZ);
337 else if (strcmp(usage[i].arg, "&") == 0)
338 sprintf(buff, "%d", MAXLEASES);
208b65c5
SK
339 else if (strcmp(usage[i].arg, "!") == 0)
340 sprintf(buff, "%d", FTABSIZ);
849a8357
SK
341 else
342 strcpy(buff, usage[i].arg);
0a852541 343 }
849a8357
SK
344 printf("%-36.36s", usage[i].flag);
345 printf(_(usage[i].desc), buff);
346 printf("\n");
347 }
348}
0a852541 349
849a8357
SK
350static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem, int nest)
351{
352 int i;
353 char *comma;
b8187c80 354
849a8357
SK
355 if(option == '?')
356 return problem;
9e4abcb5 357
849a8357
SK
358 for (i=0; optmap[i].c; i++)
359 if (option == optmap[i].c)
360 {
361 daemon->options |= optmap[i].flag;
362 return arg ? _("extraneous parameter") : NULL;
363 }
364
365 if (!arg)
366 return _("missing parameter");
367
368 switch (option)
369 {
370 case 'C':
371 {
372 char *file = safe_string_alloc(arg);
373 if (file)
374 one_file(daemon, file, nest);
375 break;
376 }
377
378 case '7':
379 {
380 DIR *dir_stream;
381 struct dirent *ent;
382 char *directory, *path;
383
384 if (!(directory = safe_string_alloc(arg)))
385 break;
386
387 if (!(dir_stream = opendir(directory)))
388 die(_("cannot access directory %s: %s"), directory);
389
390 while ((ent = readdir(dir_stream)))
9e4abcb5 391 {
849a8357
SK
392 size_t len;
393 struct stat buf;
394
395 if ((len = strlen(ent->d_name)) == 0)
396 continue;
397 /* ignore emacs backups and dotfiles */
398 if (ent->d_name[len - 1] == '~' ||
399 (ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
400 ent->d_name[0] == '.')
401 continue;
402 path = safe_malloc(strlen(directory) + len + 2);
403 strcpy(path, directory);
404 strcat(path, "/");
405 strcat(path, ent->d_name);
406 if (stat(path, &buf) == -1)
407 die(_("cannot access %s: %s"), path);
408 /* only reg files allowed. */
409 if (!S_ISREG(buf.st_mode))
410 continue;
411
412 /* dir is one level, so files must be readable */
413 one_file(daemon, path, nest + 1);
414 free(path);
9e4abcb5 415 }
849a8357
SK
416
417 closedir(dir_stream);
418 break;
419 }
420
421 case '8':
422 for (i = 0; facilitynames[i].c_name; i++)
423 if (hostname_isequal((char *)facilitynames[i].c_name, arg))
424 break;
9e4abcb5 425
849a8357
SK
426 if (facilitynames[i].c_name)
427 daemon->log_fac = facilitynames[i].c_val;
428 else
9e4abcb5 429 {
849a8357
SK
430 option = '?';
431 problem = "bad log facility";
432 }
433 break;
434
435 case 'x':
436 daemon->runfile = safe_string_alloc(arg);
437 break;
438
439 case 'r':
440 {
441 char *name = safe_string_alloc(arg);
442 struct resolvc *new, *list = daemon->resolv_files;
443
444 if (list && list->is_default)
445 {
446 /* replace default resolv file - possibly with nothing */
447 if (name)
9e4abcb5 448 {
849a8357
SK
449 list->is_default = 0;
450 list->name = name;
9e4abcb5 451 }
849a8357
SK
452 else
453 list = NULL;
454 }
455 else if (name)
456 {
457 new = safe_malloc(sizeof(struct resolvc));
458 new->next = list;
459 new->name = name;
460 new->is_default = 0;
461 new->mtime = 0;
462 new->logged = 0;
463 list = new;
464 }
465 daemon->resolv_files = list;
466 break;
467 }
468
469 case 'm':
470 {
471 int pref = 1;
472 struct mx_srv_record *new;
473
474 if ((comma = safe_strchr(arg, ',')))
475 {
476 char *prefstr;
477 *(comma++) = 0;
478 if ((prefstr=strchr(comma, ',')))
de37951c 479 {
849a8357
SK
480 *(prefstr++) = 0;
481 if (!atoi_check(prefstr, &pref))
36717eee
SK
482 {
483 option = '?';
849a8357 484 problem = _("bad MX preference");
f6b7dc47 485 break;
36717eee 486 }
de37951c 487 }
849a8357
SK
488 }
489
490 if (!canonicalise_opt(arg) || (comma && !canonicalise_opt(comma)))
491 {
492 option = '?';
493 problem = _("bad MX name");
494 break;
495 }
496
497 new = safe_malloc(sizeof(struct mx_srv_record));
498 new->next = daemon->mxnames;
499 daemon->mxnames = new;
500 new->issrv = 0;
501 new->name = safe_string_alloc(arg);
502 new->target = safe_string_alloc(comma); /* may be NULL */
503 new->weight = pref;
504 break;
505 }
506
507 case 't':
508 if (!canonicalise_opt(arg))
509 {
510 option = '?';
511 problem = _("bad MX target");
512 }
513 else
514 daemon->mxtarget = safe_string_alloc(arg);
515 break;
516
517 case 'l':
518 daemon->lease_file = safe_string_alloc(arg);
519 break;
520
521 case '6':
7cebd20f 522#ifdef NO_FORK
849a8357
SK
523 problem = _("cannot run scripts under uClinux");
524 option = '?';
7cebd20f 525#else
849a8357 526 daemon->lease_change_command = safe_string_alloc(arg);
7cebd20f 527#endif
849a8357
SK
528 break;
529
530 case 'H':
531 {
532 struct hostsfile *new = safe_malloc(sizeof(struct hostsfile));
533 static int hosts_index = 1;
534 new->fname = safe_string_alloc(arg);
535 new->index = hosts_index++;
536 new->next = daemon->addn_hosts;
537 daemon->addn_hosts = new;
538 break;
539 }
540
541 case 's':
542 if (strcmp (arg, "#") == 0)
543 daemon->options |= OPT_RESOLV_DOMAIN;
544 else if (!canonicalise_opt(arg))
545 option = '?';
546 else
547 daemon->domain_suffix = safe_string_alloc(arg);
548 break;
549
550 case 'u':
551 daemon->username = safe_string_alloc(arg);
552 break;
553
554 case 'g':
555 daemon->groupname = safe_string_alloc(arg);
556 break;
557
558 case 'i':
559 do {
560 struct iname *new = safe_malloc(sizeof(struct iname));
561 if ((comma = safe_strchr(arg, ',')))
562 *comma++ = 0;
563 new->next = daemon->if_names;
564 daemon->if_names = new;
565 /* new->name may be NULL if someone does
566 "interface=" to disable all interfaces except loop. */
567 new->name = safe_string_alloc(arg);
568 new->isloop = new->used = 0;
569 arg = comma;
570 } while (arg);
571 break;
572
573 case 'I':
574 case '2':
575 do {
576 struct iname *new = safe_malloc(sizeof(struct iname));
577 if ((comma = safe_strchr(arg, ',')))
578 *comma++ = 0;
579 new->name = safe_string_alloc(arg);
580 if (option == 'I')
581 {
582 new->next = daemon->if_except;
583 daemon->if_except = new;
584 }
585 else
586 {
587 new->next = daemon->dhcp_except;
588 daemon->dhcp_except = new;
589 }
590 arg = comma;
591 } while (arg);
592 break;
593
594 case 'B':
595 {
596 struct in_addr addr;
597 unhide_metas(arg);
598 if (arg && (addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
599 {
600 struct bogus_addr *baddr = safe_malloc(sizeof(struct bogus_addr));
601 baddr->next = daemon->bogus_addr;
602 daemon->bogus_addr = baddr;
603 baddr->addr = addr;
604 }
605 else
606 option = '?'; /* error */
607 break;
608 }
609
610 case 'a':
611 do {
612 struct iname *new = safe_malloc(sizeof(struct iname));
613 if ((comma = safe_strchr(arg, ',')))
614 *comma++ = 0;
615 unhide_metas(arg);
616 new->next = daemon->if_addrs;
617 if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
618 {
619 new->addr.sa.sa_family = AF_INET;
620#ifdef HAVE_SOCKADDR_SA_LEN
621 new->addr.in.sin_len = sizeof(new->addr.in);
622#endif
623 }
624#ifdef HAVE_IPV6
625 else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
626 {
627 new->addr.sa.sa_family = AF_INET6;
628 new->addr.in6.sin6_flowinfo = 0;
629 new->addr.in6.sin6_scope_id = 0;
630#ifdef HAVE_SOCKADDR_SA_LEN
631 new->addr.in6.sin6_len = sizeof(new->addr.in6);
632#endif
633 }
634#endif
635 else
636 {
637 option = '?'; /* error */
638 free(new);
639 break;
640 }
641
642 daemon->if_addrs = new;
643 arg = comma;
644 } while (arg);
645 break;
646
647 case 'S':
648 case 'A':
649 {
650 struct server *serv, *newlist = NULL;
651
652 unhide_metas(arg);
653
654 if (arg && *arg == '/')
655 {
656 char *end;
657 arg++;
658 while ((end = strchr(arg, '/')))
fd9fa481 659 {
849a8357
SK
660 char *domain = NULL;
661 *end = 0;
662 /* # matches everything and becomes a zero length domain string */
663 if (strcmp(arg, "#") == 0)
664 domain = "";
665 else if (!canonicalise_opt(arg) && strlen(arg) != 0)
666 option = '?';
667 else
668 domain = safe_string_alloc(arg); /* NULL if strlen is zero */
669 serv = safe_malloc(sizeof(struct server));
670 serv->next = newlist;
671 newlist = serv;
672 serv->sfd = NULL;
673 serv->domain = domain;
674 serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
675 memset(&serv->addr, 0, sizeof(serv->addr));
676 memset(&serv->source_addr, 0, sizeof(serv->source_addr));
677 arg = end+1;
fd9fa481 678 }
849a8357
SK
679 if (!newlist)
680 {
9e4abcb5 681 option = '?';
849a8357
SK
682 break;
683 }
26128d27 684
849a8357
SK
685 }
686 else
687 {
688 newlist = safe_malloc(sizeof(struct server));
689 newlist->next = NULL;
690 newlist->flags = 0;
691 newlist->sfd = NULL;
692 newlist->domain = NULL;
693 }
694
695 if (option == 'A')
696 {
697 newlist->flags |= SERV_LITERAL_ADDRESS;
698 if (!(newlist->flags & SERV_TYPE))
699 option = '?';
700 }
701
702 if (!arg || !*arg)
703 {
704 newlist->flags |= SERV_NO_ADDR; /* no server */
705 if (newlist->flags & SERV_LITERAL_ADDRESS)
706 option = '?';
707 }
708 else
709 {
710 int source_port = 0, serv_port = NAMESERVER_PORT;
711 char *portno, *source;
712
713 if ((source = strchr(arg, '@'))) /* is there a source. */
714 {
715 *source = 0;
716 if ((portno = strchr(source+1, '#')))
717 {
718 *portno = 0;
719 if (!atoi_check(portno+1, &source_port))
720 {
721 option = '?';
722 problem = _("bad port");
723 }
3d8df260 724 }
849a8357
SK
725 }
726
727 if ((portno = strchr(arg, '#'))) /* is there a port no. */
9e4abcb5 728 {
849a8357
SK
729 *portno = 0;
730 if (!atoi_check(portno+1, &serv_port))
9e4abcb5 731 {
849a8357
SK
732 option = '?';
733 problem = _("bad port");
9e4abcb5 734 }
9e4abcb5 735 }
849a8357
SK
736
737 if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
738 {
739 newlist->addr.in.sin_port = htons(serv_port);
740 newlist->source_addr.in.sin_port = htons(source_port);
741 newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
9e4abcb5 742#ifdef HAVE_SOCKADDR_SA_LEN
849a8357 743 newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
9e4abcb5 744#endif
849a8357
SK
745 if (source)
746 {
747 if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source+1)) != (in_addr_t) -1)
748 newlist->flags |= SERV_HAS_SOURCE;
749 else
750 option = '?'; /* error */
9e4abcb5 751 }
849a8357
SK
752 else
753 newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
754 }
3d8df260 755#ifdef HAVE_IPV6
849a8357
SK
756 else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
757 {
758 newlist->addr.in6.sin6_port = htons(serv_port);
759 newlist->source_addr.in6.sin6_port = htons(source_port);
760 newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
9e4abcb5 761#ifdef HAVE_SOCKADDR_SA_LEN
849a8357 762 newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
9e4abcb5 763#endif
849a8357 764 if (source)
9e4abcb5 765 {
849a8357
SK
766 if (inet_pton(AF_INET6, source+1, &newlist->source_addr.in6.sin6_addr) > 0)
767 newlist->flags |= SERV_HAS_SOURCE;
768 else
769 option = '?'; /* error */
9e4abcb5
SK
770 }
771 else
849a8357
SK
772 newlist->source_addr.in6.sin6_addr = in6addr_any;
773 }
774#endif
775 else
776 option = '?'; /* error */
777
778 }
779
780 if (option == '?')
781 while (newlist)
782 {
783 serv = newlist;
784 newlist = newlist->next;
785 free(serv);
786 }
787 else
788 {
789 serv = newlist;
790 while (serv->next)
791 {
792 serv->next->flags = serv->flags;
793 serv->next->addr = serv->addr;
794 serv->next->source_addr = serv->source_addr;
795 serv = serv->next;
796 }
797 serv->next = daemon->servers;
798 daemon->servers = newlist;
799 }
800 break;
801 }
802
803 case 'c':
804 {
805 int size;
806
807 if (!atoi_check(arg, &size))
808 option = '?';
809 else
810 {
811 /* zero is OK, and means no caching. */
812
813 if (size < 0)
814 size = 0;
815 else if (size > 10000)
816 size = 10000;
817
818 daemon->cachesize = size;
819 }
820 break;
821 }
822
823 case 'p':
824 if (!atoi_check(arg, &daemon->port))
825 option = '?';
826 break;
208b65c5
SK
827
828 case '0':
829 if (!atoi_check(arg, &daemon->ftabsize))
830 option = '?';
831 break;
832
849a8357
SK
833 case 'P':
834 {
835 int i;
836 if (!atoi_check(arg, &i))
837 option = '?';
838 daemon->edns_pktsz = (unsigned short)i;
839 break;
840 }
841
842 case 'Q':
843 if (!atoi_check(arg, &daemon->query_port))
844 option = '?';
845 break;
846
847 case 'T':
848 {
849 int ttl;
850 if (!atoi_check(arg, &ttl))
851 option = '?';
852 else
853 daemon->local_ttl = (unsigned long)ttl;
854 break;
855 }
856
857 case 'X':
858 if (!atoi_check(arg, &daemon->dhcp_max))
859 option = '?';
860 break;
861
862 case 'F':
863 {
864 int k, leasepos = 2;
865 char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL };
866 struct dhcp_context *new = safe_malloc(sizeof(struct dhcp_context));
867
868 new->next = daemon->dhcp;
869 new->lease_time = DEFLEASE;
870 new->addr_epoch = 0;
871 new->netmask.s_addr = 0;
872 new->broadcast.s_addr = 0;
873 new->router.s_addr = 0;
874 new->netid.net = NULL;
875 new->filter = NULL;
876 new->flags = 0;
877
878 problem = _("bad dhcp-range");
879
880 if (!arg)
881 {
882 option = '?';
883 break;
884 }
885
886 while(1)
887 {
888 for (cp = arg; *cp; cp++)
889 if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9')))
890 break;
891
892 if (*cp != ',' && (comma = strchr(arg, ',')))
893 {
894 *comma = 0;
895 if (strstr(arg, "net:") == arg)
9e4abcb5 896 {
849a8357
SK
897 struct dhcp_netid *tt = safe_malloc(sizeof (struct dhcp_netid));
898 tt->net = safe_string_alloc(arg+4);
899 tt->next = new->filter;
900 new->filter = tt;
9e4abcb5
SK
901 }
902 else
903 {
849a8357 904 if (new->netid.net)
9e4abcb5 905 {
849a8357
SK
906 option = '?';
907 problem = _("only one netid tag allowed");
9e4abcb5 908 }
9e4abcb5 909 else
849a8357 910 new->netid.net = safe_string_alloc(arg);
9e4abcb5 911 }
849a8357 912 arg = comma + 1;
9e4abcb5 913 }
849a8357 914 else
9e4abcb5 915 {
849a8357 916 a[0] = arg;
9e4abcb5
SK
917 break;
918 }
849a8357
SK
919 }
920
921 for (k = 1; k < 5; k++)
922 {
923 if (!(a[k] = strchr(a[k-1], ',')))
9e4abcb5 924 break;
849a8357
SK
925 *(a[k]++) = 0;
926 }
927
928 if (option == '?' || (k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1))
929 option = '?';
930 else if (strcmp(a[1], "static") == 0)
931 {
932 new->end = new->start;
933 new->flags |= CONTEXT_STATIC;
934 }
935 else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
936 option = '?';
937
938 if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
939 {
940 struct in_addr tmp = new->start;
941 new->start = new->end;
942 new->end = tmp;
943 }
944
945 if (option != '?' && k >= 3 && strchr(a[2], '.') &&
946 ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
947 {
948 new->flags |= CONTEXT_NETMASK;
949 leasepos = 3;
950 if (!is_same_net(new->start, new->end, new->netmask))
feba5c1d 951 {
849a8357 952 problem = _("inconsistent DHCP range");
a222641c 953 option = '?';
a222641c 954 }
849a8357
SK
955 }
956
957 if (option == '?')
958 {
959 free(new);
960 break;
961 }
962 else
963 daemon->dhcp = new;
964
965 if (k >= 4 && strchr(a[3], '.') &&
966 ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
967 {
968 new->flags |= CONTEXT_BRDCAST;
969 leasepos = 4;
970 }
971
972 if (k >= leasepos+1)
973 {
974 if (strcmp(a[leasepos], "infinite") == 0)
975 new->lease_time = 0xffffffff;
976 else
9e4abcb5 977 {
849a8357
SK
978 int fac = 1;
979 if (strlen(a[leasepos]) > 0)
36717eee 980 {
849a8357 981 switch (a[leasepos][strlen(a[leasepos]) - 1])
36717eee 982 {
849a8357
SK
983 case 'd':
984 case 'D':
985 fac *= 24;
986 /* fall though */
987 case 'h':
988 case 'H':
989 fac *= 60;
990 /* fall through */
991 case 'm':
992 case 'M':
993 fac *= 60;
994 /* fall through */
995 case 's':
996 case 'S':
44a2a316 997 a[leasepos][strlen(a[leasepos]) - 1] = 0;
9e4abcb5 998 }
849a8357
SK
999
1000 new->lease_time = atoi(a[leasepos]) * fac;
1001 /* Leases of a minute or less confuse
1002 some clients, notably Apple's */
1003 if (new->lease_time < 120)
1004 new->lease_time = 120;
9e4abcb5 1005 }
9e4abcb5 1006 }
849a8357
SK
1007 }
1008 break;
1009 }
1010
1011 case 'G':
1012 {
1013 int j, k;
1014 char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
1015 struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config));
1016 struct in_addr in;
1017
1018 new->next = daemon->dhcp_conf;
1019 new->flags = 0;
1020
1021
1022 if ((a[0] = arg))
1023 for (k = 1; k < 6; k++)
1024 {
1025 if (!(a[k] = strchr(a[k-1], ',')))
1026 break;
1027 *(a[k]++) = 0;
1028 }
1029 else
1030 k = 0;
1031
1032 for (j = 0; j < k; j++)
1033 if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
1034 {
1035 char *arg = a[j];
1036
1037 if ((arg[0] == 'i' || arg[0] == 'I') &&
1038 (arg[1] == 'd' || arg[1] == 'D') &&
1039 arg[2] == ':')
1040 {
1041 if (arg[3] == '*')
1042 new->flags |= CONFIG_NOCLID;
1043 else
3d8df260 1044 {
849a8357
SK
1045 int len;
1046 arg += 3; /* dump id: */
1047 if (strchr(arg, ':'))
1048 len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
1049 else
1050 len = (int) strlen(arg);
1051
1052 new->flags |= CONFIG_CLID;
1053 new->clid_len = len;
1054 new->clid = safe_malloc(len);
1055 memcpy(new->clid, arg, len);
3d8df260 1056 }
849a8357
SK
1057 }
1058 else if (strstr(arg, "net:") == arg)
1059 {
1060 new->flags |= CONFIG_NETID;
1061 new->netid.net = safe_string_alloc(arg+4);
1062 }
1063 else
1064 {
1065 new->hwaddr_len = parse_hex(a[j], new->hwaddr, DHCP_CHADDR_MAX, &new->wildcard_mask, &new->hwaddr_type);
1066 new->flags |= CONFIG_HWADDR;
1067 }
1068 }
1069 else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
1070 {
1071 new->addr = in;
1072 new->flags |= CONFIG_ADDR;
1073 }
1074 else
1075 {
1076 char *cp, *lastp = NULL, last = 0;
1077 int fac = 1;
1078
1079 if (strlen(a[j]) > 1)
1080 {
1081 lastp = a[j] + strlen(a[j]) - 1;
1082 last = *lastp;
1083 switch (last)
9e4abcb5 1084 {
849a8357
SK
1085 case 'd':
1086 case 'D':
1087 fac *= 24;
1088 /* fall through */
1089 case 'h':
1090 case 'H':
1091 fac *= 60;
1092 /* fall through */
1093 case 'm':
1094 case 'M':
1095 fac *= 60;
1096 /* fall through */
1097 case 's':
1098 case 'S':
1099 *lastp = 0;
9e4abcb5 1100 }
849a8357
SK
1101 }
1102
1103 for (cp = a[j]; *cp; cp++)
1104 if (!isdigit(*cp) && *cp != ' ')
1105 break;
1106
1107 if (*cp)
1108 {
1109 if (lastp)
1110 *lastp = last;
1111 if (strcmp(a[j], "infinite") == 0)
33820b7e 1112 {
849a8357
SK
1113 new->lease_time = 0xffffffff;
1114 new->flags |= CONFIG_TIME;
33820b7e 1115 }
849a8357
SK
1116 else if (strcmp(a[j], "ignore") == 0)
1117 new->flags |= CONFIG_DISABLE;
9e4abcb5
SK
1118 else
1119 {
849a8357
SK
1120 new->hostname = safe_string_alloc(a[j]);
1121 new->flags |= CONFIG_NAME;
9e4abcb5 1122 }
849a8357
SK
1123 }
1124 else
1125 {
1126 new->lease_time = atoi(a[j]) * fac;
1127 /* Leases of a minute or less confuse
1128 some clients, notably Apple's */
1129 if (new->lease_time < 120)
1130 new->lease_time = 120;
1131 new->flags |= CONFIG_TIME;
1132 }
1133 }
1134
1135 if (option == '?')
1136 {
1137 problem = _("bad dhcp-host");
1138 if (new->flags & CONFIG_NAME)
1139 free(new->hostname);
1140 if (new->flags & CONFIG_CLID)
1141 free(new->clid);
1142 if (new->flags & CONFIG_NETID)
1143 free(new->netid.net);
1144 free(new);
1145 }
1146 else
1147 daemon->dhcp_conf = new;
1148
1149 break;
1150 }
1151
1152 case 'O':
1153 {
1154 struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
1155 char lenchar = 0, *cp;
1156 int addrs, digs, is_addr, is_hex, is_dec;
1157
1158 new->len = 0;
1159 new->flags = 0;
1160 new->netid = NULL;
1161 new->val = NULL;
1162 new->vendor_class = NULL;
1163
1164 if ((comma = safe_strchr(arg, ',')))
1165 {
1166 struct dhcp_netid *np = NULL;
1167 *comma++ = 0;
1168
1169 do {
1170 for (cp = arg; *cp; cp++)
1171 if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
1172 break;
1173 if (!*cp)
9e4abcb5 1174 break;
9e4abcb5 1175
849a8357
SK
1176 if (strstr(arg, "vendor:") == arg)
1177 new->vendor_class = (unsigned char *)safe_string_alloc(arg+7);
1178 else
1179 {
1180 new->netid = safe_malloc(sizeof (struct dhcp_netid));
1181 /* allow optional "net:" for consistency */
1182 if (strstr(arg, "net:") == arg)
1183 new->netid->net = safe_string_alloc(arg+4);
1184 else
1185 new->netid->net = safe_string_alloc(arg);
1186 new->netid->next = np;
1187 np = new->netid;
1188 }
1189 arg = comma;
1190 if ((comma = safe_strchr(arg, ',')))
1191 *comma++ = 0;
1192 } while (arg);
1193 }
1194
1195 if (!arg || (new->opt = atoi(arg)) == 0)
1196 {
1197 option = '?';
1198 problem = _("bad dhcp-option");
1199 }
1200 else if (comma && new->opt == 119 && !new->vendor_class)
1201 {
1202 /* dns search, RFC 3397 */
1203 unsigned char *q, *r, *tail;
1204 unsigned char *p = NULL;
1205 size_t newlen, len = 0;
1206
1207 arg = comma;
1208 if ((comma = safe_strchr(arg, ',')))
1209 *(comma++) = 0;
1210
1211 while (arg && *arg)
9e4abcb5 1212 {
849a8357 1213 if (!canonicalise_opt(arg))
44a2a316 1214 {
849a8357
SK
1215 option = '?';
1216 problem = _("bad domain in dhcp-option");
1217 break;
44a2a316 1218 }
9e4abcb5 1219
849a8357
SK
1220 if (!(p = realloc(p, len + strlen(arg) + 2)))
1221 die(_("could not get memory"), NULL);
1222 q = p + len;
1223
1224 /* add string on the end in RFC1035 format */
1225 while (*arg)
9e4abcb5 1226 {
849a8357
SK
1227 unsigned char *cp = q++;
1228 int j;
1229 for (j = 0; *arg && (*arg != '.'); arg++, j++)
1230 *q++ = *arg;
1231 *cp = j;
1232 if (*arg)
1233 arg++;
9e4abcb5 1234 }
849a8357 1235 *q++ = 0;
fd9fa481 1236
849a8357
SK
1237 /* Now tail-compress using earlier names. */
1238 newlen = q - p;
1239 for (tail = p + len; *tail; tail += (*tail) + 1)
1240 for (r = p; r - p < (int)len; r += (*r) + 1)
1241 if (strcmp((char *)r, (char *)tail) == 0)
33820b7e 1242 {
849a8357
SK
1243 PUTSHORT((r - p) | 0xc000, tail);
1244 newlen = tail - p;
1245 goto end;
fd9fa481 1246 }
849a8357
SK
1247 end:
1248 len = newlen;
1249
1250 arg = comma;
1251 if ((comma = safe_strchr(arg, ',')))
1252 *(comma++) = 0;
1253 }
1254
1255 new->len = (int) len;
1256 new->val = p;
1257 }
1258 else if (comma)
1259 {
1260 /* not option 119 */
1261 /* characterise the value */
1262 is_addr = is_hex = is_dec = 1;
1263 addrs = digs = 1;
1264 for (cp = comma; *cp; cp++)
1265 if (*cp == ',')
1266 {
1267 addrs++;
1268 is_dec = is_hex = 0;
1269 }
1270 else if (*cp == ':')
1271 {
1272 digs++;
1273 is_dec = is_addr = 0;
1274 }
1275 else if (*cp == '.' || *cp == '/')
1276 is_dec = is_hex = 0;
1277 else if (!((*cp >='0' && *cp <= '9') || *cp == '-'))
1278 {
1279 is_addr = 0;
1280 if (cp[1] == 0 && is_dec &&
1281 (*cp == 'b' || *cp == 's' || *cp == 'i'))
1282 {
1283 lenchar = *cp;
1284 *cp = 0;
1285 }
1286 else
1287 is_dec = 0;
1288 if (!((*cp >='A' && *cp <= 'F') ||
1289 (*cp >='a' && *cp <= 'f')))
1290 is_hex = 0;
1291 }
1292
1293 if (is_hex && digs > 1)
1294 {
1295 new->len = digs;
1296 new->val = safe_malloc(new->len);
1297 parse_hex(comma, new->val, digs, NULL, NULL);
9e4abcb5 1298 }
849a8357 1299 else if (is_dec)
9e4abcb5 1300 {
849a8357
SK
1301 int i, val = atoi(comma);
1302 /* assume numeric arg is 1 byte except for
1303 options where it is known otherwise.
1304 For vendor class option, we have to hack. */
1305 new->len = 1;
1306 if (lenchar == 'b')
1307 new->len = 1;
1308 else if (lenchar == 's')
1309 new->len = 2;
1310 else if (lenchar == 'i')
1311 new->len = 4;
1312 else if (new->vendor_class)
26128d27 1313 {
849a8357
SK
1314 if (val & 0xffff0000)
1315 new->len = 4;
1316 else if (val & 0xff00)
1317 new->len = 2;
1318 }
1319 else
1320 switch (new->opt)
1321 {
1322 case 13: case 22: case 25: case 26:
1323 new->len = 2;
1324 break;
1325 case 2: case 24: case 35: case 38:
1326 new->len = 4;
1327 break;
1328 }
1329 new->val = safe_malloc(new->len);
1330 for (i=0; i<new->len; i++)
1331 new->val[i] = val>>((new->len - i - 1)*8);
1332 }
1333 else if (is_addr)
1334 {
1335 struct in_addr in;
1336 unsigned char *op;
1337 char *slash;
1338 /* max length of address/subnet descriptor is five bytes */
1339 new->val = op = safe_malloc(5 * addrs);
1340 if (!new->vendor_class)
1341 new->flags |= DHOPT_ADDR;
1342 while (addrs--)
9e4abcb5 1343 {
849a8357
SK
1344 cp = comma;
1345 if ((comma = strchr(cp, ',')))
26128d27 1346 *comma++ = 0;
849a8357
SK
1347 if ((slash = strchr(cp, '/')))
1348 *slash++ = 0;
1349 in.s_addr = inet_addr(cp);
1350 if (!slash)
26128d27 1351 {
849a8357
SK
1352 memcpy(op, &in, INADDRSZ);
1353 op += INADDRSZ;
26128d27 1354 }
849a8357 1355 else
26128d27 1356 {
849a8357
SK
1357 unsigned char *p = (unsigned char *)&in;
1358 int netsize = atoi(slash);
1359 *op++ = netsize;
1360 if (netsize > 0)
1361 *op++ = *p++;
1362 if (netsize > 8)
1363 *op++ = *p++;
1364 if (netsize > 16)
1365 *op++ = *p++;
1366 if (netsize > 24)
1367 *op++ = *p++;
1368 new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
1369 }
9e4abcb5 1370 }
849a8357 1371 new->len = op - new->val;
9e4abcb5 1372 }
849a8357 1373 else
cdeda28f 1374 {
849a8357
SK
1375 /* text arg */
1376 new->len = strlen(comma);
1377 /* keep terminating zero on string */
1378 new->val = (unsigned char *)safe_string_alloc(comma);
1379 new->flags |= DHOPT_STRING;
cdeda28f 1380 }
849a8357
SK
1381 }
1382
1383 if (new->len > 255)
1384 {
1385 option = '?';
1386 problem = _("dhcp-option too long");
1387 }
1388
1389 if (option == '?')
1390 {
1391 if (new->netid)
1392 free(new->netid);
1393 if (new->val)
1394 free(new->val);
1395 if (new->vendor_class)
1396 free(new->vendor_class);
1397 free(new);
1398 }
1399 else if (new->vendor_class)
1400 {
1401 new->next = daemon->vendor_opts;
1402 daemon->vendor_opts = new;
1403 }
1404 else
1405 {
1406 new->next = daemon->dhcp_opts;
1407 daemon->dhcp_opts = new;
1408 }
1409 break;
1410 }
1411
1412 case 'M':
1413 {
1414 struct dhcp_netid *id = NULL;
1415 while (arg && strstr(arg, "net:") == arg)
1416 {
1417 struct dhcp_netid *newid = safe_malloc(sizeof(struct dhcp_netid));
1418 newid->next = id;
1419 id = newid;
1420 if ((comma = strchr(arg, ',')))
1421 *comma++ = 0;
1422 newid->net = safe_string_alloc(arg+4);
1423 arg = comma;
1424 };
1425
1426 if (!arg)
1427 option = '?';
1428 else
1429 {
1430 char *dhcp_file, *dhcp_sname = NULL;
1431 struct in_addr dhcp_next_server;
1432 if ((comma = strchr(arg, ',')))
1433 *comma++ = 0;
1434 dhcp_file = safe_string_alloc(arg);
1435 dhcp_next_server.s_addr = 0;
1436 if (comma)
a84fa1d0 1437 {
849a8357
SK
1438 arg = comma;
1439 if ((comma = strchr(arg, ',')))
1440 *comma++ = 0;
1441 dhcp_sname = safe_string_alloc(arg);
1442 if (comma)
a84fa1d0 1443 {
849a8357
SK
1444 unhide_metas(comma);
1445 if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
1446 option = '?';
a84fa1d0 1447 }
a84fa1d0 1448 }
849a8357 1449 if (option != '?')
26128d27 1450 {
849a8357
SK
1451 struct dhcp_boot *new = safe_malloc(sizeof(struct dhcp_boot));
1452 new->file = dhcp_file;
1453 new->sname = dhcp_sname;
1454 new->next_server = dhcp_next_server;
1455 new->netid = id;
1456 new->next = daemon->boot_config;
1457 daemon->boot_config = new;
26128d27 1458 }
849a8357
SK
1459 }
1460
1461 if (option == '?')
1462 {
1463 struct dhcp_netid *tmp;
1464 for (; id; id = tmp)
1cff166d 1465 {
849a8357
SK
1466 tmp = id->next;
1467 free(id);
1468 }
1469 }
1470 break;
1471 }
1472
1473 case '4':
1474 {
1475 if (!(comma = safe_strchr(arg, ',')))
1476 option = '?';
1477 else
1478 {
1479 struct dhcp_mac *new = safe_malloc(sizeof(struct dhcp_mac));
1480 *comma = 0;
1481 new->netid.net = safe_string_alloc(arg);
1482 unhide_metas(comma+1);
1483 new->hwaddr_len = parse_hex(comma+1, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
1484 new->next = daemon->dhcp_macs;
1485 daemon->dhcp_macs = new;
1486 }
1487 }
1488 break;
1489
1490 case 'U':
1491 case 'j':
1492 {
1493 if (!(comma = safe_strchr(arg, ',')))
1494 option = '?';
1495 else
1496 {
1497 struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor));
1498 *comma = 0;
1499 new->netid.net = safe_string_alloc(arg);
1500 unhide_metas(comma+1);
1501 new->len = strlen(comma+1);
1502 new->data = safe_malloc(new->len);
1503 memcpy(new->data, comma+1, new->len);
1504 new->is_vendor = (option == 'U');
1505 new->next = daemon->dhcp_vendors;
1506 daemon->dhcp_vendors = new;
1507 }
1508 break;
1509 }
1510
1511 case 'J':
1512 {
1513 struct dhcp_netid_list *new = safe_malloc(sizeof(struct dhcp_netid_list));
1514 struct dhcp_netid *list = NULL;
1515 new->next = daemon->dhcp_ignore;
1516 daemon->dhcp_ignore = new;
1517 do {
1518 struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
1519 if ((comma = safe_strchr(arg, ',')))
1520 *comma++ = 0;
1521 member->next = list;
1522 list = member;
1523 member->net = safe_string_alloc(arg);
1524 arg = comma;
1525 } while (arg);
1526
1527 new->list = list;
1528 break;
1529 }
1530
1531 case 'V':
1532 {
1533 char *a[3] = { NULL, NULL, NULL };
1534 int k;
1535 struct in_addr in, out, mask;
1536 struct doctor *new;
1537
1538 mask.s_addr = 0xffffffff;
1539
1540 if ((a[0] = arg))
1541 for (k = 1; k < 3; k++)
1542 {
1543 if (!(a[k] = strchr(a[k-1], ',')))
1544 break;
1545 *(a[k]++) = 0;
1546 unhide_metas(a[k]);
1547 }
1548 else
1549 k = 0;
1550
1551 if ((k < 2) ||
1552 ((in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
1553 ((out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
1554 {
1555 option = '?';
1556 break;
1557 }
1558
1559 if (k == 3)
1560 mask.s_addr = inet_addr(a[2]);
1561
1562 new = safe_malloc(sizeof(struct doctor));
1563 new->in = in;
1564 new->out = out;
1565 new->mask = mask;
1566 new->next = daemon->doctors;
1567 daemon->doctors = new;
1568
1569 break;
1570 }
1571
1572 case 'Y':
1573 {
1574 struct txt_record *new;
1575 unsigned char *p, *q;
1576
1577 if ((comma = safe_strchr(arg, ',')))
1578 *(comma) = 0;
1579
1580 if (!canonicalise_opt(arg))
1581 {
1582 option = '?';
1583 problem = _("bad TXT record");
1584 break;
1585 }
1586
1587 if ((q = (unsigned char *)comma))
1588 while (1)
1589 {
1590 size_t len;
1591 if ((p = (unsigned char *)strchr((char*)q+1, ',')))
1592 {
1593 if ((len = p - q - 1) > 255)
1594 {
1595 option = '?';
1596 break;
3d8df260 1597 }
849a8357
SK
1598 *q = len;
1599 for (q = q+1; q < p; q++)
1600 *q = unhide_meta(*q);
1601 }
1602 else
1603 {
1604 if ((len = strlen((char *)q+1)) > 255)
1cff166d 1605 option = '?';
849a8357
SK
1606 *q = len;
1607 for (q = q+1; *q; q++)
1608 *q = unhide_meta(*q);
1609 break;
1610 }
1611 }
1612
1613 if (option == '?')
1614 {
1615 problem = _("TXT record string too long");
1616 break;
1617 }
1618
1619 new = safe_malloc(sizeof(struct txt_record));
1620 new->next = daemon->txt;
1621 daemon->txt = new;
1622 new->class = C_IN;
1623 if (comma)
1624 {
1625 new->len = q - ((unsigned char *)comma);
1626 new->txt = safe_malloc(new->len);
1627 memcpy(new->txt, comma, new->len);
1628 }
1629 else
1630 {
1631 static char empty[] = "";
1632 new->len = 1;
1633 new->txt = empty;
1634 }
1635
1636 if (comma)
1637 *comma = 0;
1638 new->name = safe_string_alloc(arg);
1639 break;
1640 }
1641
1642 case 'W':
1643 {
1644 int port = 1, priority = 0, weight = 0;
1645 char *name, *target = NULL;
1646 struct mx_srv_record *new;
1647
1648 if ((comma = safe_strchr(arg, ',')))
1649 *(comma++) = 0;
1650
1651 if (!canonicalise_opt(arg))
1652 {
1653 option = '?';
1654 problem = _("bad SRV record");
1655 break;
1656 }
1657 name = safe_string_alloc(arg);
1658
1659 if (comma)
1660 {
1661 arg = comma;
1662 if ((comma = strchr(arg, ',')))
1663 *(comma++) = 0;
1664 if (!canonicalise_opt(arg))
0a852541 1665 {
849a8357
SK
1666 option = '?';
1667 problem = _("bad SRV target");
0a852541
SK
1668 break;
1669 }
849a8357
SK
1670 target = safe_string_alloc(arg);
1671 if (comma)
f6b7dc47 1672 {
849a8357
SK
1673 arg = comma;
1674 if ((comma = strchr(arg, ',')))
f6b7dc47 1675 *(comma++) = 0;
849a8357 1676 if (!atoi_check(arg, &port))
f6b7dc47
SK
1677 {
1678 option = '?';
849a8357 1679 problem = _("invalid port number");
f6b7dc47
SK
1680 break;
1681 }
f6b7dc47
SK
1682 if (comma)
1683 {
91dccd09 1684 arg = comma;
3d8df260 1685 if ((comma = strchr(arg, ',')))
f6b7dc47 1686 *(comma++) = 0;
849a8357 1687 if (!atoi_check(arg, &priority))
f6b7dc47
SK
1688 {
1689 option = '?';
849a8357 1690 problem = _("invalid priority");
f6b7dc47
SK
1691 break;
1692 }
f6b7dc47
SK
1693 if (comma)
1694 {
91dccd09 1695 arg = comma;
3d8df260 1696 if ((comma = strchr(arg, ',')))
f6b7dc47 1697 *(comma++) = 0;
849a8357 1698 if (!atoi_check(arg, &weight))
f6b7dc47
SK
1699 {
1700 option = '?';
849a8357 1701 problem = _("invalid weight");
f6b7dc47
SK
1702 break;
1703 }
f6b7dc47
SK
1704 }
1705 }
f6b7dc47 1706 }
849a8357
SK
1707 }
1708
1709 new = safe_malloc(sizeof(struct mx_srv_record));
1710 new->next = daemon->mxnames;
1711 daemon->mxnames = new;
1712 new->issrv = 1;
1713 new->name = name;
1714 new->target = target;
1715 new->srvport = port;
1716 new->priority = priority;
1717 new->weight = weight;
1718 break;
1719 }
1720 }
1721
1722 return option == '?' ? problem : NULL;
1723}
1724
1725static void one_file(struct daemon *daemon, char *file, int nest)
1726{
1727 int i, option, lineno = 0;
1728 FILE *f;
1729 char *p, *arg, *buff = daemon->namebuff;
1730
1731 if (nest > 20)
1732 die(_("files nested too deep in %s"), file);
1733
1734 if (!(f = fopen(file, "r")))
1735 {
1736 if (errno == ENOENT && nest == 0)
1737 return; /* No conffile, all done. */
1738 else
1739 die(_("cannot read %s: %s"), file);
1740 }
1741
1742 while (fgets(buff, MAXDNAME, f))
1743 {
1744 int white;
1745 unsigned int lastquote;
1746
1747 lineno++;
1748
1749 /* Implement quotes, inside quotes we allow \\ \" \n and \t
1750 metacharacters get hidden also strip comments */
1751
1752 for (white = 1, lastquote = 0, p = buff; *p; p++)
1753 {
1754 if (*p == '"')
1755 {
1756 memmove(p, p+1, strlen(p+1)+1);
1757 for(; *p && *p != '"'; p++)
1758 {
1759 if (*p == '\\' && strchr("\"tnabr\\", p[1]))
1760 {
1761 if (p[1] == 't')
1762 p[1] = '\t';
1763 else if (p[1] == 'n')
1764 p[1] = '\n';
1765 else if (p[1] == 'a')
1766 p[1] = '\a';
1767 else if (p[1] == 'b')
1768 p[1] = '\b';
1769 else if (p[1] == 'r')
1770 p[1] = '\r';
1771 memmove(p, p+1, strlen(p+1)+1);
1772 }
1773 *p = hide_meta(*p);
1774 }
1775 if (*p == '"')
1776 {
1777 memmove(p, p+1, strlen(p+1)+1);
1778 lastquote = p - buff;
1779 }
1780 else
1781 complain(_("missing \""), lineno, file);
1782 }
1783
1784 if (white && *p == '#')
1785 {
1786 *p = 0;
1787 break;
9e4abcb5 1788 }
849a8357
SK
1789 white = isspace(unhide_meta(*p));
1790 }
1791
1792 /* fgets gets end of line char too. */
1793 while (strlen(buff) > lastquote && isspace(unhide_meta(buff[strlen(buff)-1])))
1794 buff[strlen(buff)-1] = 0;
1795
1796 if (*buff == 0)
1797 continue;
1798
1799 if ((p=strchr(buff, '=')))
1800 {
1801 /* allow spaces around "=" */
1802 for (arg = p+1; isspace(*arg); arg++);
1803 for (; p >= buff && (isspace(*p) || *p == '='); p--)
1804 *p = 0;
9e4abcb5 1805 }
849a8357
SK
1806 else
1807 arg = NULL;
9e4abcb5 1808
849a8357
SK
1809 for (option = 0, i = 0; opts[i].name; i++)
1810 if (strcmp(opts[i].name, buff) == 0)
1811 option = opts[i].val;
1812
1813 if (option)
9e4abcb5 1814 {
849a8357
SK
1815 char *errmess;
1816 if ((errmess = one_opt(daemon, option, arg, _("error"), nest + 1)))
1817 complain(errmess, lineno, file);
1818 }
1819 else
1820 complain(_("bad option"), lineno, file);
1821 }
1822
1823 fclose(f);
1824}
1825
1826struct daemon *read_opts(int argc, char **argv, char *compile_opts)
1827{
1828 struct daemon *daemon = safe_malloc(sizeof(struct daemon));
1829 char *buff = safe_malloc(MAXDNAME);
1830 int option, nest = 0;
1831 char *errmess, *arg, *conffile = CONFFILE;
1832
1833 opterr = 0;
1834
1835 memset(daemon, 0, sizeof(struct daemon));
1836 daemon->namebuff = buff;
1837
1838 /* Set defaults - everything else is zero or NULL */
1839 daemon->cachesize = CACHESIZ;
208b65c5 1840 daemon->ftabsize = FTABSIZ;
849a8357
SK
1841 daemon->port = NAMESERVER_PORT;
1842 daemon->default_resolv.is_default = 1;
1843 daemon->default_resolv.name = RESOLVFILE;
1844 daemon->resolv_files = &daemon->default_resolv;
1845 daemon->username = CHUSER;
1846 daemon->groupname = CHGRP;
1847 daemon->runfile = RUNFILE;
1848 daemon->dhcp_max = MAXLEASES;
1849 daemon->edns_pktsz = EDNS_PKTSZ;
1850 daemon->log_fac = -1;
1851 add_txt(daemon, "version.bind", "dnsmasq-" VERSION );
1852 add_txt(daemon, "authors.bind", "Simon Kelley");
1853 add_txt(daemon, "copyright.bind", COPYRIGHT);
1854
1855 while (1)
1856 {
26128d27 1857#ifdef HAVE_GETOPT_LONG
849a8357 1858 option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
26128d27 1859#else
849a8357 1860 option = getopt(argc, argv, OPTSTRING);
26128d27 1861#endif
849a8357
SK
1862
1863 if (option == -1)
1864 break;
1865
1866 /* Copy optarg so that argv doesn't get changed */
1867 if (optarg)
1868 {
1869 strncpy(buff, optarg, MAXDNAME);
1870 buff[MAXDNAME-1] = 0;
1871 arg = buff;
9e4abcb5 1872 }
849a8357
SK
1873 else
1874 arg = NULL;
9e4abcb5 1875
849a8357
SK
1876 /* command-line only stuff */
1877 if (option == 'w')
1878 {
1879 do_usage();
1880 exit(0);
1881 }
1882 else if (option == 'v')
1883 {
1884 printf(_("Dnsmasq version %s %s\n"), VERSION, COPYRIGHT);
1885 printf(_("Compile time options %s\n\n"), compile_opts);
1886 printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
1887 printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
1888 printf(_("under the terms of the GNU General Public License, version 2.\n"));
1889 exit(0);
1890 }
1891 else if (option == 'C')
1892 {
1893 conffile = safe_string_alloc(arg);
1894 nest++;
1895 }
1896 else
1897 {
1898#ifdef HAVE_GETOPT_LONG
1899 errmess = one_opt(daemon, option, arg, _("try --help"), 0);
1900#else
1901 errmess = one_opt(daemon, option, arg, _("try -w"), 0);
1902#endif
1903 if (errmess)
1904 die(_("bad command line options: %s"), errmess);
1905 }
1906 }
1907
1908 if (conffile)
1909 one_file(daemon, conffile, nest);
1910
1911 /* Do old default, if nothing set for this. */
1912 if (daemon->log_fac == -1)
1913 {
1914 daemon->log_fac = LOG_DAEMON;
1915#ifdef LOG_LOCAL0
1916 if (daemon->options & OPT_DEBUG)
1917 daemon->log_fac = LOG_LOCAL0;
1918#endif
1919 }
1920
9e4abcb5 1921 /* port might no be known when the address is parsed - fill in here */
3be34541 1922 if (daemon->servers)
9e4abcb5
SK
1923 {
1924 struct server *tmp;
3be34541 1925 for (tmp = daemon->servers; tmp; tmp = tmp->next)
9e4abcb5
SK
1926 if (!(tmp->flags & SERV_HAS_SOURCE))
1927 {
1928 if (tmp->source_addr.sa.sa_family == AF_INET)
3be34541 1929 tmp->source_addr.in.sin_port = htons(daemon->query_port);
9e4abcb5
SK
1930#ifdef HAVE_IPV6
1931 else if (tmp->source_addr.sa.sa_family == AF_INET6)
3be34541 1932 tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
9e4abcb5
SK
1933#endif
1934 }
1935 }
1936
3be34541 1937 if (daemon->if_addrs)
9e4abcb5
SK
1938 {
1939 struct iname *tmp;
3be34541 1940 for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
9e4abcb5 1941 if (tmp->addr.sa.sa_family == AF_INET)
3be34541 1942 tmp->addr.in.sin_port = htons(daemon->port);
9e4abcb5
SK
1943#ifdef HAVE_IPV6
1944 else if (tmp->addr.sa.sa_family == AF_INET6)
3be34541 1945 tmp->addr.in6.sin6_port = htons(daemon->port);
9e4abcb5
SK
1946#endif /* IPv6 */
1947 }
1948
f6b7dc47 1949 /* only one of these need be specified: the other defaults to the host-name */
3be34541 1950 if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
9e4abcb5 1951 {
0a852541
SK
1952 struct mx_srv_record *mx;
1953
9e4abcb5 1954 if (gethostname(buff, MAXDNAME) == -1)
b8187c80 1955 die(_("cannot get host-name: %s"), NULL);
f6b7dc47 1956
0a852541
SK
1957 for (mx = daemon->mxnames; mx; mx = mx->next)
1958 if (!mx->issrv && hostname_isequal(mx->name, buff))
1959 break;
1960
1961 if ((daemon->mxtarget || (daemon->options & OPT_LOCALMX)) && !mx)
de37951c 1962 {
91dccd09
SK
1963 mx = safe_malloc(sizeof(struct mx_srv_record));
1964 mx->next = daemon->mxnames;
1965 mx->issrv = 0;
1966 mx->target = NULL;
1967 mx->name = safe_string_alloc(buff);
1968 daemon->mxnames = mx;
f6b7dc47 1969 }
9e4abcb5 1970
3be34541
SK
1971 if (!daemon->mxtarget)
1972 daemon->mxtarget = safe_string_alloc(buff);
0a852541
SK
1973
1974 for (mx = daemon->mxnames; mx; mx = mx->next)
1975 if (!mx->issrv && !mx->target)
1976 mx->target = daemon->mxtarget;
9e4abcb5 1977 }
f6b7dc47 1978
208b65c5
SK
1979 if (!(daemon->options & OPT_NO_RESOLV) &&
1980 daemon->resolv_files &&
1981 daemon->resolv_files->next &&
1982 (daemon->options & OPT_NO_POLL))
b8187c80 1983 die(_("only one resolv.conf file allowed in no-poll mode."), NULL);
de37951c 1984
3be34541 1985 if (daemon->options & OPT_RESOLV_DOMAIN)
de37951c
SK
1986 {
1987 char *line;
849a8357
SK
1988 FILE *f;
1989
208b65c5
SK
1990 if ((daemon->options & OPT_NO_RESOLV) ||
1991 !daemon->resolv_files ||
1992 (daemon->resolv_files)->next)
b8187c80 1993 die(_("must have exactly one resolv.conf to read domain from."), NULL);
de37951c 1994
3be34541 1995 if (!(f = fopen((daemon->resolv_files)->name, "r")))
208b65c5 1996 die(_("failed to read %s: %s"), (daemon->resolv_files)->name);
de37951c
SK
1997
1998 while ((line = fgets(buff, MAXDNAME, f)))
1999 {
2000 char *token = strtok(line, " \t\n\r");
2001
2002 if (!token || strcmp(token, "search") != 0)
2003 continue;
2004
2005 if ((token = strtok(NULL, " \t\n\r")) &&
3d8df260 2006 canonicalise_opt(token) &&
3be34541 2007 (daemon->domain_suffix = safe_string_alloc(token)))
de37951c
SK
2008 break;
2009 }
3be34541 2010
de37951c 2011 fclose(f);
8a911ccc 2012
3be34541 2013 if (!daemon->domain_suffix)
b8187c80 2014 die(_("no search directive found in %s"), (daemon->resolv_files)->name);
de37951c 2015 }
3d8df260
SK
2016
2017 if (daemon->domain_suffix)
2018 {
2019 /* add domain for any srv record without one. */
2020 struct mx_srv_record *srv;
de37951c 2021
3d8df260
SK
2022 for (srv = daemon->mxnames; srv; srv = srv->next)
2023 if (srv->issrv &&
2024 strchr(srv->name, '.') &&
2025 strchr(srv->name, '.') == strrchr(srv->name, '.'))
2026 {
2027 strcpy(buff, srv->name);
2028 strcat(buff, ".");
2029 strcat(buff, daemon->domain_suffix);
2030 free(srv->name);
2031 srv->name = safe_string_alloc(buff);
2032 }
2033 }
2034
3be34541 2035 return daemon;
849a8357
SK
2036}
2037
9e4abcb5
SK
2038
2039