Windows: enable circular buffer management for rx socket input
[openafs.git] / src / rx / rx_xmit_nt.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* NT does not have uio structs, so we roll our own sendmsg and recvmsg.
11  *
12  * The dangerous part of this code is that it assumes that iovecs 0 and 1
13  * are contiguous and that all of 0 is used before any of 1.
14  * This is true if rx_packets are being sent, so we should be ok.
15  */
16
17 #include <afsconfig.h>
18 #include <afs/param.h>
19
20
21 #if defined(AFS_NT40_ENV)
22
23 #include <winsock2.h>
24 #if (_WIN32_WINNT < 0x0501)
25 #undef _WIN32_WINNT
26 #define _WIN32_WINNT 0x0501
27 #endif
28 #include <mswsock.h>
29
30 #if (_WIN32_WINNT < 0x0600)
31 /*
32  * WSASendMsg -- send data to a specific destination, with options, using
33  *    overlapped I/O where applicable.
34  *
35  * Valid flags for dwFlags parameter:
36  *    MSG_DONTROUTE
37  *    MSG_PARTIAL (a.k.a. MSG_EOR) (only for non-stream sockets)
38  *    MSG_OOB (only for stream style sockets) (NYI)
39  *
40  * Caller must provide either lpOverlapped or lpCompletionRoutine
41  * or neither (both NULL).
42  */
43 typedef
44 INT
45 (PASCAL FAR * LPFN_WSASENDMSG) (
46     IN SOCKET s,
47     IN LPWSAMSG lpMsg,
48     IN DWORD dwFlags,
49     __out_opt LPDWORD lpNumberOfBytesSent,
50     IN LPWSAOVERLAPPED lpOverlapped OPTIONAL,
51     IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine OPTIONAL
52     );
53
54 #define WSAID_WSASENDMSG /* a441e712-754f-43ca-84a7-0dee44cf606d */ \
55     {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
56 #endif /* _WINNT_WIN32 */
57
58 #include "rx.h"
59 #include "rx_packet.h"
60 #include "rx_globals.h"
61 #include "rx_xmit_nt.h"
62 #include <malloc.h>
63 #include <errno.h>
64
65
66 /* 
67  * WSASendMsg is only supported on Vista and above
68  * Neither function is part of the public WinSock API
69  * and therefore the function pointers must be 
70  * obtained via WSAIoctl()
71  */
72 static LPFN_WSARECVMSG pWSARecvMsg = NULL;
73 static LPFN_WSASENDMSG pWSASendMsg = NULL;
74
75 void
76 rxi_xmit_init(osi_socket s)
77 {
78     int rc;
79     GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
80     GUID WSASendMsg_GUID = WSAID_WSASENDMSG;
81     DWORD dwIn, dwOut, NumberOfBytes;
82
83     rc = WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER,
84                    &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
85                    &pWSARecvMsg, sizeof(pWSARecvMsg),
86                    &NumberOfBytes, NULL, NULL); 
87
88     rc = WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER,
89                    &WSASendMsg_GUID, sizeof(WSASendMsg_GUID),
90                    &pWSASendMsg, sizeof(pWSASendMsg),
91                    &NumberOfBytes, NULL, NULL); 
92
93     /* Turn on UDP PORT_UNREACHABLE messages */
94     dwIn = 1;
95     rc = WSAIoctl( s, SIO_UDP_CONNRESET, 
96                    &dwIn, sizeof(dwIn),
97                    &dwOut, sizeof(dwOut),
98                    &NumberOfBytes, NULL, NULL); 
99
100     /* Turn on UDP CIRCULAR QUEUEING messages */
101     dwIn = 1;
102     rc = WSAIoctl( s, SIO_ENABLE_CIRCULAR_QUEUEING,
103                    &dwIn, sizeof(dwIn),
104                    &dwOut, sizeof(dwOut),
105                    &NumberOfBytes, NULL, NULL);
106 }
107
108 int
109 recvmsg(osi_socket socket, struct msghdr *msgP, int flags)
110 {
111     int code;
112
113     if (pWSARecvMsg) {
114         WSAMSG wsaMsg;
115         DWORD  dwBytes;
116
117         wsaMsg.name = (LPSOCKADDR)(msgP->msg_name);
118         wsaMsg.namelen = (INT)(msgP->msg_namelen);
119
120         wsaMsg.lpBuffers = (LPWSABUF) msgP->msg_iov;
121         wsaMsg.dwBufferCount = msgP->msg_iovlen;
122         wsaMsg.Control.len = 0;
123         wsaMsg.Control.buf = NULL;
124         wsaMsg.dwFlags = flags;
125
126         code = pWSARecvMsg(socket, &wsaMsg, &dwBytes, NULL, NULL);
127         if (code == 0) { 
128             /* success - return the number of bytes read */
129             code = (int)dwBytes;
130         } else {
131             /* error - set errno and return -1 */
132             if (code == SOCKET_ERROR)
133                 code = WSAGetLastError();
134             if (code == WSAEWOULDBLOCK || code == WSAECONNRESET)
135                 errno = WSAEWOULDBLOCK;
136             else
137                 errno = EIO;
138             code = -1;
139         }
140     } else {
141         char rbuf[RX_MAX_PACKET_SIZE];
142         int size;
143         int off, i, n;
144         int allocd = 0;
145
146         size = rx_maxJumboRecvSize;
147         code =
148             recvfrom((SOCKET) socket, rbuf, size, flags,
149                       (struct sockaddr *)(msgP->msg_name), &(msgP->msg_namelen));
150
151         if (code > 0) {
152             size = code;
153
154             for (off = i = 0; size > 0 && i < msgP->msg_iovlen; i++) {
155                 if (msgP->msg_iov[i].iov_len) {
156                     if (msgP->msg_iov[i].iov_len < size) {
157                         n = msgP->msg_iov[i].iov_len;
158                     } else {
159                         n = size;
160                     }
161                     memcpy(msgP->msg_iov[i].iov_base, &rbuf[off], n);
162                     off += n;
163                     size -= n;
164                 }
165             }
166
167             /* Accounts for any we didn't copy in to iovecs. */
168             code -= size;
169         } else {
170             if (code == SOCKET_ERROR)
171                 code = WSAGetLastError();
172             if (code == WSAEWOULDBLOCK || code == WSAECONNRESET)
173                 errno = WSAEWOULDBLOCK;
174             else
175                 errno = EIO;
176             code = -1;
177         }
178     }
179
180     return code;
181 }
182
183 int
184 sendmsg(osi_socket socket, struct msghdr *msgP, int flags)
185 {
186     int code;
187
188     if (pWSASendMsg) {
189         WSAMSG wsaMsg;
190         DWORD  dwBytes;
191
192         wsaMsg.name = (LPSOCKADDR)(msgP->msg_name);
193         wsaMsg.namelen = (INT)(msgP->msg_namelen);
194
195         wsaMsg.lpBuffers = (LPWSABUF) msgP->msg_iov;
196         wsaMsg.dwBufferCount = msgP->msg_iovlen;
197         wsaMsg.Control.len = 0;
198         wsaMsg.Control.buf = NULL;
199         wsaMsg.dwFlags = 0;
200
201         code = pWSASendMsg(socket, &wsaMsg, flags, &dwBytes, NULL, NULL);
202         if (code == 0) { 
203             /* success - return the number of bytes read */
204             code = (int)dwBytes;
205         } else {
206             /* error - set errno and return -1 */
207             if (code == SOCKET_ERROR)
208                 code = WSAGetLastError();
209             switch (code) {
210             case WSAEINPROGRESS:
211             case WSAENETRESET:
212             case WSAENOBUFS:
213                 errno = 0;
214                 break;
215             case WSAEWOULDBLOCK:
216             case WSAECONNRESET:
217                 errno = WSAEWOULDBLOCK;
218                 break;
219             case WSAEHOSTUNREACH:
220                 errno = WSAEHOSTUNREACH;
221                 break;
222             default:
223                 errno = EIO;
224                 break;
225             }
226             code = -1;
227         }
228     } else {
229         char buf[RX_MAX_PACKET_SIZE];
230         char *sbuf = buf;
231         int size, tmp;
232         int off, i, n;
233         int allocd = 0;
234
235         for (size = i = 0; i < msgP->msg_iovlen; i++)
236             size += msgP->msg_iov[i].iov_len;
237
238         if (msgP->msg_iovlen <= 2) {
239             sbuf = msgP->msg_iov[0].iov_base;
240         } else {
241             /* Pack data into array from iovecs */
242             tmp = size;
243             for (off = i = 0; tmp > 0 && i < msgP->msg_iovlen; i++) {
244                 if (msgP->msg_iov[i].iov_len > 0) {
245                     if (tmp > msgP->msg_iov[i].iov_len)
246                         n = msgP->msg_iov[i].iov_len;
247                     else
248                         n = tmp;
249                     memcpy(&sbuf[off], msgP->msg_iov[i].iov_base, n);
250                     off += n;
251                     tmp -= n;
252                 }
253             }
254         }
255
256         code =
257             sendto((SOCKET) socket, sbuf, size, flags,
258                     (struct sockaddr *)(msgP->msg_name), msgP->msg_namelen);
259         if (code == SOCKET_ERROR) {
260             code = WSAGetLastError();
261             switch (code) {
262             case WSAEINPROGRESS:
263             case WSAENETRESET:
264             case WSAENOBUFS:
265                 errno = 0;
266                 break;
267             case WSAEWOULDBLOCK:
268             case WSAECONNRESET:
269                 errno = WSAEWOULDBLOCK;
270                 break;
271             case WSAEHOSTUNREACH:
272                 errno = WSAEHOSTUNREACH;
273                 break;
274             default:
275                 errno = EIO;
276                 break;
277             }
278             code = -1;
279         } else {
280             if (code < size) {
281                 errno = EIO;
282                 code = -1;
283             }
284         }
285     }
286     return code;
287
288 }
289 #endif /* AFS_NT40_ENV */