Changed errno to SOCKET_ERRNO/SOCKET_SET_ERRNO for Mac OT SocketsLib
[krb5.git] / src / lib / krb5 / os / localaddr.c
index 9287f3808dd0680c17ff7cfe9dbdaf9ddfdd11d7..f55c35c7899b2a63b0afe84dbccd5f2672a4bb15 100644 (file)
  * XNS support is untested, but "Should just work".
  */
 
-
 #define NEED_SOCKETS
 #include "k5-int.h"
 
-#ifndef _MSDOS
+#if !defined(HAVE_MACSOCK_H) && !defined(_MSDOS) && !defined(_WIN32)
 
 /* needed for solaris, harmless elsewhere... */
 #define BSD_COMP
@@ -47,7 +46,7 @@
  * type; you have to ask for one with a valid type.
  *
  */
-#ifdef KRB5_USE_INET
+#ifdef HAVE_NETINET_IN_H
 
 #include <netinet/in.h>
 
  * Add more address families here.
  */
 
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is 
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+
+
 extern int errno;
 
 /*
@@ -87,11 +104,12 @@ extern int errno;
  * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
  */
 
-krb5_error_code INTERFACE
-krb5_os_localaddr(addr)
-    krb5_address ***addr;
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_os_localaddr(context, addr)
+    krb5_context context;
+    krb5_address FAR * FAR * FAR *addr;
 {
-    struct ifreq *ifr;
+    struct ifreq *ifr, ifreq;
     struct ifconf ifc;
     int s, code, n, i;
     char buf[1024];
@@ -99,44 +117,43 @@ krb5_os_localaddr(addr)
     int n_found;
     int mem_err = 0;
     
+    memset(buf, 0, sizeof(buf));
     ifc.ifc_len = sizeof(buf);
     ifc.ifc_buf = buf;
-
+    
     s = socket (USE_AF, USE_TYPE, USE_PROTO);
     if (s < 0)
-       return errno;
+       return SOCKET_ERRNO;
 
     code = ioctl (s, SIOCGIFCONF, (char *)&ifc);
     if (code < 0) {
        int retval = errno;
-       close(s);
+       closesocket (s);
        return retval;
     }
-    n = ifc.ifc_len / sizeof (struct ifreq);
+    n = ifc.ifc_len;
     
-    for (n_found=0, i=0; i<n && ! mem_err; i++) {
+n_found = 0;
+    for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
        krb5_address *address;
-       ifr = &ifc.ifc_req[i];
-       
-       if (ioctl (s, SIOCGIFFLAGS, (char *)ifr) < 0)
+       ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i);
+
+       strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+       if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0)
            continue;
 
 #ifdef IFF_LOOPBACK
-       if (ifr->ifr_flags & IFF_LOOPBACK) 
+       if (ifreq.ifr_flags & IFF_LOOPBACK) 
            continue;
 #endif
 
-       if (!(ifr->ifr_flags & IFF_UP)) 
+       if (!(ifreq.ifr_flags & IFF_UP)) 
            /* interface is down; skip */
            continue;
 
-       if (ioctl (s, SIOCGIFADDR, (char *)ifr) < 0)
-           /* can't get address */
-           continue;
-
        /* ifr->ifr_addr has what we want! */
        switch (ifr->ifr_addr.sa_family) {
-#ifdef KRB5_USE_INET
+#ifdef HAVE_NETINET_IN_H
        case AF_INET:
            {
                struct sockaddr_in *in =
@@ -145,6 +162,7 @@ krb5_os_localaddr(addr)
                address = (krb5_address *)
                    malloc (sizeof(krb5_address));
                if (address) {
+                   address->magic = KV5M_ADDRESS;
                    address->addrtype = ADDRTYPE_INET;
                    address->length = sizeof(struct in_addr);
                    address->contents = (unsigned char *)malloc(address->length);
@@ -165,10 +183,11 @@ krb5_os_localaddr(addr)
            case AF_XNS:
            {  
                struct sockaddr_ns *ns =
-                   (struct sockaddr_ns *)&ifr->ifr_addr;                   
+                   (struct sockaddr_ns *)&ifr->ifr_addr;
                address = (krb5_address *)
                    malloc (sizeof (krb5_address) + sizeof (struct ns_addr));
                if (address) {
+                   address->magic = KV5M_ADDRESS;
                    address->addrtype = ADDRTYPE_XNS; 
 
                    /* XXX should we perhaps use ns_host instead? */
@@ -199,7 +218,7 @@ krb5_os_localaddr(addr)
            addr_temp[n_found++] = address;
        address = 0;
     }
-    close(s);
+    closesocket(s);
 
     *addr = (krb5_address **)malloc (sizeof (krb5_address *) * (n_found+1));
     if (*addr == 0)
@@ -220,14 +239,59 @@ krb5_os_localaddr(addr)
     return 0;
 }
 
-#else /* DOS version */
+#else /* Windows/Mac version */
+
+/*
+ * Hold on to your lunch!  Backup kludge method of obtaining your
+ * local IP address, courtesy of Windows Socket Network Programming,
+ * by Robert Quinn
+ */
+#if defined(_MSDOS) || defined(_WIN32)
+static struct hostent *local_addr_fallback_kludge()
+{
+       static struct hostent   host;
+       static SOCKADDR_IN      addr;
+       static char *           ip_ptrs[2];
+       SOCKET                  sock;
+       int                     size = sizeof(SOCKADDR);
+       int                     err;
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock == INVALID_SOCKET)
+               return NULL;
+
+       /* connect to arbitrary port and address (NOT loopback) */
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(IPPORT_ECHO);
+       addr.sin_addr.s_addr = inet_addr("204.137.220.51");
+
+       err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
+       if (err == SOCKET_ERROR)
+               return NULL;
+
+       err = getsockname(sock, (LPSOCKADDR) &addr, (int FAR *) size);
+       if (err == SOCKET_ERROR)
+               return NULL;
+
+       closesocket(sock);
+
+       host.h_name = 0;
+       host.h_aliases = 0;
+       host.h_addrtype = AF_INET;
+       host.h_length = 4;
+       host.h_addr_list = ip_ptrs;
+       ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
+       ip_ptrs[1] = NULL;
+
+       return &host;
+}
+#endif
 
 /* No ioctls in winsock so we just assume there is only one networking 
  * card per machine, so gethostent is good enough. 
  */
-
-krb5_error_code INTERFACE
-krb5_os_localaddr (krb5_address ***addr) {
+KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
+krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
     char host[64];                              /* Name of local machine */
     struct hostent *hostrec;
     int err;
@@ -236,16 +300,23 @@ krb5_os_localaddr (krb5_address ***addr) {
     if (*addr == NULL)
         return ENOMEM;
 
+    err = 0;
+    
     if (gethostname (host, sizeof(host))) {
-        err = WSAGetLastError();
-        return err;
+        err = SOCKET_ERRNO;
+    }
+
+    if (!err) {
+           hostrec = gethostbyname (host);
+           if (hostrec == NULL) {
+                   err = SOCKET_ERRNO;
+           }
     }
-        
 
-    hostrec = gethostbyname (host);
-    if (hostrec == NULL) {
-        err = WSAGetLastError();
-        return err;
+    if (err) {
+           hostrec = local_addr_fallback_kludge();
+           if (!hostrec)
+                   return err;
     }
 
     (*addr)[0] = calloc (1, sizeof(krb5_address));
@@ -253,19 +324,22 @@ krb5_os_localaddr (krb5_address ***addr) {
         free (*addr);
         return ENOMEM;
     }
-    (*addr)[0]->addrtype = ADDRTYPE_INET;
-    (*addr)[0]->length = sizeof(struct in_addr);
+    (*addr)[0]->magic = KV5M_ADDRESS;
+    (*addr)[0]->addrtype = hostrec->h_addrtype;
+    (*addr)[0]->length = hostrec->h_length;
     (*addr)[0]->contents = (unsigned char *)malloc((*addr)[0]->length);
     if (!(*addr)[0]->contents) {
         free((*addr)[0]);
         free(*addr);
         return ENOMEM;
     } else {
-        memcpy ((char *)(*addr)[0]->contents,
-                (char *)hostrec->h_addr,
+        memcpy ((*addr)[0]->contents,
+                hostrec->h_addr,
                 (*addr)[0]->length);
     }
+       /* FIXME, deal with the case where gethostent returns multiple addrs */
 
     return(0);
 }
 #endif
+