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