Fri Jan 26 12:00:45 CET 2007 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / socket-io.c
index 63a28ae16e07710a778710462339dba42615a3d3..823e1a5e3b9d196d7d2fded308f07d3a02984568 100644 (file)
@@ -297,6 +297,12 @@ static gint32 convert_socketflags (gint32 sflags)
        return (flags ? flags : -1);
 }
 
+/*
+ * Returns:
+ *    0 on success (mapped mono_level and mono_name to system_level and system_name
+ *   -1 on error
+ *   -2 on non-fatal error (ie, must ignore)
+ */
 static gint32 convert_sockopt_level_and_name(MonoSocketOptionLevel mono_level,
                                             MonoSocketOptionName mono_name,
                                             int *system_level,
@@ -440,7 +446,19 @@ static gint32 convert_sockopt_level_and_name(MonoSocketOptionLevel mono_level,
                        *system_name = IP_PKTINFO;
                        break;
 #endif /* HAVE_IP_PKTINFO */
+
                case SocketOptionName_DontFragment:
+#ifdef HAVE_IP_DONTFRAGMENT
+                       *system_name = IP_DONTFRAGMENT;
+                       break;
+#elif defined HAVE_IP_MTU_DISCOVER
+                       /* Not quite the same */
+                       *system_name = IP_MTU_DISCOVER;
+                       break;
+#else
+                       /* If the flag is not available on this system, we can ignore this error */
+                       return (-2);
+#endif /* HAVE_IP_DONTFRAGMENT */
                case SocketOptionName_AddSourceMembership:
                case SocketOptionName_DropSourceMembership:
                case SocketOptionName_BlockSource:
@@ -1223,6 +1241,92 @@ extern void ves_icall_System_Net_Sockets_Socket_Connect_internal(SOCKET sock, Mo
        g_free(sa);
 }
 
+/* These #defines from mswsock.h from wine.  Defining them here allows
+ * us to build this file on a mingw box that doesn't know the magic
+ * numbers, but still run on a newer windows box that does.
+ */
+#ifndef WSAID_DISCONNECTEX
+#define WSAID_DISCONNECTEX {0x7fda2e11,0x8630,0x436f,{0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}}
+typedef BOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET, LPOVERLAPPED, DWORD, DWORD);
+#endif
+
+#ifndef WSAID_TRANSMITFILE
+#define WSAID_TRANSMITFILE {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
+typedef BOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET, HANDLE, DWORD, DWORD, LPOVERLAPPED, LPTRANSMIT_FILE_BUFFERS, DWORD);
+#endif
+
+extern void ves_icall_System_Net_Sockets_Socket_Disconnect_internal(SOCKET sock, MonoBoolean reuse, gint32 *error)
+{
+       int ret;
+       glong output_bytes = 0;
+       GUID disco_guid = WSAID_DISCONNECTEX;
+       GUID trans_guid = WSAID_TRANSMITFILE;
+       LPFN_DISCONNECTEX _wapi_disconnectex = NULL;
+       LPFN_TRANSMITFILE _wapi_transmitfile = NULL;
+       gboolean bret;
+       
+       MONO_ARCH_SAVE_REGS;
+
+       *error = 0;
+       
+#ifdef DEBUG
+       g_message("%s: disconnecting from socket %p (reuse %d)", __func__,
+                 sock, reuse);
+#endif
+
+       /* I _think_ the extension function pointers need to be looked
+        * up for each socket.  FIXME: check the best way to store
+        * pointers to functions in managed objects that still works
+        * on 64bit platforms.
+        */
+       ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                       (void *)&disco_guid, sizeof(GUID),
+                       (void *)&_wapi_disconnectex, sizeof(void *),
+                       &output_bytes, NULL, NULL);
+       if (ret != 0) {
+               /* make sure that WSAIoctl didn't put crap in the
+                * output pointer
+                */
+               _wapi_disconnectex = NULL;
+
+               /* Look up the TransmitFile extension function pointer
+                * instead of calling TransmitFile() directly, because
+                * apparently "Several of the extension functions have
+                * been available since WinSock 1.1 and are exported
+                * from MSWsock.dll, however it's not advisable to
+                * link directly to this dll as this ties you to the
+                * Microsoft WinSock provider. A provider neutral way
+                * of accessing these extension functions is to load
+                * them dynamically via WSAIoctl using the
+                * SIO_GET_EXTENSION_FUNCTION_POINTER op code. This
+                * should, theoretically, allow you to access these
+                * functions from any provider that supports them..." 
+                * (http://www.codeproject.com/internet/jbsocketserver3.asp)
+                */
+               ret = WSAIoctl (sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                               (void *)&trans_guid, sizeof(GUID),
+                               (void *)&_wapi_transmitfile, sizeof(void *),
+                               &output_bytes, NULL, NULL);
+               if (ret != 0) {
+                       _wapi_transmitfile = NULL;
+               }
+       }
+
+       if (_wapi_disconnectex != NULL) {
+               bret = _wapi_disconnectex (sock, NULL, TF_REUSE_SOCKET, 0);
+       } else if (_wapi_transmitfile != NULL) {
+               bret = _wapi_transmitfile (sock, NULL, 0, 0, NULL, NULL,
+                                          TF_DISCONNECT | TF_REUSE_SOCKET);
+       } else {
+               *error = ERROR_NOT_SUPPORTED;
+               return;
+       }
+
+       if (bret == FALSE) {
+               *error = WSAGetLastError ();
+       }
+}
+
 gint32 ves_icall_System_Net_Sockets_Socket_Receive_internal(SOCKET sock, MonoArray *buffer, gint32 offset, gint32 count, gint32 flags, gint32 *error)
 {
        int ret;
@@ -1570,6 +1674,10 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_obj_internal(SOCKET soc
                *error = WSAENOPROTOOPT;
                return;
        }
+       if (ret == -2) {
+               *obj_val = int_to_object (domain, 0);
+               return;
+       }
        
        /* No need to deal with MulticastOption names here, because
         * you cant getsockopt AddMembership or DropMembership (the
@@ -1692,6 +1800,8 @@ void ves_icall_System_Net_Sockets_Socket_GetSocketOption_arr_internal(SOCKET soc
                *error = WSAENOPROTOOPT;
                return;
        }
+       if(ret==-2)
+               return;
 
        valsize=mono_array_length(*byte_val);
        buf=mono_array_addr(*byte_val, guchar, 0);
@@ -1787,6 +1897,9 @@ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, g
                *error = WSAENOPROTOOPT;
                return;
        }
+       if(ret==-2){
+               return;
+       }
 
        /* Only one of obj_val, byte_val or int_val has data */
        if(obj_val!=NULL) {
@@ -1900,7 +2013,21 @@ void ves_icall_System_Net_Sockets_Socket_SetSocketOption_internal(SOCKET sock, g
                }
        } else {
                /* ReceiveTimeout/SendTimeout get here */
-               ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val));
+               switch(name) {
+               case SocketOptionName_DontFragment:
+#ifdef HAVE_IP_MTU_DISCOVER
+                       /* Fiddle with the value slightly if we're
+                        * turning DF on
+                        */
+                       if (int_val == 1) {
+                               int_val = IP_PMTUDISC_DO;
+                       }
+                       /* Fall through */
+#endif
+                       
+               default:
+                       ret = _wapi_setsockopt (sock, system_level, system_name, (char *) &int_val, sizeof (int_val));
+               }
        }
 
        if(ret==SOCKET_ERROR) {