]> sjero.net Git - iperf/blobdiff - src/SocketAddr.c
Native IPv6 support for iperf
[iperf] / src / SocketAddr.c
index 0278bdf935260ccb37cdf02526220bd8d4380f4b..6e96e81a6d27261b91c25198314c46a7020f3409 100644 (file)
 #ifdef __cplusplus
 extern "C" {
 #endif
-/* -------------------------------------------------------------------
- * Create a socket address. If inHostname is not null, resolve that
- * address and fill it in. Fill in the port number. Use IPv6 ADDR_ANY
- * if that is what is desired.
- * ------------------------------------------------------------------- */
-
-void SockAddr_remoteAddr( thread_Settings *inSettings ) {
-    SockAddr_zeroAddress( &inSettings->peer );
-    if ( inSettings->mHost != NULL ) {
-        SockAddr_setHostname( inSettings->mHost, &inSettings->peer, 
-                              isIPV6( inSettings ) );
-    } else {
-#ifdef HAVE_IPV6
-        if ( isIPV6( inSettings ) ) {
-            ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET6;
-        } else {
-            ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
-        }
-    }
-
-    if ( SockAddr_isIPv6( &inSettings->peer ) ) {
-        inSettings->size_peer = sizeof( struct sockaddr_in6 );
-    } else {
-        inSettings->size_peer = sizeof( struct sockaddr_in );
-    }
-#else
-        ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
-    }
-    inSettings->size_peer = sizeof( struct sockaddr_in );
-#endif
-    SockAddr_setPort( &inSettings->peer, inSettings->mPort );
-}
-// end SocketAddr
-
-void SockAddr_localAddr( thread_Settings *inSettings ) {
-    SockAddr_zeroAddress( &inSettings->local );
-    if ( inSettings->mLocalhost != NULL ) {
-        SockAddr_setHostname( inSettings->mLocalhost, &inSettings->local, 
-                              isIPV6( inSettings ) );
-    } else {
-#ifdef HAVE_IPV6
-        if ( isIPV6( inSettings ) ) {
-            ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET6;
-        } else {
-            ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
-        }
-    }
-
-    if ( SockAddr_isIPv6( &inSettings->local ) ) {
-        inSettings->size_local = sizeof( struct sockaddr_in6 );
-    } else {
-        inSettings->size_local = sizeof( struct sockaddr_in );
-    }
-#else
-        ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
-    }
-        inSettings->size_local = sizeof( struct sockaddr_in );
-#endif
-    SockAddr_setPort( &inSettings->local, inSettings->mPort );
-}
-// end SocketAddr
-
-/* -------------------------------------------------------------------
- * Resolve the hostname address and fill it in.
- * ------------------------------------------------------------------- */
-
-void SockAddr_setHostname( const char* inHostname, 
-                           iperf_sockaddr *inSockAddr, 
-                           int isIPv6 ) {
-
-    // ..I think this works for both ipv6 & ipv4... we'll see
-#if defined(HAVE_IPV6)
-    {
-        struct addrinfo *res, *itr;
-        int ret_ga;
-
-        ret_ga = getaddrinfo(inHostname, NULL, NULL, &res);
-        if ( ret_ga ) {
-            fprintf(stderr, "error: %s\n", gai_strerror(ret_ga));
-            exit(1);
-        }
-        if ( !res->ai_addr ) {
-            fprintf(stderr, "getaddrinfo failed to get an address... target was '%s'\n", inHostname);
-            exit(1);
-        }
-
-        // Check address type before filling in the address
-        // ai_family = PF_xxx; ai_protocol = IPPROTO_xxx, see netdb.h
-        // ...but AF_INET6 == PF_INET6
-        itr = res;
-        if ( isIPv6 ) {
-            // First check all results for a IPv6 Address
-            while ( itr != NULL ) {
-                if ( itr->ai_family == AF_INET6 ) {
-                    memcpy(inSockAddr, (itr->ai_addr),
-                           (itr->ai_addrlen));
-                    freeaddrinfo(res);
-                    return;
-                } else {
-                    itr = itr->ai_next;
-                }
-            }
-        }
-        itr = res;
-        // Now find a IPv4 Address
-        while ( itr != NULL ) {
-            if ( itr->ai_family == AF_INET ) {
-                memcpy(inSockAddr, (itr->ai_addr),
-                       (itr->ai_addrlen));
-                freeaddrinfo(res);
-                return;
-            } else {
-                itr = itr->ai_next;
-            }
-        }
-    }
-#else
-    // first try just converting dotted decimal
-    // on Windows gethostbyname doesn't understand dotted decimal
-    int rc = inet_pton( AF_INET, inHostname, 
-                        (unsigned char*)&(((struct sockaddr_in*)inSockAddr)->sin_addr) );
-    inSockAddr->sin_family = AF_INET;
-    if ( rc == 0 ) {
-        struct hostent *hostP = gethostbyname( inHostname );
-        if ( hostP == NULL ) {
-            /* this is the same as herror() but works on more systems */
-            const char* format;
-            switch ( h_errno ) {
-                case HOST_NOT_FOUND:
-                    format = "%s: Unknown host\n";
-                    break;
-                case NO_ADDRESS:
-                    format = "%s: No address associated with name\n";
-                    break;
-                case NO_RECOVERY:
-                    format = "%s: Unknown server error\n";
-                    break;
-                case TRY_AGAIN:
-                    format = "%s: Host name lookup failure\n";
-                    break;
-
-                default:
-                    format = "%s: Unknown resolver error\n";
-                    break;
-            }
-            fprintf( stderr, format, inHostname );
-            exit(1);
-
-            return; // TODO throw
-        }
-
-        memcpy(&(((struct sockaddr_in*)inSockAddr)->sin_addr), *(hostP->h_addr_list),
-               (hostP->h_length));
-    }
-#endif
-}
-// end setHostname
-
-/* -------------------------------------------------------------------
- * Copy the IP address into the string.
- * ------------------------------------------------------------------- */
-void SockAddr_getHostAddress( iperf_sockaddr *inSockAddr, char* outAddress, 
-                                size_t len ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET ) {
-        inet_ntop( AF_INET, &(((struct sockaddr_in*) inSockAddr)->sin_addr), 
-                   outAddress, len);
-    }
-#ifdef HAVE_IPV6
-    else {
-        inet_ntop( AF_INET6, &(((struct sockaddr_in6*) inSockAddr)->sin6_addr), 
-                   outAddress, len);
-    }
-#endif
-}
-// end getHostAddress
 
-/* -------------------------------------------------------------------
- * Set the address to any (generally all zeros).
- * ------------------------------------------------------------------- */
-
-void SockAddr_setAddressAny( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        memset( &(((struct sockaddr_in*) inSockAddr)->sin_addr), 0, 
-                sizeof( struct in_addr ));
-#if defined(HAVE_IPV6)  
-    else
-        memset( &(((struct sockaddr_in6*) inSockAddr)->sin6_addr), 0, 
-                sizeof( struct in6_addr ));
-#endif
+/* return the port of @ss, -1 if undefined */
+int SockAddr_port(struct sockaddr_storage *ss)
+{
+       if (ss->ss_family == AF_INET)
+               return ntohs(((struct sockaddr_in *)ss)->sin_port);
+       else if (ss->ss_family == AF_INET6)
+               return ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
+       return -1;
 }
-// end setAddressAny
 
 /* -------------------------------------------------------------------
- * Set the port to the given port. Handles the byte swapping.
- * ------------------------------------------------------------------- */
-
-void SockAddr_setPort( iperf_sockaddr *inSockAddr, unsigned short inPort ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        ((struct sockaddr_in*) inSockAddr)->sin_port = htons( inPort );
-#if defined(HAVE_IPV6)  
-    else
-        ((struct sockaddr_in6*) inSockAddr)->sin6_port = htons( inPort );
+ * Print socket name into output (must be INET6_ADDRSTRLEN long).
+ * This one works around the quirk of inet_ntop/getaddrinfo to represent
+ * print IPv4-mapped-IPv6 addresses as sockaddr_in6 (::FFFF:x.x.x.x).
+ * Make the #ifndef an `#ifdef' to disable this conversion.
+ * ------------------------------------------------------------------- */
+const char *SockAddr_name(struct sockaddr_storage *ss, char *hbuf, size_t hbuflen)
+{
+       int af = ss->ss_family;
+       const void *ptr;
+
+       if (ss->ss_family == AF_INET)
+               ptr = &((struct sockaddr_in *)ss)->sin_addr;
+       else if (ss->ss_family == AF_INET6) {
+               const struct in6_addr *v6_addr;
+
+               ptr = v6_addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
+#ifndef I_WANT_V4_MAPPED_IN_MY_OUTPUT
+               if (IN6_IS_ADDR_V4MAPPED(v6_addr)) {
+                       struct in_addr ia;
+
+                       // extract the IPv4 address from the last 4 bytes
+                       memcpy(&ia.s_addr, &v6_addr->s6_addr[12], 4);
+                       ptr = &ia;
+                       af  = AF_INET;
+               }
 #endif
-
-}
-// end setPort
-
-/* -------------------------------------------------------------------
- * Set the port to zero, which lets the OS pick the port.
- * ------------------------------------------------------------------- */
-
-void SockAddr_setPortAny( iperf_sockaddr *inSockAddr ) {
-    SockAddr_setPort( inSockAddr, 0 );
-}
-// end setPortAny
-
-/* -------------------------------------------------------------------
- * Return the port. Handles the byte swapping.
- * ------------------------------------------------------------------- */
-
-unsigned short SockAddr_getPort( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        return ntohs( ((struct sockaddr_in*) inSockAddr)->sin_port );
-#if defined(HAVE_IPV6)
-    else
-        return ntohs( ((struct sockaddr_in6*) inSockAddr)->sin6_port);
-#endif
-    return 0;
-
-}
-// end getPort
-
-/* -------------------------------------------------------------------
- * Return the IPv4 Internet Address from the sockaddr_in structure
- * ------------------------------------------------------------------- */
-
-struct in_addr* SockAddr_get_in_addr( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        return &(((struct sockaddr_in*) inSockAddr)->sin_addr);
-
-    fprintf(stderr, "FATAL: get_in_addr called on IPv6 address\n");
-    return NULL;
-}
-
-/* -------------------------------------------------------------------
- * Return the IPv6 Internet Address from the sockaddr_in6 structure
- * ------------------------------------------------------------------- */
-#ifdef HAVE_IPV6
-struct in6_addr* SockAddr_get_in6_addr( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 )
-        return &(((struct sockaddr_in6*) inSockAddr)->sin6_addr);
-
-    fprintf(stderr, "FATAL: get_in6_addr called on IPv4 address\n");
-    return NULL;
-}
-#endif
-
-
-/* -------------------------------------------------------------------
- * Return the size of the appropriate address structure.
- * ------------------------------------------------------------------- */
-
-Socklen_t SockAddr_get_sizeof_sockaddr( iperf_sockaddr *inSockAddr ) {
-
-#if defined(HAVE_IPV6)
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 ) {
-        return(sizeof(struct sockaddr_in6));
-    }
-#endif
-    return(sizeof(struct sockaddr_in));
-}
-// end get_sizeof_sockaddr
-
-
-/* -------------------------------------------------------------------
- * Return if IPv6 socket
- * ------------------------------------------------------------------- */
-
-int SockAddr_isIPv6( iperf_sockaddr *inSockAddr ) {
-
-#if defined(HAVE_IPV6)
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 ) {
-        return 1;
-    }
-#endif
-    return 0;
+        } else {
+               return NULL;    // undetermined family (e.g. AF_UNSPEC)
+       }
+       return inet_ntop(af, ptr, hbuf, hbuflen);
 }
-// end get_sizeof_sockaddr
-
-/* -------------------------------------------------------------------
- * Return true if the address is a IPv4 multicast address.
- * ------------------------------------------------------------------- */
 
-int SockAddr_isMulticast( iperf_sockaddr *inSockAddr ) {
-
-#if defined(HAVE_IPV6)
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 ) {
-        return( IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr) ));
-    } else
-#endif
-    {
-        // 224.0.0.0 to 239.255.255.255 (e0.00.00.00 to ef.ff.ff.ff)
-        const unsigned long kMulticast_Mask = 0xe0000000L;
-
-        return(kMulticast_Mask ==
-               (ntohl( ((struct sockaddr_in*) inSockAddr)->sin_addr.s_addr) & kMulticast_Mask));
-    }
+static int SockAddr_AF_are_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second)
+{
+       return first->ss_family == second->ss_family;
 }
-// end isMulticast
 
 /* -------------------------------------------------------------------
- * Zero out the address structure.
+ * Compare two sockaddrs and return true if the hosts are equal
  * ------------------------------------------------------------------- */
-
-void SockAddr_zeroAddress( iperf_sockaddr *inSockAddr ) {
-    memset( inSockAddr, 0, sizeof( iperf_sockaddr ));
+int SockAddr_Hostare_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second)
+{
+       if (!SockAddr_AF_are_Equal(first, second))
+               return 0;
+       if (first->ss_family == AF_INET)
+               return !memcmp(&((struct sockaddr_in *)first)->sin_addr,
+                              &((struct sockaddr_in *)second)->sin_addr, sizeof(struct in_addr));
+       if (first->ss_family == AF_INET6)
+               return !memcmp(&((struct sockaddr_in6 *)first)->sin6_addr,
+                              &((struct sockaddr_in6 *)second)->sin6_addr, sizeof(struct in6_addr));
+       return 0;
 }
-// zeroAddress
 
 /* -------------------------------------------------------------------
- * Compare two sockaddrs and return true if they are equal
+ * Compare two socket structs and return true if they are equal
  * ------------------------------------------------------------------- */
-int SockAddr_are_Equal( struct sockaddr* first, struct sockaddr* second ) {
-    if ( first->sa_family == AF_INET && second->sa_family == AF_INET ) {
-        // compare IPv4 adresses
-        return( ((long) ((struct sockaddr_in*)first)->sin_addr.s_addr == (long) ((struct sockaddr_in*)second)->sin_addr.s_addr)
-                && ( ((struct sockaddr_in*)first)->sin_port == ((struct sockaddr_in*)second)->sin_port) );
-    }
-#if defined(HAVE_IPV6)      
-    if ( first->sa_family == AF_INET6 && second->sa_family == AF_INET6 ) {
-        // compare IPv6 addresses
-        return( !memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr, ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr)) 
-                && (((struct sockaddr_in6*)first)->sin6_port == ((struct sockaddr_in6*)second)->sin6_port) );
-    }
-#endif 
-    return 0;
-
+int SockAddr_are_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second)
+{
+       if (!SockAddr_Hostare_Equal(first, second))
+               return 0;
+       if (first->ss_family == AF_INET)
+               return ((struct sockaddr_in*)first)->sin_port == ((struct sockaddr_in*)second)->sin_port;
+       if (first->ss_family == AF_INET6)
+               return (((struct sockaddr_in6*)first)->sin6_port == ((struct sockaddr_in6*)second)->sin6_port);
+       return 0;
 }
 
-/* -------------------------------------------------------------------
- * Compare two sockaddrs and return true if the hosts are equal
- * ------------------------------------------------------------------- */
-int SockAddr_Hostare_Equal( struct sockaddr* first, struct sockaddr* second ) {
-    if ( first->sa_family == AF_INET && second->sa_family == AF_INET ) {
-        // compare IPv4 adresses
-        return( (long) ((struct sockaddr_in*)first)->sin_addr.s_addr == 
-                (long) ((struct sockaddr_in*)second)->sin_addr.s_addr);
-    }
-#if defined(HAVE_IPV6)      
-    if ( first->sa_family == AF_INET6 && second->sa_family == AF_INET6 ) {
-        // compare IPv6 addresses
-        return( !memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr, 
-                        ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr)));
-    }
-#endif 
-    return 0;
-
-}
 #ifdef __cplusplus
 } /* end extern "C" */
 #endif