ping: support -I addr in family neutral manner; reuse a bit of common code
authorDenis Vlasenko <vda.linux@googlemail.com>
Fri, 9 Feb 2007 17:32:16 +0000 (17:32 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Fri, 9 Feb 2007 17:32:16 +0000 (17:32 -0000)
include/libbb.h
include/usage.h
libbb/xconnect.c
networking/ping.c

index bb4a687..218a193 100644 (file)
@@ -321,6 +321,7 @@ int xconnect_stream(const len_and_sockaddr *lsa);
 len_and_sockaddr* host2sockaddr(const char *host, int port);
 /* Version which dies on error */
 len_and_sockaddr* xhost2sockaddr(const char *host, int port);
+len_and_sockaddr* xdotted2sockaddr(const char *host, int port);
 #if ENABLE_FEATURE_IPV6
 /* Same, useful if you want to force family (e.g. IPv6) */
 len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af);
index 17dcf9b..fb1617a 100644 (file)
 #define ping_full_usage \
        "Send ICMP ECHO_REQUEST packets to network hosts" \
        "\n\nOptions:\n" \
-       "       -c CNT  Send only CNT pings\n" \
-       "       -s SIZE Send SIZE data bytes in packets (default=56)\n" \
-       "       -I IP   Use IP as source address\n" \
-       "       -q      Quiet mode, only displays output at start\n" \
-       "               and when finished"
+       "       -4, -6          Force IPv4 or IPv6 hostname resolution\n" \
+       "       -c CNT          Send only CNT pings\n" \
+       "       -s SIZE         Send SIZE data bytes in packets (default=56)\n" \
+       "       -I iface/IP     Use interface or IP address as source\n" \
+       "       -q              Quiet mode, only displays output at start\n" \
+       "                       and when finished"
 #define ping6_trivial_usage \
        "[OPTION]... host"
 #define ping6_full_usage \
        "Send ICMP ECHO_REQUEST packets to network hosts" \
        "\n\nOptions:\n" \
-       "       -c CNT  Send only CNT pings\n" \
-       "       -s SIZE Send SIZE data bytes in packets (default=56)\n" \
-       "       -q      Quiet mode, only displays output at start\n" \
-       "               and when finished"
+       "       -c CNT          Send only CNT pings\n" \
+       "       -s SIZE         Send SIZE data bytes in packets (default=56)\n" \
+       "       -I iface/IP     Use interface or IP address as source\n" \
+       "       -q              Quiet mode, only displays output at start\n" \
+       "                       and when finished"
 #endif
 #define ping_example_usage \
        "$ ping localhost\n" \
index 66b1366..118fe3e 100644 (file)
@@ -203,7 +203,7 @@ len_and_sockaddr* xhost2sockaddr(const char *host, int port)
        return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR);
 }
 
-static len_and_sockaddr* xdotted2sockaddr(const char *host, int port)
+len_and_sockaddr* xdotted2sockaddr(const char *host, int port)
 {
        return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR);
 }
index e765843..9bf3813 100644 (file)
@@ -254,7 +254,7 @@ static union {
        struct sockaddr_in6 sin6;
 #endif
 } pingaddr;
-static struct sockaddr_in sourceaddr;
+static len_and_sockaddr *source_lsa;
 static int pingsock = -1;
 static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
 
@@ -561,9 +561,8 @@ static void ping4(len_and_sockaddr *lsa)
 
        pingsock = create_icmp_socket();
        pingaddr.sin = lsa->sin;
-       if (sourceaddr.sin_addr.s_addr) {
-               xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
-       }
+       if (source_lsa)
+               xbind(pingsock, &lsa->sa, lsa->len);
 
        /* enable broadcast pings */
        setsockopt_broadcast(pingsock);
@@ -572,13 +571,6 @@ static void ping4(len_and_sockaddr *lsa)
        sockopt = 48 * 1024; /* explain why 48k? */
        setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
 
-       printf("PING %s (%s)", hostname, dotted);
-       if (sourceaddr.sin_addr.s_addr) {
-               printf(" from %s",
-                       inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr));
-       }
-       printf(": %d data bytes\n", datalen);
-
        signal(SIGINT, pingstats);
 
        /* start the ping's going ... */
@@ -615,9 +607,9 @@ static void ping6(len_and_sockaddr *lsa)
 
        pingsock = create_icmp6_socket();
        pingaddr.sin6 = lsa->sin6;
-       //if (sourceaddr.sin_addr.s_addr) {
-       //      xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
-       //}
+       /* untested whether "-I addr" really works for IPv6: */
+       if (source_lsa)
+               xbind(pingsock, &lsa->sa, lsa->len);
 
 #ifdef ICMP6_FILTER
        {
@@ -652,8 +644,6 @@ static void ping6(len_and_sockaddr *lsa)
        if (if_index)
                pingaddr.sin6.sin6_scope_id = if_index;
 
-       printf("PING %s (%s): %d data bytes\n", hostname, dotted, datalen);
-
        signal(SIGINT, pingstats);
 
        /* start the ping's going ... */
@@ -695,25 +685,21 @@ static void ping6(len_and_sockaddr *lsa)
 }
 #endif
 
-/* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c
- * versions of below thing. BTW we have far too many "%u.%u.%u.%u" too...
-*/
-static int parse_nipquad(const char *str, struct sockaddr_in* addr)
+static void ping(len_and_sockaddr *lsa)
 {
-       char dummy;
-       unsigned i1, i2, i3, i4;
-       if (sscanf(str, "%u.%u.%u.%u%c",
-                          &i1, &i2, &i3, &i4, &dummy) == 4
-       && ( (i1|i2|i3|i4) <= 0xff )
-       ) {
-               uint8_t* ptr = (uint8_t*)&addr->sin_addr;
-               ptr[0] = i1;
-               ptr[1] = i2;
-               ptr[2] = i3;
-               ptr[3] = i4;
-               return 0;
+       printf("PING %s (%s)", hostname, dotted);
+       if (source_lsa) {
+               printf(" from %s",
+                       xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len));
        }
-       return 1; /* error */
+       printf(": %d data bytes\n", datalen);
+
+#if ENABLE_PING6
+       if (lsa->sa.sa_family == AF_INET6)
+               ping6(lsa);
+       else
+#endif
+               ping4(lsa);
 }
 
 int ping_main(int argc, char **argv);
@@ -732,9 +718,11 @@ int ping_main(int argc, char **argv)
        if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
        if (option_mask32 & OPT_I) { // -I
                if_index = if_nametoindex(opt_I);
-               if (!if_index)
-                       if (parse_nipquad(opt_I, &sourceaddr))
-                               bb_show_usage();
+               if (!if_index) {
+                       /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
+                       /* (ping doesn't support source IPv6 addresses yet anyway) */
+                       source_lsa = xdotted2sockaddr(opt_I, 0);
+               }
        }
        myid = (int16_t) getpid();
        hostname = argv[optind];
@@ -747,13 +735,13 @@ int ping_main(int argc, char **argv)
 #else
        lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
 #endif
+
+       if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
+               /* leaking it here... */
+               source_lsa = NULL;
+
        dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
-#if ENABLE_PING6
-       if (lsa->sa.sa_family == AF_INET6)
-               ping6(lsa);
-       else
-#endif
-               ping4(lsa);
+       ping(lsa);
        pingstats(0);
        return EXIT_SUCCESS;
 }