2005-11-16 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / socket-io.c
index a0caca9c366efce4970b870132c846936c8b7c56..3e893ed24afe32224a1933cf706a6e3772b40d9e 100644 (file)
@@ -1,10 +1,12 @@
 /*
  * socket-io.c: Socket IO internal calls
  *
- * Author:
+ * Authors:
  *     Dick Porter (dick@ximian.com)
+ *     Gonzalo Paniagua Javier (gonzalo@ximian.com)
  *
  * (C) 2001 Ximian, Inc.
+ * Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
  */
 
 #include <config.h>
 #include <mono/metadata/assembly.h>
 #include <mono/metadata/appdomain.h>
 #include <mono/metadata/threads.h>
+#include <mono/metadata/threads-types.h>
+#include <mono/utils/mono-poll.h>
 /* FIXME change this code to not mess so much with the internals */
 #include <mono/metadata/class-internals.h>
+#include <mono/metadata/threadpool-internals.h>
 
 #include <sys/time.h> 
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
 
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
@@ -450,7 +461,9 @@ static gint32 convert_sockopt_level_and_name(MonoSocketOptionLevel mono_level,
                        *system_name = IPV6_LEAVE_GROUP;
                        break;
                case SocketOptionName_PacketInformation:
+#ifdef HAVE_IPV6_PKTINFO
                        *system_name = IPV6_PKTINFO;
+#endif
                        break;
                case SocketOptionName_HeaderIncluded:
                case SocketOptionName_IPOptions:
@@ -666,7 +679,10 @@ void ves_icall_System_Net_Sockets_Socket_Close_internal(SOCKET sock,
 #endif
 
        *error = 0;
-       
+
+       /* Clear any pending work item from this socket if the underlying
+        * polling system does not notify when the socket is closed */
+       mono_thread_pool_remove_socket (GPOINTER_TO_INT (sock));
        closesocket(sock);
 }
 
@@ -1042,12 +1058,12 @@ ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode,
        struct timeval tv;
        struct timeval *tvptr;
        div_t divvy;
+       time_t start;
 
        MONO_ARCH_SAVE_REGS;
 
+       start = time (NULL);
        do {
-               /* FIXME: in case of extra iteration (WSAEINTR), substract time
-                * from the initial timeout */
                *error = 0;
                FD_ZERO (&fds);
                _wapi_FD_SET (sock, &fds);
@@ -1069,6 +1085,17 @@ ves_icall_System_Net_Sockets_Socket_Poll_internal (SOCKET sock, gint mode,
                } else {
                        g_assert_not_reached ();
                }
+
+               if (timeout > 0 && ret < 0) {
+                       int err = errno;
+                       int sec = time (NULL) - start;
+
+                       timeout -= sec * 1000;
+                       if (timeout < 0)
+                               timeout = 0;
+                       errno = err;
+               }
+
        } while ((ret == SOCKET_ERROR) && (*error == WSAGetLastError ()) == WSAEINTR);
 
        return (ret != SOCKET_ERROR && _wapi_FD_ISSET (sock, &fds));
@@ -1263,165 +1290,122 @@ static SOCKET Socket_to_SOCKET(MonoObject *sockobj)
        return(sock);
 }
 
-void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **read_socks, MonoArray **write_socks, MonoArray **err_socks, gint32 timeout, gint32 *error)
+#define POLL_ERRORS (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL)
+void ves_icall_System_Net_Sockets_Socket_Select_internal(MonoArray **sockets, gint32 timeout, gint32 *error)
 {
-       fd_set readfds, writefds, errfds;
-       fd_set *readptr = NULL, *writeptr = NULL, *errptr = NULL;
-       struct timeval tv;
-       div_t divvy;
+       MonoThread *thread = NULL;
+       MonoObject *obj;
+       mono_pollfd *pfds;
+       int nfds, idx;
        int ret;
-       int readarrsize = 0, writearrsize = 0, errarrsize = 0;
-       MonoDomain *domain=mono_domain_get();
+       int i, count;
+       int mode;
        MonoClass *sock_arr_class;
        MonoArray *socks;
-       int count;
-       int i;
-       SOCKET handle;
+       time_t start;
        
        MONO_ARCH_SAVE_REGS;
 
-       if (*read_socks)
-               readarrsize=mono_array_length(*read_socks);
-
-       *error = 0;
-       
-       if(readarrsize>FD_SETSIZE) {
-               *error = WSAEFAULT;
-               return;
-       }
-       
-       if (readarrsize) {
-               readptr = &readfds;
-               FD_ZERO(&readfds);
-               for(i=0; i<readarrsize; i++) {
-                       handle = Socket_to_SOCKET(mono_array_get(*read_socks, MonoObject *, i));
-                       _wapi_FD_SET(handle, &readfds);
-               }
-       }
-       
-       if (*write_socks)
-               writearrsize=mono_array_length(*write_socks);
-
-       if(writearrsize>FD_SETSIZE) {
-               *error = WSAEFAULT;
-               return;
-       }
-       
-       if (writearrsize) {
-               writeptr = &writefds;
-               FD_ZERO(&writefds);
-               for(i=0; i<writearrsize; i++) {
-                       handle = Socket_to_SOCKET(mono_array_get(*write_socks, MonoObject *, i));
-                       _wapi_FD_SET(handle, &writefds);
+       /* *sockets -> READ, null, WRITE, null, ERROR, null */
+       count = mono_array_length (*sockets);
+       nfds = count - 3; /* NULL separators */
+       pfds = g_new0 (mono_pollfd, nfds);
+       mode = idx = 0;
+       for (i = 0; i < count; i++) {
+               obj = mono_array_get (*sockets, MonoObject *, i);
+               if (obj == NULL) {
+                       mode++;
+                       continue;
                }
-       }
-       
-       if (*err_socks)
-               errarrsize=mono_array_length(*err_socks);
 
-       if(errarrsize>FD_SETSIZE) {
-               *error = WSAEFAULT;
-               return;
-       }
-       
-       if (errarrsize) {
-               errptr = &errfds;
-               FD_ZERO(&errfds);
-               for(i=0; i<errarrsize; i++) {
-                       handle = Socket_to_SOCKET(mono_array_get(*err_socks, MonoObject *, i));
-                       _wapi_FD_SET(handle, &errfds);
-               }
+               pfds [idx].fd = GPOINTER_TO_INT (Socket_to_SOCKET (obj));
+               pfds [idx].events = (mode == 0) ? MONO_POLLIN : (mode == 1) ? MONO_POLLOUT : POLL_ERRORS;
+               idx++;
        }
 
-       /* Negative timeout meaning block until ready is only
-        * specified in Poll, not Select
-        */
-
-       divvy = div (timeout, 1000000);
-       
+       timeout = (timeout >= 0) ? (timeout / 1000) : -1;
+       start = time (NULL);
        do {
-               if(timeout>=0) {
-                       tv.tv_sec=divvy.quot;
-                       tv.tv_usec=divvy.rem;
+               *error = 0;
+               ret = mono_poll (pfds, nfds, timeout);
+               if (timeout > 0 && ret < 0) {
+                       int err = errno;
+                       int sec = time (NULL) - start;
+
+                       timeout -= sec * 1000;
+                       if (timeout < 0)
+                               timeout = 0;
+                       errno = err;
+               }
 
-                       ret = _wapi_select (0, readptr, writeptr, errptr, &tv);
-               } else {
-                       ret = _wapi_select (0, readptr, writeptr, errptr, NULL);
+               if (ret == -1 && errno == EINTR) {
+                       int leave = 0;
+                       if (thread == NULL)
+                               thread = mono_thread_current ();
+
+                       mono_monitor_enter (thread->synch_lock);
+                       leave = ((thread->state & ThreadState_AbortRequested) != 0 || 
+                                (thread->state & ThreadState_StopRequested) != 0);
+                       mono_monitor_exit (thread->synch_lock);
+                       if (leave != 0) {
+                               g_free (pfds);
+                               *sockets = NULL;
+                               return;
+                       } else {
+                               /* Suspend requested? */
+                               mono_thread_interruption_checkpoint ();
+                       }
+                       errno = EINTR;
                }
-       } while ((ret==SOCKET_ERROR) && (WSAGetLastError() == WSAEINTR));
+       } while (ret == -1 && errno == EINTR);
        
-       if(ret==SOCKET_ERROR) {
+       if (ret == -1) {
+#ifdef PLATFORM_WIN32
                *error = WSAGetLastError ();
+#else
+               *error = errno_to_WSA (errno, __func__);
+#endif
+               g_free (pfds);
                return;
        }
 
-       if (readarrsize) {
-               sock_arr_class=((MonoObject *)*read_socks)->vtable->klass;
-               
-               count=0;
-               for(i=0; i<readarrsize; i++) {
-                       if(_wapi_FD_ISSET(Socket_to_SOCKET(mono_array_get(*read_socks, MonoObject *, i)), &readfds)) {
-                               count++;
-                       }
-               }
-               socks=mono_array_new_full(domain, sock_arr_class, &count, NULL);
-               count=0;
-               for(i=0; i<readarrsize; i++) {
-                       MonoObject *sock=mono_array_get(*read_socks, MonoObject *, i);
-                       
-                       if(_wapi_FD_ISSET(Socket_to_SOCKET(sock), &readfds)) {
-                               mono_array_set(socks, MonoObject *, count, sock);
-                               count++;
-                       }
-               }
-               *read_socks=socks;
-       } else {
-               *read_socks = NULL;
+       if (ret == 0) {
+               g_free (pfds);
+               *sockets = NULL;
+               return;
        }
 
-       if (writearrsize) {
-               sock_arr_class=((MonoObject *)*write_socks)->vtable->klass;
-               count=0;
-               for(i=0; i<writearrsize; i++) {
-                       if(_wapi_FD_ISSET(Socket_to_SOCKET(mono_array_get(*write_socks, MonoObject *, i)), &writefds)) {
-                               count++;
-                       }
-               }
-               socks=mono_array_new_full(domain, sock_arr_class, &count, NULL);
-               count=0;
-               for(i=0; i<writearrsize; i++) {
-                       MonoObject *sock=mono_array_get(*write_socks, MonoObject *, i);
-                       
-                       if(_wapi_FD_ISSET(Socket_to_SOCKET(sock), &writefds)) {
-                               mono_array_set(socks, MonoObject *, count, sock);
-                               count++;
-                       }
-               }
-               *write_socks=socks;
-       } else {
-               *write_socks = NULL;
-       }
+       sock_arr_class= ((MonoObject *)*sockets)->vtable->klass;
+       ret += 3; /* space for the NULL delimiters */
+       socks = mono_array_new_full (mono_domain_get (), sock_arr_class, &ret, NULL);
+       ret -= 3;
+       mode = idx = 0;
+       for (i = 0; i < count && ret > 0; i++) {
+               mono_pollfd *pfd;
 
-       if (errarrsize) {
-               sock_arr_class=((MonoObject *)*err_socks)->vtable->klass;
-               count=0;
-               for(i=0; i<errarrsize; i++) {
-                       if(_wapi_FD_ISSET(Socket_to_SOCKET(mono_array_get(*err_socks, MonoObject *, i)), &errfds)) {
-                               count++;
-                       }
+               obj = mono_array_get (*sockets, MonoObject *, i);
+               if (obj == NULL) {
+                       mode++;
+                       idx++;
+                       continue;
                }
-               socks=mono_array_new_full(domain, sock_arr_class, &count, NULL);
-               count=0;
-               for(i=0; i<errarrsize; i++) {
-                       MonoObject *sock=mono_array_get(*err_socks, MonoObject *, i);
-                       
-                       if(_wapi_FD_ISSET(Socket_to_SOCKET(sock), &errfds)) {
-                               mono_array_set(socks, MonoObject *, count, sock);
-                               count++;
-                       }
+
+               pfd = &pfds [i - mode];
+               if (pfd->revents == 0)
+                       continue;
+
+               ret--;
+               if (mode == 0 && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0) {
+                       mono_array_set (socks, MonoObject *, idx++, obj);
+               } else if (mode == 1 && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0) {
+                       mono_array_set (socks, MonoObject *, idx++, obj);
+               } else if ((pfd->revents & POLL_ERRORS) != 0) {
+                       mono_array_set (socks, MonoObject *, idx++, obj);
                }
-               *err_socks=socks;
        }
+
+       *sockets = socks;
+       g_free (pfds);
 }
 
 static MonoObject* int_to_object (MonoDomain *domain, int val)
@@ -1439,8 +1423,8 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
        int valsize=sizeof(val);
        struct linger linger;
        int lingersize=sizeof(linger);
-       struct timeval tv;
-       int tvsize=sizeof(tv);
+       int time_ms = 0;
+       int time_ms_size = sizeof (time_ms);
 #ifdef SO_PEERCRED
        struct ucred cred;
        int credsize = sizeof(cred);
@@ -1474,8 +1458,7 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
                
        case SocketOptionName_SendTimeout:
        case SocketOptionName_ReceiveTimeout:
-               ret = _wapi_getsockopt (sock, system_level, system_name, &tv,
-                          &tvsize);
+               ret = _wapi_getsockopt (sock, system_level, system_name, (char *) &time_ms, &time_ms_size);
                break;
 
 #ifdef SO_PEERCRED
@@ -1521,7 +1504,7 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
                
        case SocketOptionName_SendTimeout:
        case SocketOptionName_ReceiveTimeout:
-               obj = int_to_object (domain, (tv.tv_sec * 1000) + (tv.tv_usec / 1000));
+               obj = int_to_object (domain, time_ms);
                break;
 
 #ifdef SO_PEERCRED
@@ -1599,7 +1582,7 @@ static struct in_addr ipaddress_to_struct_in_addr(MonoObject *ipaddr)
        struct in_addr inaddr;
        MonoClassField *field;
        
-       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "address");
+       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "m_Address");
 
        /* No idea why .net uses a 64bit type to hold a 32bit value...
         *
@@ -1620,7 +1603,7 @@ static struct in6_addr ipaddress_to_struct_in6_addr(MonoObject *ipaddr)
        MonoArray *data;
        int i;
 
-       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "_numbers");
+       field=mono_class_get_field_from_name(ipaddr->vtable->klass, "m_Numbers");
        data=*(MonoArray **)(((char *)ipaddr) + field->offset);
 
 /* Solaris has only the 8 bit version. */
@@ -1790,19 +1773,8 @@ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, g
                        return;
                }
        } else {
-               switch(name) {
-                       case SocketOptionName_SendTimeout:
-                       case SocketOptionName_ReceiveTimeout: {
-                               struct timeval tv;
-                               tv.tv_sec = int_val / 1000;
-                               tv.tv_usec = (int_val % 1000) * 1000;
-                               ret = _wapi_setsockopt (sock, system_level, system_name, &tv, sizeof (tv));
-                               break;
-                       }
-                       default:
-                               ret = _wapi_setsockopt (sock, system_level, system_name, &int_val,
-                              sizeof(int_val));
-               }
+               /* ReceiveTimeout/SendTimeout get here */
+               ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val));
        }
 
        if(ret==SOCKET_ERROR) {
@@ -1873,13 +1845,122 @@ ves_icall_System_Net_Sockets_Socket_WSAIoctl (SOCKET sock, gint32 code,
        return (gint) output_bytes;
 }
 
+#ifdef HAVE_SIOCGIFCONF
+static gboolean
+is_loopback (int family, void *ad)
+{
+       char *ptr = (char *) ad;
+
+       if (family == AF_INET) {
+               return (ptr [0] == 127);
+       }
+#ifdef AF_INET6
+       else {
+               return (IN6_IS_ADDR_LOOPBACK ((struct in6_addr *) ptr));
+       }
+#endif
+       return FALSE;
+}
+
+static void *
+get_local_ips (int family, int *nips)
+{
+       int addr_size, offset, fd, i, count;
+       int max_ifaces = 50; /* 50 interfaces should be enough... */
+       struct ifconf ifc;
+       struct ifreq *ifr;
+       struct ifreq iflags;
+       char *result, *tmp_ptr;
+       gboolean ignore_loopback = FALSE;
+
+       *nips = 0;
+       if (family == AF_INET) {
+               addr_size = sizeof (struct in_addr);
+               offset = G_STRUCT_OFFSET (struct sockaddr_in, sin_addr);
+#ifdef AF_INET6
+       } else if (family == AF_INET6) {
+               addr_size = sizeof (struct in6_addr);
+               offset = G_STRUCT_OFFSET (struct sockaddr_in6, sin6_addr);
+#endif
+       } else {
+               return NULL;
+       }
+
+       fd = socket (family, SOCK_STREAM, 0);
+
+       ifc.ifc_len = max_ifaces * sizeof (struct ifreq);
+       ifc.ifc_buf = g_malloc (ifc.ifc_len);
+       if (ioctl (fd, SIOCGIFCONF, &ifc) < 0) {
+               close (fd);
+               g_free (ifc.ifc_buf);
+               return NULL;
+       }
+
+       count = ifc.ifc_len / sizeof (struct ifreq);
+       *nips = count;
+       if (count == 0) {
+               g_free (ifc.ifc_buf);
+               close (fd);
+               return NULL;
+       }
+
+       for (i = 0, ifr = ifc.ifc_req; i < *nips; i++, ifr++) {
+               strcpy (iflags.ifr_name, ifr->ifr_name);
+               if (ioctl (fd, SIOCGIFFLAGS, &iflags) < 0) {
+                       continue;
+               }
+
+               if ((iflags.ifr_flags & IFF_UP) == 0) {
+                       ifr->ifr_name [0] = '\0';
+                       continue;
+               }
+
+               if ((iflags.ifr_flags & IFF_LOOPBACK) == 0) {
+                       ignore_loopback = TRUE;
+               }
+       }
+
+       close (fd);
+       result = g_malloc (addr_size * count);
+       tmp_ptr = result;
+       for (i = 0, ifr = ifc.ifc_req; i < count; i++, ifr++) {
+               if (ifr->ifr_name [0] == '\0') {
+                       (*nips)--;
+                       continue;
+               }
+
+               if (ignore_loopback && is_loopback (family, ((char *) &ifr->ifr_addr) + offset)) {
+                       (*nips)--;
+                       continue;
+               }
+
+               memcpy (tmp_ptr, ((char *) &ifr->ifr_addr) + offset, addr_size);
+               tmp_ptr += addr_size;
+       }
+
+       g_free (ifc.ifc_buf);
+       return result;
+}
+#else
+static void *
+get_local_ips (int family, int *nips)
+{
+       *nips = 0;
+       return NULL;
+}
+
+#endif /* HAVE_SIOCGIFCONF */
+
 #ifndef AF_INET6
 static gboolean hostent_to_IPHostEntry(struct hostent *he, MonoString **h_name,
                                       MonoArray **h_aliases,
-                                      MonoArray **h_addr_list)
+                                      MonoArray **h_addr_list,
+                                      gboolean add_local_ips)
 {
        MonoDomain *domain = mono_domain_get ();
        int i;
+       struct in_addr *local_in = NULL;
+       int nlocal_in = 0;
 
        if(he->h_length!=4 || he->h_addrtype!=AF_INET) {
                return(FALSE);
@@ -1902,26 +1983,52 @@ static gboolean hostent_to_IPHostEntry(struct hostent *he, MonoString **h_name,
                i++;
        }
 
-       i=0;
-       while(he->h_addr_list[i]!=NULL) {
-               i++;
+       if (add_local_ips) {
+               local_in = (struct in_addr *) get_local_ips (AF_INET, &nlocal_in);
+               if (nlocal_in) {
+                       *h_addr_list = mono_array_new(domain, mono_get_string_class (), nlocal_in);
+                       for (i = 0; i < nlocal_in; i++) {
+                               MonoString *addr_string;
+                               char addr [16], *ptr;
+                               
+                               ptr = (char *) &local_in [i];
+                               g_snprintf(addr, 16, "%u.%u.%u.%u",
+                                        (unsigned char) ptr [0],
+                                        (unsigned char) ptr [1],
+                                        (unsigned char) ptr [2],
+                                        (unsigned char) ptr [3]);
+                               
+                               addr_string = mono_string_new (domain, addr);
+                               mono_array_set (*h_addr_list, MonoString *, i, addr_string);
+                               i++;
+                       }
+
+                       g_free (local_in);
+               }
        }
        
-       *h_addr_list=mono_array_new(domain, mono_get_string_class (), i);
-       i=0;
-       while(he->h_addr_list[i]!=NULL) {
-               MonoString *addr_string;
-               char addr[16];
-               
-               g_snprintf(addr, 16, "%u.%u.%u.%u",
-                        (unsigned char)he->h_addr_list[i][0],
-                        (unsigned char)he->h_addr_list[i][1],
-                        (unsigned char)he->h_addr_list[i][2],
-                        (unsigned char)he->h_addr_list[i][3]);
-               
-               addr_string=mono_string_new(domain, addr);
-               mono_array_set(*h_addr_list, MonoString *, i, addr_string);
-               i++;
+       if (nlocal_in == 0) {
+               i = 0;
+               while (he->h_addr_list[i]!=NULL) {
+                       i++;
+               }
+
+               *h_addr_list=mono_array_new(domain, mono_get_string_class (), i);
+               i=0;
+               while(he->h_addr_list[i]!=NULL) {
+                       MonoString *addr_string;
+                       char addr[16];
+                       
+                       g_snprintf(addr, 16, "%u.%u.%u.%u",
+                                (unsigned char)he->h_addr_list[i][0],
+                                (unsigned char)he->h_addr_list[i][1],
+                                (unsigned char)he->h_addr_list[i][2],
+                                (unsigned char)he->h_addr_list[i][3]);
+                       
+                       addr_string=mono_string_new(domain, addr);
+                       mono_array_set(*h_addr_list, MonoString *, i, addr_string);
+                       i++;
+               }
        }
 
        return(TRUE);
@@ -1929,10 +2036,16 @@ static gboolean hostent_to_IPHostEntry(struct hostent *he, MonoString **h_name,
 #endif
 
 #if defined(AF_INET6) && defined(HAVE_GETHOSTBYNAME2_R)
-static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list)
+static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2, MonoString **h_name,
+                               MonoArray **h_aliases, MonoArray **h_addr_list, gboolean add_local_ips)
 {
        MonoDomain *domain = mono_domain_get ();
        int i, host_count, host_index, family_hint;
+       struct in_addr *local_in = NULL;
+       int nlocal_in = 0;
+       struct in6_addr *local_in6 = NULL;
+       int nlocal_in6 = 0;
+       gboolean from_local = FALSE;
 
        family_hint = get_family_hint ();
 
@@ -2019,17 +2132,67 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
        /*
         * Fills the array
         */
-       *h_addr_list=mono_array_new (domain, mono_get_string_class (),
-                                    host_count);
-
        host_index = 0;
+       if (add_local_ips) {
+               if (family_hint == PF_UNSPEC || family_hint == PF_INET)
+                       local_in = (struct in_addr *) get_local_ips (AF_INET, &nlocal_in);
+
+               if (family_hint == PF_UNSPEC || family_hint == PF_INET6)
+                       local_in6 = (struct in6_addr *) get_local_ips (AF_INET6, &nlocal_in6);
+
+               if (nlocal_in || nlocal_in6) {
+                       from_local = TRUE;
+                       *h_addr_list = mono_array_new (domain, mono_get_string_class (),
+                                                            nlocal_in + nlocal_in6);
+
+                       if (nlocal_in6) {
+                               int n;
+                               for (n = 0; n < nlocal_in6; n++) {
+                                       MonoString *addr_string;
+                                       char addr[46]; /* INET6_ADDRSTRLEN == 46 */
+
+                                       inet_ntop (AF_INET6, &local_in6 [n], addr,
+                                                  sizeof(addr));
+
+                                       addr_string = mono_string_new (domain, addr);
+                                       mono_array_set (*h_addr_list, MonoString *, host_index,
+                                                       addr_string);
+                                       host_index++;
+                               }
+                       }
+
+                       if (nlocal_in) {
+                               int n;
+                               for (n = 0; n < nlocal_in; n++) {
+                                       MonoString *addr_string;
+                                       char addr[16]; /* INET_ADDRSTRLEN == 16 */
+
+                                       inet_ntop (AF_INET, &local_in [n], addr,
+                                                  sizeof(addr));
+
+                                       addr_string = mono_string_new (domain, addr);
+                                       mono_array_set (*h_addr_list, MonoString *, host_index,
+                                                       addr_string);
+                                       host_index++;
+                               }
+                       }
+                       g_free (local_in);
+                       g_free (local_in6);
+                       return TRUE;
+               }
+
+               g_free (local_in);
+               g_free (local_in6);
+       }
+
+       *h_addr_list=mono_array_new (domain, mono_get_string_class (), host_count);
 
        if (he2 != NULL && (family_hint == PF_UNSPEC ||
                            family_hint == PF_INET6)) {
                i = 0;
                while(he2->h_addr_list[i] != NULL) {
                        MonoString *addr_string;
-                       char addr[40];
+                       char addr[46]; /* INET6_ADDRSTRLEN == 46 */
 
                        inet_ntop (AF_INET6, he2->h_addr_list[i], addr,
                                   sizeof(addr));
@@ -2047,7 +2210,7 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
                i=0;
                while(he1->h_addr_list[i] != NULL) {
                        MonoString *addr_string;
-                       char addr[17];
+                       char addr[16]; /* INET_ADDRSTRLEN == 16 */
 
                        inet_ntop (AF_INET, he1->h_addr_list[i], addr,
                                   sizeof(addr));
@@ -2068,64 +2231,101 @@ static gboolean hostent_to_IPHostEntry2(struct hostent *he1,struct hostent *he2,
 static gboolean 
 addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
                                                MonoArray **h_aliases,
-                                               MonoArray **h_addr_list)
+                                               MonoArray **h_addr_list,
+                                               gboolean add_local_ips)
 {
        gint32 count, i;
        struct addrinfo *ai = NULL;
+       struct in_addr *local_in = NULL;
+       int nlocal_in = 0;
+       struct in6_addr *local_in6 = NULL;
+       int nlocal_in6 = 0;
+       int addr_index;
 
        MonoDomain *domain = mono_domain_get ();
 
+       addr_index = 0;
+       *h_aliases=mono_array_new(domain, mono_get_string_class (), 0);
+       if (add_local_ips) {
+               local_in = (struct in_addr *) get_local_ips (AF_INET, &nlocal_in);
+               local_in6 = (struct in6_addr *) get_local_ips (AF_INET6, &nlocal_in);
+               if (nlocal_in || nlocal_in6) {
+                       *h_addr_list=mono_array_new(domain, mono_get_string_class (), nlocal_in + nlocal_in6);
+                       if (nlocal_in) {
+                               MonoString *addr_string;
+                               char addr [16];
+                               int i;
+
+                               for (i = 0; i < nlocal_in; i++) {
+                                       inet_ntop (AF_INET, &local_in [i], addr, sizeof (addr));
+                                       addr_string = mono_string_new (domain, addr);
+                                       mono_array_set (*h_addr_list, MonoString *, addr_index, addr_string);
+                                       addr_index++;
+                               }
+                       }
+
+                       if (nlocal_in6) {
+                               MonoString *addr_string;
+                               char addr [46];
+                               int i;
+
+                               for (i = 0; i < nlocal_in6; i++) {
+                                       inet_ntop (AF_INET6, &local_in6 [i], addr, sizeof (addr));
+                                       addr_string = mono_string_new (domain, addr);
+                                       mono_array_set (*h_addr_list, MonoString *, addr_index, addr_string);
+                                       addr_index++;
+                               }
+                       }
+
+                       g_free (local_in);
+                       g_free (local_in6);
+                       if (info) {
+                               freeaddrinfo (info);
+                       }
+                       return TRUE;
+               }
+
+               g_free (local_in);
+               g_free (local_in6);
+       }
+
        for (count=0, ai=info; ai!=NULL; ai=ai->ai_next) {
-               if((ai->ai_family != PF_INET) && (ai->ai_family != PF_INET6)) {
+               if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
                        continue;
-               }
 
                count++;
        }
 
-       *h_aliases=mono_array_new(domain, mono_get_string_class (), 0);
        *h_addr_list=mono_array_new(domain, mono_get_string_class (), count);
 
        for (ai=info, i=0; ai!=NULL; ai=ai->ai_next) {
                MonoString *addr_string;
                const char *ret;
-               char *buffer;
-               gint32 buffer_size = 0;
+               char buffer [46]; /* Max. size for IPv6 */
 
                if((ai->ai_family != PF_INET) && (ai->ai_family != PF_INET6)) {
                        continue;
                }
 
-               buffer_size = 256;
-               do {
-                       buffer = g_malloc0(buffer_size);
-
-                       if(ai->ai_family == PF_INET) {
-                               ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in*)ai->ai_addr)->sin_addr), buffer, buffer_size);
-                       } else {
-                               ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr), buffer, buffer_size);
-                       }
-
-                       if(ret == 0) {
-                               g_free(buffer);
-                               buffer_size += 256;
-                       }
-               } while(ret == 0 && errno == ENOSPC);
+               if(ai->ai_family == PF_INET) {
+                       ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in*)ai->ai_addr)->sin_addr), buffer, 16);
+               } else {
+                       ret = inet_ntop(ai->ai_family, (void*)&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr), buffer, 46);
+               }
 
                if(ret) {
                        addr_string=mono_string_new(domain, buffer);
-                       g_free(buffer);
                } else {
                        addr_string=mono_string_new(domain, "");
                }
 
-               mono_array_set(*h_addr_list, MonoString *, i, addr_string);
+               mono_array_set(*h_addr_list, MonoString *, addr_index, addr_string);
 
                if(!i && ai->ai_canonname != NULL) {
                        *h_name=mono_string_new(domain, ai->ai_canonname);
                }
 
-               i++;
+               addr_index++;
        }
 
        if(info) {
@@ -2139,6 +2339,10 @@ addrinfo_to_IPHostEntry(struct addrinfo *info, MonoString **h_name,
 #ifdef AF_INET6
 MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, MonoString **h_name, MonoArray **h_aliases, MonoArray **h_addr_list)
 {
+       gboolean add_local_ips = FALSE;
+#ifdef HAVE_SIOCGIFCONF
+       guchar this_hostname [256];
+#endif
 #if !defined(HAVE_GETHOSTBYNAME2_R)
        struct addrinfo *info = NULL, hints;
        char *hostname;
@@ -2146,7 +2350,13 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
        MONO_ARCH_SAVE_REGS;
        
        hostname=mono_string_to_utf8 (host);
-       
+#ifdef HAVE_SIOCGIFCONF
+       if (gethostname (this_hostname, sizeof (this_hostname)) != -1) {
+               if (!strcmp (hostname, this_hostname))
+                       add_local_ips = TRUE;
+       }
+#endif
+
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = get_family_hint ();
        hints.ai_socktype = SOCK_STREAM;
@@ -2158,7 +2368,7 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
        
        g_free(hostname);
 
-       return(addrinfo_to_IPHostEntry(info, h_name, h_aliases, h_addr_list));
+       return(addrinfo_to_IPHostEntry(info, h_name, h_aliases, h_addr_list, add_local_ips));
 #else
        struct hostent he1,*hp1, he2, *hp2;
        int buffer_size1, buffer_size2;
@@ -2171,6 +2381,13 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
        
        hostname=mono_string_to_utf8 (host);
 
+#ifdef HAVE_SIOCGIFCONF
+       if (gethostname (this_hostname, sizeof (this_hostname)) != -1) {
+               if (!strcmp (hostname, this_hostname))
+                       add_local_ips = TRUE;
+       }
+#endif
+
        buffer_size1 = 512;
        buffer_size2 = 512;
        buffer1 = g_malloc0(buffer_size1);
@@ -2194,7 +2411,7 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
                hp2 = NULL;
 
        return_value = hostent_to_IPHostEntry2(hp1, hp2, h_name, h_aliases,
-                                              h_addr_list);
+                                              h_addr_list, add_local_ips);
 
        g_free(buffer1);
        g_free(buffer2);
@@ -2208,10 +2425,20 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
 {
        struct hostent *he;
        char *hostname;
+       gboolean add_local_ips = FALSE;
+#ifdef HAVE_SIOCGIFCONF
+       guchar this_hostname [256];
+#endif
        
        MONO_ARCH_SAVE_REGS;
 
        hostname=mono_string_to_utf8(host);
+#ifdef HAVE_SIOCGIFCONF
+       if (gethostname (this_hostname, sizeof (this_hostname)) != -1) {
+               if (!strcmp (hostname, this_hostname))
+                       add_local_ips = TRUE;
+       }
+#endif
 
        he = _wapi_gethostbyname (hostname);
        g_free(hostname);
@@ -2220,7 +2447,7 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
                return(FALSE);
        }
 
-       return(hostent_to_IPHostEntry(he, h_name, h_aliases, h_addr_list));
+       return(hostent_to_IPHostEntry(he, h_name, h_aliases, h_addr_list, add_local_ips));
 }
 #endif /* AF_INET6 */
 
@@ -2323,7 +2550,7 @@ extern MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal(MonoString *a
                return(FALSE);
        }
 
-       return(addrinfo_to_IPHostEntry (info, h_name, h_aliases, h_addr_list));
+       return(addrinfo_to_IPHostEntry (info, h_name, h_aliases, h_addr_list, FALSE));
 #else
        if (inet_pton (AF_INET, address, &inaddr) <= 0) {
                g_free (address);
@@ -2335,7 +2562,7 @@ extern MonoBoolean ves_icall_System_Net_Dns_GetHostByAddr_internal(MonoString *a
                return(FALSE);
        }
 
-       return(hostent_to_IPHostEntry (he, h_name, h_aliases, h_addr_list));
+       return(hostent_to_IPHostEntry (he, h_name, h_aliases, h_addr_list, FALSE));
 #endif
 }