nslookup: rework option parsing
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 15 Apr 2018 10:01:46 +0000 (12:01 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 15 Apr 2018 10:01:46 +0000 (12:01 +0200)
function                                             old     new   delta
nslookup_main                                       2715    2754     +39
packed_usage                                       32179   32211     +32
add_ns                                                65      66      +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 72/0)               Total: 72 bytes
   text    data     bss     dec     hex filename
 926262     555    5740  932557   e3acd busybox_old
 926239     555    5740  932534   e3ab6 busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/nslookup.c

index c6f431347e14af448d1ab955fcadb2f0a8eb3756..112e8763c70f1f338252af90d5b698329fab1f08 100644 (file)
@@ -6,7 +6,7 @@
 //config:      help
 //config:      nslookup is a tool to query Internet name servers.
 //config:
-//config:config NSLOOKUP_BIG
+//config:config FEATURE_NSLOOKUP_BIG
 //config:      bool "Use internal resolver code instead of libc"
 //config:      depends on NSLOOKUP
 //config:      default y
 //config:config FEATURE_NSLOOKUP_LONG_OPTIONS
 //config:      bool "Enable long options"
 //config:      default y
-//config:      depends on NSLOOKUP_BIG && LONG_OPTS
+//config:      depends on FEATURE_NSLOOKUP_BIG && LONG_OPTS
 
 //applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
 
 //kbuild:lib-$(CONFIG_NSLOOKUP) += nslookup.o
 
 //usage:#define nslookup_trivial_usage
-//usage:       "[HOST] [SERVER]"
+//usage:       IF_FEATURE_NSLOOKUP_BIG("[-type=QUERY_TYPE] ") "HOST [DNS_SERVER]"
 //usage:#define nslookup_full_usage "\n\n"
-//usage:       "Query the nameserver for the IP address of the given HOST\n"
-//usage:       "optionally using a specified DNS server"
+//usage:       "Query DNS about HOST"
 //usage:
 //usage:#define nslookup_example_usage
 //usage:       "$ nslookup localhost\n"
@@ -42,7 +41,7 @@
 #include "common_bufsiz.h"
 
 
-#if !ENABLE_NSLOOKUP_BIG
+#if !ENABLE_FEATURE_NSLOOKUP_BIG
 
 /*
  * Mini nslookup implementation for busybox
@@ -326,7 +325,7 @@ struct globals {
 } while (0)
 
 enum {
-       OPT_stats = (1 << 4),
+       OPT_stats = (1 << 0),
 };
 
 static int parse_reply(const unsigned char *msg, size_t len)
@@ -687,7 +686,7 @@ static void add_ns(const char *addr)
        G.server = xrealloc_vector(G.server, /*8=2^3:*/ 3, count);
        ns = &G.server[count];
        ns->name = addr;
-       ns->lsa = xhost2sockaddr(addr, 53);
+       ns->lsa = xhost2sockaddr(addr, G.default_port);
        /*ns->replies = 0; - already is */
        /*ns->failures = 0; - already is */
 }
@@ -747,107 +746,130 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv)
 {
        struct ns *ns;
        struct query *queries;
-       llist_t *type_strings;
        int n_queries;
        unsigned types;
-       int opts;
        int rc;
        int err;
 
        INIT_G();
 
-       type_strings = NULL;
-#if ENABLE_FEATURE_NSLOOKUP_LONG_OPTIONS
-       opts = getopt32long(argv, "^"
-                       "+" /* '+': stop at first non-option (why?) */
-                       "q:*p:+r:+t:+s"
-                       "\0"
-                       "-1:q::", /* minimum 1 arg, -q is a list */
-                               "type\0"      Required_argument "q"
-                               "querytype\0" Required_argument "q"
-                               "port\0"      Required_argument "p"
-                               "retry\0"     Required_argument "r"
-                               "timeout\0"   Required_argument "t"
-                               "stats\0"     No_argument       "s",
-                       &type_strings, &G.default_port,
-                       &G.default_retry, &G.default_timeout
-       );
-#else
-       opts = getopt32(argv, "^"
-                       "+" /* '+': stop at first non-option (why?) */
-                       "q:*p:+r:+t:+s"
-                       "\0"
-                       "-1:q::", /* minimum 1 arg, -q is a list */
-                       &type_strings, &G.default_port,
-                       &G.default_retry, &G.default_timeout
-       );
-#endif
-       if (G.default_port > 65535)
-               bb_error_msg_and_die("invalid server port");
-       if (G.default_retry == 0)
-               bb_error_msg_and_die("invalid retry value");
-       if (G.default_timeout == 0)
-               bb_error_msg_and_die("invalid timeout value");
-
+       /* manpage: "Options can also be specified on the command line
+        * if they precede the arguments and are prefixed with a hyphen."
+        */
        types = 0;
-       while (type_strings) {
-               int c;
-               char *ptr, *chr;
-
-               ptr = llist_pop(&type_strings);
-
-               /* skip leading text, e.g. when invoked with -querytype=AAAA */
-               chr = strchr(ptr, '=');
-               if (chr)
-                       ptr = chr + 1;
+       argv++;
+       for (;;) {
+               const char *options =
+// bind-utils-9.11.3 accept these:
+// class=   cl=
+// type=    ty= querytype= query= qu= q=
+// domain=  do=
+// port=    po=
+// timeout= t=
+// retry=   ret=
+// ndots=
+// recurse
+// norecurse
+// defname
+// nodefname
+// vc
+// novc
+// debug
+// nodebug
+// d2
+// nod2
+// search
+// nosearch
+// sil
+// fail
+// nofail
+// ver (prints version and exits)
+                       "type\0"      /* 0 */
+                       "querytype\0" /* 1 */
+                       "port\0"      /* 2 */
+                       "retry\0"     /* 3 */
+                       "stats\0"     /* 4 */
+                       "t\0" /* disambiguate with "type": else -t=2 fails */
+                       "timeout\0"   /* 6 */
+                       "";
+               int i;
+               char *arg;
+               char *val;
+
+               if (!*argv)
+                       bb_show_usage();
+               if (argv[0][0] != '-')
+                       break;
 
-               for (c = 0;; c++) {
-                       if (c == ARRAY_SIZE(qtypes))
-                               bb_error_msg_and_die("invalid query type \"%s\"", ptr);
-                       if (strcmp(qtypes[c].name, ptr) == 0)
-                               break;
+               /* Separate out "=val" part */
+               arg = (*argv++) + 1;
+               val = strchrnul(arg, '=');
+               if (*val)
+                       *val++ = '\0';
+
+               i = index_in_substrings(options, arg);
+               //bb_error_msg("i:%d arg:'%s' val:'%s'", i, arg, val);
+               if (i < 0)
+                       bb_show_usage();
+
+               if (i <= 1) {
+                       for (i = 0;; i++) {
+                               if (i == ARRAY_SIZE(qtypes))
+                                       bb_error_msg_and_die("invalid query type \"%s\"", val);
+                               if (strcmp(qtypes[i].name, val) == 0)
+                                       break;
+                       }
+                       types |= (1 << i);
+                       continue;
+               }
+               if (i == 2) {
+                       G.default_port = xatou_range(val, 1, 0xffff);
+               }
+               if (i == 3) {
+                       G.default_retry = xatou_range(val, 1, INT_MAX);
+               }
+               if (i == 4) {
+                       option_mask32 |= OPT_stats;
+               }
+               if (i > 4) {
+                       G.default_timeout = xatou_range(val, 1, INT_MAX / 1000);
                }
-
-               types |= (1 << c);
        }
 
-       argv += optind;
-
        n_queries = 0;
        queries = NULL;
-       do {
-               if (types == 0) {
-                       /* No explicit type given, guess query type.
-                        * If we can convert the domain argument into a ptr (means that
-                        * inet_pton() could read it) we assume a PTR request, else
-                        * we issue A+AAAA queries and switch to an output format
-                        * mimicking the one of the traditional nslookup applet.
-                        */
-                       char *ptr;
-                       char buf80[80];
-
-                       ptr = make_ptr(buf80, *argv);
-                       if (ptr) {
-                               add_query(&queries, &n_queries, T_PTR, xstrdup(ptr));
-                       } else {
-                               add_query(&queries, &n_queries, T_A, *argv);
+       if (types == 0) {
+               /* No explicit type given, guess query type.
+                * If we can convert the domain argument into a ptr (means that
+                * inet_pton() could read it) we assume a PTR request, else
+                * we issue A+AAAA queries and switch to an output format
+                * mimicking the one of the traditional nslookup applet.
+                */
+               char *ptr;
+               char buf80[80];
+
+               ptr = make_ptr(buf80, argv[0]);
+               if (ptr) {
+                       add_query(&queries, &n_queries, T_PTR, xstrdup(ptr));
+               } else {
+                       add_query(&queries, &n_queries, T_A, argv[0]);
 #if ENABLE_FEATURE_IPV6
-                               add_query(&queries, &n_queries, T_AAAA, *argv);
+                       add_query(&queries, &n_queries, T_AAAA, argv[0]);
 #endif
-                       }
-               } else {
-                       int c;
-                       for (c = 0; c < ARRAY_SIZE(qtypes); c++) {
-                               if (types & (1 << c))
-                                       add_query(&queries, &n_queries, qtypes[c].type, *argv);
-                       }
                }
-               argv++;
-       } while (argv[0] && argv[1]);
+       } else {
+               int c;
+               for (c = 0; c < ARRAY_SIZE(qtypes); c++) {
+                       if (types & (1 << c))
+                               add_query(&queries, &n_queries, qtypes[c].type, argv[0]);
+               }
+       }
 
        /* Use given DNS server if present */
-       if (argv[0]) {
-               add_ns(argv[0]);
+       if (argv[1]) {
+               if (argv[2])
+                       bb_show_usage();
+               add_ns(argv[1]);
        } else {
                parse_resolvconf();
                /* Fall back to localhost if we could not find NS in resolv.conf */
@@ -861,7 +883,7 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv)
                c = send_queries(&G.server[rc], queries, n_queries);
                if (c > 0) {
                        /* more than zero replies received */
-                       if (opts & OPT_stats) {
+                       if (option_mask32 & OPT_stats) {
                                printf("Replies:\t%d\n", G.server[rc].replies);
                                printf("Failures:\t%d\n\n", G.server[rc].failures);
                        }