1/*
2    Title:      Network functions.
3
4    Copyright (c) 2000-7, 2016, 2018, 2019 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#ifdef HAVE_ARPA_INET_H
104#include <arpa/inet.h>
105#endif
106
107#ifdef HAVE_LIMITS_H
108#include <limits.h>
109#endif
110
111#ifndef HAVE_SOCKLEN_T
112typedef int socklen_t;
113#endif
114
115
116#if (defined(_WIN32))
117#include <winsock2.h>
118#include <ws2tcpip.h> // For getaddrinfo
119#else
120typedef int SOCKET;
121#endif
122
123#ifdef HAVE_WINDOWS_H
124#include <windows.h>
125#endif
126
127#include <new>
128
129#include "globals.h"
130#include "gc.h"
131#include "arb.h"
132#include "run_time.h"
133#include "mpoly.h"
134#include "processes.h"
135#include "network.h"
136#include "io_internal.h"
137#include "sys.h"
138#include "polystring.h"
139#include "save_vec.h"
140#include "rts_module.h"
141#include "machine_dep.h"
142#include "errors.h"
143#include "rtsentry.h"
144#include "timing.h"
145
146extern "C" {
147    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAddrList(FirstArgument threadId);
148    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetSockTypeList(FirstArgument threadId);
149    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCreateSocket(FirstArgument threadId, PolyWord af, PolyWord st, PolyWord prot);
150    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSetOption(FirstArgument threadId, PolyWord code, PolyWord sock, PolyWord opt);
151    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetOption(FirstArgument threadId, PolyWord code, PolyWord arg);
152    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSetLinger(FirstArgument threadId, PolyWord sock, PolyWord linger);
153    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetLinger(FirstArgument threadId, PolyWord arg);
154    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetPeerName(FirstArgument threadId, PolyWord arg);
155    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetSockName(FirstArgument threadId, PolyWord arg);
156    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkBytesAvailable(FirstArgument threadId, PolyWord arg);
157    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAtMark(FirstArgument threadId, PolyWord arg);
158    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkBind(FirstArgument threadId, PolyWord sock, PolyWord addr);
159    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkListen(FirstArgument threadId, PolyWord sock, PolyWord back);
160    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkShutdown(FirstArgument threadId, PolyWord skt, PolyWord smode);
161    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCreateSocketPair(FirstArgument threadId, PolyWord af, PolyWord st, PolyWord prot);
162    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkUnixPathToSockAddr(FirstArgument threadId, PolyWord arg);
163    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkUnixSockAddrToPath(FirstArgument threadId, PolyWord arg);
164    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByName(FirstArgument threadId, PolyWord servName);
165    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByNameAndProtocol(FirstArgument threadId, PolyWord servName, PolyWord protName);
166    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByPort(FirstArgument threadId, PolyWord portNo);
167    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetServByPortAndProtocol(FirstArgument threadId, PolyWord portNo, PolyWord protName);
168    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetProtByName(FirstArgument threadId, PolyWord protocolName);
169    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetProtByNo(FirstArgument threadId, PolyWord protoNo);
170    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetHostName(FirstArgument threadId);
171    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAddrInfo(FirstArgument threadId, PolyWord hostName, PolyWord addrFamily);
172    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetNameInfo(FirstArgument threadId, PolyWord sockAddr);
173    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCloseSocket(FirstArgument threadId, PolyWord arg);
174    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSelect(FirstArgument threadId, PolyWord fdVecTriple, PolyWord maxMillisecs);
175    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetSocketError(FirstArgument threadId, PolyWord skt);
176    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkConnect(FirstArgument threadId, PolyWord skt, PolyWord addr);
177    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkAccept(FirstArgument threadId, PolyWord skt);
178    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSend(FirstArgument threadId, PolyWord args);
179    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSendTo(FirstArgument threadId, PolyWord args);
180    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReceive(FirstArgument threadId, PolyWord args);
181    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReceiveFrom(FirstArgument threadId, PolyWord args);
182    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetFamilyFromAddress(PolyWord sockAddress);
183    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAddressAndPortFromIP4(FirstArgument threadId, PolyWord sockAddress);
184    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCreateIP4Address(FirstArgument threadId, PolyWord ip4Address, PolyWord portNumber);
185    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReturnIP4AddressAny(FirstArgument threadId);
186    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAddressAndPortFromIP6(FirstArgument threadId, PolyWord sockAddress);
187    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCreateIP6Address(FirstArgument threadId, PolyWord ip6Address, PolyWord portNumber);
188    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReturnIP6AddressAny(FirstArgument threadId);
189    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkIP6AddressToString(FirstArgument threadId, PolyWord ip6Address);
190    POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkStringToIP6Address(FirstArgument threadId, PolyWord stringRep);
191}
192
193#define SAVE(x) taskData->saveVec.push(x)
194#define ALLOC(n) alloc_and_save(taskData, n)
195#define SIZEOF(x) (sizeof(x)/sizeof(PolyWord))
196
197#if (defined(_WIN32))
198static int winsock_init = 0; /* Check that it has been initialised. */
199
200#else
201#define INVALID_SOCKET (-1)
202#define SOCKET_ERROR    (-1)
203#endif
204
205#ifndef HAVE_SOCKLEN_T
206typedef int socklen_t; // This must be int for Windows at least
207#endif
208
209#ifndef SHUT_RD
210#define SHUT_RD     0
211#endif
212
213#ifndef SHUT_WR
214#define SHUT_WR     1
215#endif
216
217#ifndef SHUT_RDWR
218#define SHUT_RDWR   2
219#endif
220
221/* Address families.  Although this table is in ascending
222   numerical order of address family nothing depends on that.
223   The only requirement is that "INET" => AF_INET must always
224   be present and "UNIX" => AF_UNIX must be present on Unix.
225   Other entries are entirely optional and are for amusement
226   only. */
227struct af_tab_struct {
228    const char *af_name;
229    int af_num;
230} af_table[] =
231{
232#ifdef AF_UNIX
233    { "UNIX",       AF_UNIX }, /* This is nearly always there. */
234#endif
235#ifdef AF_LOCAL
236    { "LOCAL",      AF_LOCAL },
237#endif
238    { "INET",       AF_INET }, /* This one should always be there. */
239#ifdef AF_IMPLINK
240    { "IMPLINK",    AF_IMPLINK },
241#endif
242#ifdef AF_PUP
243    { "PUP",        AF_PUP },
244#endif
245#ifdef AF_CHAOS
246    { "CHAOS",      AF_CHAOS },
247#endif
248#ifdef AF_IPX
249    { "IPX",        AF_IPX },
250#endif
251#ifdef AF_NS
252    { "NS",         AF_NS },
253#endif
254#ifdef AF_ISO
255    { "ISO",        AF_ISO },
256#endif
257#ifdef AF_OSI
258    { "OSI",        AF_OSI },
259#endif
260#ifdef AF_ECMA
261    { "ECMA",       AF_ECMA },
262#endif
263#ifdef AF_DATAKIT
264    { "DATAKIT",    AF_DATAKIT },
265#endif
266#ifdef AF_CCITT
267    { "CCITT",      AF_CCITT },
268#endif
269#ifdef AF_SNA
270    { "SNA",        AF_SNA },
271#endif
272#ifdef AF_DECnet
273    { "DECnet",     AF_DECnet },
274#endif
275#ifdef AF_DLI
276    { "DLI",        AF_DLI },
277#endif
278#ifdef AF_LAT
279    { "LAT",        AF_LAT },
280#endif
281#ifdef AF_HYLINK
282    { "HYLINK",     AF_HYLINK },
283#endif
284#ifdef AF_APPLETALK
285    { "APPLETALK",  AF_APPLETALK },
286#endif
287#ifdef AF_NETBIOS
288    { "NETBIOS",    AF_NETBIOS },
289#endif
290#ifdef AF_ROUTE
291    { "ROUTE",      AF_ROUTE },
292#endif
293#ifdef AF_VOICEVIEW
294    { "VOICEVIEW",  AF_VOICEVIEW },
295#endif
296#ifdef AF_FIREFOX
297    { "FIREFOX",    AF_FIREFOX },
298#endif
299#ifdef AF_BAN
300    { "BAN",        AF_BAN },
301#endif
302#ifdef AF_LINK
303    { "LINK",       AF_LINK },
304#endif
305#ifdef AF_COIP
306    { "COIP",       AF_COIP },
307#endif
308#ifdef AF_CNT
309    { "CNT",        AF_CNT },
310#endif
311#ifdef AF_SIP
312    { "SIP",        AF_SIP },
313#endif
314#ifdef AF_ISDN
315    { "ISDN",       AF_ISDN },
316#endif
317#ifdef AF_E164
318    { "E164",       AF_E164 },
319#endif
320#ifdef AF_INET6
321    { "INET6",      AF_INET6 },  // This one should always be there.
322#endif
323#ifdef AF_NATM
324    { "NATM",       AF_NATM },
325#endif
326#ifdef AF_ATM
327    { "ATM",        AF_ATM },
328#endif
329#ifdef AF_NETGRAPH
330    { "NETGRAPH",   AF_NETGRAPH },
331#endif
332#ifdef AF_CLUSTER
333    { "CLUSTER",    AF_CLUSTER },
334#endif
335#ifdef AF_12844
336    { "12844",      AF_12844 },
337#endif
338#ifdef AF_IRDA
339    { "IRDA",       AF_IRDA },
340#endif
341#ifdef AF_NETDES
342    { "NETDES",     AF_NETDES },
343#endif
344#ifdef AF_TCNPROCESS
345    { "TCNPROCESS", AF_TCNPROCESS },
346#endif
347#ifdef AF_TCNMESSAGE
348    { "TCNMESSAGE", AF_TCNMESSAGE },
349#endif
350#ifdef AF_ICLFXBM
351    { "ICLFXBM",    AF_ICLFXBM },
352#endif
353#ifdef AF_BTH
354    { "BTH",        AF_BTH },
355#endif
356#ifdef AF_HYPERV
357    { "HYPERV",     AF_HYPERV },
358#endif
359#ifdef AF_FILE
360    { "FILE",       AF_FILE },
361#endif
362#ifdef AF_AX25
363    { "AX25",       AF_AX25 },
364#endif
365#ifdef AF_NETROM
366    { "NETROM",     AF_NETROM },
367#endif
368#ifdef AF_BRIDGE
369    { "BRIDGE",     AF_BRIDGE },
370#endif
371#ifdef AF_ATMPVC
372    { "ATMPVC",     AF_ATMPVC },
373#endif
374#ifdef AF_X25
375    { "X25",        AF_X25 },
376#endif
377#ifdef AF_ROSE
378    { "ROSE",       AF_ROSE },
379#endif
380#ifdef AF_NETBEUI
381    { "NETBEUI",    AF_NETBEUI },
382#endif
383#ifdef AF_SECURITY
384    { "SECURITY",   AF_SECURITY },
385#endif
386#ifdef AF_KEY
387    { "KEY",        AF_KEY },
388#endif
389#ifdef AF_NETLINK
390    { "NETLINK",    AF_NETLINK },
391#endif
392#ifdef AF_PACKET
393    { "PACKET",     AF_PACKET },
394#endif
395#ifdef AF_ASH
396    { "ASH",        AF_ASH },
397#endif
398#ifdef AF_ECONET
399    { "ECONET",     AF_ECONET },
400#endif
401#ifdef AF_ATMSVC
402    { "ATMSVC",     AF_ATMSVC },
403#endif
404#ifdef AF_RDS
405    { "RDS",        AF_RDS },
406#endif
407#ifdef AF_PPPOX
408    { "PPPOX",      AF_PPPOX },
409#endif
410#ifdef AF_WANPIPE
411    { "WANPIPE",    AF_WANPIPE },
412#endif
413#ifdef AF_LLC
414    { "LLC",        AF_LLC },
415#endif
416#ifdef AF_IB
417    { "IB",         AF_IB },
418#endif
419#ifdef AF_MPLS
420    { "MPLS",       AF_MPLS },
421#endif
422#ifdef AF_CAN
423    { "CAN",        AF_CAN },
424#endif
425#ifdef AF_TIPC
426    { "TIPC",       AF_TIPC },
427#endif
428#ifdef AF_BLUETOOTH
429    { "BLUETOOTH",  AF_BLUETOOTH },
430#endif
431#ifdef AF_IUCV
432    { "IUCV",       AF_IUCV },
433#endif
434#ifdef AF_RXRPC
435    { "RXRPC",      AF_RXRPC },
436#endif
437#ifdef AF_PHONET
438    { "PHONET",     AF_PHONET },
439#endif
440#ifdef AF_IEEE802154
441    { "IEEE802154", AF_IEEE802154 },
442#endif
443#ifdef AF_CAIF
444    { "CAIF",       AF_CAIF },
445#endif
446#ifdef AF_ALG
447    { "ALG",        AF_ALG },
448#endif
449#ifdef AF_NFC
450    { "NFC",        AF_NFC },
451#endif
452#ifdef AF_VSOCK
453    { "VSOCK",      AF_VSOCK },
454#endif
455#ifdef AF_KCM
456    { "KCM",        AF_KCM },
457#endif
458};
459
460/* Socket types.  Only STREAM and DGRAM are required.  */
461struct sk_tab_struct {
462    const char *sk_name;
463    int sk_num;
464} sk_table[] =
465{
466    { "STREAM",     SOCK_STREAM },
467    { "DGRAM",      SOCK_DGRAM },
468    { "RAW",        SOCK_RAW },
469    { "RDM",        SOCK_RDM },
470    { "SEQPACKET",  SOCK_SEQPACKET },
471#ifdef SOCK_DCCP
472    { "DCCP",       SOCK_DCCP },
473#endif
474};
475
476static Handle makeProtoEntry(TaskData *taskData, struct protoent *proto);
477static Handle mkAftab(TaskData *taskData, void*, char *p);
478static Handle mkSktab(TaskData *taskData, void*, char *p);
479static Handle setSocketOption(TaskData *taskData, Handle sockHandle, Handle optHandle, int level, int opt);
480static Handle getSocketOption(TaskData *taskData, Handle args, int level, int opt);
481
482#if (defined(_WIN32))
483#define GETERROR     (WSAGetLastError())
484#define TOOMANYFILES    WSAEMFILE
485#define NOMEMORY        WSA_NOT_ENOUGH_MEMORY
486#define STREAMCLOSED    WSA_INVALID_HANDLE
487#define WOULDBLOCK      WSAEWOULDBLOCK
488#define INPROGRESS      WSAEINPROGRESS
489#define CALLINTERRUPTED WSAEINTR
490#undef EBADF
491#undef EMFILE
492#undef EAGAIN
493#undef EINTR
494#undef EWOULDBLOCK
495#undef ENOMEM
496#else
497#define GETERROR    (errno)
498#define TOOMANYFILES EMFILE
499#define NOMEMORY ENOMEM
500#define STREAMCLOSED EBADF
501#define ERRORNUMBER errno
502#define FILEDOESNOTEXIST ENOENT
503#define WOULDBLOCK EWOULDBLOCK
504#define INPROGRESS EINPROGRESS
505#define CALLINTERRUPTED EINTR
506#endif
507
508
509// Wait until "select" returns.  In Windows this is used only for networking.
510class WaitSelect: public Waiter
511{
512public:
513    WaitSelect(unsigned maxMillisecs=(unsigned)-1);
514    virtual void Wait(unsigned maxMillisecs);
515    void SetRead(SOCKET fd) {  FD_SET(fd, &readSet); }
516    void SetWrite(SOCKET fd) {  FD_SET(fd, &writeSet); }
517    void SetExcept(SOCKET fd)  {  FD_SET(fd, &exceptSet); }
518    bool IsSetRead(SOCKET fd) { return FD_ISSET(fd, &readSet) != 0; }
519    bool IsSetWrite(SOCKET fd) { return FD_ISSET(fd, &writeSet) != 0; }
520    bool IsSetExcept(SOCKET fd) { return FD_ISSET(fd, &exceptSet) != 0; }
521    // Save the result of the select call and any associated error
522    int SelectResult(void) { return selectResult; }
523    int SelectError(void) { return errorResult; }
524private:
525    fd_set readSet, writeSet, exceptSet;
526    int selectResult;
527    int errorResult;
528    unsigned maxTime;
529};
530
531WaitSelect::WaitSelect(unsigned maxMillisecs)
532{
533    FD_ZERO(&readSet);
534    FD_ZERO(&writeSet);
535    FD_ZERO(&exceptSet);
536    selectResult = 0;
537    errorResult = 0;
538    maxTime = maxMillisecs;
539}
540
541void WaitSelect::Wait(unsigned maxMillisecs)
542{
543    if (maxTime < maxMillisecs) maxMillisecs = maxTime;
544    struct timeval toWait = { 0, 0 };
545    toWait.tv_sec = maxMillisecs / 1000;
546    toWait.tv_usec = (maxMillisecs % 1000) * 1000;
547    selectResult = select(FD_SETSIZE, &readSet, &writeSet, &exceptSet, &toWait);
548    if (selectResult < 0) errorResult = GETERROR;
549}
550
551#if (defined(_WIN32))
552class WinSocket : public WinStreamBase
553{
554public:
555    WinSocket(SOCKET skt) : socket(skt) {}
556
557    virtual SOCKET getSocket() {
558        return socket;
559    }
560
561    virtual int pollTest() {
562        // We can poll for any of these.
563        return POLL_BIT_IN | POLL_BIT_OUT | POLL_BIT_PRI;
564    }
565
566    virtual int poll(TaskData *taskData, int test);
567
568public:
569    SOCKET socket;
570};
571
572// Poll without blocking.
573int WinSocket::poll(TaskData *taskData, int bits)
574{
575    int result = 0;
576    if (bits & POLL_BIT_PRI)
577    {
578        u_long atMark = 0;
579        if (ioctlsocket(socket, SIOCATMARK, &atMark) != 0)
580            raise_syscall(taskData, "ioctlsocket failed", GETERROR);
581        if (atMark) { result |= POLL_BIT_PRI; }
582    }
583    if (bits & (POLL_BIT_IN | POLL_BIT_OUT))
584    {
585        FD_SET readFds, writeFds;
586        TIMEVAL poll = { 0, 0 };
587        FD_ZERO(&readFds); FD_ZERO(&writeFds);
588        if (bits & POLL_BIT_IN) FD_SET(socket, &readFds);
589        if (bits & POLL_BIT_OUT) FD_SET(socket, &writeFds);
590        int selRes = select(FD_SETSIZE, &readFds, &writeFds, NULL, &poll);
591        if (selRes < 0)
592            raise_syscall(taskData, "select failed", GETERROR);
593        else if (selRes > 0)
594        {
595            // N.B. select only tells us about out-of-band data if SO_OOBINLINE is FALSE. */
596            if (FD_ISSET(socket, &readFds)) result |= POLL_BIT_IN;
597            if (FD_ISSET(socket, &writeFds)) result |= POLL_BIT_OUT;
598        }
599    }
600    return result;
601}
602
603static SOCKET getStreamSocket(TaskData *taskData, PolyWord strm)
604{
605    WinSocket *winskt = *(WinSocket**)(strm.AsObjPtr());
606    if (winskt == 0)
607        raise_syscall(taskData, "Stream is closed", STREAMCLOSED);
608    return winskt->getSocket();
609}
610
611static Handle wrapStreamSocket(TaskData *taskData, SOCKET skt)
612{
613    try {
614        WinSocket *winskt = new WinSocket(skt);
615        return MakeVolatileWord(taskData, winskt);
616    }
617    catch (std::bad_alloc&) {
618        raise_syscall(taskData, "Insufficient memory", NOMEMORY);
619    }
620}
621
622#else
623
624static SOCKET getStreamSocket(TaskData *taskData, PolyWord strm)
625{
626    return getStreamFileDescriptor(taskData, strm);
627}
628
629static Handle wrapStreamSocket(TaskData *taskData, SOCKET skt)
630{
631    return wrapFileDescriptor(taskData, skt);
632}
633#endif
634
635static Handle makeProtoEntry(TaskData *taskData, struct protoent *proto)
636{
637    int i;
638    char **p;
639    Handle aliases, name, protocol, result;
640
641    /* Canonical name. */
642    name = SAVE(C_string_to_Poly(taskData, proto->p_name));
643
644    /* Aliases. */
645    for (i=0, p = proto->p_aliases; *p != NULL; p++, i++);
646    aliases = convert_string_list(taskData, i, proto->p_aliases);
647
648    /* Protocol number. */
649    protocol = Make_fixed_precision(taskData, proto->p_proto);
650
651    /* Make the result structure. */
652    result = ALLOC(3);
653    DEREFHANDLE(result)->Set(0, name->Word());
654    DEREFHANDLE(result)->Set(1, aliases->Word());
655    DEREFHANDLE(result)->Set(2, protocol->Word());
656    return result;
657}
658
659static Handle makeServEntry(TaskData *taskData, struct servent *serv)
660{
661    int i;
662    char **p;
663    Handle aliases, name, protocol, result, port;
664
665    /* Canonical name. */
666    name = SAVE(C_string_to_Poly(taskData, serv->s_name));
667
668    /* Aliases. */
669    for (i=0, p = serv->s_aliases; *p != NULL; p++, i++);
670    aliases = convert_string_list(taskData, i, serv->s_aliases);
671
672    /* Port number. */
673    port = Make_fixed_precision(taskData, ntohs(serv->s_port));
674
675    /* Protocol name. */
676    protocol = SAVE(C_string_to_Poly(taskData, serv->s_proto));
677
678    /* Make the result structure. */
679    result = ALLOC(4);
680    DEREFHANDLE(result)->Set(0, name->Word());
681    DEREFHANDLE(result)->Set(1, aliases->Word());
682    DEREFHANDLE(result)->Set(2, port->Word());
683    DEREFHANDLE(result)->Set(3, protocol->Word());
684    return result;
685}
686
687static Handle mkAftab(TaskData *taskData, void *arg, char *p)
688{
689    struct af_tab_struct *af = (struct af_tab_struct *)p;
690    Handle result, name, num;
691    /* Construct a pair of the string and the number. */
692    name = SAVE(C_string_to_Poly(taskData, af->af_name));
693    num = Make_fixed_precision(taskData, af->af_num);
694    result = ALLOC(2);
695    DEREFHANDLE(result)->Set(0, name->Word());
696    DEREFHANDLE(result)->Set(1, num->Word());
697    return result;
698}
699
700static Handle mkSktab(TaskData *taskData, void *arg, char *p)
701{
702    struct sk_tab_struct *sk = (struct sk_tab_struct *)p;
703    Handle result, name, num;
704    /* Construct a pair of the string and the number. */
705    name = SAVE(C_string_to_Poly(taskData, sk->sk_name));
706    num = Make_fixed_precision(taskData, sk->sk_num);
707    result = ALLOC(2);
708    DEREFHANDLE(result)->Set(0, name->Word());
709    DEREFHANDLE(result)->Set(1, num->Word());
710    return result;
711}
712
713/* This sets an option and can also be used to set an integer. */
714static Handle setSocketOption(TaskData *taskData, Handle sockHandle, Handle optHandle, int level, int opt)
715{
716    SOCKET sock = getStreamSocket(taskData, sockHandle->Word());
717    int onOff = get_C_int(taskData, optHandle->Word());
718    if (setsockopt(sock, level, opt,
719        (char*)&onOff, sizeof(int)) != 0)
720        raise_syscall(taskData, "setsockopt failed", GETERROR);
721    return Make_fixed_precision(taskData, 0);
722}
723
724// Get a socket option as an integer.
725static Handle getSocketOption(TaskData *taskData, Handle args, int level, int opt)
726{
727    SOCKET sock = getStreamSocket(taskData, args->Word());
728    int optVal = 0;
729    socklen_t size = sizeof(int);
730    if (getsockopt(sock, level, opt, (char*)&optVal, &size) != 0)
731        raise_syscall(taskData, "getsockopt failed", GETERROR);
732    return Make_fixed_precision(taskData, optVal);
733}
734
735// Get and clear the error state for the socket.  Returns a SysWord.word value.
736POLYUNSIGNED PolyNetworkGetSocketError(FirstArgument threadId, PolyWord skt)
737{
738    TaskData *taskData = TaskData::FindTaskForId(threadId);
739    ASSERT(taskData != 0);
740    taskData->PreRTSCall();
741    Handle reset = taskData->saveVec.mark();
742    Handle result = 0;
743
744    try {
745        SOCKET sock = getStreamSocket(taskData, skt);
746        int intVal = 0;
747        socklen_t size = sizeof(int);
748        if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&intVal, &size) != 0)
749            raise_syscall(taskData, "getsockopt failed", GETERROR);
750        result = Make_sysword(taskData, intVal);
751    }
752    catch (...) {} // If an ML exception is raised
753
754    taskData->saveVec.reset(reset);
755    taskData->PostRTSCall();
756    if (result == 0) return TAGGED(0).AsUnsigned();
757    else return result->Word().AsUnsigned();
758}
759
760// Helper function for selectCall.  Creates the result vector of active sockets.
761static bool testBit(int offset, SOCKET fd, WaitSelect *pSelect)
762{
763    switch (offset)
764    {
765    case 0: return pSelect->IsSetRead(fd);
766    case 1: return pSelect->IsSetWrite(fd);
767    case 2: return pSelect->IsSetExcept(fd);
768    default: return false;
769    }
770}
771
772static Handle getSelectResult(TaskData *taskData, Handle args, int offset, WaitSelect *pSelect)
773{
774    /* Construct the result vectors. */
775    PolyObject *inVec = DEREFHANDLE(args)->Get(offset).AsObjPtr();
776    POLYUNSIGNED nVec = inVec->Length();
777    int nRes = 0;
778    POLYUNSIGNED i;
779    for (i = 0; i < nVec; i++) {
780        SOCKET sock = getStreamSocket(taskData, inVec->Get(i));
781        if (testBit(offset, sock, pSelect)) nRes++;
782    }
783    if (nRes == 0)
784        return ALLOC(0); /* None - return empty vector. */
785    else {
786        Handle result = ALLOC(nRes);
787        inVec = DEREFHANDLE(args)->Get(offset).AsObjPtr(); /* It could have moved as a result of a gc. */
788        nRes = 0;
789        for (i = 0; i < nVec; i++) {
790            SOCKET sock = getStreamSocket(taskData, inVec->Get(i));
791            if (testBit(offset, sock, pSelect))
792                DEREFWORDHANDLE(result)->Set(nRes++, inVec->Get(i));
793        }
794        return result;
795    }
796}
797
798/* Wrapper for "select" call.  The arguments are arrays of socket ids.  These arrays are
799   updated so that "active" sockets are left unchanged and inactive sockets are set to
800   minus one.  */
801POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSelect(FirstArgument threadId, PolyWord fdVecTriple, PolyWord maxMillisecs)
802{
803    TaskData *taskData = TaskData::FindTaskForId(threadId);
804    ASSERT(taskData != 0);
805    taskData->PreRTSCall();
806    Handle reset = taskData->saveVec.mark();
807    Handle result = 0;
808    POLYUNSIGNED maxMilliseconds = maxMillisecs.UnTaggedUnsigned();
809    Handle fdVecTripleHandle = taskData->saveVec.push(fdVecTriple);
810    /* Set up the bitmaps for the select call from the arrays. */
811
812    try {
813        WaitSelect waitSelect((unsigned int)maxMilliseconds);
814        PolyObject *readVec = fdVecTripleHandle->WordP()->Get(0).AsObjPtr();
815        PolyObject *writeVec = fdVecTripleHandle->WordP()->Get(1).AsObjPtr();
816        PolyObject *excVec = fdVecTripleHandle->WordP()->Get(2).AsObjPtr();
817        for (POLYUNSIGNED i = 0; i < readVec->Length(); i++)
818            waitSelect.SetRead(getStreamSocket(taskData, readVec->Get(i)));
819        for (POLYUNSIGNED i = 0; i < writeVec->Length(); i++)
820            waitSelect.SetWrite(getStreamSocket(taskData, writeVec->Get(i)));
821        for (POLYUNSIGNED i = 0; i < excVec->Length(); i++)
822            waitSelect.SetExcept(getStreamSocket(taskData, excVec->Get(i)));
823
824        // Do the select.  This may return immediately if the maximum time-out is short.
825        processes->ThreadPauseForIO(taskData, &waitSelect);
826        if (waitSelect.SelectResult() < 0)
827            raise_syscall(taskData, "select failed", waitSelect.SelectError());
828
829        // Construct the result vectors.
830        Handle rdResult = getSelectResult(taskData, fdVecTripleHandle, 0, &waitSelect);
831        Handle wrResult = getSelectResult(taskData, fdVecTripleHandle, 1, &waitSelect);
832        Handle exResult = getSelectResult(taskData, fdVecTripleHandle, 2, &waitSelect);
833        result = ALLOC(3);
834        DEREFHANDLE(result)->Set(0, rdResult->Word());
835        DEREFHANDLE(result)->Set(1, wrResult->Word());
836        DEREFHANDLE(result)->Set(2, exResult->Word());
837    }
838    catch (...) {} // If an ML exception is raised
839
840    taskData->saveVec.reset(reset);
841    taskData->PostRTSCall();
842    if (result == 0) return TAGGED(0).AsUnsigned();
843    else return result->Word().AsUnsigned();
844
845}
846
847POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkConnect(FirstArgument threadId, PolyWord skt, PolyWord addr)
848{
849    TaskData *taskData = TaskData::FindTaskForId(threadId);
850    ASSERT(taskData != 0);
851    taskData->PreRTSCall();
852    Handle reset = taskData->saveVec.mark();
853    try {
854        SOCKET sock = getStreamSocket(taskData, skt);
855        PolyStringObject * psAddr = (PolyStringObject *)(addr.AsObjPtr());
856        struct sockaddr *psock = (struct sockaddr *)&psAddr->chars;
857        // Begin the connection.  The socket is always non-blocking so this will return immediately.
858        if (connect(sock, psock, (int)psAddr->length) != 0)
859            raise_syscall(taskData, "connect failed", GETERROR);
860    }
861    catch (...) {} // If an ML exception is raised
862
863    taskData->saveVec.reset(reset);
864    taskData->PostRTSCall();
865    return TAGGED(0).AsUnsigned(); // Always returns unit
866}
867
868POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkAccept(FirstArgument threadId, PolyWord skt)
869{
870    TaskData *taskData = TaskData::FindTaskForId(threadId);
871    ASSERT(taskData != 0);
872    taskData->PreRTSCall();
873    Handle reset = taskData->saveVec.mark();
874    Handle result = 0;
875
876    try {
877        SOCKET sock = getStreamSocket(taskData, skt);
878        struct sockaddr_storage resultAddr;
879        socklen_t addrLen = sizeof(resultAddr);
880        SOCKET resultSkt = accept(sock, (struct sockaddr*)&resultAddr, &addrLen);
881        if (resultSkt == INVALID_SOCKET)
882            raise_syscall(taskData, "accept failed", GETERROR);
883        if (addrLen > sizeof(resultAddr)) addrLen = sizeof(resultAddr);
884        Handle addrHandle = taskData->saveVec.push(C_string_to_Poly(taskData, (char*)&resultAddr, addrLen));
885        // Return a pair of the new socket and the address.
886        Handle resSkt = wrapStreamSocket(taskData, resultSkt);
887        result = alloc_and_save(taskData, 2);
888        result->WordP()->Set(0, resSkt->Word());
889        result->WordP()->Set(1, addrHandle->Word());
890    }
891    catch (...) {} // If an ML exception is raised
892
893    taskData->saveVec.reset(reset);
894    taskData->PostRTSCall();
895    if (result == 0) return TAGGED(0).AsUnsigned();
896    else return result->Word().AsUnsigned();
897}
898
899POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSend(FirstArgument threadId, PolyWord argsAsWord)
900{
901    TaskData *taskData = TaskData::FindTaskForId(threadId);
902    ASSERT(taskData != 0);
903    taskData->PreRTSCall();
904    Handle reset = taskData->saveVec.mark();
905    Handle args = taskData->saveVec.push(argsAsWord);
906#if(defined(_WIN32) && ! defined(_CYGWIN))
907    int sent = 0;
908#else
909    ssize_t sent = 0;
910#endif
911
912    try {
913        SOCKET sock = getStreamSocket(taskData, DEREFHANDLE(args)->Get(0));
914        PolyWord pBase = DEREFHANDLE(args)->Get(1);
915        POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(2));
916#if(defined(_WIN32) && ! defined(_CYGWIN))
917        int length = get_C_int(taskData, DEREFHANDLE(args)->Get(3));
918#else
919        ssize_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
920#endif
921        unsigned int dontRoute = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(4));
922        unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
923        int flags = 0;
924        if (dontRoute != 0) flags |= MSG_DONTROUTE;
925        if (outOfBand != 0) flags |= MSG_OOB;
926        char *base = (char*)pBase.AsObjPtr()->AsBytePtr();
927        sent = send(sock, base + offset, length, flags);
928        if (sent == SOCKET_ERROR)
929            raise_syscall(taskData, "send failed", GETERROR);
930    }
931    catch (...) {} // If an ML exception is raised
932
933    taskData->saveVec.reset(reset);
934    taskData->PostRTSCall();
935    return TAGGED(sent).AsUnsigned();
936}
937
938POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkSendTo(FirstArgument threadId, PolyWord argsAsWord)
939{
940    TaskData *taskData = TaskData::FindTaskForId(threadId);
941    ASSERT(taskData != 0);
942    taskData->PreRTSCall();
943    Handle reset = taskData->saveVec.mark();
944    Handle args = taskData->saveVec.push(argsAsWord);
945#if(defined(_WIN32) && ! defined(_CYGWIN))
946    int sent = 0;
947#else
948    ssize_t sent = 0;
949#endif
950
951    try {
952        SOCKET sock = getStreamSocket(taskData, DEREFHANDLE(args)->Get(0));
953        PolyStringObject * psAddr = (PolyStringObject *)args->WordP()->Get(1).AsObjPtr();
954        PolyWord pBase = DEREFHANDLE(args)->Get(2);
955
956        POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
957#if(defined(_WIN32) && ! defined(_CYGWIN))
958        int length = get_C_int(taskData, DEREFHANDLE(args)->Get(4));
959#else
960        size_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(4));
961#endif
962        unsigned int dontRoute = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
963        unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(6));
964        int flags = 0;
965        if (dontRoute != 0) flags |= MSG_DONTROUTE;
966        if (outOfBand != 0) flags |= MSG_OOB;
967        char *base = (char*)pBase.AsObjPtr()->AsBytePtr();
968        sent = sendto(sock, base + offset, length, flags,
969                (struct sockaddr *)psAddr->chars, (int)psAddr->length);
970        if (sent == SOCKET_ERROR)
971            raise_syscall(taskData, "sendto failed", GETERROR);
972    }
973    catch (...) {} // If an ML exception is raised
974
975    taskData->saveVec.reset(reset);
976    taskData->PostRTSCall();
977    return TAGGED(sent).AsUnsigned();
978}
979
980POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReceive(FirstArgument threadId, PolyWord argsAsWord)
981{
982    TaskData *taskData = TaskData::FindTaskForId(threadId);
983    ASSERT(taskData != 0);
984    taskData->PreRTSCall();
985    Handle reset = taskData->saveVec.mark();
986    Handle args = taskData->saveVec.push(argsAsWord);
987#if(defined(_WIN32) && ! defined(_CYGWIN))
988    int recvd = 0;
989#else
990    ssize_t recvd = 0;
991#endif
992
993    try {
994        SOCKET sock = getStreamSocket(taskData, DEREFHANDLE(args)->Get(0));
995        char *base = (char*)DEREFHANDLE(args)->Get(1).AsObjPtr()->AsBytePtr();
996        POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(2));
997#if(defined(_WIN32) && ! defined(_CYGWIN))
998        int length = get_C_int(taskData, DEREFHANDLE(args)->Get(3));
999#else
1000        size_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
1001#endif
1002        unsigned int peek = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(4));
1003        unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
1004        int flags = 0;
1005        if (peek != 0) flags |= MSG_PEEK;
1006        if (outOfBand != 0) flags |= MSG_OOB;
1007
1008        recvd = recv(sock, base + offset, length, flags);
1009        if (recvd == SOCKET_ERROR)
1010            raise_syscall(taskData, "recv failed", GETERROR);
1011    }
1012    catch (...) {} // If an ML exception is raised
1013
1014    taskData->saveVec.reset(reset);
1015    taskData->PostRTSCall();
1016    return TAGGED(recvd).AsUnsigned();
1017}
1018
1019POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReceiveFrom(FirstArgument threadId, PolyWord argsAsWord)
1020{
1021    TaskData *taskData = TaskData::FindTaskForId(threadId);
1022    ASSERT(taskData != 0);
1023    taskData->PreRTSCall();
1024    Handle reset = taskData->saveVec.mark();
1025    Handle args = taskData->saveVec.push(argsAsWord);
1026    Handle result = 0;
1027
1028    try {
1029        SOCKET sock = getStreamSocket(taskData, DEREFHANDLE(args)->Get(0));
1030        char *base = (char*)DEREFHANDLE(args)->Get(1).AsObjPtr()->AsBytePtr();
1031        POLYUNSIGNED offset = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(2));
1032#if(defined(_WIN32) && ! defined(_CYGWIN))
1033        int length = get_C_int(taskData, DEREFHANDLE(args)->Get(3));
1034#else
1035        size_t length = getPolyUnsigned(taskData, DEREFHANDLE(args)->Get(3));
1036#endif
1037        unsigned int peek = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(4));
1038        unsigned int outOfBand = get_C_unsigned(taskData, DEREFHANDLE(args)->Get(5));
1039        int flags = 0;
1040        struct sockaddr_storage resultAddr;
1041        socklen_t addrLen = sizeof(resultAddr);
1042
1043        if (peek != 0) flags |= MSG_PEEK;
1044        if (outOfBand != 0) flags |= MSG_OOB;
1045
1046#if(defined(_WIN32) && ! defined(_CYGWIN))
1047        int recvd;
1048#else
1049        ssize_t recvd;
1050#endif
1051        recvd = recvfrom(sock, base + offset, length, flags, (struct sockaddr*)&resultAddr, &addrLen);
1052        if (recvd == SOCKET_ERROR)
1053            raise_syscall(taskData, "recvfrom failed", GETERROR);
1054
1055        if (recvd > (int)length) recvd = length;
1056        Handle lengthHandle = Make_fixed_precision(taskData, recvd);
1057        if (addrLen > sizeof(resultAddr)) addrLen = sizeof(resultAddr);
1058        Handle addrHandle = SAVE(C_string_to_Poly(taskData, (char*)&resultAddr, addrLen));
1059        result = ALLOC(2);
1060        DEREFHANDLE(result)->Set(0, lengthHandle->Word());
1061        DEREFHANDLE(result)->Set(1, addrHandle->Word());
1062    }
1063    catch (...) {} // If an ML exception is raised
1064
1065    taskData->saveVec.reset(reset);
1066    taskData->PostRTSCall();
1067    if (result == 0) return TAGGED(0).AsUnsigned();
1068    else return result->Word().AsUnsigned();
1069}
1070
1071/* Return a list of known address families. */
1072POLYUNSIGNED PolyNetworkGetAddrList(FirstArgument threadId)
1073{
1074    TaskData* taskData = TaskData::FindTaskForId(threadId);
1075    ASSERT(taskData != 0);
1076    taskData->PreRTSCall();
1077    Handle reset = taskData->saveVec.mark();
1078    Handle result = 0;
1079
1080    try {
1081        result = makeList(taskData, sizeof(af_table) / sizeof(af_table[0]),
1082            (char*)af_table, sizeof(af_table[0]), 0, mkAftab);
1083    }
1084    catch (...) {} // If an ML exception is raised
1085
1086    taskData->saveVec.reset(reset);
1087    taskData->PostRTSCall();
1088    if (result == 0) return TAGGED(0).AsUnsigned();
1089    else return result->Word().AsUnsigned();
1090}
1091
1092/* Return a list of known socket types. */
1093POLYUNSIGNED PolyNetworkGetSockTypeList(FirstArgument threadId)
1094{
1095    TaskData* taskData = TaskData::FindTaskForId(threadId);
1096    ASSERT(taskData != 0);
1097    taskData->PreRTSCall();
1098    Handle reset = taskData->saveVec.mark();
1099    Handle result = 0;
1100
1101    try {
1102        result = makeList(taskData, sizeof(sk_table) / sizeof(sk_table[0]),
1103            (char*)sk_table, sizeof(sk_table[0]),
1104            0, mkSktab);
1105    }
1106    catch (...) {} // If an ML exception is raised
1107
1108    taskData->saveVec.reset(reset);
1109    taskData->PostRTSCall();
1110    if (result == 0) return TAGGED(0).AsUnsigned();
1111    else return result->Word().AsUnsigned();
1112}
1113
1114// Create a socket */
1115POLYUNSIGNED PolyNetworkCreateSocket(FirstArgument threadId, PolyWord family, PolyWord st, PolyWord prot)
1116{
1117    TaskData* taskData = TaskData::FindTaskForId(threadId);
1118    ASSERT(taskData != 0);
1119    taskData->PreRTSCall();
1120    Handle reset = taskData->saveVec.mark();
1121    Handle result = 0;
1122    int af = (int)family.UnTagged();
1123    int type = (int)st.UnTagged();
1124    int proto = (int)prot.UnTagged();
1125
1126    try {
1127        SOCKET skt = 0;
1128        do {
1129            skt = socket(af, type, proto);
1130        } while (skt == INVALID_SOCKET && GETERROR == CALLINTERRUPTED);
1131
1132        if (skt == INVALID_SOCKET)
1133            raise_syscall(taskData, "socket failed", GETERROR);
1134
1135        /* Set the socket to non-blocking mode. */
1136#if (defined(_WIN32) && ! defined(__CYGWIN__))
1137        unsigned long onOff = 1;
1138        if (ioctlsocket(skt, FIONBIO, &onOff) != 0)
1139#else
1140        int onOff = 1;
1141        if (ioctl(skt, FIONBIO, &onOff) < 0)
1142#endif
1143        {
1144#if (defined(_WIN32) && ! defined(__CYGWIN__))
1145            closesocket(skt);
1146#else
1147            close(skt);
1148#endif
1149            raise_syscall(taskData, "ioctl failed", GETERROR);
1150        }
1151        result = wrapStreamSocket(taskData, skt);
1152    }
1153    catch (...) {} // If an ML exception is raised
1154
1155    taskData->saveVec.reset(reset);
1156    taskData->PostRTSCall();
1157    if (result == 0) return TAGGED(0).AsUnsigned();
1158    else return result->Word().AsUnsigned();
1159}
1160
1161POLYUNSIGNED PolyNetworkSetOption(FirstArgument threadId, PolyWord code, PolyWord sock, PolyWord opt)
1162{
1163    TaskData* taskData = TaskData::FindTaskForId(threadId);
1164    ASSERT(taskData != 0);
1165    taskData->PreRTSCall();
1166    Handle reset = taskData->saveVec.mark();
1167    Handle pushedSock = taskData->saveVec.push(sock);
1168    Handle pushedOpt = taskData->saveVec.push(opt);
1169
1170    try {
1171        switch (UNTAGGED(code))
1172        {
1173        case 15: /* Set TCP No-delay option. */
1174            setSocketOption(taskData, pushedSock, pushedOpt, IPPROTO_TCP, TCP_NODELAY);
1175            break;
1176
1177        case 17: /* Set Debug option. */
1178            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_DEBUG);
1179            break;
1180
1181        case 19: /* Set REUSEADDR option. */
1182            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_REUSEADDR);
1183            break;
1184
1185        case 21: /* Set KEEPALIVE option. */
1186            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_KEEPALIVE);
1187            break;
1188
1189        case 23: /* Set DONTROUTE option. */
1190            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_DONTROUTE);
1191            break;
1192
1193        case 25: /* Set BROADCAST option. */
1194            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_BROADCAST);
1195            break;
1196
1197        case 27: /* Set OOBINLINE option. */
1198            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_OOBINLINE);
1199            break;
1200
1201        case 29: /* Set SNDBUF size. */
1202            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_SNDBUF);
1203            break;
1204
1205        case 31: /* Set RCVBUF size. */
1206            setSocketOption(taskData, pushedSock, pushedOpt, SOL_SOCKET, SO_RCVBUF);
1207            break;
1208        }
1209    }
1210    catch (KillException&) {
1211        processes->ThreadExit(taskData); // May test for kill
1212    }
1213    catch (...) {} // If an ML exception is raised
1214
1215    taskData->saveVec.reset(reset);
1216    taskData->PostRTSCall();
1217    return TAGGED(0).AsUnsigned();
1218}
1219
1220POLYUNSIGNED PolyNetworkGetOption(FirstArgument threadId, PolyWord code, PolyWord arg)
1221{
1222    TaskData* taskData = TaskData::FindTaskForId(threadId);
1223    ASSERT(taskData != 0);
1224    taskData->PreRTSCall();
1225    Handle reset = taskData->saveVec.mark();
1226    Handle pushedArg = taskData->saveVec.push(arg);
1227    Handle result = 0;
1228
1229    try {
1230        switch (UNTAGGED(code))
1231        {
1232        case 16: /* Get TCP No-delay option. */
1233            result = getSocketOption(taskData, pushedArg, IPPROTO_TCP, TCP_NODELAY);
1234            break;
1235
1236        case 18: /* Get Debug option. */
1237            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_DEBUG);
1238            break;
1239
1240        case 20: /* Get REUSEADDR option. */
1241            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_REUSEADDR);
1242            break;
1243
1244        case 22: /* Get KEEPALIVE option. */
1245            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_KEEPALIVE);
1246            break;
1247
1248        case 24: /* Get DONTROUTE option. */
1249            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_DONTROUTE);
1250            break;
1251
1252        case 26: /* Get BROADCAST option. */
1253            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_BROADCAST);
1254            break;
1255
1256        case 28: /* Get OOBINLINE option. */
1257            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_OOBINLINE);
1258            break;
1259
1260        case 30: /* Get SNDBUF size. */
1261            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_SNDBUF);
1262            break;
1263
1264        case 32: /* Get RCVBUF size. */
1265            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_RCVBUF);
1266            break;
1267
1268        case 33: /* Get socket type e.g. SOCK_STREAM. */
1269            result = getSocketOption(taskData, pushedArg, SOL_SOCKET, SO_TYPE);
1270            break;
1271        }
1272    }
1273    catch (KillException&) {
1274        processes->ThreadExit(taskData); // May test for kill
1275    }
1276    catch (...) {} // If an ML exception is raised
1277
1278    taskData->saveVec.reset(reset);
1279    taskData->PostRTSCall();
1280    if (result == 0) return TAGGED(0).AsUnsigned();
1281    else return result->Word().AsUnsigned();
1282}
1283
1284/* Set Linger time. */
1285POLYUNSIGNED PolyNetworkSetLinger(FirstArgument threadId, PolyWord sock, PolyWord lingerTime)
1286{
1287    TaskData* taskData = TaskData::FindTaskForId(threadId);
1288    ASSERT(taskData != 0);
1289    taskData->PreRTSCall();
1290    Handle reset = taskData->saveVec.mark();
1291
1292    try {
1293        SOCKET skt = getStreamSocket(taskData, sock);
1294        int lTime = get_C_int(taskData, lingerTime);
1295        struct linger linger;
1296        /* We pass in a negative value to turn the option off,
1297           zero or positive to turn it on. */
1298        if (lTime < 0)
1299        {
1300            linger.l_onoff = 0;
1301            linger.l_linger = 0;
1302        }
1303        else
1304        {
1305            linger.l_onoff = 1;
1306            linger.l_linger = lTime;
1307        }
1308        if (setsockopt(skt, SOL_SOCKET, SO_LINGER,
1309            (char*)& linger, sizeof(linger)) != 0)
1310            raise_syscall(taskData, "setsockopt failed", GETERROR);
1311    }
1312    catch (...) {} // If an ML exception is raised
1313
1314    taskData->saveVec.reset(reset);
1315    taskData->PostRTSCall();
1316    return TAGGED(0).AsUnsigned();
1317}
1318
1319/* Get Linger time. */
1320POLYUNSIGNED PolyNetworkGetLinger(FirstArgument threadId, PolyWord sock)
1321{
1322    TaskData* taskData = TaskData::FindTaskForId(threadId);
1323    ASSERT(taskData != 0);
1324    taskData->PreRTSCall();
1325    Handle reset = taskData->saveVec.mark();
1326    Handle result = 0;
1327
1328    try {
1329        SOCKET skt = getStreamSocket(taskData, sock);
1330        socklen_t size = sizeof(linger);
1331        int lTime = 0;
1332        struct linger linger;
1333        if (getsockopt(skt, SOL_SOCKET, SO_LINGER, (char*)& linger, &size) != 0)
1334            raise_syscall(taskData, "getsockopt failed", GETERROR);
1335        /* If the option is off return a negative. */
1336        if (linger.l_onoff == 0) lTime = -1;
1337        else lTime = linger.l_linger;
1338        result = Make_arbitrary_precision(taskData, lTime); // Returns LargeInt.int
1339    }
1340    catch (...) {} // If an ML exception is raised
1341
1342    taskData->saveVec.reset(reset);
1343    taskData->PostRTSCall();
1344    if (result == 0) return TAGGED(0).AsUnsigned();
1345    else return result->Word().AsUnsigned();
1346}
1347
1348/* Get peer name. */
1349POLYUNSIGNED PolyNetworkGetPeerName(FirstArgument threadId, PolyWord sock)
1350{
1351    TaskData* taskData = TaskData::FindTaskForId(threadId);
1352    ASSERT(taskData != 0);
1353    taskData->PreRTSCall();
1354    Handle reset = taskData->saveVec.mark();
1355    Handle result = 0;
1356
1357    try {
1358        SOCKET skt = getStreamSocket(taskData, sock);
1359        struct sockaddr_storage sockA;
1360        socklen_t size = sizeof(sockA);
1361        if (getpeername(skt, (struct sockaddr*) & sockA, &size) != 0)
1362            raise_syscall(taskData, "getpeername failed", GETERROR);
1363        if (size > sizeof(sockA)) size = sizeof(sockA);
1364        /* Addresses are treated as strings. */
1365        result = (SAVE(C_string_to_Poly(taskData, (char*)& sockA, size)));
1366    }
1367    catch (...) {} // If an ML exception is raised
1368
1369    taskData->saveVec.reset(reset);
1370    taskData->PostRTSCall();
1371    if (result == 0) return TAGGED(0).AsUnsigned();
1372    else return result->Word().AsUnsigned();
1373}
1374
1375/* Get socket name. */
1376POLYUNSIGNED PolyNetworkGetSockName(FirstArgument threadId, PolyWord sock)
1377{
1378    TaskData* taskData = TaskData::FindTaskForId(threadId);
1379    ASSERT(taskData != 0);
1380    taskData->PreRTSCall();
1381    Handle reset = taskData->saveVec.mark();
1382    Handle result = 0;
1383
1384    try {
1385        SOCKET skt = getStreamSocket(taskData, sock);
1386        struct sockaddr_storage sockA;
1387        socklen_t   size = sizeof(sockA);
1388        if (getsockname(skt, (struct sockaddr*) & sockA, &size) != 0)
1389            raise_syscall(taskData, "getsockname failed", GETERROR);
1390        if (size > sizeof(sockA)) size = sizeof(sockA);
1391        result = (SAVE(C_string_to_Poly(taskData, (char*)& sockA, size)));
1392    }
1393    catch (...) {} // If an ML exception is raised
1394
1395    taskData->saveVec.reset(reset);
1396    taskData->PostRTSCall();
1397    if (result == 0) return TAGGED(0).AsUnsigned();
1398    else return result->Word().AsUnsigned();
1399}
1400
1401/* Find number of bytes available. */
1402POLYUNSIGNED PolyNetworkBytesAvailable(FirstArgument threadId, PolyWord sock)
1403{
1404    TaskData* taskData = TaskData::FindTaskForId(threadId);
1405    ASSERT(taskData != 0);
1406    taskData->PreRTSCall();
1407    Handle reset = taskData->saveVec.mark();
1408    Handle result = 0;
1409
1410    try {
1411        SOCKET skt = getStreamSocket(taskData, sock);
1412#if (defined(_WIN32) && ! defined(__CYGWIN__))
1413        unsigned long readable;
1414        if (ioctlsocket(skt, FIONREAD, &readable) != 0)
1415            raise_syscall(taskData, "ioctlsocket failed", GETERROR);
1416#else
1417        int readable;
1418        if (ioctl(skt, FIONREAD, &readable) < 0)
1419            raise_syscall(taskData, "ioctl failed", GETERROR);
1420#endif
1421        result = Make_fixed_precision(taskData, readable);
1422    }
1423    catch (...) {} // If an ML exception is raised
1424
1425    taskData->saveVec.reset(reset);
1426    taskData->PostRTSCall();
1427    if (result == 0) return TAGGED(0).AsUnsigned();
1428    else return result->Word().AsUnsigned();
1429}
1430
1431/* Find out if we are at the mark. */
1432POLYUNSIGNED PolyNetworkGetAtMark(FirstArgument threadId, PolyWord sock)
1433{
1434    TaskData* taskData = TaskData::FindTaskForId(threadId);
1435    ASSERT(taskData != 0);
1436    taskData->PreRTSCall();
1437    Handle reset = taskData->saveVec.mark();
1438    Handle result = 0;
1439
1440    try {
1441        SOCKET skt = getStreamSocket(taskData, sock);
1442#if (defined(_WIN32) && ! defined(__CYGWIN__))
1443        unsigned long atMark;
1444        if (ioctlsocket(skt, SIOCATMARK, &atMark) != 0)
1445            raise_syscall(taskData, "ioctlsocket failed", GETERROR);
1446#else
1447        int atMark;
1448        if (ioctl(skt, SIOCATMARK, &atMark) < 0)
1449            raise_syscall(taskData, "ioctl failed", GETERROR);
1450#endif
1451        result = Make_fixed_precision(taskData, atMark == 0 ? 0 : 1);
1452    }
1453    catch (...) {} // If an ML exception is raised
1454
1455    taskData->saveVec.reset(reset);
1456    taskData->PostRTSCall();
1457    if (result == 0) return TAGGED(0).AsUnsigned();
1458    else return result->Word().AsUnsigned();
1459}
1460
1461/* Bind an address to a socket. */
1462POLYUNSIGNED PolyNetworkBind(FirstArgument threadId, PolyWord sock, PolyWord addr)
1463{
1464    TaskData* taskData = TaskData::FindTaskForId(threadId);
1465    ASSERT(taskData != 0);
1466    taskData->PreRTSCall();
1467    Handle reset = taskData->saveVec.mark();
1468
1469    try {
1470        SOCKET skt = getStreamSocket(taskData, sock);
1471        PolyStringObject* psAddr = (PolyStringObject*)addr.AsObjPtr();
1472        struct sockaddr* psock = (struct sockaddr*) & psAddr->chars;
1473        if (bind(skt, psock, (int)psAddr->length) != 0)
1474            raise_syscall(taskData, "bind failed", GETERROR);
1475    }
1476    catch (...) {} // If an ML exception is raised
1477
1478    taskData->saveVec.reset(reset);
1479    taskData->PostRTSCall();
1480    return TAGGED(0).AsUnsigned();
1481}
1482
1483/* Put socket into listening mode. */
1484POLYUNSIGNED PolyNetworkListen(FirstArgument threadId, PolyWord skt, PolyWord back)
1485{
1486    TaskData* taskData = TaskData::FindTaskForId(threadId);
1487    ASSERT(taskData != 0);
1488    taskData->PreRTSCall();
1489    Handle reset = taskData->saveVec.mark();
1490
1491    try {
1492         SOCKET sock = getStreamSocket(taskData, skt);
1493         int backlog = get_C_int(taskData, back);
1494         if (listen(sock, backlog) != 0)
1495             raise_syscall(taskData, "listen failed", GETERROR);
1496    }
1497    catch (...) {} // If an ML exception is raised
1498
1499    taskData->saveVec.reset(reset);
1500    taskData->PostRTSCall();
1501    return TAGGED(0).AsUnsigned();
1502}
1503
1504/* Shutdown the socket. */
1505POLYUNSIGNED PolyNetworkShutdown(FirstArgument threadId, PolyWord skt, PolyWord smode)
1506{
1507    TaskData* taskData = TaskData::FindTaskForId(threadId);
1508    ASSERT(taskData != 0);
1509    taskData->PreRTSCall();
1510    Handle reset = taskData->saveVec.mark();
1511
1512    try {
1513        SOCKET sock = getStreamSocket(taskData, skt);
1514        int mode = 0;
1515        switch (get_C_ulong(taskData, smode))
1516        {
1517        case 1: mode = SHUT_RD; break;
1518        case 2: mode = SHUT_WR; break;
1519        case 3: mode = SHUT_RDWR;
1520        }
1521        if (shutdown(sock, mode) != 0)
1522            raise_syscall(taskData, "shutdown failed", GETERROR);
1523    }
1524    catch (...) {} // If an ML exception is raised
1525
1526    taskData->saveVec.reset(reset);
1527    taskData->PostRTSCall();
1528    return TAGGED(0).AsUnsigned();
1529}
1530
1531/* Create a socket pair. */
1532POLYUNSIGNED PolyNetworkCreateSocketPair(FirstArgument threadId, PolyWord family, PolyWord st, PolyWord prot)
1533{
1534    TaskData* taskData = TaskData::FindTaskForId(threadId);
1535    ASSERT(taskData != 0);
1536    taskData->PreRTSCall();
1537    Handle reset = taskData->saveVec.mark();
1538    Handle result = 0;
1539
1540    try {
1541#if (defined(_WIN32) && ! defined(__CYGWIN__))
1542        /* Not implemented. */
1543       raise_syscall(taskData, "socketpair not implemented", WSAEAFNOSUPPORT);
1544#else
1545        int af = family.UnTagged();
1546        int type = st.UnTagged();
1547        int proto = prot.UnTagged();
1548        SOCKET skt[2];
1549        int skPRes = 0;
1550
1551        do {
1552            skPRes = socketpair(af, type, proto, skt);
1553        } while (skPRes != 0 && GETERROR == CALLINTERRUPTED);
1554
1555        int onOff = 1;
1556        /* Set the sockets to non-blocking mode. */
1557        if (ioctl(skt[0], FIONBIO, &onOff) < 0 ||
1558            ioctl(skt[1], FIONBIO, &onOff) < 0)
1559        {
1560            close(skt[0]);
1561            close(skt[1]);
1562            raise_syscall(taskData, "ioctl failed", GETERROR);
1563        }
1564        Handle str_token1 = wrapStreamSocket(taskData, skt[0]);
1565        Handle str_token2 = wrapStreamSocket(taskData, skt[1]);
1566        /* Return the two streams as a pair. */
1567        result = ALLOC(2);
1568        DEREFHANDLE(result)->Set(0, DEREFWORD(str_token1));
1569        DEREFHANDLE(result)->Set(1, DEREFWORD(str_token2));
1570#endif
1571    }
1572    catch (KillException&) {
1573        processes->ThreadExit(taskData); // May test for kill
1574    }
1575    catch (...) {} // If an ML exception is raised
1576
1577    taskData->saveVec.reset(reset);
1578    taskData->PostRTSCall();
1579    if (result == 0) return TAGGED(0).AsUnsigned();
1580    else return result->Word().AsUnsigned();
1581}
1582
1583/* Create a Unix socket address from a string. */
1584POLYUNSIGNED PolyNetworkUnixPathToSockAddr(FirstArgument threadId, PolyWord arg)
1585{
1586    TaskData* taskData = TaskData::FindTaskForId(threadId);
1587    ASSERT(taskData != 0);
1588    taskData->PreRTSCall();
1589    Handle reset = taskData->saveVec.mark();
1590    Handle result = 0;
1591
1592    try {
1593#if (defined(_WIN32) && ! defined(__CYGWIN__))
1594        /* Not implemented. */
1595        raise_syscall(taskData, "Unix addresses not implemented", WSAEAFNOSUPPORT);
1596#else
1597        struct sockaddr_un addr;
1598        memset(&addr, 0, sizeof(addr));
1599        addr.sun_family = AF_UNIX;
1600#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
1601        addr.sun_len = sizeof(addr); // Used in FreeBSD only.
1602#endif
1603        POLYUNSIGNED length = Poly_string_to_C(arg, addr.sun_path, sizeof(addr.sun_path));
1604        if (length > (int)sizeof(addr.sun_path))
1605            raise_syscall(taskData, "Address too long", ENAMETOOLONG);
1606        result = SAVE(C_string_to_Poly(taskData, (char*)& addr, sizeof(addr)));
1607#endif
1608    }
1609    catch (...) {} // If an ML exception is raised
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
1617/* Get the file name from a Unix socket address. */
1618POLYUNSIGNED PolyNetworkUnixSockAddrToPath(FirstArgument threadId, PolyWord arg)
1619{
1620    TaskData* taskData = TaskData::FindTaskForId(threadId);
1621    ASSERT(taskData != 0);
1622    taskData->PreRTSCall();
1623    Handle reset = taskData->saveVec.mark();
1624    Handle result = 0;
1625
1626    try {
1627#if (defined(_WIN32) && ! defined(__CYGWIN__))
1628        /* Not implemented. */
1629        raise_syscall(taskData, "Unix addresses not implemented", WSAEAFNOSUPPORT);
1630#else
1631        PolyStringObject* psAddr = (PolyStringObject*)arg.AsObjPtr();
1632        struct sockaddr_un* psock = (struct sockaddr_un*) & psAddr->chars;
1633        result = SAVE(C_string_to_Poly(taskData, psock->sun_path));
1634#endif
1635    }
1636    catch (...) {} // If an ML exception is raised
1637
1638    taskData->saveVec.reset(reset);
1639    taskData->PostRTSCall();
1640    if (result == 0) return TAGGED(0).AsUnsigned();
1641    else return result->Word().AsUnsigned();
1642}
1643
1644POLYUNSIGNED PolyNetworkGetServByName(FirstArgument threadId, PolyWord serviceName)
1645{
1646    TaskData *taskData = TaskData::FindTaskForId(threadId);
1647    ASSERT(taskData != 0);
1648    taskData->PreRTSCall();
1649    Handle reset = taskData->saveVec.mark();
1650
1651    /* Get service given service name only. */
1652    TempCString servName(Poly_string_to_C_alloc(serviceName));
1653    struct servent *serv = getservbyname (servName, NULL);
1654    // If this fails the ML function returns NONE
1655    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1656
1657    taskData->saveVec.reset(reset);
1658    taskData->PostRTSCall();
1659    if (result == 0) return TAGGED(0).AsUnsigned();
1660    else return result->Word().AsUnsigned();
1661}
1662
1663POLYUNSIGNED PolyNetworkGetServByNameAndProtocol(FirstArgument threadId, PolyWord serviceName, PolyWord protName)
1664{
1665    TaskData *taskData = TaskData::FindTaskForId(threadId);
1666    ASSERT(taskData != 0);
1667    taskData->PreRTSCall();
1668    Handle reset = taskData->saveVec.mark();
1669
1670    /* Get service given service name and protocol name. */
1671    TempCString servName(Poly_string_to_C_alloc(serviceName));
1672    TempCString protoName(Poly_string_to_C_alloc(protName));
1673    struct servent *serv = getservbyname (servName, protoName);
1674    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1675
1676    taskData->saveVec.reset(reset);
1677    taskData->PostRTSCall();
1678    if (result == 0) return TAGGED(0).AsUnsigned();
1679    else return result->Word().AsUnsigned();
1680}
1681
1682POLYUNSIGNED PolyNetworkGetServByPort(FirstArgument threadId, PolyWord portNo)
1683{
1684    TaskData *taskData = TaskData::FindTaskForId(threadId);
1685    ASSERT(taskData != 0);
1686    taskData->PreRTSCall();
1687    Handle reset = taskData->saveVec.mark();
1688
1689    /* Get service given port number only. */
1690    long port = htons(get_C_ushort(taskData, portNo));
1691    struct servent *serv = getservbyport(port, NULL);
1692    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1693
1694    taskData->saveVec.reset(reset);
1695    taskData->PostRTSCall();
1696    if (result == 0) return TAGGED(0).AsUnsigned();
1697    else return result->Word().AsUnsigned();
1698}
1699
1700POLYUNSIGNED PolyNetworkGetServByPortAndProtocol(FirstArgument threadId, PolyWord portNo, PolyWord protName)
1701{
1702    TaskData *taskData = TaskData::FindTaskForId(threadId);
1703    ASSERT(taskData != 0);
1704    taskData->PreRTSCall();
1705    Handle reset = taskData->saveVec.mark();
1706
1707    /* Get service given port number and protocol name. */
1708    long port = htons(get_C_ushort(taskData, portNo));
1709    TempCString protoName(Poly_string_to_C_alloc(protName));
1710    struct servent *serv = getservbyport (port, protoName);
1711    Handle result = serv == NULL ? 0 : makeServEntry(taskData, serv);
1712
1713    taskData->saveVec.reset(reset);
1714    taskData->PostRTSCall();
1715    if (result == 0) return TAGGED(0).AsUnsigned();
1716    else return result->Word().AsUnsigned();
1717}
1718
1719POLYUNSIGNED PolyNetworkGetProtByName(FirstArgument threadId, PolyWord protocolName)
1720{
1721    TaskData *taskData = TaskData::FindTaskForId(threadId);
1722    ASSERT(taskData != 0);
1723    taskData->PreRTSCall();
1724    Handle reset = taskData->saveVec.mark();
1725
1726    /* Look up protocol entry. */
1727    TempCString protoName(Poly_string_to_C_alloc(protocolName));
1728    struct protoent *proto = getprotobyname(protoName);
1729    // If this fails the ML function returns NONE
1730    Handle result = proto == NULL ? 0 : makeProtoEntry(taskData, proto);
1731
1732    taskData->saveVec.reset(reset);
1733    taskData->PostRTSCall();
1734    if (result == 0) return TAGGED(0).AsUnsigned();
1735    else return result->Word().AsUnsigned();
1736}
1737
1738POLYUNSIGNED PolyNetworkGetProtByNo(FirstArgument threadId, PolyWord protoNo)
1739{
1740    TaskData *taskData = TaskData::FindTaskForId(threadId);
1741    ASSERT(taskData != 0);
1742    taskData->PreRTSCall();
1743    Handle reset = taskData->saveVec.mark();
1744
1745    /* Look up protocol entry. */
1746    int pNum = get_C_int(taskData, protoNo);
1747    struct protoent *proto = getprotobynumber(pNum);
1748    Handle result = proto == NULL ? 0 : makeProtoEntry(taskData, proto);
1749
1750    taskData->saveVec.reset(reset);
1751    taskData->PostRTSCall();
1752    if (result == 0) return TAGGED(0).AsUnsigned();
1753    else return result->Word().AsUnsigned();
1754}
1755
1756POLYUNSIGNED PolyNetworkGetHostName(FirstArgument threadId)
1757{
1758    TaskData *taskData = TaskData::FindTaskForId(threadId);
1759    ASSERT(taskData != 0);
1760    taskData->PreRTSCall();
1761    Handle reset = taskData->saveVec.mark();
1762    Handle result = 0;
1763
1764    try { /* Get the current host name. */
1765        // Since the maximum length of a FQDN is 256 bytes it should fit in the buffer.
1766#ifdef HOST_NAME_MAX
1767        char hostName[HOST_NAME_MAX+1];
1768#else
1769        char hostName[1024];
1770#endif
1771        int err = gethostname(hostName, sizeof(hostName));
1772        if (err != 0)
1773            raise_syscall(taskData, "gethostname failed", GETERROR);
1774        // Add a null at the end just in case.  See gethostname man page.
1775        hostName[sizeof(hostName) - 1] = 0;
1776
1777        result = SAVE(C_string_to_Poly(taskData, hostName));
1778    }
1779    catch (...) { } // If an ML exception is raised
1780
1781    taskData->saveVec.reset(reset);
1782    taskData->PostRTSCall();
1783    if (result == 0) return TAGGED(0).AsUnsigned();
1784    else return result->Word().AsUnsigned();
1785}
1786
1787POLYUNSIGNED PolyNetworkGetNameInfo(FirstArgument threadId, PolyWord sockAddr)
1788{
1789    TaskData *taskData = TaskData::FindTaskForId(threadId);
1790    ASSERT(taskData != 0);
1791    taskData->PreRTSCall();
1792    Handle reset = taskData->saveVec.mark();
1793    Handle result = 0;
1794
1795    try {
1796        PolyStringObject* psAddr = (PolyStringObject*)sockAddr.AsObjPtr();
1797        struct sockaddr* psock = (struct sockaddr*) & psAddr->chars;
1798        // Since the maximum length of a FQDN is 256 bytes it should fit in the buffer.
1799        char hostName[1024];
1800        int gniRes = getnameinfo(psock, (socklen_t)psAddr->length, hostName, sizeof(hostName), NULL, 0, 0);
1801        if (gniRes != 0)
1802        {
1803#if (defined(_WIN32) && ! defined(__CYGWIN__))
1804            raise_syscall(taskData, "getnameinfo failed", GETERROR);
1805#else
1806            if (gniRes == EAI_SYSTEM)
1807                raise_syscall(taskData, "getnameinfo failed", GETERROR);
1808            else raise_syscall(taskData, gai_strerror(gniRes), 0);
1809#endif
1810        }
1811        result = SAVE(C_string_to_Poly(taskData, hostName));
1812    }
1813    catch (...) {} // If an ML exception is raised
1814
1815    taskData->saveVec.reset(reset);
1816    taskData->PostRTSCall();
1817    if (result == 0) return TAGGED(0).AsUnsigned();
1818    else return result->Word().AsUnsigned();
1819}
1820
1821// Copy addrInfo data into ML memory.  We copy this although most of it
1822// is currently unused.
1823static Handle extractAddrInfo(TaskData *taskData, struct addrinfo *ainfo)
1824{
1825    if (ainfo == 0)
1826        return taskData->saveVec.push(ListNull);
1827
1828    Handle reset = taskData->saveVec.mark();
1829    Handle tail = extractAddrInfo(taskData, ainfo->ai_next);
1830    Handle name = 0;
1831    // Only the first entry may have a canonical name.
1832    if (ainfo->ai_canonname == 0)
1833        name = taskData->saveVec.push(C_string_to_Poly(taskData, ""));
1834    else name = taskData->saveVec.push(C_string_to_Poly(taskData, ainfo->ai_canonname));
1835
1836    Handle address = taskData->saveVec.push(C_string_to_Poly(taskData, (char*)ainfo->ai_addr, ainfo->ai_addrlen));
1837
1838    Handle value = alloc_and_save(taskData, 6);
1839    value->WordP()->Set(0, TAGGED(ainfo->ai_flags));
1840    value->WordP()->Set(1, TAGGED(ainfo->ai_family));
1841    value->WordP()->Set(2, TAGGED(ainfo->ai_socktype));
1842    value->WordP()->Set(3, TAGGED(ainfo->ai_protocol));
1843    value->WordP()->Set(4, address->Word());
1844    value->WordP()->Set(5, name->Word());
1845
1846    ML_Cons_Cell *next = (ML_Cons_Cell*)alloc(taskData, SIZEOF(ML_Cons_Cell));
1847    next->h = value->Word();
1848    next->t = tail->Word();
1849
1850    taskData->saveVec.reset(reset);
1851    return taskData->saveVec.push(next);
1852}
1853
1854POLYUNSIGNED PolyNetworkGetAddrInfo(FirstArgument threadId, PolyWord hName, PolyWord addrFamily)
1855{
1856    TaskData *taskData = TaskData::FindTaskForId(threadId);
1857    ASSERT(taskData != 0);
1858    taskData->PreRTSCall();
1859    Handle reset = taskData->saveVec.mark();
1860    Handle result = 0;
1861    struct addrinfo *resAddr = 0;
1862
1863    try {
1864        TempCString hostName(Poly_string_to_C_alloc(hName));
1865        struct addrinfo hints;
1866        memset(&hints, 0, sizeof(hints));
1867        hints.ai_family = (int)UNTAGGED(addrFamily); // AF_INET or AF_INET6 or, possibly, AF_UNSPEC.
1868        hints.ai_flags = AI_CANONNAME;
1869
1870        int gaiRes = getaddrinfo(hostName, 0, &hints, &resAddr);
1871        if (gaiRes != 0)
1872        {
1873#if (defined(_WIN32) && ! defined(__CYGWIN__))
1874            raise_syscall(taskData, "getaddrinfo failed", GETERROR);
1875#else
1876            if (gaiRes == EAI_SYSTEM)
1877                raise_syscall(taskData, "getnameinfo failed", GETERROR);
1878            else raise_syscall(taskData, gai_strerror(gaiRes), 0);
1879#endif
1880        }
1881
1882        result = extractAddrInfo(taskData, resAddr);
1883    }
1884    catch (...) { } // Could raise an exception if we run out of heap space
1885
1886    if (resAddr) freeaddrinfo(resAddr);
1887
1888    taskData->saveVec.reset(reset);
1889    taskData->PostRTSCall();
1890    if (result == 0) return TAGGED(0).AsUnsigned();
1891    else return result->Word().AsUnsigned();
1892}
1893
1894POLYUNSIGNED PolyNetworkCloseSocket(FirstArgument threadId, PolyWord strm)
1895{
1896    TaskData *taskData = TaskData::FindTaskForId(threadId);
1897    ASSERT(taskData != 0);
1898    taskData->PreRTSCall();
1899    Handle reset = taskData->saveVec.mark();
1900    Handle result = 0;
1901    Handle pushedStream = taskData->saveVec.push(strm);
1902
1903    try {
1904        // This is defined to raise an exception if the socket has already been closed
1905#if (defined(_WIN32))
1906        WinSocket *winskt = *(WinSocket**)(pushedStream->WordP());
1907        if (winskt != 0)
1908        {
1909            if (closesocket(winskt->getSocket()) != 0)
1910                raise_syscall(taskData, "Error during close", GETERROR);
1911        }
1912        else raise_syscall(taskData, "Socket is closed", WSAEBADF);
1913        *(WinSocket **)(pushedStream->WordP()) = 0; // Mark as closed
1914#else
1915        int descr = getStreamFileDescriptorWithoutCheck(pushedStream->Word());
1916        if (descr >= 0)
1917        {
1918            if (close(descr) != 0)
1919                raise_syscall(taskData, "Error during close", GETERROR);
1920        }
1921        else raise_syscall(taskData, "Socket is closed", EBADF);
1922        *(int*)(pushedStream->WordP()) = 0; // Mark as closed
1923#endif
1924        result = Make_fixed_precision(taskData, 0);
1925    }
1926    catch (...) {} // If an ML exception is raised
1927
1928    taskData->saveVec.reset(reset);
1929    taskData->PostRTSCall();
1930    if (result == 0) return TAGGED(0).AsUnsigned();
1931    else return result->Word().AsUnsigned();
1932}
1933
1934// Return the family
1935POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetFamilyFromAddress(PolyWord sockAddress)
1936{
1937    PolyStringObject* psAddr = (PolyStringObject*)sockAddress.AsObjPtr();
1938    struct sockaddr* psock = (struct sockaddr*) & psAddr->chars;
1939    return TAGGED(psock->sa_family).AsUnsigned();
1940}
1941
1942// Return internet address and port from an internet socket address.
1943// Assumes that we've already checked the address family.
1944POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAddressAndPortFromIP4(FirstArgument threadId, PolyWord sockAddress)
1945{
1946    TaskData* taskData = TaskData::FindTaskForId(threadId);
1947    ASSERT(taskData != 0);
1948    taskData->PreRTSCall();
1949    Handle reset = taskData->saveVec.mark();
1950    Handle result = 0;
1951
1952    try {
1953        PolyStringObject* psAddr = (PolyStringObject*)sockAddress.AsObjPtr();
1954        struct sockaddr_in* psock = (struct sockaddr_in*) & psAddr->chars;
1955        Handle ipAddr = Make_arbitrary_precision(taskData, ntohl(psock->sin_addr.s_addr)); // IPv4 addr is LargeInt.int
1956        result = alloc_and_save(taskData, 2);
1957        result->WordP()->Set(0, ipAddr->Word());
1958        result->WordP()->Set(1, TAGGED(ntohs(psock->sin_port)));
1959    }
1960    catch (...) {} // If an ML exception is raised
1961
1962    taskData->saveVec.reset(reset);
1963    taskData->PostRTSCall();
1964    if (result == 0) return TAGGED(0).AsUnsigned();
1965    else return result->Word().AsUnsigned();
1966}
1967
1968// Create a socket address from a port number and internet address.
1969POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCreateIP4Address(FirstArgument threadId, PolyWord ip4Address, PolyWord portNumber)
1970{
1971    TaskData* taskData = TaskData::FindTaskForId(threadId);
1972    ASSERT(taskData != 0);
1973    taskData->PreRTSCall();
1974    Handle reset = taskData->saveVec.mark();
1975    Handle result = 0;
1976
1977    try {
1978        struct sockaddr_in sockaddr;
1979        memset(&sockaddr, 0, sizeof(sockaddr));
1980        sockaddr.sin_family = AF_INET;
1981        sockaddr.sin_port = htons(get_C_ushort(taskData, portNumber));
1982        sockaddr.sin_addr.s_addr = htonl(get_C_unsigned(taskData, ip4Address));
1983        result = SAVE(C_string_to_Poly(taskData, (char*)&sockaddr, sizeof(sockaddr)));
1984    }
1985    catch (...) {} // If an ML exception is raised
1986
1987    taskData->saveVec.reset(reset);
1988    taskData->PostRTSCall();
1989    if (result == 0) return TAGGED(0).AsUnsigned();
1990    else return result->Word().AsUnsigned();
1991}
1992
1993// Return the value of INADDR_ANY.
1994POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReturnIP4AddressAny(FirstArgument threadId)
1995{
1996    TaskData* taskData = TaskData::FindTaskForId(threadId);
1997    ASSERT(taskData != 0);
1998    taskData->PreRTSCall();
1999    Handle reset = taskData->saveVec.mark();
2000    Handle result = 0;
2001
2002    try {
2003        result = Make_arbitrary_precision(taskData, INADDR_ANY); // IPv4 addr is LargeInt.int
2004    }
2005    catch (...) {} // If an ML exception is raised
2006
2007    taskData->saveVec.reset(reset);
2008    taskData->PostRTSCall();
2009    if (result == 0) return TAGGED(0).AsUnsigned();
2010    else return result->Word().AsUnsigned();
2011}
2012
2013POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkGetAddressAndPortFromIP6(FirstArgument threadId, PolyWord sockAddress)
2014{
2015    TaskData* taskData = TaskData::FindTaskForId(threadId);
2016    ASSERT(taskData != 0);
2017    taskData->PreRTSCall();
2018    Handle reset = taskData->saveVec.mark();
2019    Handle result = 0;
2020
2021    try {
2022        PolyStringObject* psAddr = (PolyStringObject*)sockAddress.AsObjPtr();
2023        if (psAddr->length != sizeof(struct sockaddr_in6))
2024            raise_fail(taskData, "Invalid length");
2025        struct sockaddr_in6* psock = (struct sockaddr_in6*) & psAddr->chars;
2026        Handle ipAddr = SAVE(C_string_to_Poly(taskData, (const char*)&psock->sin6_addr, sizeof(struct in6_addr)));
2027        result = alloc_and_save(taskData, 2);
2028        result->WordP()->Set(0, ipAddr->Word());
2029        result->WordP()->Set(1, TAGGED(ntohs(psock->sin6_port)));
2030
2031    }
2032    catch (...) {} // If an ML exception is raised
2033
2034    taskData->saveVec.reset(reset);
2035    taskData->PostRTSCall();
2036    if (result == 0) return TAGGED(0).AsUnsigned();
2037    else return result->Word().AsUnsigned();
2038}
2039
2040POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkCreateIP6Address(FirstArgument threadId, PolyWord ip6Address, PolyWord portNumber)
2041{
2042    TaskData* taskData = TaskData::FindTaskForId(threadId);
2043    ASSERT(taskData != 0);
2044    taskData->PreRTSCall();
2045    Handle reset = taskData->saveVec.mark();
2046    Handle result = 0;
2047
2048    try {
2049        struct sockaddr_in6 addr;
2050        memset(&addr, 0, sizeof(addr));
2051        result = SAVE(C_string_to_Poly(taskData, (const char*)&addr, sizeof(struct in6_addr)));
2052        addr.sin6_family = AF_INET6;
2053        addr.sin6_port = htons(get_C_ushort(taskData, portNumber));
2054        PolyStringObject* addrAsString = (PolyStringObject*)ip6Address.AsObjPtr();
2055        if (addrAsString->length != sizeof(addr.sin6_addr))
2056            raise_fail(taskData, "Invalid address length");
2057        memcpy(&addr.sin6_addr, addrAsString->chars, sizeof(addr.sin6_addr));
2058        result = SAVE(C_string_to_Poly(taskData, (char*)&addr, sizeof(addr)));
2059    }
2060    catch (...) {} // If an ML exception is raised
2061
2062    taskData->saveVec.reset(reset);
2063    taskData->PostRTSCall();
2064    if (result == 0) return TAGGED(0).AsUnsigned();
2065    else return result->Word().AsUnsigned();
2066}
2067
2068POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkReturnIP6AddressAny(FirstArgument threadId)
2069{
2070    TaskData* taskData = TaskData::FindTaskForId(threadId);
2071    ASSERT(taskData != 0);
2072    taskData->PreRTSCall();
2073    Handle reset = taskData->saveVec.mark();
2074    Handle result = 0;
2075
2076    try {
2077        result = SAVE(C_string_to_Poly(taskData, (const char*)&in6addr_any, sizeof(struct in6_addr)));
2078    }
2079    catch (...) {} // If an ML exception is raised
2080
2081    taskData->saveVec.reset(reset);
2082    taskData->PostRTSCall();
2083    if (result == 0) return TAGGED(0).AsUnsigned();
2084    else return result->Word().AsUnsigned();
2085}
2086
2087// Convert an IPV6 address to string.  This could be done in ML but the rules
2088// for converting zeros to double-colon are complicated.
2089POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkIP6AddressToString(FirstArgument threadId, PolyWord ip6Address)
2090{
2091    TaskData* taskData = TaskData::FindTaskForId(threadId);
2092    ASSERT(taskData != 0);
2093    taskData->PreRTSCall();
2094    Handle reset = taskData->saveVec.mark();
2095    Handle result = 0;
2096
2097    try {
2098        char buffer[80]; // 40 should actually be enough: 32 hex bytes, 7 colons and a null.
2099        PolyStringObject* addrAsString = (PolyStringObject*)ip6Address.AsObjPtr();
2100        if (addrAsString->length != sizeof(struct in6_addr))
2101            raise_fail(taskData, "Invalid address length");
2102        if (inet_ntop(AF_INET6, addrAsString->chars, buffer, sizeof(buffer)) == 0)
2103            raise_syscall(taskData, "inet_ntop", GETERROR);
2104        result = SAVE(C_string_to_Poly(taskData, buffer));
2105    }
2106    catch (...) {} // If an ML exception is raised
2107
2108    taskData->saveVec.reset(reset);
2109    taskData->PostRTSCall();
2110    if (result == 0) return TAGGED(0).AsUnsigned();
2111    else return result->Word().AsUnsigned();
2112}
2113
2114// Convert a string to an IPv6 address.  The parsing has to be done in ML.
2115POLYEXTERNALSYMBOL POLYUNSIGNED PolyNetworkStringToIP6Address(FirstArgument threadId, PolyWord stringRep)
2116{
2117    TaskData* taskData = TaskData::FindTaskForId(threadId);
2118    ASSERT(taskData != 0);
2119    taskData->PreRTSCall();
2120    Handle reset = taskData->saveVec.mark();
2121    Handle result = 0;
2122
2123    try {
2124        struct in6_addr address;
2125        TempCString stringAddr(Poly_string_to_C_alloc(stringRep));
2126        if (inet_pton(AF_INET6, stringAddr, &address) != 1)
2127            raise_fail(taskData, "Invalid IPv6 address");
2128        result = taskData->saveVec.push(C_string_to_Poly(taskData, (const char *)&address, sizeof(struct in6_addr)));
2129    }
2130    catch (...) {} // If an ML exception is raised
2131
2132    taskData->saveVec.reset(reset);
2133    taskData->PostRTSCall();
2134    if (result == 0) return TAGGED(0).AsUnsigned();
2135    else return result->Word().AsUnsigned();
2136}
2137
2138struct _entrypts networkingEPT[] =
2139{
2140    { "PolyNetworkGetAddrList",                 (polyRTSFunction)&PolyNetworkGetAddrList},
2141    { "PolyNetworkGetSockTypeList",             (polyRTSFunction)&PolyNetworkGetSockTypeList},
2142    { "PolyNetworkCreateSocket",                (polyRTSFunction)&PolyNetworkCreateSocket},
2143    { "PolyNetworkSetOption",                   (polyRTSFunction)&PolyNetworkSetOption},
2144    { "PolyNetworkGetOption",                   (polyRTSFunction)&PolyNetworkGetOption},
2145    { "PolyNetworkSetLinger",                   (polyRTSFunction)&PolyNetworkSetLinger},
2146    { "PolyNetworkGetLinger",                   (polyRTSFunction)&PolyNetworkGetLinger},
2147    { "PolyNetworkGetPeerName",                 (polyRTSFunction)&PolyNetworkGetPeerName},
2148    { "PolyNetworkGetSockName",                 (polyRTSFunction)&PolyNetworkGetSockName},
2149    { "PolyNetworkBytesAvailable",              (polyRTSFunction)&PolyNetworkBytesAvailable},
2150    { "PolyNetworkGetAtMark",                   (polyRTSFunction)&PolyNetworkGetAtMark},
2151    { "PolyNetworkBind",                        (polyRTSFunction)&PolyNetworkBind},
2152    { "PolyNetworkListen",                      (polyRTSFunction)&PolyNetworkListen},
2153    { "PolyNetworkShutdown",                    (polyRTSFunction)&PolyNetworkShutdown},
2154    { "PolyNetworkCreateSocketPair",            (polyRTSFunction)&PolyNetworkCreateSocketPair},
2155    { "PolyNetworkUnixPathToSockAddr",          (polyRTSFunction)&PolyNetworkUnixPathToSockAddr},
2156    { "PolyNetworkUnixSockAddrToPath",          (polyRTSFunction)&PolyNetworkUnixSockAddrToPath},
2157    { "PolyNetworkGetServByName",               (polyRTSFunction)&PolyNetworkGetServByName},
2158    { "PolyNetworkGetServByNameAndProtocol",    (polyRTSFunction)&PolyNetworkGetServByNameAndProtocol},
2159    { "PolyNetworkGetServByPort",               (polyRTSFunction)&PolyNetworkGetServByPort},
2160    { "PolyNetworkGetServByPortAndProtocol",    (polyRTSFunction)&PolyNetworkGetServByPortAndProtocol},
2161    { "PolyNetworkGetProtByName",               (polyRTSFunction)&PolyNetworkGetProtByName},
2162    { "PolyNetworkGetProtByNo",                 (polyRTSFunction)&PolyNetworkGetProtByNo},
2163    { "PolyNetworkGetHostName",                 (polyRTSFunction)&PolyNetworkGetHostName},
2164    { "PolyNetworkGetNameInfo",                 (polyRTSFunction)&PolyNetworkGetNameInfo},
2165    { "PolyNetworkCloseSocket",                 (polyRTSFunction)&PolyNetworkCloseSocket },
2166    { "PolyNetworkSelect",                      (polyRTSFunction)&PolyNetworkSelect },
2167    { "PolyNetworkGetSocketError",              (polyRTSFunction)&PolyNetworkGetSocketError },
2168    { "PolyNetworkConnect",                     (polyRTSFunction)&PolyNetworkConnect },
2169    { "PolyNetworkAccept",                      (polyRTSFunction)&PolyNetworkAccept },
2170    { "PolyNetworkSend",                        (polyRTSFunction)&PolyNetworkSend },
2171    { "PolyNetworkSendTo",                      (polyRTSFunction)&PolyNetworkSendTo },
2172    { "PolyNetworkReceive",                     (polyRTSFunction)&PolyNetworkReceive },
2173    { "PolyNetworkReceiveFrom",                 (polyRTSFunction)&PolyNetworkReceiveFrom },
2174    { "PolyNetworkGetAddrInfo",                 (polyRTSFunction)&PolyNetworkGetAddrInfo },
2175    { "PolyNetworkGetFamilyFromAddress",        (polyRTSFunction)&PolyNetworkGetFamilyFromAddress },
2176    { "PolyNetworkGetAddressAndPortFromIP4",    (polyRTSFunction)&PolyNetworkGetAddressAndPortFromIP4 },
2177    { "PolyNetworkCreateIP4Address",            (polyRTSFunction)&PolyNetworkCreateIP4Address },
2178    { "PolyNetworkReturnIP4AddressAny",         (polyRTSFunction)&PolyNetworkReturnIP4AddressAny },
2179    { "PolyNetworkGetAddressAndPortFromIP6",    (polyRTSFunction)&PolyNetworkGetAddressAndPortFromIP6 },
2180    { "PolyNetworkCreateIP6Address",            (polyRTSFunction)&PolyNetworkCreateIP6Address },
2181    { "PolyNetworkReturnIP6AddressAny",         (polyRTSFunction)&PolyNetworkReturnIP4AddressAny },
2182    { "PolyNetworkIP6AddressToString",          (polyRTSFunction)&PolyNetworkIP6AddressToString },
2183    { "PolyNetworkStringToIP6Address",          (polyRTSFunction)&PolyNetworkStringToIP6Address },
2184
2185    { NULL, NULL} // End of list.
2186};
2187
2188class Networking: public RtsModule
2189{
2190public:
2191    virtual void Init(void);
2192    virtual void Stop(void);
2193};
2194
2195// Declare this.  It will be automatically added to the table.
2196static Networking networkingModule;
2197
2198void Networking::Init(void)
2199{
2200#if (defined(_WIN32))
2201#define WINSOCK_MAJOR_VERSION   2
2202#define WINSOCK_MINOR_VERSION   2
2203    WSADATA wsaData;
2204    WORD wVersion = MAKEWORD(WINSOCK_MINOR_VERSION, WINSOCK_MAJOR_VERSION);
2205    /* Initialise the system and check that the version it supplied
2206       is the one we requested. */
2207    if(WSAStartup(wVersion, &wsaData) == 0)
2208    {
2209        if (wsaData.wVersion == wVersion)
2210            winsock_init = 1;
2211        else WSACleanup();
2212    }
2213#endif
2214}
2215
2216void Networking::Stop(void)
2217{
2218#if (defined(_WIN32))
2219    if (winsock_init) WSACleanup();
2220    winsock_init = 0;
2221#endif
2222}
2223