1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* OS/2 Sockets module
8  *
9  */
10 
11 /*Note from DSR111297 - it should be noted that there are two flavors of select() on OS/2    */
12 /*There is standard BSD (which is kind of slow) and a new flavor of select() that takes      */
13 /*an integer list of sockets, the number of read sockets, write sockets, except sockets, and */
14 /*a millisecond count for timeout. In the interest of performance I have choosen the OS/2    */
15 /*specific version of select(). See OS/2 TCP/IP Programmer's Toolkit for more info.          */
16 
17 #include "primpl.h"
18 
19 #include <sys/time.h> /* For timeval. */
20 
21 #define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
22 #define READ_FD   1
23 #define WRITE_FD  2
24 
25 /* --- SOCKET IO --------------------------------------------------------- */
26 
27 
28 PRInt32
_PR_MD_SOCKET(int domain,int type,int flags)29 _PR_MD_SOCKET(int domain, int type, int flags)
30 {
31     PRInt32 osfd, err;
32 
33     osfd = socket(domain, type, flags);
34 
35     if (osfd == -1)
36     {
37         err = sock_errno();
38         _PR_MD_MAP_SOCKET_ERROR(err);
39     }
40 
41     return(osfd);
42 }
43 
44 /*
45 ** _MD_CloseSocket() -- Close a socket
46 **
47 */
48 PRInt32
_MD_CloseSocket(PRInt32 osfd)49 _MD_CloseSocket(PRInt32 osfd)
50 {
51     PRInt32 rv, err;
52 
53     rv = soclose(osfd);
54     if (rv == -1) {
55         err = sock_errno();
56         _PR_MD_MAP_CLOSE_ERROR(err);
57     }
58     return rv;
59 }
60 
61 PRInt32
_MD_SocketAvailable(PRFileDesc * fd)62 _MD_SocketAvailable(PRFileDesc *fd)
63 {
64     PRInt32 result;
65 
66     if (so_ioctl(fd->secret->md.osfd, FIONREAD, (char *) &result, sizeof(result)) < 0) {
67         PR_SetError(PR_BAD_DESCRIPTOR_ERROR, sock_errno());
68         return -1;
69     }
70     return result;
71 }
72 
73 static PRInt32
socket_io_wait(PRInt32 osfd,PRInt32 fd_type,PRIntervalTime timeout)74 socket_io_wait( PRInt32 osfd, PRInt32 fd_type, PRIntervalTime timeout )
75 {
76     PRInt32 rv = -1;
77     PRThread *me = _PR_MD_CURRENT_THREAD();
78     PRIntervalTime epoch, now, elapsed, remaining;
79     PRBool wait_for_remaining;
80     PRInt32 syserror;
81 #ifdef BSD_SELECT
82     struct timeval tv;
83     fd_set rd_wr;
84 #else
85     int socks[1];
86     long lTimeout;
87 #endif
88 
89     switch (timeout) {
90         case PR_INTERVAL_NO_WAIT:
91             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
92             break;
93         case PR_INTERVAL_NO_TIMEOUT:
94             /*
95              * This is a special case of the 'default' case below.
96              * Please see the comments there.
97              */
98 #ifdef BSD_SELECT
99             tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
100             tv.tv_usec = 0;
101             FD_ZERO(&rd_wr);
102             do {
103                 FD_SET(osfd, &rd_wr);
104                 if (fd_type == READ_FD) {
105                     rv = bsdselect(osfd + 1, &rd_wr, NULL, NULL, &tv);
106                 }
107                 else {
108                     rv = bsdselect(osfd + 1, NULL, &rd_wr, NULL, &tv);
109                 }
110 #else
111             lTimeout = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
112             do {
113                 socks[0] = osfd;
114                 if (fd_type == READ_FD) {
115                     rv = os2_select(socks, 1, 0, 0, lTimeout);
116                 }
117                 else {
118                     rv = os2_select(socks, 0, 1, 0, lTimeout);
119                 }
120 #endif
121                 if (rv == -1 && (syserror = sock_errno()) != EINTR) {
122                     _PR_MD_MAP_SELECT_ERROR(syserror);
123                     break;
124                 }
125                 if (_PR_PENDING_INTERRUPT(me)) {
126                     me->flags &= ~_PR_INTERRUPT;
127                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
128                     rv = -1;
129                     break;
130                 }
131             } while (rv == 0 || (rv == -1 && syserror == EINTR));
132             break;
133         default:
134             now = epoch = PR_IntervalNow();
135             remaining = timeout;
136 #ifdef BSD_SELECT
137             FD_ZERO(&rd_wr);
138 #endif
139             do {
140                 /*
141                  * We block in select for at most
142                  * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
143                  * so that there is an upper limit on the delay
144                  * before the interrupt bit is checked.
145                  */
146 #ifdef BSD_SELECT
147                 wait_for_remaining = PR_TRUE;
148                 tv.tv_sec = PR_IntervalToSeconds(remaining);
149                 if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
150                     wait_for_remaining = PR_FALSE;
151                     tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
152                     tv.tv_usec = 0;
153                 } else {
154                     tv.tv_usec = PR_IntervalToMicroseconds(
155                                      remaining -
156                                      PR_SecondsToInterval(tv.tv_sec));
157                 }
158                 FD_SET(osfd, &rd_wr);
159                 if (fd_type == READ_FD) {
160                     rv = bsdselect(osfd + 1, &rd_wr, NULL, NULL, &tv);
161                 }
162                 else {
163                     rv = bsdselect(osfd + 1, NULL, &rd_wr, NULL, &tv);
164                 }
165 #else
166                 wait_for_remaining = PR_TRUE;
167                 lTimeout = PR_IntervalToMilliseconds(remaining);
168                 if (lTimeout > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) {
169                     wait_for_remaining = PR_FALSE;
170                     lTimeout = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
171                 }
172                 socks[0] = osfd;
173                 if (fd_type == READ_FD) {
174                     rv = os2_select(socks, 1, 0, 0, lTimeout);
175                 }
176                 else {
177                     rv = os2_select(socks, 0, 1, 0, lTimeout);
178                 }
179 #endif
180                 /*
181                  * we don't consider EINTR a real error
182                  */
183                 if (rv == -1 && (syserror = sock_errno()) != EINTR) {
184                     _PR_MD_MAP_SELECT_ERROR(syserror);
185                     break;
186                 }
187                 if (_PR_PENDING_INTERRUPT(me)) {
188                     me->flags &= ~_PR_INTERRUPT;
189                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
190                     rv = -1;
191                     break;
192                 }
193                 /*
194                  * We loop again if select timed out or got interrupted
195                  * by a signal, and the timeout deadline has not passed yet.
196                  */
197                 if (rv == 0 || (rv == -1 && syserror == EINTR)) {
198                     /*
199                      * If select timed out, we know how much time
200                      * we spent in blocking, so we can avoid a
201                      * PR_IntervalNow() call.
202                      */
203                     if (rv == 0) {
204                         if (wait_for_remaining) {
205                             now += remaining;
206                         } else {
207 #ifdef BSD_SELECT
208                             now += PR_SecondsToInterval(tv.tv_sec)
209                                    + PR_MicrosecondsToInterval(tv.tv_usec);
210 #else
211                             now += PR_MillisecondsToInterval(lTimeout);
212 #endif
213                         }
214                     } else {
215                         now = PR_IntervalNow();
216                     }
217                     elapsed = (PRIntervalTime) (now - epoch);
218                     if (elapsed >= timeout) {
219                         PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
220                         rv = -1;
221                         break;
222                     } else {
223                         remaining = timeout - elapsed;
224                     }
225                 }
226             } while (rv == 0 || (rv == -1 && syserror == EINTR));
227             break;
228     }
229     return(rv);
230 }
231 
232 PRInt32
233 _MD_Accept(PRFileDesc *fd, PRNetAddr *addr,
234            PRUint32 *addrlen, PRIntervalTime timeout)
235 {
236     PRInt32 osfd = fd->secret->md.osfd;
237     PRInt32 rv, err;
238     PRThread *me = _PR_MD_CURRENT_THREAD();
239 
240     while ((rv = accept(osfd, (struct sockaddr*) addr, (int*)addrlen)) == -1)
241     {
242         err = sock_errno();
243         if ((err == EWOULDBLOCK) || (err == ECONNABORTED))
244         {
245             if (fd->secret->nonblocking) {
246                 break;
247             }
248             if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
249                 goto done;
250             }
251         } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
252             continue;
253         } else {
254             break;
255         }
256     }
257     if (rv < 0) {
258         _PR_MD_MAP_ACCEPT_ERROR(err);
259     }
260 done:
261     return(rv);
262 }
263 
264 PRInt32
265 _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
266                PRIntervalTime timeout)
267 {
268     PRInt32 rv, err;
269     PRThread *me = _PR_MD_CURRENT_THREAD();
270     PRInt32 osfd = fd->secret->md.osfd;
271     PRNetAddr addrCopy = *addr; /* Work around a bug in OS/2 where connect
272                                  * modifies the sockaddr structure.
273                                  * See Bugzilla bug 100776. */
274 
275     /*
276      * We initiate the connection setup by making a nonblocking connect()
277      * call.  If the connect() call fails, there are two cases we handle
278      * specially:
279      * 1. The connect() call was interrupted by a signal.  In this case
280      *    we simply retry connect().
281      * 2. The NSPR socket is nonblocking and connect() fails with
282      *    EINPROGRESS.  We first wait until the socket becomes writable.
283      *    Then we try to find out whether the connection setup succeeded
284      *    or failed.
285      */
286 
287 retry:
288     if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1)
289     {
290         err = sock_errno();
291 
292         if (err == EINTR) {
293             if (_PR_PENDING_INTERRUPT(me)) {
294                 me->flags &= ~_PR_INTERRUPT;
295                 PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
296                 return -1;
297             }
298             goto retry;
299         }
300 
301         if (!fd->secret->nonblocking && (err == EINPROGRESS))
302         {
303             /*
304              * socket_io_wait() may return -1 or 1.
305              */
306 
307             rv = socket_io_wait(osfd, WRITE_FD, timeout);
308             if (rv == -1) {
309                 return -1;
310             }
311 
312             PR_ASSERT(rv == 1);
313             if (_PR_PENDING_INTERRUPT(me)) {
314                 me->flags &= ~_PR_INTERRUPT;
315                 PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
316                 return -1;
317             }
318             err = _MD_os2_get_nonblocking_connect_error(osfd);
319             if (err != 0) {
320                 _PR_MD_MAP_CONNECT_ERROR(err);
321                 return -1;
322             }
323             return 0;
324         }
325 
326         _PR_MD_MAP_CONNECT_ERROR(err);
327     }
328 
329     return rv;
330 }  /* _MD_connect */
331 
332 PRInt32
333 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
334 {
335     PRInt32 rv, err;
336     rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen);
337     if (rv < 0) {
338         err = sock_errno();
339         _PR_MD_MAP_BIND_ERROR(err);
340     }
341     return(rv);
342 }
343 
344 
345 PRInt32
346 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
347 {
348     PRInt32 rv, err;
349     rv = listen(fd->secret->md.osfd, backlog);
350     if (rv < 0)  {
351         err = sock_errno();
352         _PR_MD_MAP_DEFAULT_ERROR(err);
353     }
354     return(rv);
355 }
356 
357 
358 PRInt32
359 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
360             PRIntervalTime timeout)
361 {
362     PRInt32 osfd = fd->secret->md.osfd;
363     PRInt32 rv, err;
364     PRThread *me = _PR_MD_CURRENT_THREAD();
365 
366     while ((rv = recv(osfd,buf,amount,flags)) == -1)
367     {
368         err = sock_errno();
369         if ((err == EWOULDBLOCK)) {
370             if (fd->secret->nonblocking) {
371                 break;
372             }
373             if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
374                 goto done;
375             }
376         } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
377             continue;
378         } else {
379             break;
380         }
381     }
382     if (rv < 0) {
383         _PR_MD_MAP_RECV_ERROR(err);
384     }
385 done:
386     return(rv);
387 }
388 
389 PRInt32
390 _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
391             PRIntervalTime timeout)
392 {
393     PRInt32 osfd = fd->secret->md.osfd;
394     PRInt32 rv, err;
395     PRThread *me = _PR_MD_CURRENT_THREAD();
396 
397     while ((rv = send(osfd,buf,amount,flags)) == -1)
398     {
399         err = sock_errno();
400         if ((err == EWOULDBLOCK)) {
401             if (fd->secret->nonblocking) {
402                 break;
403             }
404             if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
405                 goto done;
406             }
407         } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
408             continue;
409         } else {
410             break;
411         }
412     }
413 
414     /*
415      * optimization; if bytes sent is less than "amount" call
416      * select before returning. This is because it is likely that
417      * the next send() call will return EWOULDBLOCK.
418      */
419     if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
420         && (timeout != PR_INTERVAL_NO_WAIT))
421     {
422         if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) {
423             rv = -1;
424             goto done;
425         }
426     }
427     if (rv < 0) {
428         _PR_MD_MAP_SEND_ERROR(err);
429     }
430 done:
431     return(rv);
432 }
433 
434 PRInt32
435 _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
436               const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
437 {
438     PRInt32 osfd = fd->secret->md.osfd;
439     PRInt32 rv, err;
440     PRThread *me = _PR_MD_CURRENT_THREAD();
441     while ((rv = sendto(osfd, buf, amount, flags,
442                         (struct sockaddr *) addr, addrlen)) == -1)
443     {
444         err = sock_errno();
445         if ((err == EWOULDBLOCK))
446         {
447             if (fd->secret->nonblocking) {
448                 break;
449             }
450             if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
451                 goto done;
452             }
453         } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
454             continue;
455         } else {
456             break;
457         }
458     }
459     if (rv < 0) {
460         _PR_MD_MAP_SENDTO_ERROR(err);
461     }
462 done:
463     return(rv);
464 }
465 
466 PRInt32
467 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
468                 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
469 {
470     PRInt32 osfd = fd->secret->md.osfd;
471     PRInt32 rv, err;
472     PRThread *me = _PR_MD_CURRENT_THREAD();
473 
474     while( (*addrlen = PR_NETADDR_SIZE(addr)),
475            ((rv = recvfrom(osfd, buf, amount, flags,
476                            (struct sockaddr *) addr, (int *)addrlen)) == -1))
477     {
478         err = sock_errno();
479         if ((err == EWOULDBLOCK)) {
480             if (fd->secret->nonblocking) {
481                 break;
482             }
483             if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
484                 goto done;
485             }
486         } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
487             continue;
488         } else {
489             break;
490         }
491     }
492     if (rv < 0) {
493         _PR_MD_MAP_RECVFROM_ERROR(err);
494     }
495 done:
496     return(rv);
497 }
498 
499 PRInt32
500 _PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size,
501               PRIntervalTime timeout)
502 {
503     PRInt32 rv, err;
504     PRThread *me = _PR_MD_CURRENT_THREAD();
505     PRInt32 index, amount = 0;
506     PRInt32 osfd = fd->secret->md.osfd;
507     struct iovec osiov[PR_MAX_IOVECTOR_SIZE];
508 
509     /* Ensured by PR_Writev */
510     PR_ASSERT(iov_size <= PR_MAX_IOVECTOR_SIZE);
511 
512     /*
513      * We can't pass iov to so_writev because PRIOVec and struct iovec
514      * may not be binary compatible.  Make osiov a copy of iov and
515      * pass osiov to so_writev .
516      */
517     for (index = 0; index < iov_size; index++) {
518         osiov[index].iov_base = iov[index].iov_base;
519         osiov[index].iov_len = iov[index].iov_len;
520     }
521 
522     /*
523      * Calculate the total number of bytes to be sent; needed for
524      * optimization later.
525      * We could avoid this if this number was passed in; but it is
526      * probably not a big deal because iov_size is usually small (less than
527      * 3)
528      */
529     if (!fd->secret->nonblocking) {
530         for (index=0; index<iov_size; index++) {
531             amount += iov[index].iov_len;
532         }
533     }
534 
535     while ((rv = so_writev(osfd, osiov, iov_size)) == -1) {
536         err = sock_errno();
537         if ((err == EWOULDBLOCK))    {
538             if (fd->secret->nonblocking) {
539                 break;
540             }
541             if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) {
542                 goto done;
543             }
544         } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
545             continue;
546         } else {
547             break;
548         }
549     }
550 
551     /*
552      * optimization; if bytes sent is less than "amount" call
553      * select before returning. This is because it is likely that
554      * the next writev() call will return EWOULDBLOCK.
555      */
556     if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
557         && (timeout != PR_INTERVAL_NO_WAIT)) {
558         if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) {
559             rv = -1;
560             goto done;
561         }
562     }
563     if (rv < 0) {
564         _PR_MD_MAP_WRITEV_ERROR(err);
565     }
566 done:
567     return(rv);
568 }
569 
570 PRInt32
571 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
572 {
573     PRInt32 rv;
574 
575     rv = shutdown(fd->secret->md.osfd, how);
576     if (rv < 0) {
577         _PR_MD_MAP_SHUTDOWN_ERROR(sock_errno());
578     }
579     return rv;
580 }
581 
582 PRInt32
583 _PR_MD_SOCKETPAIR(int af, int type, int flags, PRInt32 *osfd)
584 {
585     PRInt32 rv, err;
586 
587     rv = socketpair(af, type, flags, osfd);
588     if (rv < 0) {
589         err = _MD_ERRNO();
590         _PR_MD_MAP_SOCKETPAIR_ERROR(err);
591     }
592     return rv;
593 }
594 
595 PRStatus
596 _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
597 {
598     PRInt32 rv, err;
599 
600     rv = getsockname(fd->secret->md.osfd,
601                      (struct sockaddr *) addr, (int *)addrlen);
602     if (rv < 0) {
603         err = sock_errno();
604         _PR_MD_MAP_GETSOCKNAME_ERROR(err);
605     }
606     return rv==0?PR_SUCCESS:PR_FAILURE;
607 }
608 
609 PRStatus
610 _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
611 {
612     PRInt32 rv, err;
613 
614     rv = getpeername(fd->secret->md.osfd,
615                      (struct sockaddr *) addr, (int *)addrlen);
616     if (rv < 0) {
617         err = sock_errno();
618         _PR_MD_MAP_GETPEERNAME_ERROR(err);
619     }
620     return rv==0?PR_SUCCESS:PR_FAILURE;
621 }
622 
623 PRStatus
624 _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname,
625                   char* optval, PRInt32* optlen)
626 {
627     PRInt32 rv, err;
628 
629     rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (int *)optlen);
630     if (rv < 0) {
631         err = sock_errno();
632         _PR_MD_MAP_GETSOCKOPT_ERROR(err);
633     }
634     return rv==0?PR_SUCCESS:PR_FAILURE;
635 }
636 
637 PRStatus
638 _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname,
639                   const char* optval, PRInt32 optlen)
640 {
641     PRInt32 rv, err;
642 
643     rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen);
644     if (rv < 0) {
645         err = sock_errno();
646         _PR_MD_MAP_SETSOCKOPT_ERROR(err);
647     }
648     return rv==0?PR_SUCCESS:PR_FAILURE;
649 }
650 
651 void
652 _MD_MakeNonblock(PRFileDesc *fd)
653 {
654     PRInt32 osfd = fd->secret->md.osfd;
655     PRInt32 err;
656     PRUint32  one = 1;
657 
658     if (osfd <= 2) {
659         /* Don't mess around with stdin, stdout or stderr */
660         return;
661     }
662 
663     err = so_ioctl( osfd, FIONBIO, (char *) &one, sizeof(one));
664     if ( err != 0 )
665     {
666         err = sock_errno();
667         _PR_MD_MAP_SOCKET_ERROR(err);
668     }
669 }
670