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