Move declaration to a more appropriate header.
[mono.git] / mono / metadata / socket-io.c
index 070880498815c68a33ea9fa8f25680659856c4d8..961fc37272e722a7363a9f7010b7cfaedd49c3fc 100644 (file)
@@ -11,6 +11,8 @@
 
 #include <config.h>
 
+#ifndef DISABLE_SOCKETS
+
 #include <glib.h>
 #include <string.h>
 #include <stdlib.h>
@@ -69,7 +71,7 @@
 
 #include "mono/io-layer/socket-wrappers.h"
 
-#ifdef HOST_WIN32
+#if defined(HOST_WIN32)
 /* This is a kludge to make this file build under cygwin:
  * w32api/ws2tcpip.h has definitions for some AF_INET6 values and
  * prototypes for some but not all required functions (notably
@@ -551,6 +553,7 @@ static gint32 convert_sockopt_level_and_name(MonoSocketOptionLevel mono_level,
 
                switch(mono_name) {
                case SocketOptionName_IpTimeToLive:
+               case SocketOptionName_HopLimit:
                        *system_name = IPV6_UNICAST_HOPS;
                        break;
                case SocketOptionName_MulticastInterface:
@@ -657,14 +660,16 @@ static MonoImage *get_socket_assembly (void)
 {
        static const char *version = NULL;
        static gboolean moonlight;
-       static MonoImage *socket_assembly = NULL;
+       MonoDomain *domain = mono_domain_get ();
        
        if (version == NULL) {
                version = mono_get_runtime_info ()->framework_version;
                moonlight = !strcmp (version, "2.1");
        }
        
-       if (socket_assembly == NULL) {
+       if (domain->socket_assembly == NULL) {
+               MonoImage *socket_assembly;
+
                if (moonlight) {
                        socket_assembly = mono_image_loaded ("System.Net");
                        if (!socket_assembly) {
@@ -688,9 +693,11 @@ static MonoImage *get_socket_assembly (void)
                                }
                        }
                }
+
+               domain->socket_assembly = socket_assembly;
        }
        
-       return(socket_assembly);
+       return domain->socket_assembly;
 }
 
 #ifdef AF_INET6
@@ -900,17 +907,21 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
 {
        MonoDomain *domain = mono_domain_get ();
        MonoObject *sockaddr_obj;
-       MonoClass *sockaddr_class;
-       MonoClassField *field;
        MonoArray *data;
        MonoAddressFamily family;
 
        /* Build a System.Net.SocketAddress object instance */
-       sockaddr_class=mono_class_from_name_cached (get_socket_assembly (), "System.Net", "SocketAddress");
-       sockaddr_obj=mono_object_new(domain, sockaddr_class);
+       if (!domain->sockaddr_class) {
+               domain->sockaddr_class=mono_class_from_name (get_socket_assembly (), "System.Net", "SocketAddress");
+               g_assert (domain->sockaddr_class);
+       }
+       sockaddr_obj=mono_object_new(domain, domain->sockaddr_class);
        
        /* Locate the SocketAddress data buffer in the object */
-       field=mono_class_get_field_from_name_cached (sockaddr_class, "data");
+       if (!domain->sockaddr_data_field) {
+               domain->sockaddr_data_field=mono_class_get_field_from_name (domain->sockaddr_class, "data");
+               g_assert (domain->sockaddr_data_field);
+       }
 
        /* Make sure there is space for the family and size bytes */
 #ifdef HAVE_SYS_UN_H
@@ -958,7 +969,7 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
                mono_array_set(data, guint8, 6, (address>>8) & 0xff);
                mono_array_set(data, guint8, 7, (address) & 0xff);
        
-               mono_field_set_value (sockaddr_obj, field, data);
+               mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data);
 
                return(sockaddr_obj);
 #ifdef AF_INET6
@@ -988,7 +999,7 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
                mono_array_set(data, guint8, 27,
                               (sa_in->sin6_scope_id >> 24) & 0xff);
 
-               mono_field_set_value (sockaddr_obj, field, data);
+               mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data);
 
                return(sockaddr_obj);
 #endif
@@ -1000,7 +1011,7 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
                        mono_array_set (data, guint8, i+2, saddr->sa_data[i]);
                }
                
-               mono_field_set_value (sockaddr_obj, field, data);
+               mono_field_set_value (sockaddr_obj, domain->sockaddr_data_field, data);
 
                return sockaddr_obj;
 #endif
@@ -1010,52 +1021,92 @@ static MonoObject *create_object_from_sockaddr(struct sockaddr *saddr,
        }
 }
 
-extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SOCKET sock, gint32 *error)
+static int
+get_sockaddr_size (int family)
+{
+       int size;
+
+       size = 0;
+       if (family == AF_INET) {
+               size = sizeof (struct sockaddr_in);
+#ifdef AF_INET6
+       } else if (family == AF_INET6) {
+               size = sizeof (struct sockaddr_in6);
+#endif
+#ifdef HAVE_SYS_UN_H
+       } else if (family == AF_UNIX) {
+               size = sizeof (struct sockaddr_un);
+#endif
+       }
+       return size;
+}
+
+extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SOCKET sock, gint32 af, gint32 *error)
 {
-       gchar sa[32];   /* sockaddr in not big enough for sockaddr_in6 */
+       gchar *sa;
        socklen_t salen;
        int ret;
+       MonoObject *result;
        
        MONO_ARCH_SAVE_REGS;
 
        *error = 0;
        
-       salen=sizeof(sa);
+       salen = get_sockaddr_size (convert_family (af));
+       if (salen == 0) {
+               *error = WSAEAFNOSUPPORT;
+               return NULL;
+       }
+       sa = (salen <= 128) ? alloca (salen) : g_malloc0 (salen);
        ret = _wapi_getsockname (sock, (struct sockaddr *)sa, &salen);
        
        if(ret==SOCKET_ERROR) {
                *error = WSAGetLastError ();
+               if (salen > 128)
+                       g_free (sa);
                return(NULL);
        }
        
        LOGDEBUG (g_message("%s: bound to %s port %d", __func__, inet_ntoa(((struct sockaddr_in *)&sa)->sin_addr), ntohs(((struct sockaddr_in *)&sa)->sin_port)));
 
-       return(create_object_from_sockaddr((struct sockaddr *)sa, salen,
-                                          error));
+       result = create_object_from_sockaddr((struct sockaddr *)sa, salen, error);
+       if (salen > 128)
+               g_free (sa);
+       return result;
 }
 
-extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(SOCKET sock, gint32 *error)
+extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(SOCKET sock, gint32 af, gint32 *error)
 {
-       gchar sa[32];   /* sockaddr in not big enough for sockaddr_in6 */
+       gchar *sa;
        socklen_t salen;
        int ret;
+       MonoObject *result;
        
        MONO_ARCH_SAVE_REGS;
 
        *error = 0;
        
-       salen=sizeof(sa);
+       salen = get_sockaddr_size (convert_family (af));
+       if (salen == 0) {
+               *error = WSAEAFNOSUPPORT;
+               return NULL;
+       }
+       sa = (salen <= 128) ? alloca (salen) : g_malloc0 (salen);
+       /* Note: linux returns just 2 for AF_UNIX. Always. */
        ret = _wapi_getpeername (sock, (struct sockaddr *)sa, &salen);
-       
        if(ret==SOCKET_ERROR) {
                *error = WSAGetLastError ();
+               if (salen > 128)
+                       g_free (sa);
                return(NULL);
        }
        
        LOGDEBUG (g_message("%s: connected to %s port %d", __func__, inet_ntoa(((struct sockaddr_in *)&sa)->sin_addr), ntohs(((struct sockaddr_in *)&sa)->sin_port)));
 
-       return(create_object_from_sockaddr((struct sockaddr *)sa, salen,
-                                          error));
+       result = create_object_from_sockaddr((struct sockaddr *)sa, salen, error);
+       if (salen > 128)
+               g_free (sa);
+       return result;
 }
 
 static struct sockaddr *create_sockaddr_from_object(MonoObject *saddr_obj,
@@ -1772,7 +1823,11 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
        int time_ms = 0;
        socklen_t time_ms_size = sizeof (time_ms);
 #ifdef SO_PEERCRED
+#  if defined(__OpenBSD__)
+       struct sockpeercred cred;
+#  else
        struct ucred cred;
+#  endif
        socklen_t credsize = sizeof(cred);
 #endif
        MonoDomain *domain=mono_domain_get();
@@ -1784,8 +1839,18 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
 
        *error = 0;
        
-       ret=convert_sockopt_level_and_name(level, name, &system_level,
-                                          &system_name);
+#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR)
+       if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) {
+               system_level = SOL_SOCKET;
+               system_name = SO_REUSEADDR;
+               ret = 0;
+       } else
+#endif
+       {
+
+               ret = convert_sockopt_level_and_name (level, name, &system_level, &system_name);
+       }
+
        if(ret==-1) {
                *error = WSAENOPROTOOPT;
                return;
@@ -1892,9 +1957,12 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
 #endif
 
        default:
+#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR)
+               if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse)
+                       val = val ? 0 : 1;
+#endif
                obj = int_to_object (domain, val);
        }
-
        *obj_val=obj;
 }
 
@@ -1944,7 +2012,6 @@ static struct in_addr ipaddress_to_struct_in_addr(MonoObject *ipaddr)
        
        return(inaddr);
 }
-#endif
 
 #ifdef AF_INET6
 static struct in6_addr ipaddress_to_struct_in6_addr(MonoObject *ipaddr)
@@ -1971,6 +2038,7 @@ static struct in6_addr ipaddress_to_struct_in6_addr(MonoObject *ipaddr)
        return(in6addr);
 }
 #endif /* AF_INET6 */
+#endif
 
 void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, gint32 level, gint32 name, MonoObject *obj_val, MonoArray *byte_val, gint32 int_val, gint32 *error)
 {
@@ -2009,6 +2077,15 @@ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, g
 
        ret=convert_sockopt_level_and_name(level, name, &system_level,
                                           &system_name);
+
+#if !defined(SO_EXCLUSIVEADDRUSE) && defined(SO_REUSEADDR)
+       if (level == SocketOptionLevel_Socket && name == SocketOptionName_ExclusiveAddressUse) {
+               system_name = SO_REUSEADDR;
+               int_val = int_val ? 0 : 1;
+               ret = 0;
+       }
+#endif
+
        if(ret==-1) {
                *error = WSAENOPROTOOPT;
                return;
@@ -2777,7 +2854,7 @@ MonoBoolean ves_icall_System_Net_Dns_GetHostByName_internal(MonoString *host, Mo
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = get_family_hint ();
        hints.ai_socktype = SOCK_STREAM;
-       hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+       hints.ai_flags = AI_CANONNAME;
 
        if (*hostname && getaddrinfo(hostname, NULL, &hints, &info) == -1) {
                return(FALSE);
@@ -3075,3 +3152,5 @@ void mono_network_cleanup(void)
        WSACleanup();
 }
 
+
+#endif /* #ifndef DISABLE_SOCKETS */