1 /*
   2  * Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 #pragma ident   "%Z%%M% %I%     %E% SMI"
   7 
   8 /*
   9  * Utility functions which help with address and hostname manipulation in a
  10  * mixed IPv4 / IPv6 environment.
  11  */
  12 
  13 #include "config.h"
  14 #include <sys/socket.h>
  15 #include <netinet/in.h>
  16 #include <arpa/inet.h>
  17 #include <netdb.h>
  18 #include <string.h>
  19 #include "proto.h"
  20 
  21 #ifndef MAXHOSTNAMELEN
  22 #define MAXHOSTNAMELEN 64
  23 #endif
  24 
  25 /* Converts a hostname into an IP address in presentation form */
  26 char *inet_htop(const char *hostname)
  27 {
  28 #ifdef INET6
  29     static char abuf[INET6_ADDRSTRLEN];
  30     struct addrinfo hints, *result;
  31     char *str = NULL;
  32     void *addr = NULL;
  33 
  34     memset(&hints, 0, sizeof(hints));
  35     hints.ai_flags = AI_CANONNAME;
  36     hints.ai_family = PF_UNSPEC;
  37 
  38     if (getaddrinfo(hostname, NULL, &hints, &result) == 0) {
  39         if (result->ai_family == AF_INET)
  40             addr = ((void *)&((struct sockaddr_in *)result->ai_addr)->sin_addr);
  41         else if (result->ai_family == AF_INET6)
  42             addr = ((void *)&((struct sockaddr_in6 *)result->ai_addr)->sin6_addr);
  43         if (addr)
  44             str = (char *)inet_ntop_native(result->ai_family, addr, abuf,
  45                                            sizeof(abuf));
  46         freeaddrinfo(result);
  47         return str;
  48     }
  49 #else
  50     struct hostent *hp;
  51     struct in_addr in;
  52 
  53     if ((hp = gethostbyname(hostname)) != NULL) {
  54         memcpy(&in, hp->h_addr, sizeof(in));
  55         return inet_ntoa(in);
  56     }
  57 #endif
  58     return NULL;
  59 }
  60 
  61 /*
  62  * Converts a socket structures IP address into presentation form.
  63  * Note: returns a pointer to a buffer which is overwritten on each call.
  64  */
  65 char *inet_stop(struct SOCKSTORAGE *ss)
  66 {
  67 #ifdef INET6
  68     static char abuf[INET6_ADDRSTRLEN];
  69     struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
  70 
  71     if (ss->ss_family == AF_INET6)
  72         return (char *)inet_ntop_native(AF_INET6, &sin6->sin6_addr, abuf, sizeof (abuf));
  73 #endif
  74     return inet_ntoa(((struct sockaddr_in *)ss)->sin_addr);
  75 }
  76 
  77 char *wu_gethostbyname(const char *hostname)
  78 {
  79 #ifdef INET6
  80     static char hostbuf[MAXHOSTNAMELEN];
  81     struct addrinfo hints, *result;
  82 
  83     memset(&hints, 0, sizeof(hints));
  84     hints.ai_flags = AI_CANONNAME;
  85     hints.ai_family = PF_UNSPEC;
  86 
  87     if (getaddrinfo(hostname, NULL, &hints, &result) == 0) {
  88         strncpy(hostbuf, result->ai_canonname, sizeof(hostbuf));
  89         hostbuf[sizeof(hostbuf) - 1] = '\0';
  90         freeaddrinfo(result);
  91         return hostbuf;
  92     }
  93 #else
  94     struct hostent *hp = gethostbyname(hostname);
  95 
  96     if (hp)
  97         return hp->h_name;
  98 #endif
  99     return NULL;
 100 }
 101 
 102 int wu_gethostbyaddr(struct SOCKSTORAGE *ss, char *hostname, int hostlen)
 103 {
 104 #ifdef INET6
 105     char hostbuf[NI_MAXHOST];
 106 #else
 107     struct hostent *hp;
 108 #endif
 109 
 110     if ((ss == NULL) || (hostname == NULL) || (hostlen < 1))
 111         return 0;
 112 
 113 #ifdef INET6
 114     if (getnameinfo((struct sockaddr *)ss, SOCK_LEN(*ss), hostbuf,
 115                     sizeof(hostbuf), NULL, 0, NI_NAMEREQD) == 0) {
 116         strncpy(hostname, hostbuf, hostlen);
 117         hostname[hostlen - 1] = '\0';
 118         return 1;
 119     }
 120 #else
 121     hp = gethostbyaddr((char *)&ss->sin_addr, sizeof(struct in_addr), AF_INET);
 122     if (hp) {
 123         strncpy(hostname, hp->h_name, hostlen);
 124         hostname[hostlen - 1] = '\0';
 125         return 1;
 126     }
 127 #endif
 128     return 0;
 129 }
 130 
 131 /* Compares a socket structures IP address with addr, returning 0 on a match */
 132 int sock_cmp_inaddr(struct SOCKSTORAGE *ss, struct in_addr addr) {
 133 #ifdef INET6
 134     if (ss->ss_family == AF_INET6) {
 135         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
 136 
 137         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
 138             u_char *a = (u_char *)&sin6->sin6_addr;
 139 
 140             /* compare the IPv4 part of an IPv4-mapped IPv6 address */
 141             return memcmp(&addr, a + sizeof(struct in6_addr) - sizeof(struct in_addr), sizeof(struct in_addr));
 142         }
 143         return 1;
 144     }
 145 #endif
 146     return ((struct sockaddr_in *)ss)->sin_addr.s_addr != addr.s_addr;
 147 }
 148 
 149 #ifdef INET6
 150 /* Sets a socket structures IP address to addr */
 151 void sock_set_inaddr(struct SOCKSTORAGE *ss, struct in_addr addr) {
 152     if (ss->ss_family == AF_INET6) {
 153         struct in6_addr *in6;
 154 
 155         in6 = &((struct sockaddr_in6 *)ss)->sin6_addr;
 156         memset(&in6->s6_addr[0], 0, 10);
 157         memset(&in6->s6_addr[10], 0xff, 2);
 158         memcpy(&in6->s6_addr[12], &addr, sizeof(struct in_addr));
 159         return;
 160     }
 161     ((struct sockaddr_in *)ss)->sin_addr = addr;
 162 }
 163 
 164 /* Compares two socket structure IP addresses, returning 0 if they match */
 165 int sock_cmp_addr(struct SOCKSTORAGE *ss1, struct SOCKSTORAGE *ss2) {
 166     if (ss1->ss_family == AF_INET6) {
 167         if (ss2->ss_family == AF_INET6)
 168             return memcmp(&((struct sockaddr_in6 *)ss1)->sin6_addr,
 169                           &((struct sockaddr_in6 *)ss2)->sin6_addr,
 170                           sizeof(struct in6_addr));
 171         return sock_cmp_inaddr(ss1, ((struct sockaddr_in *)ss2)->sin_addr);
 172     }
 173     return sock_cmp_inaddr(ss2, ((struct sockaddr_in *)ss1)->sin_addr);
 174 }
 175 
 176 void sock_set_scope(struct SOCKSTORAGE *dst, struct SOCKSTORAGE *src) {
 177 #ifdef HAVE_SIN6_SCOPE_ID
 178     struct sockaddr_in6 *src_in6 = (struct sockaddr_in6 *)src;
 179     struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *)dst;
 180 
 181     if (dst->ss_family == AF_INET6) {
 182         if ((src->ss_family == AF_INET6) &&
 183             (memcmp(&src_in6->sin6_addr, &dst_in6->sin6_addr,
 184                     sizeof(struct in6_addr)) == 0))
 185             dst_in6->sin6_scope_id = src_in6->sin6_scope_id;
 186         else
 187             dst_in6->sin6_scope_id = 0;
 188     }
 189 #endif
 190 }
 191 
 192 /*
 193  * Similar to inet_pton(), str can be an IPv4 or IPv6 address, but an IPv6
 194  * address is returned in addr.
 195  */
 196 int inet_pton6(char *str, struct in6_addr *addr)
 197 {
 198     struct in_addr v4addr;
 199 
 200     /* Try v6 first */
 201     if (inet_pton(AF_INET6, str, addr) != 1) {
 202         /* If that fails, try v4 and map it */
 203         if (inet_pton(AF_INET, str, &v4addr) == 1) {
 204             memset(&addr->s6_addr[0], 0, 10);
 205             memset(&addr->s6_addr[10], 0xff, 2);
 206             memcpy(&addr->s6_addr[12], &v4addr, sizeof(struct in_addr));
 207         }
 208         else
 209             return 0;
 210     }
 211     return 1;
 212 }
 213 
 214 /*
 215  * Similar to inet_ntop(), except when addr is an IPv4-mapped IPv6 address
 216  * returns a printable IPv4 address (not an IPv4-mapped IPv6 address).
 217  */
 218 const char *inet_ntop_native(int af, const void *addr, char *dst, size_t size)
 219 {
 220     const char *result;
 221 
 222     if (af == AF_INET6) {
 223         if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr))
 224             result = inet_ntop(AF_INET, (char *)addr + sizeof(struct in6_addr)
 225                                - sizeof(struct in_addr), dst, size);
 226         else
 227             result = inet_ntop(AF_INET6, addr, dst, size);
 228     }
 229     else
 230         result = inet_ntop(af, addr, dst, size);
 231     return result;
 232 }
 233 #endif /* INET6 */