1/*
2    Title:      Network functions.
3
4    Copyright (c) 2000-7, 2016 David C. J. Matthews
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License version 2.1 as published by the Free Software Foundation.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19*/
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#elif defined(_WIN32)
23#include "winconfig.h"
24#else
25#error "No configuration file"
26#endif
27
28#ifdef HAVE_STDIO_H
29#include <stdio.h>
30#endif
31
32#ifdef HAVE_STDLIB_H
33#include <stdlib.h>
34#endif
35
36#ifdef HAVE_ERRNO_H
37#include <errno.h>
38#endif
39
40#ifdef HAVE_ASSERT_H
41#include <assert.h>
42#define ASSERT(x) assert(x)
43#else
44#define ASSERT(x) 0
45#endif
46
47#ifdef HAVE_STRING_H
48#include <string.h>
49#endif
50
51#ifdef HAVE_UNISTD_H
52#include <unistd.h>
53#endif
54
55#ifdef HAVE_SYS_PARAM_H
56#include <sys/param.h>
57#endif
58
59#ifdef HAVE_SYS_TIME_H
60#include <sys/time.h>
61#endif
62
63#ifdef HAVE_NETDB_H
64#include <netdb.h>
65#endif
66
67#ifdef HAVE_SYS_SOCKET_H
68#include <sys/socket.h>
69#endif
70
71#ifdef HAVE_NETINET_IN_H
72#include <netinet/in.h>
73#endif
74
75#ifdef HAVE_NETINET_TCP_H
76#include <netinet/tcp.h>
77#endif
78
79#ifdef HAVE_UNISTD_H
80#include <unistd.h>
81#endif
82
83#ifdef HAVE_SYS_IOCTL_H
84#include <sys/ioctl.h>
85#endif
86
87#ifdef HAVE_SYS_UN_H
88#include <sys/un.h>
89#endif
90
91#ifdef HAVE_SYS_FILIO_H
92#include <sys/filio.h>
93#endif
94
95#ifdef HAVE_SYS_SOCKIO_H
96#include <sys/sockio.h>
97#endif
98
99#ifdef HAVE_SYS_SELECT_H
100#include <sys/select.h>
101#endif
102
103#ifndef HAVE_SOCKLEN_T
104typedef int socklen_t;
105#endif
106
107#if (defined(_WIN32) && ! defined(__CYGWIN__))
108#include <winsock2.h>
109#endif
110
111#ifdef HAVE_WINDOWS_H
112#include <windows.h>
113#endif
114
115#include <limits>
116#ifdef max
117#undef max
118#endif
119
120#include "globals.h"
121#include "gc.h"
122#include "arb.h"
123#include "run_time.h"
124#include "mpoly.h"
125#include "processes.h"
126#include "network.h"
127#include "io_internal.h"
128#include "sys.h"
129#include "polystring.h"
130#include "save_vec.h"
131#include "rts_module.h"
132#include "machine_dep.h"
133#include "errors.h"
134#include "rtsentry.h"
135
136extern "C" {
137    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGeneral(PolyObject *threadId, PolyWord code, PolyWord arg);
138    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByName(PolyObject *threadId, PolyWord servName);
139    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByNameAndProtocol(PolyObject *threadId, PolyWord servName, PolyWord protName);
140    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByPort(PolyObject *threadId, PolyWord portNo);
141    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByPortAndProtocol(PolyObject *threadId, PolyWord portNo, PolyWord protName);
142    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetProtByName(PolyObject *threadId, PolyWord protocolName);
143    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetProtByNo(PolyObject *threadId, PolyWord protoNo);
144    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetHostName(PolyObject *threadId);
145    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetHostByName(PolyObject *threadId, PolyWord hostName);
146    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetHostByAddr(PolyObject *threadId, PolyWord hostAddr);
147}
148
149#define STREAMID(x) (DEREFSTREAMHANDLE(x)->streamNo)
150#define SAVE(x) taskData->saveVec.push(x)
151#define ALLOC(n) alloc_and_save(taskData, n)
152#define SIZEOF(x) (sizeof(x)/sizeof(PolyWord))
153
154#if (defined(_WIN32) && ! defined(__CYGWIN__))
155static int winsock_init = 0; /* Check that it has been initialised. */
156
157#else
158#define INVALID_SOCKET (-1)
159#define SOCKET_ERROR    (-1)
160#endif
161
162#ifndef HAVE_SOCKLEN_T
163typedef int socklen_t; // This must be int for Windows at least
164#endif
165
166#ifndef SHUT_RD
167#define SHUT_RD     0
168#endif
169
170#ifndef SHUT_WR
171#define SHUT_WR     1
172#endif
173
174#ifndef SHUT_RDWR
175#define SHUT_RDWR   2
176#endif
177
178/* Address families.  Although this table is in ascending
179   numerical order of address family nothing depends on that.
180   The only requirement is that "INET" => AF_INET must always
181   be present and "UNIX" => AF_UNIX must be present on Unix.
182   Other entries are entirely optional and are for amusement
183   only. */
184struct af_tab_struct {
185    const char *af_name;
186    int af_num;
187} af_table[] =
188{
189#ifdef AF_UNIX
190    { "UNIX",       AF_UNIX }, /* This is nearly always there. */
191#endif
192#ifdef AF_LOCAL
193    { "LOCAL",      AF_LOCAL },
194#endif
195    { "INET",       AF_INET }, /* This one should always be there. */
196#ifdef AF_IMPLINK
197    { "IMPLINK",        AF_IMPLINK },
198#endif
199#ifdef AF_PUP
200    { "PUP",        AF_PUP },
201#endif
202#ifdef AF_CHAOS
203    { "CHAOS",      AF_CHAOS },
204#endif
205#ifdef AF_IPX
206    { "IPX",        AF_IPX },
207#endif
208#ifdef AF_NS
209    { "NS",         AF_NS },
210#endif
211#ifdef AF_ISO
212    { "ISO",        AF_ISO },
213#endif
214#ifdef AF_OSI
215    { "OSI",        AF_OSI },
216#endif
217#ifdef AF_ECMA
218    { "ECMA",       AF_ECMA },
219#endif
220#ifdef AF_DATAKIT
221    { "DATAKIT",        AF_DATAKIT },
222#endif
223#ifdef AF_CCITT
224    { "CCITT",      AF_CCITT },
225#endif
226#ifdef AF_SNA
227    { "SNA",        AF_SNA },
228#endif
229#ifdef AF_DECnet
230    { "DECnet",     AF_DECnet },
231#endif
232#ifdef AF_DLI
233    { "DLI",        AF_DLI },
234#endif
235#ifdef AF_LAT
236    { "LAT",        AF_LAT },
237#endif
238#ifdef AF_HYLINK
239    { "HYLINK",     AF_HYLINK },
240#endif
241#ifdef AF_APPLETALK
242    { "APPLETALK",      AF_APPLETALK },
243#endif
244#ifdef AF_NETBIOS
245    { "NETBIOS",        AF_NETBIOS },
246#endif
247#ifdef AF_ROUTE
248    { "ROUTE",      AF_ROUTE },
249#endif
250#ifdef AF_VOICEVIEW
251    { "VOICEVIEW",      AF_VOICEVIEW },
252#endif
253#ifdef AF_FIREFOX
254    { "FIREFOX",        AF_FIREFOX },
255#endif
256#ifdef AF_BAN
257    { "BAN",        AF_BAN },
258#endif
259#ifdef AF_LINK
260    { "LINK",       AF_LINK },
261#endif
262#ifdef AF_COIP
263    { "COIP",       AF_COIP },
264#endif
265#ifdef AF_CNT
266    { "CNT",        AF_CNT },
267#endif
268#ifdef AF_SIP
269    { "SIP",        AF_SIP },
270#endif
271#ifdef AF_ISDN
272    { "ISDN",       AF_ISDN },
273#endif
274#ifdef AF_E164
275    { "E164",       AF_E164 },
276#endif
277#ifdef AF_INET6
278    { "INET6",      AF_INET6 },
279#endif
280#ifdef AF_NATM
281    { "NATM",       AF_NATM },
282#endif
283#ifdef AF_ATM
284    { "ATM",        AF_ATM },
285#endif
286#ifdef AF_NETGRAPH
287    { "NETGRAPH",       AF_NETGRAPH },
288#endif
289};
290
291/* Socket types.  Only STREAM and DGRAM are required.  */
292struct sk_tab_struct {
293    const char *sk_name;
294    int sk_num;
295} sk_table[] =
296{
297    { "STREAM",     SOCK_STREAM },
298    { "DGRAM",      SOCK_DGRAM },
299    { "RAW",        SOCK_RAW },
300    { "RDM",        SOCK_RDM },
301    { "SEQPACKET",  SOCK_SEQPACKET }
302};
303
304static Handle makeHostEntry(TaskData *taskData, struct hostent *host);
305static Handle makeProtoEntry(TaskData *taskData, struct protoent *proto);
306static Handle mkAftab(TaskData *taskData, void*, char *p);
307static Handle mkSktab(TaskData *taskData, void*, char *p);
308static Handle setSocketOption(TaskData *taskData, Handle args, int level, int opt);
309static Handle getSocketOption(TaskData *taskData, Handle args, int level, int opt);
310static Handle getSocketInt(TaskData *taskData, Handle args, int level, int opt);
311static Handle selectCall(TaskData *taskData, Handle args, int blockType);
312
313#if (defined(_WIN32) && ! defined(__CYGWIN__))
314#define GETERROR     (WSAGetLastError())
315#define TOOMANYFILES    WSAEMFILE
316#define NOMEMORY        WSA_NOT_ENOUGH_MEMORY
317#define STREAMCLOSED    WSA_INVALID_HANDLE
318#define WOULDBLOCK      WSAEWOULDBLOCK
319#define INPROGRESS      WSAEINPROGRESS
320#define CALLINTERRUPTED WSAEINTR
321#undef EBADF
322#undef EMFILE
323#undef EAGAIN
324#undef EINTR
325#undef EWOULDBLOCK
326#undef ENOMEM
327#else
328#define GETERROR    (errno)
329#define TOOMANYFILES EMFILE
330#define NOMEMORY ENOMEM
331#define STREAMCLOSED EBADF
332#define ERRORNUMBER errno
333#define FILEDOESNOTEXIST ENOENT
334#define WOULDBLOCK EWOULDBLOCK
335#define INPROGRESS EINPROGRESS
336#define CALLINTERRUPTED EINTR
337#endif
338
339
340// Wait until "select" returns.  In Windows this is used only for networking.
341class WaitSelect: public Waiter
342{
343public:
344    WaitSelect();
345    virtual void Wait(unsigned maxMillisecs);
346    void SetRead(SOCKET fd) {  FD_SET(fd, &readSet); }
347    void SetWrite(SOCKET fd) {  FD_SET(fd, &writeSet); }
348    void SetExcept(SOCKET fd)  {  FD_SET(fd, &exceptSet); }
349    // Save the result of the select call and any associated error
350    int SelectResult(void) { return selectResult; }
351    int SelectError(void) { return errorResult; }
352private:
353    fd_set readSet, writeSet, exceptSet;
354    int selectResult;
355    int errorResult;
356};
357
358WaitSelect::WaitSelect()
359{
360    FD_ZERO(&readSet);
361    FD_ZERO(&writeSet);
362    FD_ZERO(&exceptSet);
363    selectResult = 0;
364    errorResult = 0;
365}
366
367void WaitSelect::Wait(unsigned maxMillisecs)
368{
369    struct timeval toWait = { 0, 0 };
370    toWait.tv_sec = maxMillisecs / 1000;
371    toWait.tv_usec = (maxMillisecs % 1000) * 1000;
372    selectResult = select(FD_SETSIZE, &readSet, &writeSet, &exceptSet, &toWait);
373    if (selectResult < 0) errorResult = GETERROR;
374}
375
376class WaitNet: public WaitSelect {
377public:
378    WaitNet(SOCKET sock, bool isOOB = false);
379};
380
381// Use "select" in both Windows and Unix.  In Windows that means we
382// don't watch hWakeupEvent but that's only a hint.
383WaitNet::WaitNet(SOCKET sock, bool isOOB)
384{
385    if (isOOB) SetExcept(sock); else SetRead(sock);
386}
387
388// Wait for a socket to be free to write.
389class WaitNetSend: public WaitSelect {
390public:
391    WaitNetSend(SOCKET sock) { SetWrite(sock); }
392};
393
394static Handle Net_dispatch_c(TaskData *taskData, Handle args, Handle code)
395{
396    unsigned c = get_C_unsigned(taskData, code->Word());
397    Handle hSave = taskData->saveVec.mark();
398TryAgain: // Used for various retries.
399          // N.B.  If we call ThreadPause etc we may GC.  We MUST reload any handles so for
400          // safety we always come back here.
401    switch (c)
402    {
403
404    case 11:
405        {
406            /* Return a list of known address families. */
407            return makeList(taskData, sizeof(af_table)/sizeof(af_table[0]),
408                            (char*)af_table, sizeof(af_table[0]),
409                            0, mkAftab);
410        }
411
412    case 12:
413        {
414            /* Return a list of known socket types. */
415            return makeList(taskData, sizeof(sk_table)/sizeof(sk_table[0]),
416                            (char*)sk_table, sizeof(sk_table[0]),
417                            0, mkSktab);
418        }
419
420    case 13: /* Return the "any" internet address. */
421        return Make_arbitrary_precision(taskData, INADDR_ANY);
422
423    case 14: /* Create a socket */
424        {
425            Handle str_token = make_stream_entry(taskData);
426            if (str_token == NULL) raise_syscall(taskData, "Insufficient memory", NOMEMORY);
427            PIOSTRUCT strm;
428            POLYUNSIGNED stream_no = STREAMID(str_token);
429            int af = get_C_int(taskData, DEREFHANDLE(args)->Get(0));
430            int type = get_C_int(taskData, DEREFHANDLE(args)->Get(1));
431            int proto = get_C_int(taskData, DEREFHANDLE(args)->Get(2));
432            SOCKET skt = socket(af, type, proto);
433            if (skt == INVALID_SOCKET)
434            {
435                free_stream_entry(stream_no);
436                switch (GETERROR)
437                {
438                case TOOMANYFILES: /* too many open files */
439                    {
440                        if (emfileFlag) /* Previously had an EMFILE error. */
441                            raise_syscall(taskData, "socket failed", TOOMANYFILES);
442                        emfileFlag = true;
443                        taskData->saveVec.reset(hSave);
444                        FullGC(taskData); /* May clear emfileFlag if we close a file. */
445                        goto TryAgain;
446                    }
447                case CALLINTERRUPTED:
448                    taskData->saveVec.reset(hSave);
449                    goto TryAgain;
450                default: raise_syscall(taskData, "socket failed", GETERROR);
451                }
452            }
453            /* Set the socket to non-blocking mode. */
454#if (defined(_WIN32) && ! defined(__CYGWIN__))
455            unsigned long onOff = 1;
456            if (ioctlsocket(skt, FIONBIO, &onOff) != 0)
457#else
458            int onOff = 1;
459            if (ioctl(skt, FIONBIO, &onOff) < 0)
460#endif
461            {
462                free_stream_entry(stream_no);
463#if (defined(_WIN32) && ! defined(__CYGWIN__))
464                closesocket(skt);
465#else
466                close(skt);
467#endif
468                raise_syscall(taskData, "ioctl failed", GETERROR);
469            }
470            strm = &basic_io_vector[stream_no];
471            strm->device.sock = skt;
472            strm->ioBits =
473                IO_BIT_OPEN | IO_BIT_READ | IO_BIT_WRITE | IO_BIT_SOCKET ;
474            return(str_token);
475        }
476
477    case 15: /* Set TCP No-delay option. */
478        return setSocketOption(taskData, args, IPPROTO_TCP, TCP_NODELAY);
479
480    case 16: /* Get TCP No-delay option. */
481        return getSocketOption(taskData, args, IPPROTO_TCP, TCP_NODELAY);
482
483    case 17: /* Set Debug option. */
484        return setSocketOption(taskData, args, SOL_SOCKET, SO_DEBUG);
485
486    case 18: /* Get Debug option. */
487        return getSocketOption(taskData, args, SOL_SOCKET, SO_DEBUG);
488
489    case 19: /* Set REUSEADDR option. */
490        return setSocketOption(taskData, args, SOL_SOCKET, SO_REUSEADDR);
491
492    case 20: /* Get REUSEADDR option. */
493        return getSocketOption(taskData, args, SOL_SOCKET, SO_REUSEADDR);
494
495    case 21: /* Set KEEPALIVE option. */
496        return setSocketOption(taskData, args, SOL_SOCKET, SO_KEEPALIVE);
497
498    case 22: /* Get KEEPALIVE option. */
499        return getSocketOption(taskData, args, SOL_SOCKET, SO_KEEPALIVE);
500
501    case 23: /* Set DONTROUTE option. */
502        return setSocketOption(taskData, args, SOL_SOCKET, SO_DONTROUTE);
503
504    case 24: /* Get DONTROUTE option. */
505        return getSocketOption(taskData, args, SOL_SOCKET, SO_DONTROUTE);
506
507    case 25: /* Set BROADCAST option. */
508        return setSocketOption(taskData, args, SOL_SOCKET, SO_BROADCAST);
509
510    case 26: /* Get BROADCAST option. */
511        return getSocketOption(taskData, args, SOL_SOCKET, SO_BROADCAST);
512
513    case 27: /* Set OOBINLINE option. */
514        return setSocketOption(taskData, args, SOL_SOCKET, SO_OOBINLINE);
515
516    case 28: /* Get OOBINLINE option. */
517        return getSocketOption(taskData, args, SOL_SOCKET, SO_OOBINLINE);
518
519    case 29: /* Set SNDBUF size. */
520        return setSocketOption(taskData, args, SOL_SOCKET, SO_SNDBUF);
521
522    case 30: /* Get SNDBUF size. */
523        return getSocketInt(taskData, args, SOL_SOCKET, SO_SNDBUF);
524
525    case 31: /* Set RCVBUF size. */
526        return setSocketOption(taskData, args, SOL_SOCKET, SO_RCVBUF);
527
528    case 32: /* Get RCVBUF size. */
529        return getSocketInt(taskData, args, SOL_SOCKET, SO_RCVBUF);
530
531    case 33: /* Get socket type e.g. SOCK_STREAM. */
532        return getSocketInt(taskData, args, SOL_SOCKET, SO_TYPE);
533
534    case 34: /* Get error status and clear it. */
535        return getSocketOption(taskData, args, SOL_SOCKET, SO_ERROR);
536
537    case 35: /* Set Linger time. */
538        {
539            struct linger linger;
540            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
541            int lTime = get_C_int(taskData, DEREFHANDLE(args)->Get(1));
542            /* We pass in a negative value to turn the option off,
543               zero or positive to turn it on. */
544            if (lTime < 0)
545            {
546                linger.l_onoff = 0;
547                linger.l_linger = 0;
548            }
549            else
550            {
551                linger.l_onoff = 1;
552                linger.l_linger = lTime;
553            }
554            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
555            if (setsockopt(strm->device.sock, SOL_SOCKET, SO_LINGER,
556                (char*)&linger, sizeof(linger)) != 0)
557                raise_syscall(taskData, "setsockopt failed", GETERROR);
558            return Make_arbitrary_precision(taskData, 0);
559        }
560
561    case 36: /* Get Linger time. */
562        {
563            struct linger linger;
564            PIOSTRUCT strm = get_stream(args->Word());
565            socklen_t size = sizeof(linger);
566            int lTime = 0;
567            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
568            if (getsockopt(strm->device.sock, SOL_SOCKET, SO_LINGER,
569                (char*)&linger, &size) != 0)
570                raise_syscall(taskData, "getsockopt failed", GETERROR);
571            /* If the option is off return a negative. */
572            if (linger.l_onoff == 0) lTime = -1;
573            else lTime = linger.l_linger;
574            return Make_arbitrary_precision(taskData, lTime);
575        }
576
577    case 37: /* Get peer name. */
578        {
579            PIOSTRUCT strm = get_stream(args->Word());
580            struct sockaddr sockA;
581            socklen_t   size = sizeof(sockA);
582            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
583            if (getpeername(strm->device.sock, &sockA, &size) != 0)
584                raise_syscall(taskData, "getpeername failed", GETERROR);
585            /* Addresses are treated as strings. */
586            return(SAVE(C_string_to_Poly(taskData, (char*)&sockA, size)));
587        }
588
589    case 38: /* Get socket name. */
590        {
591            PIOSTRUCT strm = get_stream(args->Word());
592            struct sockaddr sockA;
593            socklen_t   size = sizeof(sockA);
594            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
595            if (getsockname(strm->device.sock, &sockA, &size) != 0)
596                raise_syscall(taskData, "getsockname failed", GETERROR);
597            return(SAVE(C_string_to_Poly(taskData, (char*)&sockA, size)));
598        }
599
600    case 39: /* Return the address family from an address. */
601        {
602            PolyStringObject *psAddr = (PolyStringObject *)args->WordP();
603            struct sockaddr *psock = (struct sockaddr *)&psAddr->chars;
604            return Make_arbitrary_precision(taskData, psock->sa_family);
605        }
606
607    case 40: /* Create a socket address from a port number and
608                internet address. */
609        {
610            struct sockaddr_in sockaddr;
611            memset(&sockaddr, 0, sizeof(sockaddr));
612            sockaddr.sin_family = AF_INET;
613            sockaddr.sin_port = htons(get_C_ushort(taskData, DEREFHANDLE(args)->Get(0)));
614            sockaddr.sin_addr.s_addr =
615                htonl(get_C_unsigned(taskData, DEREFHANDLE(args)->Get(1)));
616            return(SAVE(C_string_to_Poly(taskData, (char*)&sockaddr, sizeof(sockaddr))));
617        }
618
619    case 41: /* Return port number from an internet socket address.
620                Assumes that we've already checked the address family. */
621        {
622            PolyStringObject *psAddr = (PolyStringObject *)args->WordP();
623            struct sockaddr_in *psock =
624                (struct sockaddr_in *)&psAddr->chars;
625            return Make_arbitrary_precision(taskData, ntohs(psock->sin_port));
626        }
627
628    case 42: /* Return internet address from an internet socket address.
629                Assumes that we've already checked the address family. */
630        {
631            PolyStringObject * psAddr = (PolyStringObject *)args->WordP();
632            struct sockaddr_in *psock =
633                (struct sockaddr_in *)&psAddr->chars;
634            return Make_arbitrary_precision(taskData, ntohl(psock->sin_addr.s_addr));
635        }
636
637        /* 43 - Set non-blocking mode.  Now removed. */
638
639    case 44: /* Find number of bytes available. */
640        {
641            PIOSTRUCT strm = get_stream(args->Word());
642            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
643#if (defined(_WIN32) && ! defined(__CYGWIN__))
644            unsigned long readable;
645            if (ioctlsocket(strm->device.sock, FIONREAD, &readable) != 0)
646                raise_syscall(taskData, "ioctlsocket failed", GETERROR);
647#else
648            int readable;
649            if (ioctl(strm->device.sock, FIONREAD, &readable) < 0)
650                raise_syscall(taskData, "ioctl failed", GETERROR);
651#endif
652            return Make_arbitrary_precision(taskData, readable);
653        }
654
655    case 45: /* Find out if we are at the mark. */
656        {
657            PIOSTRUCT strm = get_stream(args->Word());
658            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
659#if (defined(_WIN32) && ! defined(__CYGWIN__))
660            unsigned long atMark;
661            if (ioctlsocket(strm->device.sock, SIOCATMARK, &atMark) != 0)
662                raise_syscall(taskData, "ioctlsocket failed", GETERROR);
663#else
664            int atMark;
665            if (ioctl(strm->device.sock, SIOCATMARK, &atMark) < 0)
666                raise_syscall(taskData, "ioctl failed", GETERROR);
667#endif
668            return Make_arbitrary_precision(taskData, atMark == 0 ? 0 : 1);
669        }
670
671    case 46: /* Accept a connection. */
672        // We should check for interrupts even if we're not going to block.
673        processes->TestAnyEvents(taskData);
674
675    case 58: /* Non-blocking accept. */
676        {
677            PIOSTRUCT strm = get_stream(args->Word());
678            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
679            else {
680                SOCKET sock = strm->device.sock;
681                struct sockaddr resultAddr;
682                Handle addrHandle, pair;
683                PIOSTRUCT newStrm;
684                /* Get a token for the new socket - may raise an
685                   exception if it fails. */
686                Handle str_token = make_stream_entry(taskData);
687                if (str_token == NULL) raise_syscall(taskData, "Insufficient memory", NOMEMORY);
688                POLYUNSIGNED stream_no = STREAMID(str_token);
689                socklen_t addrLen = sizeof(resultAddr);
690                SOCKET result = accept(sock, &resultAddr, &addrLen);
691
692                if (result == INVALID_SOCKET)
693                {
694                    /* Free the stream entry */
695                    free_stream_entry(stream_no);
696                    switch (GETERROR)
697                    {
698                    case CALLINTERRUPTED:
699                        taskData->saveVec.reset(hSave);
700                        goto TryAgain; /* Have to retry if we got EINTR. */
701                    case TOOMANYFILES: /* Too many files. */
702                        {
703                            if (emfileFlag) /* Previously had an EMFILE error. */
704                                raise_syscall(taskData, "accept failed", TOOMANYFILES);
705                            emfileFlag = true;
706                            taskData->saveVec.reset(hSave);
707                            FullGC(taskData); /* May clear emfileFlag if we close a file. */
708                            goto TryAgain;
709                        }
710                    case WOULDBLOCK:
711#if (WOULDBLOCK != INPROGRESS)
712                    case INPROGRESS:
713#endif
714                        /* If the socket is in non-blocking mode we pass
715                           this back to the caller.  If it is blocking we
716                           suspend this process and try again later. */
717                        if (c == 46 /* blocking version. */) {
718                            WaitNet waiter(strm->device.sock);
719                            processes->ThreadPauseForIO(taskData, &waiter);
720                            taskData->saveVec.reset(hSave);
721                            goto TryAgain;
722                        }
723                        /* else drop through. */
724                    default:
725                        raise_syscall(taskData, "accept failed", GETERROR);
726                    }
727                }
728
729                addrHandle = SAVE(C_string_to_Poly(taskData, (char*)&resultAddr, addrLen));
730                newStrm = &basic_io_vector[stream_no];
731                newStrm->device.sock = result;
732                newStrm->ioBits =
733                    IO_BIT_OPEN | IO_BIT_READ | IO_BIT_WRITE | IO_BIT_SOCKET;
734                /* Return a pair of the new socket and the address. */
735                pair = ALLOC(2);
736                DEREFHANDLE(pair)->Set(0, str_token->Word());
737                DEREFHANDLE(pair)->Set(1, addrHandle->Word());
738                return pair;
739            }
740        }
741
742    case 47: /* Bind an address to a socket. */
743        {
744            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
745            PolyStringObject * psAddr = (PolyStringObject *)args->WordP()->Get(1).AsObjPtr();
746            struct sockaddr *psock = (struct sockaddr *)&psAddr->chars;
747            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
748            if (bind(strm->device.sock, psock, (int)psAddr->length) != 0)
749                raise_syscall(taskData, "bind failed", GETERROR);
750            return Make_arbitrary_precision(taskData, 0);
751        }
752
753    case 48: /* Connect to an address. */
754        // We should check for interrupts even if we're not going to block.
755        processes->TestAnyEvents(taskData);
756    case 59: /* Non-blocking connect. */
757        {
758            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
759            PolyStringObject * psAddr = (PolyStringObject *)args->WordP()->Get(1).AsObjPtr();
760            struct sockaddr *psock = (struct sockaddr *)&psAddr->chars;
761            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
762            /* In Windows, and possibly also in Unix, if we have
763               received a previous EWOULDBLOCK we have to use "select"
764               to tell us whether the connection actually succeeded. */
765            while (1)
766            {
767                int res = connect(strm->device.sock, psock, (int)psAddr->length);
768                if (res == 0) return Make_arbitrary_precision(taskData, 0); /* OK */
769                /* It isn't clear that EINTR can ever occur with
770                    connect, but just to be safe, we retry. */
771                int err = GETERROR;
772                if ((err == WOULDBLOCK || err == INPROGRESS) && c == 48 /*blocking version*/)
773                    break; // It's in progress and we need to wait for completion
774                else if (err != CALLINTERRUPTED)
775                    raise_syscall(taskData, "connect failed", err);
776                /* else try again. */
777            }
778
779
780            while (1)
781            {
782                // ThreadPause may GC.  We need to reload the socket for security.
783                strm = get_stream(DEREFHANDLE(args)->Get(0));
784                if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
785                SOCKET sock = strm->device.sock;
786                /* In Windows failure is indicated by the bit being set in
787                    the exception set rather than the write set. */
788                WaitSelect waiter;
789                waiter.SetWrite(sock);
790                waiter.SetExcept(sock);
791                processes->ThreadPauseForIO(taskData, &waiter);
792
793                if (waiter.SelectResult() < 0)
794                {
795                    int err = waiter.SelectError();
796                    if (err != CALLINTERRUPTED)
797                        raise_syscall(taskData, "select failed", err);
798                    /* else continue */
799                }
800                else if (waiter.SelectResult() != 0) /* Definite result. */
801                {
802                    int result = 0;
803                    socklen_t len = sizeof(result);
804                    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&result, &len) != 0)
805                        raise_syscall(taskData, "connect failed", GETERROR);
806                    else if (result != 0)
807                        raise_syscall(taskData, "connect failed", result);
808                    return Make_arbitrary_precision(taskData, 0); /* Success. */
809                }
810            }
811        }
812
813    case 49: /* Put socket into listening mode. */
814        {
815            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
816            int backlog = get_C_int(taskData, DEREFHANDLE(args)->Get(1));
817            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
818            if (listen(strm->device.sock, backlog) != 0)
819                raise_syscall(taskData, "listen failed", GETERROR);
820            return Make_arbitrary_precision(taskData, 0);
821        }
822
823    case 50: /* Shutdown the socket. */
824        {
825            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
826            int mode = 0;
827            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
828            switch (get_C_ulong(taskData, DEREFHANDLE(args)->Get(1)))
829            {
830            case 1: mode = SHUT_RD; break;
831            case 2: mode = SHUT_WR; break;
832            case 3: mode = SHUT_RDWR;
833            }
834            if (shutdown(strm->device.sock, mode) != 0)
835                raise_syscall(taskData, "shutdown failed", GETERROR);
836            return Make_arbitrary_precision(taskData, 0);
837        }
838
839    case 51: /* Send data on a socket. */
840        // We should check for interrupts even if we're not going to block.
841        processes->TestAnyEvents(taskData);
842    case 60: /* Non-blocking send. */
843        {
844            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
845            PolyWord pBase = DEREFHANDLE(args)->Get(1);
846            char    ch, *base;
847            POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(2));
848#if(defined(_WIN32) && ! defined(_CYGWIN))
849            int length = get_C_int(taskData, DEREFHANDLE(args)->Get(3));
850#else
851            ssize_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
852#endif
853            unsigned int dontRoute = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(4));
854            unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
855            int flags = 0;
856            if (dontRoute != 0) flags |= MSG_DONTROUTE;
857            if (outOfBand != 0) flags |= MSG_OOB;
858            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
859            if (IS_INT(pBase)) {
860                /* Handle the special case where we are sending a single
861                   byte vector and the "address" is the tagged byte itself. */
862                ch = (char)UNTAGGED(pBase);
863                base = &ch;
864                offset = 0;
865                length = 1;
866            }
867            else base = (char*)pBase.AsObjPtr()->AsBytePtr();
868
869            while (1)
870            {
871                int err;
872#if(defined(_WIN32) && ! defined(_CYGWIN))
873                int sent;
874#else
875                ssize_t sent;
876#endif
877                sent = send(strm->device.sock, base+offset, length, flags);
878                /* It isn't clear that EINTR can ever occur with
879                   send but just to be safe we deal with that case and
880                   retry the send. */
881                if (sent != SOCKET_ERROR) /* OK. */
882                    return Make_arbitrary_precision(taskData, sent);
883                err = GETERROR;
884                if ((err == WOULDBLOCK || err == INPROGRESS) && c == 51 /* blocking */)
885                {
886                    WaitNetSend waiter(strm->device.sock);
887                    processes->ThreadPauseForIO(taskData, &waiter);
888                    // It is NOT safe to just loop here.  We may have GCed.
889                    taskData->saveVec.reset(hSave);
890                    goto TryAgain;
891                }
892                else if (err != CALLINTERRUPTED)
893                    raise_syscall(taskData, "send failed", err);
894                /* else try again */
895            }
896        }
897
898    case 52: /* Send data on a socket to a given address. */
899        // We should check for interrupts even if we're not going to block.
900        processes->TestAnyEvents(taskData);
901    case 61: /* Non-blocking send. */
902        {
903            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
904            PolyStringObject * psAddr = (PolyStringObject *)args->WordP()->Get(1).AsObjPtr();
905            PolyWord pBase = DEREFHANDLE(args)->Get(2);
906            char    ch, *base;
907            POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
908#if(defined(_WIN32) && ! defined(_CYGWIN))
909            int length = get_C_int(taskData, DEREFHANDLE(args)->Get(4));
910#else
911            size_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(4));
912#endif
913            unsigned int dontRoute = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
914            unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(6));
915            int flags = 0;
916            if (dontRoute != 0) flags |= MSG_DONTROUTE;
917            if (outOfBand != 0) flags |= MSG_OOB;
918            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
919            if (IS_INT(pBase)) {
920                /* Handle the special case where we are sending a single
921                   byte vector and the "address" is the tagged byte itself. */
922                ch = (char)UNTAGGED(pBase);
923                base = &ch;
924                offset = 0;
925                length = 1;
926            }
927            else base = (char*)pBase.AsObjPtr()->AsBytePtr();
928
929            while (1)
930            {
931                int err;
932#if(defined(_WIN32) && ! defined(_CYGWIN))
933                int sent;
934#else
935                ssize_t sent;
936#endif
937                sent = sendto(strm->device.sock, base+offset, length, flags,
938                            (struct sockaddr *)psAddr->chars, (int)psAddr->length);
939                /* It isn't clear that EINTR can ever occur with
940                   send but just to be safe we deal with that case and
941                   retry the send. */
942                if (sent != SOCKET_ERROR) /* OK. */
943                    return Make_arbitrary_precision(taskData, sent);
944                err = GETERROR;
945                if ((err == WOULDBLOCK || err == INPROGRESS) && c == 52 /* blocking */)
946                {
947                    WaitNetSend waiter(strm->device.sock);
948                    processes->ThreadPauseForIO(taskData, &waiter);
949                    // It is NOT safe to just loop here.  We may have GCed.
950                    taskData->saveVec.reset(hSave);
951                    goto TryAgain;
952                }
953                else if (err != CALLINTERRUPTED)
954                    raise_syscall(taskData, "sendto failed", err);
955                /* else try again */
956            }
957        }
958
959    case 53: /* Receive data into an array. */
960        // We should check for interrupts even if we're not going to block.
961        processes->TestAnyEvents(taskData);
962    case 62: /* Non-blocking receive. */
963        {
964            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
965            char *base = (char*)DEREFHANDLE(args)->Get(1).AsObjPtr()->AsBytePtr();
966            POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(2));
967#if(defined(_WIN32) && ! defined(_CYGWIN))
968            int length = get_C_int(taskData, DEREFHANDLE(args)->Get(3));
969#else
970            size_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
971#endif
972            unsigned int peek = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(4));
973            unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
974            int flags = 0;
975            if (peek != 0) flags |= MSG_PEEK;
976            if (outOfBand != 0) flags |= MSG_OOB;
977            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
978
979            while (1) {
980                int err;
981#if(defined(_WIN32) && ! defined(_CYGWIN))
982                int recvd;
983#else
984                ssize_t recvd;
985#endif
986                recvd = recv(strm->device.sock, base+offset, length, flags);
987                err = GETERROR;
988                if (recvd != SOCKET_ERROR) { /* OK. */
989                    /* It appears that recv may return the length of the
990                       message if that is longer than the buffer. */
991                    if (recvd > (int)length) recvd = length;
992                    return Make_arbitrary_precision(taskData, recvd);
993                }
994                if ((err == WOULDBLOCK || err == INPROGRESS) && c == 53 /* blocking */)
995                {
996                    /* Block until something arrives. */
997                    WaitNet waiter(strm->device.sock, outOfBand != 0);
998                    processes->ThreadPauseForIO(taskData, &waiter);
999                    // It is NOT safe to just loop here.  We may have GCed.
1000                    taskData->saveVec.reset(hSave);
1001                    goto TryAgain;
1002                }
1003                else if (err != CALLINTERRUPTED)
1004                    raise_syscall(taskData, "recv failed", err);
1005                /* else try again */
1006            }
1007        }
1008
1009    case 54: /* Receive data into an array and return the sender's
1010                address along with the length.  In Windows this can
1011                only be used with datagrams. */
1012        // We should check for interrupts even if we're not going to block.
1013        processes->TestAnyEvents(taskData);
1014    case 63: /* Non-blocking receive. */
1015        {
1016            PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
1017            char *base = (char*)DEREFHANDLE(args)->Get(1).AsObjPtr()->AsBytePtr();
1018            POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(2));
1019#if(defined(_WIN32) && ! defined(_CYGWIN))
1020            int length = get_C_int(taskData, DEREFHANDLE(args)->Get(3));
1021#else
1022            size_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
1023#endif
1024            unsigned int peek = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(4));
1025            unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
1026            int flags = 0;
1027            socklen_t addrLen;
1028            struct sockaddr resultAddr;
1029
1030            if (peek != 0) flags |= MSG_PEEK;
1031            if (outOfBand != 0) flags |= MSG_OOB;
1032            if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1033
1034            while (1) {
1035                int err;
1036#if(defined(_WIN32) && ! defined(_CYGWIN))
1037                int recvd;
1038#else
1039                ssize_t recvd;
1040#endif
1041                recvd = recvfrom(strm->device.sock, base+offset,
1042                                 length, flags, &resultAddr, &addrLen);
1043                err = GETERROR;
1044
1045                if (recvd != SOCKET_ERROR) { /* OK. */
1046                    Handle addrHandle, lengthHandle, pair;
1047                    if (recvd > (int)length) recvd = length;
1048                    lengthHandle = Make_arbitrary_precision(taskData, recvd);
1049                    addrHandle = SAVE(C_string_to_Poly(taskData, (char*)&resultAddr, addrLen));
1050                    pair = ALLOC(2);
1051                    DEREFHANDLE(pair)->Set(0, lengthHandle->Word());
1052                    DEREFHANDLE(pair)->Set(1, addrHandle->Word());
1053                    return pair;
1054                }
1055                if ((err == WOULDBLOCK || err == INPROGRESS) && c == 54 /* blocking */)
1056                {
1057                    WaitNet waiter(strm->device.sock, outOfBand != 0);
1058                    processes->ThreadPauseForIO(taskData, &waiter);
1059                    // It is NOT safe to just loop here.  We may have GCed.
1060                    taskData->saveVec.reset(hSave);
1061                    goto TryAgain;
1062                }
1063                else if (err != CALLINTERRUPTED)
1064                    raise_syscall(taskData, "recvfrom failed", err);
1065                /* else try again */
1066            }
1067        }
1068
1069    case 55: /* Create a socket pair. */
1070#if (defined(_WIN32) && ! defined(__CYGWIN__))
1071        /* Not implemented. */
1072        raise_syscall(taskData, "socketpair not implemented", WSAEAFNOSUPPORT);
1073#else
1074        {
1075            Handle str_token1 = make_stream_entry(taskData);
1076            if (str_token1 == NULL) raise_syscall(taskData, "Insufficient memory", ENOMEM);
1077            Handle str_token2 = make_stream_entry(taskData);
1078            if (str_token2 == NULL) raise_syscall(taskData, "Insufficient memory", ENOMEM);
1079            Handle pair;
1080            PIOSTRUCT strm1, strm2;
1081            unsigned stream_no1 = STREAMID(str_token1);
1082            unsigned stream_no2 = STREAMID(str_token2);
1083            int af = get_C_long(taskData, DEREFHANDLE(args)->Get(0));
1084            int type = get_C_long(taskData, DEREFHANDLE(args)->Get(1));
1085            int proto = get_C_long(taskData, DEREFHANDLE(args)->Get(2));
1086            int onOff = 1;
1087            SOCKET skt[2];
1088            if (socketpair(af, type, proto, skt) != 0)
1089            {
1090                free_stream_entry(stream_no1);
1091                free_stream_entry(stream_no2);
1092                switch (GETERROR)
1093                {
1094                case TOOMANYFILES: /* too many open files */
1095                    {
1096                        if (emfileFlag) /* Previously had an EMFILE error. */
1097                            raise_syscall(taskData, "socket failed", TOOMANYFILES);
1098                        emfileFlag = true;
1099                        FullGC(taskData); /* May clear emfileFlag if we close a file. */
1100                        taskData->saveVec.reset(hSave);
1101                        goto TryAgain;
1102                    }
1103                case CALLINTERRUPTED:
1104                    taskData->saveVec.reset(hSave);
1105                    goto TryAgain;
1106                default: raise_syscall(taskData, "socketpair failed", GETERROR);
1107                }
1108            }
1109            /* Set the sockets to non-blocking mode. */
1110            if (ioctl(skt[0], FIONBIO, &onOff) < 0 ||
1111                ioctl(skt[1], FIONBIO, &onOff) < 0)
1112            {
1113                free_stream_entry(stream_no1);
1114                free_stream_entry(stream_no2);
1115                close(skt[0]);
1116                close(skt[1]);
1117                raise_syscall(taskData, "ioctl failed", GETERROR);
1118            }
1119            strm1 = &basic_io_vector[stream_no1];
1120            strm1->device.sock = skt[0];
1121            strm1->ioBits =
1122                IO_BIT_OPEN | IO_BIT_READ | IO_BIT_WRITE | IO_BIT_SOCKET ;
1123            strm2 = &basic_io_vector[stream_no2];
1124            strm2->device.sock = skt[1];
1125            strm2->ioBits =
1126                IO_BIT_OPEN | IO_BIT_READ | IO_BIT_WRITE | IO_BIT_SOCKET ;
1127            /* Return the two streams as a pair. */
1128            pair = ALLOC(2);
1129            DEREFHANDLE(pair)->Set(0, DEREFWORD(str_token1));
1130            DEREFHANDLE(pair)->Set(1, DEREFWORD(str_token2));
1131            return pair;
1132        }
1133#endif
1134
1135    case 56: /* Create a Unix socket address from a string. */
1136#if (defined(_WIN32) && ! defined(__CYGWIN__))
1137        /* Not implemented. */
1138        raise_syscall(taskData, "Unix addresses not implemented", WSAEAFNOSUPPORT);
1139#else
1140        {
1141            struct sockaddr_un addr;
1142            memset(&addr, 0, sizeof(addr));
1143            addr.sun_family = AF_UNIX;
1144#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
1145            addr.sun_len = sizeof(addr); // Used in FreeBSD only.
1146#endif
1147            POLYUNSIGNED length = Poly_string_to_C(DEREFWORD(args), addr.sun_path, sizeof(addr.sun_path));
1148            if (length > (int)sizeof(addr.sun_path))
1149                raise_syscall(taskData, "Address too long", ENAMETOOLONG);
1150            return SAVE(C_string_to_Poly(taskData, (char*)&addr, sizeof(addr)));
1151        }
1152#endif
1153
1154    case 57: /* Get the file name from a Unix socket address. */
1155#if (defined(_WIN32) && ! defined(__CYGWIN__))
1156        /* Not implemented. */
1157        raise_syscall(taskData, "Unix addresses not implemented", WSAEAFNOSUPPORT);
1158#else
1159        {
1160            PolyStringObject * psAddr = (PolyStringObject *)args->WordP();
1161            struct sockaddr_un *psock = (struct sockaddr_un *)&psAddr->chars;
1162            return SAVE(C_string_to_Poly(taskData, psock->sun_path));
1163        }
1164#endif
1165
1166    case 64: /* Blocking select call. Infinite timeout. */
1167        return selectCall(taskData, args, 1);
1168
1169    case 65: /* Polling select call. Zero timeout. */
1170        return selectCall(taskData, args, 2);
1171
1172    case 66: /* Select call with non-zero timeout. */
1173        return selectCall(taskData, args, 0);
1174
1175
1176    default:
1177        {
1178            char msg[100];
1179            sprintf(msg, "Unknown net function: %d", c);
1180            raise_exception_string(taskData, EXC_Fail, msg);
1181            return 0;
1182        }
1183    }
1184}
1185
1186static Handle mkAddr(TaskData *taskData, void *arg, char *p)
1187{
1188    int j;
1189    struct hostent *host = (struct hostent *)arg;
1190    unsigned long addr = 0;
1191    /* Addresses are in network order so this is fairly easy.
1192       In practice they will be 4 byte entries so we could
1193       just use ntohl. */
1194    for (j = 0; j < host->h_length; j++)
1195        addr = (addr << 8) | ((*(char**)p)[j] & 255);
1196    return Make_arbitrary_precision(taskData, addr);
1197}
1198
1199/* Convert a host entry into a tuple for ML. */
1200static Handle makeHostEntry(TaskData *taskData, struct hostent *host)
1201{
1202    /* We need to do all this in the right order.  We cannot
1203       construct the result tuple until all the values are
1204       ready.  We have to save each entry on the save stack
1205       just in case of a garbage collection. */
1206    int i;
1207    char **p;
1208    Handle aliases, name, addrType, result;
1209    Handle addrList = SAVE(ListNull);
1210
1211    /* Canonical name. */
1212    name = SAVE(C_string_to_Poly(taskData, host->h_name));
1213
1214    /* Aliases. */
1215    for (i=0, p = host->h_aliases; *p != NULL; p++, i++);
1216    aliases = convert_string_list(taskData, i, host->h_aliases);
1217
1218    /* Address type. */
1219    addrType = Make_arbitrary_precision(taskData, host->h_addrtype);
1220
1221    /* Addresses. */
1222    /* Count them first and then work from the end back. */
1223    for (i=0, p = host->h_addr_list; *p != NULL; p++, i++);
1224    addrList = makeList(taskData, i, (char*)host->h_addr_list, sizeof(char*), host, mkAddr);
1225
1226    /* Make the result structure. */
1227    result = ALLOC(4);
1228    DEREFHANDLE(result)->Set(0, name->Word());
1229    DEREFHANDLE(result)->Set(1, aliases->Word());
1230    DEREFHANDLE(result)->Set(2, addrType->Word());
1231    DEREFHANDLE(result)->Set(3, addrList->Word());
1232    return result;
1233}
1234
1235static Handle makeProtoEntry(TaskData *taskData, struct protoent *proto)
1236{
1237    int i;
1238    char **p;
1239    Handle aliases, name, protocol, result;
1240
1241    /* Canonical name. */
1242    name = SAVE(C_string_to_Poly(taskData, proto->p_name));
1243
1244    /* Aliases. */
1245    for (i=0, p = proto->p_aliases; *p != NULL; p++, i++);
1246    aliases = convert_string_list(taskData, i, proto->p_aliases);
1247
1248    /* Protocol number. */
1249    protocol = Make_arbitrary_precision(taskData, proto->p_proto);
1250
1251    /* Make the result structure. */
1252    result = ALLOC(3);
1253    DEREFHANDLE(result)->Set(0, name->Word());
1254    DEREFHANDLE(result)->Set(1, aliases->Word());
1255    DEREFHANDLE(result)->Set(2, protocol->Word());
1256    return result;
1257}
1258
1259static Handle makeServEntry(TaskData *taskData, struct servent *serv)
1260{
1261    int i;
1262    char **p;
1263    Handle aliases, name, protocol, result, port;
1264
1265    /* Canonical name. */
1266    name = SAVE(C_string_to_Poly(taskData, serv->s_name));
1267
1268    /* Aliases. */
1269    for (i=0, p = serv->s_aliases; *p != NULL; p++, i++);
1270    aliases = convert_string_list(taskData, i, serv->s_aliases);
1271
1272    /* Port number. */
1273    port = Make_arbitrary_precision(taskData, ntohs(serv->s_port));
1274
1275    /* Protocol name. */
1276    protocol = SAVE(C_string_to_Poly(taskData, serv->s_proto));
1277
1278    /* Make the result structure. */
1279    result = ALLOC(4);
1280    DEREFHANDLE(result)->Set(0, name->Word());
1281    DEREFHANDLE(result)->Set(1, aliases->Word());
1282    DEREFHANDLE(result)->Set(2, port->Word());
1283    DEREFHANDLE(result)->Set(3, protocol->Word());
1284    return result;
1285}
1286
1287static Handle mkAftab(TaskData *taskData, void *arg, char *p)
1288{
1289    struct af_tab_struct *af = (struct af_tab_struct *)p;
1290    Handle result, name, num;
1291    /* Construct a pair of the string and the number. */
1292    name = SAVE(C_string_to_Poly(taskData, af->af_name));
1293    num = Make_arbitrary_precision(taskData, af->af_num);
1294    result = ALLOC(2);
1295    DEREFHANDLE(result)->Set(0, name->Word());
1296    DEREFHANDLE(result)->Set(1, num->Word());
1297    return result;
1298}
1299
1300static Handle mkSktab(TaskData *taskData, void *arg, char *p)
1301{
1302    struct sk_tab_struct *sk = (struct sk_tab_struct *)p;
1303    Handle result, name, num;
1304    /* Construct a pair of the string and the number. */
1305    name = SAVE(C_string_to_Poly(taskData, sk->sk_name));
1306    num = Make_arbitrary_precision(taskData, sk->sk_num);
1307    result = ALLOC(2);
1308    DEREFHANDLE(result)->Set(0, name->Word());
1309    DEREFHANDLE(result)->Set(1, num->Word());
1310    return result;
1311}
1312
1313/* This sets an option and can also be used to set an integer. */
1314static Handle setSocketOption(TaskData *taskData, Handle args, int level, int opt)
1315{
1316    PIOSTRUCT strm = get_stream(DEREFHANDLE(args)->Get(0));
1317    int onOff = get_C_int(taskData, DEREFHANDLE(args)->Get(1));
1318    if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1319    if (setsockopt(strm->device.sock, level, opt,
1320        (char*)&onOff, sizeof(int)) != 0)
1321        raise_syscall(taskData, "setsockopt failed", GETERROR);
1322    return Make_arbitrary_precision(taskData, 0);
1323}
1324
1325/* Get a socket option as a boolean */
1326static Handle getSocketOption(TaskData *taskData, Handle args, int level, int opt)
1327{
1328    PIOSTRUCT strm = get_stream(args->Word());
1329    int onOff = 0;
1330    socklen_t size = sizeof(int);
1331    if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1332    if (getsockopt(strm->device.sock, level, opt,
1333        (char*)&onOff, &size) != 0)
1334        raise_syscall(taskData, "getsockopt failed", GETERROR);
1335    return Make_arbitrary_precision(taskData, onOff == 0 ? 0 : 1);
1336}
1337
1338/* Get a socket option as an integer */
1339static Handle getSocketInt(TaskData *taskData, Handle args, int level, int opt)
1340{
1341    PIOSTRUCT strm = get_stream(args->Word());
1342    int optVal = 0;
1343    socklen_t size = sizeof(int);
1344    if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1345    if (getsockopt(strm->device.sock, level, opt,
1346        (char*)&optVal, &size) != 0)
1347        raise_syscall(taskData, "getsockopt failed", GETERROR);
1348    return Make_arbitrary_precision(taskData, optVal);
1349}
1350
1351/* Helper function for selectCall.  Creates the result vector of active sockets. */
1352static Handle getSelectResult(TaskData *taskData, Handle args, int offset, fd_set *pFds)
1353{
1354    /* Construct the result vectors. */
1355    PolyObject *inVec = DEREFHANDLE(args)->Get(offset).AsObjPtr();
1356    POLYUNSIGNED nVec = inVec->Length();
1357    int nRes = 0;
1358    POLYUNSIGNED i;
1359    for (i = 0; i < nVec; i++) {
1360        PIOSTRUCT strm = get_stream(inVec->Get(i));
1361        if (FD_ISSET(strm->device.sock, pFds)) nRes++;
1362    }
1363    if (nRes == 0)
1364        return ALLOC(0); /* None - return empty vector. */
1365    else {
1366        Handle result = ALLOC(nRes);
1367        inVec = DEREFHANDLE(args)->Get(offset).AsObjPtr(); /* It could have moved as a result of a gc. */
1368        nRes = 0;
1369        for (i = 0; i < nVec; i++) {
1370            PIOSTRUCT strm = get_stream(inVec->Get(i));
1371            if (FD_ISSET(strm->device.sock, pFds))
1372                DEREFWORDHANDLE(result)->Set(nRes++, inVec->Get(i));
1373        }
1374        return result;
1375    }
1376}
1377
1378/* Wrapper for "select" call.  The arguments are arrays of socket ids.  These arrays are
1379   updated so that "active" sockets are left unchanged and inactive sockets are set to
1380   minus one.  */
1381static Handle selectCall(TaskData *taskData, Handle args, int blockType)
1382{
1383    Handle hSave = taskData->saveVec.mark();
1384    TryAgain:
1385    // We should check for interrupts even if we're not going to block.
1386    processes->TestAnyEvents(taskData);
1387    fd_set readers, writers, excepts;
1388    struct timeval timeout;
1389    int selectRes;
1390    POLYUNSIGNED i, nVec;
1391    Handle rdResult, wrResult, exResult, result;
1392    /* Set up the bitmaps for the select call from the arrays. */
1393    PolyObject *readVec = DEREFHANDLE(args)->Get(0).AsObjPtr();
1394    PolyObject *writeVec = DEREFHANDLE(args)->Get(1).AsObjPtr();
1395    PolyObject *excVec = DEREFHANDLE(args)->Get(2).AsObjPtr();
1396    FD_ZERO(&readers);
1397    FD_ZERO(&writers);
1398    FD_ZERO(&excepts);
1399    nVec = readVec->Length();
1400    for (i = 0; i < nVec; i++) {
1401        PIOSTRUCT strm = get_stream(readVec->Get(i));
1402        if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1403        FD_SET(strm->device.sock, &readers);
1404    }
1405    nVec = writeVec->Length();
1406    for (i = 0; i < nVec; i++) {
1407        PIOSTRUCT strm = get_stream(writeVec->Get(i));
1408        if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1409        FD_SET(strm->device.sock, &writers);
1410    }
1411    nVec = excVec->Length();
1412    for (i = 0; i < nVec; i++) {
1413        PIOSTRUCT strm = get_stream(excVec->Get(i));
1414        if (strm == NULL) raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
1415        FD_SET(strm->device.sock, &excepts);
1416    }
1417    /* Whatever the timeout specified we simply poll here. */
1418    memset(&timeout, 0, sizeof(timeout));
1419    selectRes = select(FD_SETSIZE, &readers, &writers, &excepts, &timeout);
1420    if (selectRes < 0) raise_syscall(taskData, "select failed", GETERROR);
1421
1422    if (selectRes == 0) { /* Timed out.  Have to look at the timeout value. */
1423        switch (blockType)
1424        {
1425        case 0: /* Check the timeout. */
1426            {
1427            /* The time argument is an absolute time. */
1428#if (defined(_WIN32) && ! defined(__CYGWIN__))
1429            FILETIME ftTime, ftNow;
1430            /* Get the file time. */
1431            getFileTimeFromArb(taskData, taskData->saveVec.push(DEREFHANDLE(args)->Get(3)), &ftTime);
1432            GetSystemTimeAsFileTime(&ftNow);
1433            /* If the timeout time is earlier than the current time
1434               we must return, otherwise we block. */
1435            if (CompareFileTime(&ftTime, &ftNow) <= 0)
1436                break; /* Return the empty set. */
1437            /* else drop through and block. */
1438#else /* Unix */
1439            struct timeval tv;
1440            /* We have a value in microseconds.  We need to split
1441               it into seconds and microseconds. */
1442            Handle hTime = SAVE(DEREFWORDHANDLE(args)->Get(3));
1443            Handle hMillion = Make_arbitrary_precision(taskData, 1000000);
1444            unsigned long secs =
1445                get_C_ulong(taskData, DEREFWORD(div_longc(taskData, hMillion, hTime)));
1446            unsigned long usecs =
1447                get_C_ulong(taskData, DEREFWORD(rem_longc(taskData, hMillion, hTime)));
1448            /* If the timeout time is earlier than the current time
1449               we must return, otherwise we block. */
1450            if (gettimeofday(&tv, NULL) != 0)
1451                raise_syscall(taskData, "gettimeofday failed", errno);
1452            if ((unsigned long)tv.tv_sec > secs ||
1453                ((unsigned long)tv.tv_sec == secs && (unsigned long)tv.tv_usec >= usecs))
1454                break;
1455            /* else block. */
1456#endif
1457        }
1458        case 1: /* Block until one of the descriptors is ready. */
1459            processes->ThreadPause(taskData);
1460            taskData->saveVec.reset(hSave);
1461            goto TryAgain;
1462        case 2: /* Just a simple poll - drop through. */
1463            break;
1464        }
1465    }
1466
1467    /* Construct the result vectors. */
1468    rdResult = getSelectResult(taskData, args, 0, &readers);
1469    wrResult = getSelectResult(taskData, args, 1, &writers);
1470    exResult = getSelectResult(taskData, args, 2, &excepts);
1471
1472    result = ALLOC(3);
1473    DEREFHANDLE(result)->Set(0, rdResult->Word());
1474    DEREFHANDLE(result)->Set(1, wrResult->Word());
1475    DEREFHANDLE(result)->Set(2, exResult->Word());
1476    return result;
1477}
1478
1479// General interface to networking.  Ideally the various cases will be made into
1480// separate functions.
1481POLYUNSIGNED PolyNetworkGeneral(PolyObject *threadId, PolyWord code, PolyWord arg)
1482{
1483    TaskData *taskData = TaskData::FindTaskForId(threadId);
1484    ASSERT(taskData != 0);
1485    taskData->PreRTSCall();
1486    Handle reset = taskData->saveVec.mark();
1487    Handle pushedCode = taskData->saveVec.push(code);
1488    Handle pushedArg = taskData->saveVec.push(arg);
1489    Handle result = 0;
1490
1491    try {
1492        result = Net_dispatch_c(taskData, pushedArg, pushedCode);
1493    }
1494    catch (KillException &) {
1495        processes->ThreadExit(taskData); // May test for kill
1496    }
1497    catch (...) { } // If an ML exception is raised
1498
1499    taskData->saveVec.reset(reset);
1500    taskData->PostRTSCall();
1501    if (result == 0) return TAGGED(0).AsUnsigned();
1502    else return result->Word().AsUnsigned();
1503}
1504
1505POLYUNSIGNED PolyNetworkGetServByName(PolyObject *threadId, PolyWord serviceName)
1506{
1507    TaskData *taskData = TaskData::FindTaskForId(threadId);
1508    ASSERT(taskData != 0);
1509    taskData->PreRTSCall();
1510    Handle reset = taskData->saveVec.mark();
1511
1512    /* Get service given service name only. */
1513    TempCString servName(Poly_string_to_C_alloc(serviceName));
1514    struct servent *serv = getservbyname (servName, NULL);
1515    // If this fails the ML function returns NONE
1516    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1517
1518    taskData->saveVec.reset(reset);
1519    taskData->PostRTSCall();
1520    if (result == 0) return TAGGED(0).AsUnsigned();
1521    else return result->Word().AsUnsigned();
1522}
1523
1524POLYUNSIGNED PolyNetworkGetServByNameAndProtocol(PolyObject *threadId, PolyWord serviceName, PolyWord protName)
1525{
1526    TaskData *taskData = TaskData::FindTaskForId(threadId);
1527    ASSERT(taskData != 0);
1528    taskData->PreRTSCall();
1529    Handle reset = taskData->saveVec.mark();
1530
1531    /* Get service given service name and protocol name. */
1532    TempCString servName(Poly_string_to_C_alloc(serviceName));
1533    TempCString protoName(Poly_string_to_C_alloc(protName));
1534    struct servent *serv = getservbyname (servName, protoName);
1535    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1536
1537    taskData->saveVec.reset(reset);
1538    taskData->PostRTSCall();
1539    if (result == 0) return TAGGED(0).AsUnsigned();
1540    else return result->Word().AsUnsigned();
1541}
1542
1543POLYUNSIGNED PolyNetworkGetServByPort(PolyObject *threadId, PolyWord portNo)
1544{
1545    TaskData *taskData = TaskData::FindTaskForId(threadId);
1546    ASSERT(taskData != 0);
1547    taskData->PreRTSCall();
1548    Handle reset = taskData->saveVec.mark();
1549
1550    /* Get service given port number only. */
1551    long port = htons(get_C_ushort(taskData, portNo));
1552    struct servent *serv = getservbyport(port, NULL);
1553    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1554
1555    taskData->saveVec.reset(reset);
1556    taskData->PostRTSCall();
1557    if (result == 0) return TAGGED(0).AsUnsigned();
1558    else return result->Word().AsUnsigned();
1559}
1560
1561POLYUNSIGNED PolyNetworkGetServByPortAndProtocol(PolyObject *threadId, PolyWord portNo, PolyWord protName)
1562{
1563    TaskData *taskData = TaskData::FindTaskForId(threadId);
1564    ASSERT(taskData != 0);
1565    taskData->PreRTSCall();
1566    Handle reset = taskData->saveVec.mark();
1567
1568    /* Get service given port number and protocol name. */
1569    long port = htons(get_C_ushort(taskData, portNo));
1570    TempCString protoName(Poly_string_to_C_alloc(protName));
1571    struct servent *serv = getservbyport (port, protoName);
1572    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1573
1574    taskData->saveVec.reset(reset);
1575    taskData->PostRTSCall();
1576    if (result == 0) return TAGGED(0).AsUnsigned();
1577    else return result->Word().AsUnsigned();
1578}
1579
1580POLYUNSIGNED PolyNetworkGetProtByName(PolyObject *threadId, PolyWord protocolName)
1581{
1582    TaskData *taskData = TaskData::FindTaskForId(threadId);
1583    ASSERT(taskData != 0);
1584    taskData->PreRTSCall();
1585    Handle reset = taskData->saveVec.mark();
1586
1587    /* Look up protocol entry. */
1588    TempCString protoName(Poly_string_to_C_alloc(protocolName));
1589    struct protoent *proto = getprotobyname(protoName);
1590    // If this fails the ML function returns NONE
1591    Handle result = proto == NULL ? 0 : makeProtoEntry(taskData, proto);
1592
1593    taskData->saveVec.reset(reset);
1594    taskData->PostRTSCall();
1595    if (result == 0) return TAGGED(0).AsUnsigned();
1596    else return result->Word().AsUnsigned();
1597}
1598
1599POLYUNSIGNED PolyNetworkGetProtByNo(PolyObject *threadId, PolyWord protoNo)
1600{
1601    TaskData *taskData = TaskData::FindTaskForId(threadId);
1602    ASSERT(taskData != 0);
1603    taskData->PreRTSCall();
1604    Handle reset = taskData->saveVec.mark();
1605
1606    /* Look up protocol entry. */
1607    int pNum = get_C_int(taskData, protoNo);
1608    struct protoent *proto = getprotobynumber(pNum);
1609    Handle result = proto == NULL ? 0 : makeProtoEntry(taskData, proto);
1610
1611    taskData->saveVec.reset(reset);
1612    taskData->PostRTSCall();
1613    if (result == 0) return TAGGED(0).AsUnsigned();
1614    else return result->Word().AsUnsigned();
1615}
1616
1617POLYUNSIGNED PolyNetworkGetHostName(PolyObject *threadId)
1618{
1619    TaskData *taskData = TaskData::FindTaskForId(threadId);
1620    ASSERT(taskData != 0);
1621    taskData->PreRTSCall();
1622    Handle reset = taskData->saveVec.mark();
1623    Handle result = 0;
1624
1625    try { /* Get the current host name. */
1626        size_t size = 4096;
1627        TempCString hostName((char *)malloc(size));
1628        if (hostName == NULL) raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1629        int err;
1630        while ((err = gethostname(hostName, size)) != 0 && GETERROR == ENAMETOOLONG)
1631        {
1632            if (size > std::numeric_limits<size_t>::max() / 2) raise_fail(taskData, "gethostname needs too large a buffer");
1633            size *= 2;
1634            char *new_buf = (char *)realloc(hostName, size);
1635            if (new_buf == NULL) raise_syscall(taskData, "Insufficient memory", NOMEMORY);
1636            hostName = new_buf;
1637        }
1638
1639        if (err != 0)
1640            raise_syscall(taskData, "gethostname failed", GETERROR);
1641
1642        result = SAVE(C_string_to_Poly(taskData, hostName));
1643    }
1644    catch (...) { } // If an ML exception is raised
1645
1646    taskData->saveVec.reset(reset);
1647    taskData->PostRTSCall();
1648    if (result == 0) return TAGGED(0).AsUnsigned();
1649    else return result->Word().AsUnsigned();
1650}
1651
1652POLYUNSIGNED PolyNetworkGetHostByName(PolyObject *threadId, PolyWord hName)
1653{
1654    TaskData *taskData = TaskData::FindTaskForId(threadId);
1655    ASSERT(taskData != 0);
1656    taskData->PreRTSCall();
1657    Handle reset = taskData->saveVec.mark();
1658
1659    /* Look up a host name. */
1660    TempCString hostName(Poly_string_to_C_alloc(hName));
1661    struct hostent *host = gethostbyname(hostName);
1662    // If this fails the ML function returns NONE
1663    Handle result = host == NULL ? 0 : makeHostEntry(taskData, host);
1664
1665    taskData->saveVec.reset(reset);
1666    taskData->PostRTSCall();
1667    if (result == 0) return TAGGED(0).AsUnsigned();
1668    else return result->Word().AsUnsigned();
1669}
1670
1671POLYUNSIGNED PolyNetworkGetHostByAddr(PolyObject *threadId, PolyWord hostAddr)
1672{
1673    TaskData *taskData = TaskData::FindTaskForId(threadId);
1674    ASSERT(taskData != 0);
1675    taskData->PreRTSCall();
1676    Handle reset = taskData->saveVec.mark();
1677
1678    /* Look up entry by address. */
1679    unsigned long addr = htonl(get_C_unsigned(taskData, hostAddr));
1680    /* Look up a host name given an address. */
1681    struct hostent *host = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET);
1682    Handle result = host == NULL ? 0 : makeHostEntry(taskData, host);
1683
1684    taskData->saveVec.reset(reset);
1685    taskData->PostRTSCall();
1686    if (result == 0) return TAGGED(0).AsUnsigned();
1687    else return result->Word().AsUnsigned();
1688}
1689
1690struct _entrypts networkingEPT[] =
1691{
1692    { "PolyNetworkGeneral",                     (polyRTSFunction)&PolyNetworkGeneral},
1693    { "PolyNetworkGetServByName",               (polyRTSFunction)&PolyNetworkGetServByName},
1694    { "PolyNetworkGetServByNameAndProtocol",    (polyRTSFunction)&PolyNetworkGetServByNameAndProtocol},
1695    { "PolyNetworkGetServByPort",               (polyRTSFunction)&PolyNetworkGetServByPort},
1696    { "PolyNetworkGetServByPortAndProtocol",    (polyRTSFunction)&PolyNetworkGetServByPortAndProtocol},
1697    { "PolyNetworkGetProtByName",               (polyRTSFunction)&PolyNetworkGetProtByName},
1698    { "PolyNetworkGetProtByNo",                 (polyRTSFunction)&PolyNetworkGetProtByNo},
1699    { "PolyNetworkGetHostName",                 (polyRTSFunction)&PolyNetworkGetHostName},
1700    { "PolyNetworkGetHostByName",               (polyRTSFunction)&PolyNetworkGetHostByName},
1701    { "PolyNetworkGetHostByAddr",               (polyRTSFunction)&PolyNetworkGetHostByAddr},
1702
1703    { NULL, NULL} // End of list.
1704};
1705
1706class Networking: public RtsModule
1707{
1708public:
1709    virtual void Init(void);
1710    virtual void Stop(void);
1711};
1712
1713// Declare this.  It will be automatically added to the table.
1714static Networking networkingModule;
1715
1716void Networking::Init(void)
1717{
1718#if (defined(_WIN32) && ! defined(__CYGWIN__))
1719#define WINSOCK_MAJOR_VERSION   2
1720#define WINSOCK_MINOR_VERSION   2
1721    WSADATA wsaData;
1722    WORD wVersion = MAKEWORD(WINSOCK_MINOR_VERSION, WINSOCK_MAJOR_VERSION);
1723    /* Initialise the system and check that the version it supplied
1724       is the one we requested. */
1725    if(WSAStartup(wVersion, &wsaData) == 0)
1726    {
1727        if (wsaData.wVersion == wVersion)
1728            winsock_init = 1;
1729        else WSACleanup();
1730    }
1731#endif
1732}
1733
1734void Networking::Stop(void)
1735{
1736#if (defined(_WIN32) && ! defined(__CYGWIN__))
1737    if (winsock_init) WSACleanup();
1738    winsock_init = 0;
1739#endif
1740}
1741