1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /* Win95 Sockets module
7  *
8  */
9 
10 #if defined(_WIN64)
11 #include <winsock2.h>
12 #endif
13 #include "primpl.h"
14 
15 #define READ_FD     1
16 #define WRITE_FD    2
17 #define CONNECT_FD  3
18 
19 static PRInt32 socket_io_wait(
20     PROsfd osfd,
21     PRInt32 fd_type,
22     PRIntervalTime timeout);
23 
24 
25 /* --- SOCKET IO --------------------------------------------------------- */
26 
27 static PRBool socketFixInet6RcvBuf = PR_FALSE;
28 
_PR_MD_InitSockets(void)29 void _PR_MD_InitSockets(void)
30 {
31     OSVERSIONINFO osvi;
32 
33     memset(&osvi, 0, sizeof(osvi));
34     osvi.dwOSVersionInfoSize = sizeof(osvi);
35     GetVersionEx(&osvi);
36 
37     if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
38     {
39         /* if Windows XP (32-bit) */
40         socketFixInet6RcvBuf = PR_TRUE;
41     }
42 }
43 
_PR_MD_CleanupSockets(void)44 void _PR_MD_CleanupSockets(void)
45 {
46     socketFixInet6RcvBuf = PR_FALSE;
47 }
48 
49 PROsfd
_PR_MD_SOCKET(int af,int type,int flags)50 _PR_MD_SOCKET(int af, int type, int flags)
51 {
52     SOCKET sock;
53     u_long one = 1;
54 
55     sock = socket(af, type, flags);
56 
57     if (sock == INVALID_SOCKET )
58     {
59         _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
60         return (PROsfd)sock;
61     }
62 
63     /*
64     ** Make the socket Non-Blocking
65     */
66     if (ioctlsocket( sock, FIONBIO, &one) != 0)
67     {
68         PR_SetError(PR_UNKNOWN_ERROR, WSAGetLastError());
69         closesocket(sock);
70         return -1;
71     }
72 
73     if (af == AF_INET6 && socketFixInet6RcvBuf)
74     {
75         int bufsize;
76         int len = sizeof(bufsize);
77         int rv;
78 
79         /* Windows XP 32-bit returns an error on getpeername() for AF_INET6
80          * sockets if the receive buffer size is greater than 65535 before
81          * the connection is initiated. The default receive buffer size may
82          * be 128000 so fix it here to always be <= 65535. See bug 513659
83          * and IBM DB2 support technote "Receive/Send IPv6 Socket Size
84          * Problem in Windows XP SP2 & SP3".
85          */
86         rv = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, &len);
87         if (rv == 0 && bufsize > 65535)
88         {
89             bufsize = 65535;
90             setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, len);
91         }
92     }
93 
94     return (PROsfd)sock;
95 }
96 
97 /*
98 ** _MD_CloseSocket() -- Close a socket
99 **
100 */
101 PRInt32
_MD_CloseSocket(PROsfd osfd)102 _MD_CloseSocket(PROsfd osfd)
103 {
104     PRInt32 rv;
105 
106     rv = closesocket((SOCKET) osfd );
107     if (rv < 0) {
108         _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
109     }
110 
111     return rv;
112 }
113 
114 PRInt32
_MD_SocketAvailable(PRFileDesc * fd)115 _MD_SocketAvailable(PRFileDesc *fd)
116 {
117     PRInt32 result;
118 
119     if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
120         PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
121         return -1;
122     }
123     return result;
124 }
125 
_MD_Accept(PRFileDesc * fd,PRNetAddr * raddr,PRUint32 * rlen,PRIntervalTime timeout)126 PROsfd _MD_Accept(
127     PRFileDesc *fd,
128     PRNetAddr *raddr,
129     PRUint32 *rlen,
130     PRIntervalTime timeout )
131 {
132     PROsfd osfd = fd->secret->md.osfd;
133     SOCKET sock;
134     PRInt32 rv, err;
135 
136     while ((sock = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
137     {
138         err = WSAGetLastError();
139         if ((err == WSAEWOULDBLOCK) && (!fd->secret->nonblocking))
140         {
141             if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
142             {
143                 break;
144             }
145         }
146         else
147         {
148             _PR_MD_MAP_ACCEPT_ERROR(err);
149             break;
150         }
151     }
152     return(sock);
153 } /* end _MD_accept() */
154 
155 PRInt32
_PR_MD_CONNECT(PRFileDesc * fd,const PRNetAddr * addr,PRUint32 addrlen,PRIntervalTime timeout)156 _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
157                PRIntervalTime timeout)
158 {
159     PROsfd osfd = fd->secret->md.osfd;
160     PRInt32 rv;
161     int     err;
162 
163     if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
164     {
165         err = WSAGetLastError();
166         if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK))
167         {
168             rv = socket_io_wait(osfd, CONNECT_FD, timeout);
169             if ( rv < 0 )
170             {
171                 return(-1);
172             }
173             else
174             {
175                 PR_ASSERT(rv > 0);
176                 /* it's connected */
177                 return(0);
178             }
179         }
180         _PR_MD_MAP_CONNECT_ERROR(err);
181     }
182     return rv;
183 }
184 
185 PRInt32
_PR_MD_BIND(PRFileDesc * fd,const PRNetAddr * addr,PRUint32 addrlen)186 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
187 {
188     PRInt32 rv;
189 
190     rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
191 
192     if (rv == SOCKET_ERROR)  {
193         _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
194         return -1;
195     }
196 
197     return 0;
198 }
199 
200 PRInt32
_PR_MD_LISTEN(PRFileDesc * fd,PRIntn backlog)201 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
202 {
203     PRInt32 rv;
204 
205     rv = listen(fd->secret->md.osfd, backlog);
206 
207     if (rv == SOCKET_ERROR)  {
208         _PR_MD_MAP_DEFAULT_ERROR(WSAGetLastError());
209         return -1;
210     }
211 
212     return 0;
213 }
214 
215 PRInt32
_PR_MD_RECV(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)216 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
217             PRIntervalTime timeout)
218 {
219     PROsfd osfd = fd->secret->md.osfd;
220     PRInt32 rv, err;
221     int osflags;
222 
223     if (0 == flags) {
224         osflags = 0;
225     } else {
226         PR_ASSERT(PR_MSG_PEEK == flags);
227         osflags = MSG_PEEK;
228     }
229     while ((rv = recv( osfd, buf, amount, osflags)) == -1)
230     {
231         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
232             && (!fd->secret->nonblocking))
233         {
234             rv = socket_io_wait(osfd, READ_FD, timeout);
235             if ( rv < 0 )
236             {
237                 return -1;
238             }
239         }
240         else
241         {
242             _PR_MD_MAP_RECV_ERROR(err);
243             break;
244         }
245     } /* end while() */
246     return(rv);
247 }
248 
249 PRInt32
_PR_MD_SEND(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)250 _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
251             PRIntervalTime timeout)
252 {
253     PROsfd osfd = fd->secret->md.osfd;
254     PRInt32 rv, err;
255     PRInt32 bytesSent = 0;
256 
257     while(bytesSent < amount )
258     {
259         while ((rv = send( osfd, buf, amount, 0 )) == -1)
260         {
261             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
262                 && (!fd->secret->nonblocking))
263             {
264                 rv = socket_io_wait(osfd, WRITE_FD, timeout);
265                 if ( rv < 0 )
266                 {
267                     return -1;
268                 }
269             }
270             else
271             {
272                 _PR_MD_MAP_SEND_ERROR(err);
273                 return -1;
274             }
275         }
276         bytesSent += rv;
277         if (fd->secret->nonblocking)
278         {
279             break;
280         }
281         if (bytesSent < amount)
282         {
283             rv = socket_io_wait(osfd, WRITE_FD, timeout);
284             if ( rv < 0 )
285             {
286                 return -1;
287             }
288         }
289     }
290     return bytesSent;
291 }
292 
293 PRInt32
_PR_MD_SENDTO(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,const PRNetAddr * addr,PRUint32 addrlen,PRIntervalTime timeout)294 _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
295               const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
296 {
297     PROsfd osfd = fd->secret->md.osfd;
298     PRInt32 rv, err;
299     PRInt32 bytesSent = 0;
300 
301     do {
302         while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr,
303                              addrlen)) == -1)
304         {
305             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
306                 && (!fd->secret->nonblocking))
307             {
308                 rv = socket_io_wait(osfd, WRITE_FD, timeout);
309                 if ( rv < 0 )
310                 {
311                     return -1;
312                 }
313             }
314             else
315             {
316                 _PR_MD_MAP_SENDTO_ERROR(err);
317                 return -1;
318             }
319         }
320         bytesSent += rv;
321         if (fd->secret->nonblocking)
322         {
323             break;
324         }
325         if (bytesSent < amount)
326         {
327             rv = socket_io_wait(osfd, WRITE_FD, timeout);
328             if (rv < 0)
329             {
330                 return -1;
331             }
332         }
333     } while(bytesSent < amount);
334     return bytesSent;
335 }
336 
337 #if defined(_WIN64)
338 
339 static PRCallOnceType _pr_has_connectex_once;
340 typedef BOOL (PASCAL FAR * _pr_win_connectex_ptr)(_In_ SOCKET s, _In_reads_bytes_(namelen) const struct sockaddr FAR *name, _In_ int namelen, _In_reads_bytes_opt_(dwSendDataLength) PVOID lpSendBuffer, _In_ DWORD dwSendDataLength, _Out_ LPDWORD lpdwBytesSent, _Inout_ LPOVERLAPPED lpOverlapped);
341 
342 #ifndef WSAID_CONNECTEX
343 #define WSAID_CONNECTEX \
344   {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
345 #endif
346 #ifndef SIO_GET_EXTENSION_FUNCTION_POINTER
347 #define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006
348 #endif
349 #ifndef TCP_FASTOPEN
350 #define TCP_FASTOPEN 15
351 #endif
352 
353 #ifndef SO_UPDATE_CONNECT_CONTEXT
354 #define SO_UPDATE_CONNECT_CONTEXT 0x7010
355 #endif
356 
357 static _pr_win_connectex_ptr _pr_win_connectex = NULL;
358 
_pr_set_connectex(void)359 static PRStatus PR_CALLBACK _pr_set_connectex(void)
360 {
361     _pr_win_connectex = NULL;
362     SOCKET sock;
363     PRInt32 dwBytes;
364     int rc;
365 
366     /* Dummy socket needed for WSAIoctl */
367     sock = socket(AF_INET, SOCK_STREAM, 0);
368     if (sock == INVALID_SOCKET) {
369         return PR_SUCCESS;
370     }
371 
372     GUID guid = WSAID_CONNECTEX;
373     rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
374                   &guid, sizeof(guid),
375                   &_pr_win_connectex, sizeof(_pr_win_connectex),
376                   &dwBytes, NULL, NULL);
377     if (rc != 0) {
378         _pr_win_connectex = NULL;
379         return PR_SUCCESS;
380     }
381 
382     rc = closesocket(sock);
383     return PR_SUCCESS;
384 }
385 
386 PRInt32
_PR_MD_TCPSENDTO(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,const PRNetAddr * addr,PRUint32 addrlen,PRIntervalTime timeout)387 _PR_MD_TCPSENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
388                  const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
389 {
390     if (!_fd_waiting_for_overlapped_done_lock) {
391         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
392         return PR_FAILURE;
393     }
394 
395     if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) {
396         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
397         return PR_FAILURE;
398     }
399 
400     if (_pr_win_connectex == NULL) {
401         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
402         return PR_FAILURE;
403     }
404 
405     PROsfd osfd = fd->secret->md.osfd;
406     PRInt32 rv, err;
407     PRInt32 bytesSent = 0;
408     DWORD rvSent;
409 
410     BOOL option = 1;
411     rv = setsockopt((SOCKET)osfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&option, sizeof(option));
412     if (rv != 0) {
413         err = WSAGetLastError();
414         PR_LOG(_pr_io_lm, PR_LOG_MIN,
415                ("_PR_MD_TCPSENDTO error set opt TCP_FASTOPEN failed %d\n", err));
416         if (err == WSAENOPROTOOPT) {
417             PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
418         } else {
419             _PR_MD_MAP_SETSOCKOPT_ERROR(err);
420         }
421         return -1;
422     }
423 
424     /* ConnectEx requires the socket to be initially bound. We will use INADDR_ANY. */
425     PRNetAddr bindAddr;
426     memset(&bindAddr, 0, sizeof(bindAddr));
427     bindAddr.raw.family = addr->raw.family;
428 
429     rv = bind((SOCKET)osfd, (const struct sockaddr *)&(bindAddr.inet), PR_NETADDR_SIZE(&bindAddr));
430     if (rv != 0) {
431         err = WSAGetLastError();
432         PR_LOG(_pr_io_lm, PR_LOG_MIN,
433                ("_PR_MD_TCPSENDTO error bind failed %d\n", err));
434         _PR_MD_MAP_SETSOCKOPT_ERROR(err);
435         return -1;
436     }
437 
438     PR_LOG(_pr_io_lm, PR_LOG_MIN,
439            ("_PR_MD_TCPSENDTO calling _pr_win_connectex  %d %p\n", amount, (char*)buf));
440 
441     rvSent = 0;
442     memset(&fd->secret->ol, 0, sizeof(fd->secret->ol));
443     /* ConnectEx return TRUE on a success and FALSE on an error. */
444     if (_pr_win_connectex( (SOCKET)osfd, (struct sockaddr *) addr,
445                            addrlen, buf, amount,
446                            &rvSent, &fd->secret->ol) == TRUE) {
447         /* When ConnectEx is used, all previously set socket options and
448          * property are not enabled and to enable them
449          * SO_UPDATE_CONNECT_CONTEXT option need to be set. */
450         rv = setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
451         if (rv != 0) {
452             err = WSAGetLastError();
453             PR_LOG(_pr_io_lm, PR_LOG_MIN,
454                    ("_PR_MD_TCPSENDTO setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err));
455             _PR_MD_MAP_SETSOCKOPT_ERROR(err);
456             return -1;
457         }
458         /* We imitate Linux here. SendTo will return number of bytes send but
459          * it can not return connection success at the same time, so we return
460          * number of bytes send and "connection success" will be return on the
461          * connectcontinue. */
462         fd->secret->alreadyConnected = PR_TRUE;
463         return rvSent;
464     } else {
465         err = WSAGetLastError();
466         PR_LOG(_pr_io_lm, PR_LOG_MIN,
467                ("_PR_MD_TCPSENDTO error _pr_win_connectex failed %d\n", err));
468         if (err != ERROR_IO_PENDING) {
469             _PR_MD_MAP_CONNECT_ERROR(err);
470             return -1;
471         } else if (fd->secret->nonblocking) {
472             /* Remember that overlapped structure is set. We will need to get
473              * the final result of ConnectEx call. */
474             fd->secret->overlappedActive = PR_TRUE;
475 
476             /* ConnectEx will copy supplied data to a internal buffer and send
477              * them during Fast Open or after connect. Therefore we can assumed
478              * this data already send. */
479             if (amount > 0) {
480                 return amount;
481             }
482 
483             _PR_MD_MAP_CONNECT_ERROR(WSAEWOULDBLOCK);
484             return -1;
485         }
486         // err is ERROR_IO_PENDING and socket is blocking, so query
487         // GetOverlappedResult.
488         err = ERROR_IO_INCOMPLETE;
489         while (err == ERROR_IO_INCOMPLETE) {
490             rv = socket_io_wait(osfd, WRITE_FD, timeout);
491             if ( rv < 0 ) {
492                 return -1;
493             }
494             rv = GetOverlappedResult(osfd, &fd->secret->ol, &rvSent, FALSE);
495             if ( rv == TRUE ) {
496                 return rvSent;
497             } else {
498                 err = WSAGetLastError();
499                 if (err != ERROR_IO_INCOMPLETE) {
500                     _PR_MD_MAP_CONNECT_ERROR(err);
501                     return -1;
502                 }
503             }
504         }
505     }
506     return -1;
507 }
508 #endif
509 
510 PRInt32
_PR_MD_RECVFROM(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRNetAddr * addr,PRUint32 * addrlen,PRIntervalTime timeout)511 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
512                 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
513 {
514     PROsfd osfd = fd->secret->md.osfd;
515     PRInt32 rv, err;
516 
517     while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
518                            addrlen)) == -1)
519     {
520         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
521             && (!fd->secret->nonblocking))
522         {
523             rv = socket_io_wait(osfd, READ_FD, timeout);
524             if ( rv < 0)
525             {
526                 return -1;
527             }
528         }
529         else
530         {
531             _PR_MD_MAP_RECVFROM_ERROR(err);
532             break;
533         }
534     }
535     return(rv);
536 }
537 
538 PRInt32
_PR_MD_WRITEV(PRFileDesc * fd,const PRIOVec * iov,PRInt32 iov_size,PRIntervalTime timeout)539 _PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
540 {
541     int index;
542     int sent = 0;
543     int rv;
544 
545     for (index=0; index < iov_size; index++)
546     {
547         rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
548         if (rv > 0) {
549             sent += rv;
550         }
551         if ( rv != iov[index].iov_len )
552         {
553             if (rv < 0)
554             {
555                 if (fd->secret->nonblocking
556                     && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
557                     && (sent > 0))
558                 {
559                     return sent;
560                 }
561                 else
562                 {
563                     return -1;
564                 }
565             }
566             /* Only a nonblocking socket can have partial sends */
567             PR_ASSERT(fd->secret->nonblocking);
568             return sent;
569         }
570     }
571     return sent;
572 }
573 
574 PRInt32
_PR_MD_SHUTDOWN(PRFileDesc * fd,PRIntn how)575 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
576 {
577     PRInt32 rv;
578 
579     rv = shutdown(fd->secret->md.osfd, how);
580     if (rv < 0) {
581         _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
582     }
583     return rv;
584 }
585 
586 PRStatus
_PR_MD_GETSOCKNAME(PRFileDesc * fd,PRNetAddr * addr,PRUint32 * len)587 _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
588 {
589     PRInt32 rv;
590 
591     rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
592     if (rv==0) {
593         return PR_SUCCESS;
594     } else {
595         _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
596         return PR_FAILURE;
597     }
598 }
599 
600 PRStatus
_PR_MD_GETPEERNAME(PRFileDesc * fd,PRNetAddr * addr,PRUint32 * len)601 _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
602 {
603     PRInt32 rv;
604 
605     rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
606     if (rv==0) {
607         return PR_SUCCESS;
608     } else {
609         _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
610         return PR_FAILURE;
611     }
612 }
613 
614 PRStatus
_PR_MD_GETSOCKOPT(PRFileDesc * fd,PRInt32 level,PRInt32 optname,char * optval,PRInt32 * optlen)615 _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
616 {
617     PRInt32 rv;
618 
619     rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
620     if (rv==0) {
621         return PR_SUCCESS;
622     } else {
623         _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
624         return PR_FAILURE;
625     }
626 }
627 
628 PRStatus
_PR_MD_SETSOCKOPT(PRFileDesc * fd,PRInt32 level,PRInt32 optname,const char * optval,PRInt32 optlen)629 _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
630 {
631     PRInt32 rv;
632 
633     rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
634     if (rv==0) {
635         return PR_SUCCESS;
636     } else {
637         _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
638         return PR_FAILURE;
639     }
640 }
641 
642 void
_MD_MakeNonblock(PRFileDesc * f)643 _MD_MakeNonblock(PRFileDesc *f)
644 {
645     return; /* do nothing */
646 }
647 
648 
649 
650 /*
651  * socket_io_wait --
652  *
653  * Wait for socket i/o, periodically checking for interrupt.
654  *
655  * This function returns 1 on success.  On failure, it returns
656  * -1 and sets the error codes.  It never returns 0.
657  */
658 #define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
659 
socket_io_wait(PROsfd osfd,PRInt32 fd_type,PRIntervalTime timeout)660 static PRInt32 socket_io_wait(
661     PROsfd osfd,
662     PRInt32 fd_type,
663     PRIntervalTime timeout)
664 {
665     PRInt32 rv = -1;
666     struct timeval tv;
667     PRThread *me = _PR_MD_CURRENT_THREAD();
668     PRIntervalTime elapsed, remaining;
669     PRBool wait_for_remaining;
670     fd_set rd_wr, ex;
671     int err, len;
672 
673     switch (timeout) {
674         case PR_INTERVAL_NO_WAIT:
675             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
676             break;
677         case PR_INTERVAL_NO_TIMEOUT:
678             /*
679              * This is a special case of the 'default' case below.
680              * Please see the comments there.
681              */
682             tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
683             tv.tv_usec = 0;
684             FD_ZERO(&rd_wr);
685             FD_ZERO(&ex);
686             do {
687                 FD_SET(osfd, &rd_wr);
688                 FD_SET(osfd, &ex);
689                 switch( fd_type )
690                 {
691                     case READ_FD:
692                         rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv);
693                         break;
694                     case WRITE_FD:
695                         rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv);
696                         break;
697                     case CONNECT_FD:
698                         rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv);
699                         break;
700                     default:
701                         PR_ASSERT(0);
702                         break;
703                 } /* end switch() */
704                 if (rv == -1 )
705                 {
706                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
707                     break;
708                 }
709                 if ( rv > 0 && fd_type == CONNECT_FD )
710                 {
711                     /*
712                      * Call Sleep(0) to work around a Winsock timing bug.
713                      */
714                     Sleep(0);
715                     if (FD_ISSET((SOCKET)osfd, &ex))
716                     {
717                         len = sizeof(err);
718                         if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
719                                        (char *) &err, &len) == SOCKET_ERROR)
720                         {
721                             _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
722                             return -1;
723                         }
724                         if (err != 0) {
725                             _PR_MD_MAP_CONNECT_ERROR(err);
726                         }
727                         else {
728                             PR_SetError(PR_UNKNOWN_ERROR, 0);
729                         }
730                         return -1;
731                     }
732                     if (FD_ISSET((SOCKET)osfd, &rd_wr))
733                     {
734                         /* it's connected */
735                         return 1;
736                     }
737                     PR_ASSERT(0);
738                 }
739                 if (_PR_PENDING_INTERRUPT(me)) {
740                     me->flags &= ~_PR_INTERRUPT;
741                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
742                     rv = -1;
743                     break;
744                 }
745             } while (rv == 0);
746             break;
747         default:
748             remaining = timeout;
749             FD_ZERO(&rd_wr);
750             FD_ZERO(&ex);
751             do {
752                 /*
753                  * We block in _MD_SELECT for at most
754                  * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
755                  * so that there is an upper limit on the delay
756                  * before the interrupt bit is checked.
757                  */
758                 wait_for_remaining = PR_TRUE;
759                 tv.tv_sec = PR_IntervalToSeconds(remaining);
760                 if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
761                     wait_for_remaining = PR_FALSE;
762                     tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
763                     tv.tv_usec = 0;
764                 } else {
765                     tv.tv_usec = PR_IntervalToMicroseconds(
766                                      remaining -
767                                      PR_SecondsToInterval(tv.tv_sec));
768                 }
769                 FD_SET(osfd, &rd_wr);
770                 FD_SET(osfd, &ex);
771                 switch( fd_type )
772                 {
773                     case READ_FD:
774                         rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv);
775                         break;
776                     case WRITE_FD:
777                         rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv);
778                         break;
779                     case CONNECT_FD:
780                         rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv);
781                         break;
782                     default:
783                         PR_ASSERT(0);
784                         break;
785                 } /* end switch() */
786                 if (rv == -1)
787                 {
788                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
789                     break;
790                 }
791                 if ( rv > 0 && fd_type == CONNECT_FD )
792                 {
793                     /*
794                      * Call Sleep(0) to work around a Winsock timing bug.
795                      */
796                     Sleep(0);
797                     if (FD_ISSET((SOCKET)osfd, &ex))
798                     {
799                         len = sizeof(err);
800                         if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
801                                        (char *) &err, &len) == SOCKET_ERROR)
802                         {
803                             _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
804                             return -1;
805                         }
806                         if (err != 0) {
807                             _PR_MD_MAP_CONNECT_ERROR(err);
808                         }
809                         else {
810                             PR_SetError(PR_UNKNOWN_ERROR, 0);
811                         }
812                         return -1;
813                     }
814                     if (FD_ISSET((SOCKET)osfd, &rd_wr))
815                     {
816                         /* it's connected */
817                         return 1;
818                     }
819                     PR_ASSERT(0);
820                 }
821                 if (_PR_PENDING_INTERRUPT(me)) {
822                     me->flags &= ~_PR_INTERRUPT;
823                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
824                     rv = -1;
825                     break;
826                 }
827                 /*
828                  * We loop again if _MD_SELECT timed out and the
829                  * timeout deadline has not passed yet.
830                  */
831                 if (rv == 0 )
832                 {
833                     if (wait_for_remaining) {
834                         elapsed = remaining;
835                     } else {
836                         elapsed = PR_SecondsToInterval(tv.tv_sec)
837                                   + PR_MicrosecondsToInterval(tv.tv_usec);
838                     }
839                     if (elapsed >= remaining) {
840                         PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
841                         rv = -1;
842                         break;
843                     } else {
844                         remaining = remaining - elapsed;
845                     }
846                 }
847             } while (rv == 0 );
848             break;
849     }
850     return(rv);
851 } /* end socket_io_wait() */
852