chiark / gitweb /
Licensing: Copy GPL-3 into COPYING
[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
8  *    Copyright (C) 1997-2000,2003,2006  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 }