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