Licensing: Update copyright dates for Ian Jackson
[adns] / client / adh-opts.c
CommitLineData
b0f83da6 1/*
d1cff511 2 * adh-opts.c
b0f83da6 3 * - useful general-purpose resolver client program
d1cff511 4 * option handling tables etc.
b0f83da6 5 */
6/*
ae8cc977 7 * This file is part of adns, which is
26e1c3d6 8 * Copyright (C) 1997-2000,2003,2006,2014 Ian Jackson
ae8cc977 9 * Copyright (C) 1999-2000,2003,2006 Tony Finch
10 * Copyright (C) 1991 Massachusetts Institute of Technology
11 * (See the file INSTALL for full details.)
b0f83da6 12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
7f8bbe29 15 * the Free Software Foundation; either version 3, or (at your option)
b0f83da6 16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
8c09a4c6 24 * along with this program; if not, write to the Free Software Foundation.
b0f83da6 25 */
26
e3f575ff 27#include "adnshost.h"
28
d1cff511 29int ov_env=1, ov_pipe=0, ov_asynch=0;
30int ov_verbose= 0;
e3f575ff 31adns_rrtype ov_type= adns_r_none;
d1cff511 32int ov_search=0, ov_qc_query=0, ov_qc_anshost=0, ov_qc_cname=1;
138722f0 33int ov_tcp=0, ov_cname=0, ov_afflags=0, ov_v6map=0, ov_format=fmt_default;
e3f575ff 34char *ov_id= 0;
35struct perqueryflags_remember ov_pqfr = { 1,1,1, tm_none };
c8bea377 36
e3f575ff 37static const struct optioninfo global_options[]= {
c8bea377 38 { ot_desconly, "global binary options:" },
39 { ot_flag, "Do not look at environment variables at all",
40 "e", "env", &ov_env, 0 },
41 { ot_flag, "Read queries on stdin instead of using args",
42 "f", "pipe", &ov_pipe, 1 },
43 { ot_flag, "Allow answers to be reordered",
44 "a", "asynch", &ov_asynch, 1 },
413b9ad6 45
46 { ot_desconly, "answer/error output format and destination (see below):" },
47 { ot_value, "Answers to stdout, errors as messages to stderr (default)",
48 "Fs", "fmt-simple", &ov_format, fmt_simple },
49 { ot_value, "Answers and errors both to stdout in parseable format",
50 "Fi", "fmt-inline", &ov_format, fmt_inline },
51 { ot_value, "Fully-parseable output format (default for --asynch)",
52 "Fa", "fmt-asynch", &ov_format, fmt_asynch },
c8bea377 53
54 { ot_desconly, "global verbosity level:" },
55 { ot_value, "Do not print anything to stderr",
56 "Vq", "quiet", &ov_verbose, adns_if_noerrprint },
57 { ot_value, "Report unexpected kinds of problem only (default)",
58 "Vn", "no-quiet", &ov_verbose, 0 },
59 { ot_value, "Debugging mode",
60 "Vd", "debug", &ov_verbose, adns_if_debug },
61
62 { ot_desconly, "other global options:" },
0ebff22d 63 { ot_funcarg, "Configuration to use instead of /etc/resolv.conf",
64 0, "config", 0,0, of_config, "<config-text>" },
aa3ffb57 65 { ot_func, "Print version number",
66 0, "version", 0,0, of_version },
c8bea377 67 { ot_func, "Print usage information",
68 0, "help", 0,0, of_help },
69
70 { ot_end }
71};
72
e3f575ff 73static const struct optioninfo perquery_options[]= {
c8bea377 74 { ot_desconly, "per-query options:" },
75 { ot_funcarg, "Query type (see below)",
76 "t", "type", 0,0, &of_type, "type" },
94be415a 77 { ot_funcarg, "Do reverse query (address -> name lookup)",
d7449548 78 "i", "ptr", 0,0, &of_ptr, "addr" },
2b1c6979 79 { ot_funcarg2, "Lookup in in-addr-like `zone' (eg MAPS RBL)",
80 0, "reverse", 0,0, &of_reverse, "addr","zone" },
c8bea377 81
82 { ot_desconly, "per-query binary options:" },
83 { ot_flag, "Use the search list",
84 "s", "search", &ov_search, 1 },
85 { ot_flag, "Let query domains contain quote-requiring chars",
86 "Qq", "qc-query", &ov_qc_query, 1 },
87 { ot_flag, "Let hostnames in answers contain ...",
88 "Qa", "qc-anshost", &ov_qc_anshost, 1 },
89 { ot_flag, "Prevent CNAME target domains from containing ...",
90 "Qc", "qc-cname", &ov_qc_cname, 0 },
91 { ot_flag, "Force use of a virtual circuit",
92 "u", "tcp", &ov_tcp, 1 },
93 { ot_flag, "Do not display owner name in output",
d1cff511 94 "Do", "show-owner", &ov_pqfr.show_owner, 0 },
c8bea377 95 { ot_flag, "Do not display RR type in output",
d1cff511 96 "Dt", "show-type", &ov_pqfr.show_type, 0 },
c8bea377 97 { ot_flag, "Do not display CNAME target in output",
d1cff511 98 "Dc", "show-cname", &ov_pqfr.show_cname, 0 },
c8bea377 99
100 { ot_desconly, "per-query TTL mode (NB TTL is minimum across all info in reply):" },
101 { ot_value, "Show the TTL as a TTL",
d1cff511 102 "Tt", "ttl-ttl", &ov_pqfr.ttl, tm_rel },
c8bea377 103 { ot_value, "Show the TTL as a time_t when the data might expire",
d1cff511 104 "Ta", "ttl-abs", &ov_pqfr.ttl, tm_abs },
c8bea377 105 { ot_value, "Do not show the TTL (default)",
d1cff511 106 "Tn", "no-ttl", &ov_pqfr.ttl, tm_none },
cdf51ff0 107
138722f0
MW
108 { ot_desconly, "per-query address family options (for `addr' etc.):" },
109 { ot_value, "Only return IPv4 addresses",
110 "A4", "--addr-ipv4-only", &ov_afflags, adns_qf_want_ipv4 },
111 { ot_value, "Only return IPv6 addresses",
112 "A6", "--addr-ipv6-only", &ov_afflags, adns_qf_want_ipv6 },
113 { ot_value, "Return all available addresses (default)",
114 "Aa", "--addr-all", &ov_afflags, 0 },
cdf51ff0
MW
115 { ot_flag, "Return IPv4 addresses as IPv6-mapped",
116 "Am", "--addr-ipv6-mapped", &ov_v6map, 1 },
c8bea377 117
118 { ot_desconly, "per-query CNAME handling mode:" },
119 { ot_value, "Call it an error if a CNAME is found",
120 "Cf", "cname-reject", &ov_cname, adns_qf_cname_forbid },
121 { ot_value, "Allow references to CNAMEs in other RRs",
122 "Cl", "cname-loose", &ov_cname, adns_qf_cname_loose },
123 { ot_value, "CNAME ok for query domain, but not in RRs (default)",
124 "Cs", "cname-ok", &ov_cname, 0 },
125
d1cff511 126 { ot_desconly, "asynchronous/pipe mode options:" },
c8bea377 127 { ot_funcarg, "Set <id>, default is decimal sequence starting 0",
94be415a 128 0, "asynch-id", 0,0, &of_asynch_id, "id" },
9fa14499 129 { ot_funcarg, "Cancel the query with id <id> (no error if not found)",
c8bea377 130 0, "cancel-id", 0,0, &of_cancel_id, "id" },
131
132 { ot_end }
133};
b0f83da6 134
135static void printusage(void) {
e3f575ff 136 static const struct optioninfo *const all_optiontables[]= {
d1cff511 137 global_options, perquery_options, 0
c8bea377 138 };
139
e3f575ff 140 const struct optioninfo *const *oiap, *oip=0;
c8bea377 141 int maxsopt, maxlopt, l;
142
143 maxsopt= maxlopt= 0;
144
e3f575ff 145 for (oiap=all_optiontables; *oiap; oiap++) {
c8bea377 146 for (oip=*oiap; oip->type != ot_end; oip++) {
147 if (oip->type == ot_funcarg) continue;
148 if (oip->sopt) { l= strlen(oip->sopt); if (l>maxsopt) maxsopt= l; }
149 if (oip->lopt) {
150 l= strlen(oip->lopt);
151 if (oip->type == ot_flag && !oip->value) l+= 3;
152 if (l>maxlopt) maxlopt= l;
153 }
154 }
155 }
156
157 fputs("usage: adnshost [global-opts] [query-opts] query-domain\n"
158 " [[query-opts] query-domain ...]\n"
159 " adnshost [global-opts] [query-opts] -f|--pipe\n",
160 stdout);
161
e3f575ff 162 for (oiap=all_optiontables; *oiap; oiap++) {
c8bea377 163 putchar('\n');
164 for (oip=*oiap; oip->type != ot_end; oip++) {
165 switch (oip->type) {
166 case ot_flag:
167 if (!oip->value) {
168 if (oip->sopt) {
169 printf(" +%-*s --no-%-*s %s\n",
170 maxsopt, oip->sopt,
171 maxlopt-2, oip->lopt,
172 oip->desc);
173 } else {
174 printf(" --no-%-*s %s\n",
175 maxlopt+maxsopt+1, oip->lopt,
176 oip->desc);
177 }
178 break;
179 }
180 case ot_value: case ot_func: /* fall through */
181 if (oip->sopt) {
182 printf(" -%-*s --%-*s %s\n",
183 maxsopt, oip->sopt,
184 maxlopt+1, oip->lopt,
185 oip->desc);
186 } else {
187 printf(" --%-*s %s\n",
188 maxlopt+maxsopt+3, oip->lopt,
189 oip->desc);
190 }
191 break;
192 case ot_funcarg:
193 if (oip->sopt) {
194 l= (maxlopt + maxsopt - 9 -
195 (strlen(oip->sopt) + strlen(oip->lopt) + 2*strlen(oip->argdesc)));
196 printf(" -%s<%s> / --%s <%s>%*s%s\n",
197 oip->sopt, oip->argdesc, oip->lopt, oip->argdesc,
198 l>2 ? l : 2, "",
199 oip->desc);
200 } else {
201 l= (maxlopt + maxsopt + 1 -
202 (strlen(oip->lopt) + strlen(oip->argdesc)));
203 printf(" --%s <%s>%*s%s\n",
204 oip->lopt, oip->argdesc,
205 l>2 ? l : 2, "",
206 oip->desc);
207 }
208 break;
2b1c6979 209 case ot_funcarg2:
210 assert(!oip->sopt);
211 l= (maxlopt + maxsopt - 2 -
212 (strlen(oip->lopt) + strlen(oip->argdesc) + strlen(oip->argdesc2)));
213 printf(" --%s <%s> <%s>%*s%s\n",
214 oip->lopt, oip->argdesc, oip->argdesc2,
215 l>2 ? l : 2, "",
216 oip->desc);
217 break;
c8bea377 218 case ot_desconly:
219 printf("%s\n", oip->desc);
220 break;
221 default:
222 abort();
223 }
224 }
225 }
226
227 printf("\nEscaping domains which might start with `-':\n"
228 " - %-*s Next argument is a domain, but more options may follow\n",
229 maxlopt+maxsopt+3, "<domain>");
230
231 fputs("\n"
232 "Query domains should always be quoted according to master file format.\n"
233 "\n"
234 "For binary options, --FOO and --no-FOO are opposites, as are\n"
235 "-X and +X. In each case the default is the one not listed.\n"
236 "Per query options stay set a particular way until they are reset,\n"
237 "whether they appear on the command line or on stdin.\n"
238 "All global options must preceed the first query domain.\n"
239 "\n"
240 "With -f, the input should be lines with either an option, possibly\n"
241 "with a value argument (separated from the option by a space if it's a long\n"
242 "option), or a domain (possibly preceded by a hyphen and a space to\n"
243 "distinguish it from an option).\n"
244 "\n"
245 "Output format is master file format without class or TTL by default:\n"
246 " [<owner>] [<ttl>] [<type>] <data>\n"
247 "or if the <owner> domain refers to a CNAME and --show-cname is on\n"
248 " [<owner>] [<ttl>] CNAME <cname>\n"
249 " [<cname>] [<ttl>] <type> <data>\n"
413b9ad6 250 "When a query fails you get an error message to stderr (with --fmt-simple).\n"
251 "Specify --fmt-inline for lines like this (broken here for readability):\n"
57d68ed1 252 " ; failed <statustype> <statusnum> <statusabbrev> \\\n"
253 " [<owner>] [<ttl>] [<cname>] \"<status string>\"\n"
413b9ad6 254 "If you use --fmt-asynch, which is the default for --asynch,\n"
255 "each answer (success or failure) is preceded by a line\n"
57d68ed1 256 " <id> <nrrs> <statustype> <statusnum> <statusabbrev> \\\n"
257 " [<owner>] [<ttl>] [<cname>] \"<status string>\"\n"
c8bea377 258 "where <nrrs> is the number of RRs that follow and <cname> will be `$' or\n"
259 "the CNAME target; the CNAME indirection and error formats above are not used.\n"
260 "\n"
261 "Exit status:\n"
262 " 0 all went well\n"
263 " 1-6 at least one query failed with statustype:\n"
264 " 1 localfail )\n"
265 " 2 remotefail ) temporary errors\n"
266 " 3 tempfail __)_________________\n"
267 " 4 misconfig )\n"
268 " 5 misquery ) permanent errors\n"
269 " 6 permfail )\n"
270 " 10 system trouble\n"
271 " 11 usage problems\n"
272 "\n"
273 "Query types (see adns.h; default is addr):\n"
50e2b0c3
MW
274 " ns soa ptr mx rp srv addr - enhanced versions\n"
275 " cname hinfo txt - types with only one version\n"
276 " a aaaa ns- soa- ptr- mx- rp- srv- - _raw versions\n"
277 " type<number> - `unknown' type, RFC3597\n"
94be415a 278 "Default is addr, or ptr for -i/--ptr queries\n",
c8bea377 279 stdout);
280 if (ferror(stdout)) sysfail("write usage message",errno);
b0f83da6 281}
282
aa3ffb57 283void of_version(const struct optioninfo *oi, const char *arg, const char *arg2) {
284 VERSION_PRINT_QUIT("adnshost");
285}
286
2b1c6979 287void of_help(const struct optioninfo *oi, const char *arg, const char *arg2) {
b0f83da6 288 printusage();
289 if (fclose(stdout)) sysfail("finish writing output",errno);
0ebff22d 290 quitnow(0);
b0f83da6 291}
c8bea377 292
e3f575ff 293typedef int comparer_type(const char **optp, const struct optioninfo *entry);
294
295static int oc_long(const char **optp, const struct optioninfo *entry) {
296 return entry->lopt && !strcmp(*optp,entry->lopt);
297}
298
299static int oc_short(const char **optp, const struct optioninfo *entry) {
300 const char *sopt;
301 int l;
302
303 sopt= entry->sopt;
304 if (!sopt) return 0;
305 l= strlen(sopt);
306 if (memcmp(*optp,sopt,l)) return 0;
307 (*optp) += l;
308 return 1;
309}
310
311static const struct optioninfo *find1(const char **optp,
312 const struct optioninfo *table,
313 comparer_type *comparer) {
314 for (;;) {
315 if (table->type == ot_end) return 0;
316 if (comparer(optp,table)) return table;
317 table++;
318 }
319}
320
321static const struct optioninfo *find(const char **optp,
322 const char *prefix,
323 comparer_type *comparer) {
324 const struct optioninfo *oip;
4f257c51 325 const char *opt;
e3f575ff 326
4f257c51 327 opt= *optp;
e3f575ff 328 oip= find1(optp,perquery_options,comparer);
329 if (oip) return oip;
330 oip= find1(optp,global_options,comparer);
4f257c51 331 if (!oip) usageerr("unknown option %s%s",prefix,opt);
332 if (ads) usageerr("global option %s%s specified after query domain(s)",prefix,opt);
e3f575ff 333 return oip;
334}
335
336const struct optioninfo *opt_findl(const char *opt) { return find(&opt,"--",oc_long); }
337const struct optioninfo *opt_finds(const char **optp) { return find(optp,"-",oc_short); }
338
0b6f56cc 339static void noninvert(const struct optioninfo *oip) NONRETURNING;
340static void noninvert(const struct optioninfo *oip) {
341 usageerr("option %s%s%s%s%s may not be inverted",
342 oip->sopt ? "-" : "", oip->sopt ? oip->sopt : "",
343 oip->lopt && oip->sopt ? " / " : "",
344 oip->lopt ? "--" : "", oip->lopt ? oip->lopt : "");
345}
346
2b1c6979 347void opt_do(const struct optioninfo *oip, int invert,
348 const char *arg, const char *arg2) {
e3f575ff 349 switch (oip->type) {
0b6f56cc 350 case ot_flag:
351 assert(!arg);
e0d855e1 352 *oip->storep= !invert;
0b6f56cc 353 return;
354 case ot_value:
e3f575ff 355 assert(!arg);
0b6f56cc 356 if (invert) noninvert(oip);
e3f575ff 357 *oip->storep= oip->value;
358 return;
2b1c6979 359 case ot_func: case ot_funcarg: case ot_funcarg2:
0b6f56cc 360 if (invert) noninvert(oip);
2b1c6979 361 oip->func(oip,arg,arg2);
e3f575ff 362 return;
363 default:
364 abort();
365 }
366}