Licensing: Update copyright dates for Ian Jackson
[adns] / client / adh-opts.c
1 /*
2 * adh-opts.c
3 * - useful general-purpose resolver client program
4 * option handling tables etc.
5 */
6 /*
7 * This file is part of adns, which is
8 * Copyright (C) 1997-2000,2003,2006,2014 Ian Jackson
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.)
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
15 * the Free Software Foundation; either version 3, or (at your option)
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
24 * along with this program; if not, write to the Free Software Foundation.
25 */
26
27 #include "adnshost.h"
28
29 int ov_env=1, ov_pipe=0, ov_asynch=0;
30 int ov_verbose= 0;
31 adns_rrtype ov_type= adns_r_none;
32 int ov_search=0, ov_qc_query=0, ov_qc_anshost=0, ov_qc_cname=1;
33 int ov_tcp=0, ov_cname=0, ov_afflags=0, ov_v6map=0, ov_format=fmt_default;
34 char *ov_id= 0;
35 struct perqueryflags_remember ov_pqfr = { 1,1,1, tm_none };
36
37 static const struct optioninfo global_options[]= {
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 },
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 },
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:" },
63 { ot_funcarg, "Configuration to use instead of /etc/resolv.conf",
64 0, "config", 0,0, of_config, "<config-text>" },
65 { ot_func, "Print version number",
66 0, "version", 0,0, of_version },
67 { ot_func, "Print usage information",
68 0, "help", 0,0, of_help },
69
70 { ot_end }
71 };
72
73 static const struct optioninfo perquery_options[]= {
74 { ot_desconly, "per-query options:" },
75 { ot_funcarg, "Query type (see below)",
76 "t", "type", 0,0, &of_type, "type" },
77 { ot_funcarg, "Do reverse query (address -> name lookup)",
78 "i", "ptr", 0,0, &of_ptr, "addr" },
79 { ot_funcarg2, "Lookup in in-addr-like `zone' (eg MAPS RBL)",
80 0, "reverse", 0,0, &of_reverse, "addr","zone" },
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",
94 "Do", "show-owner", &ov_pqfr.show_owner, 0 },
95 { ot_flag, "Do not display RR type in output",
96 "Dt", "show-type", &ov_pqfr.show_type, 0 },
97 { ot_flag, "Do not display CNAME target in output",
98 "Dc", "show-cname", &ov_pqfr.show_cname, 0 },
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",
102 "Tt", "ttl-ttl", &ov_pqfr.ttl, tm_rel },
103 { ot_value, "Show the TTL as a time_t when the data might expire",
104 "Ta", "ttl-abs", &ov_pqfr.ttl, tm_abs },
105 { ot_value, "Do not show the TTL (default)",
106 "Tn", "no-ttl", &ov_pqfr.ttl, tm_none },
107
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 },
115 { ot_flag, "Return IPv4 addresses as IPv6-mapped",
116 "Am", "--addr-ipv6-mapped", &ov_v6map, 1 },
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
126 { ot_desconly, "asynchronous/pipe mode options:" },
127 { ot_funcarg, "Set <id>, default is decimal sequence starting 0",
128 0, "asynch-id", 0,0, &of_asynch_id, "id" },
129 { ot_funcarg, "Cancel the query with id <id> (no error if not found)",
130 0, "cancel-id", 0,0, &of_cancel_id, "id" },
131
132 { ot_end }
133 };
134
135 static void printusage(void) {
136 static const struct optioninfo *const all_optiontables[]= {
137 global_options, perquery_options, 0
138 };
139
140 const struct optioninfo *const *oiap, *oip=0;
141 int maxsopt, maxlopt, l;
142
143 maxsopt= maxlopt= 0;
144
145 for (oiap=all_optiontables; *oiap; oiap++) {
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
162 for (oiap=all_optiontables; *oiap; oiap++) {
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;
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;
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"
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"
252 " ; failed <statustype> <statusnum> <statusabbrev> \\\n"
253 " [<owner>] [<ttl>] [<cname>] \"<status string>\"\n"
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"
256 " <id> <nrrs> <statustype> <statusnum> <statusabbrev> \\\n"
257 " [<owner>] [<ttl>] [<cname>] \"<status string>\"\n"
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"
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"
278 "Default is addr, or ptr for -i/--ptr queries\n",
279 stdout);
280 if (ferror(stdout)) sysfail("write usage message",errno);
281 }
282
283 void of_version(const struct optioninfo *oi, const char *arg, const char *arg2) {
284 VERSION_PRINT_QUIT("adnshost");
285 }
286
287 void of_help(const struct optioninfo *oi, const char *arg, const char *arg2) {
288 printusage();
289 if (fclose(stdout)) sysfail("finish writing output",errno);
290 quitnow(0);
291 }
292
293 typedef int comparer_type(const char **optp, const struct optioninfo *entry);
294
295 static int oc_long(const char **optp, const struct optioninfo *entry) {
296 return entry->lopt && !strcmp(*optp,entry->lopt);
297 }
298
299 static 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
311 static 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
321 static const struct optioninfo *find(const char **optp,
322 const char *prefix,
323 comparer_type *comparer) {
324 const struct optioninfo *oip;
325 const char *opt;
326
327 opt= *optp;
328 oip= find1(optp,perquery_options,comparer);
329 if (oip) return oip;
330 oip= find1(optp,global_options,comparer);
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);
333 return oip;
334 }
335
336 const struct optioninfo *opt_findl(const char *opt) { return find(&opt,"--",oc_long); }
337 const struct optioninfo *opt_finds(const char **optp) { return find(optp,"-",oc_short); }
338
339 static void noninvert(const struct optioninfo *oip) NONRETURNING;
340 static 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
347 void opt_do(const struct optioninfo *oip, int invert,
348 const char *arg, const char *arg2) {
349 switch (oip->type) {
350 case ot_flag:
351 assert(!arg);
352 *oip->storep= !invert;
353 return;
354 case ot_value:
355 assert(!arg);
356 if (invert) noninvert(oip);
357 *oip->storep= oip->value;
358 return;
359 case ot_func: case ot_funcarg: case ot_funcarg2:
360 if (invert) noninvert(oip);
361 oip->func(oip,arg,arg2);
362 return;
363 default:
364 abort();
365 }
366 }