/*===========================================================================* * * * sflsock.c - * * * * Copyright (c) 1991-2003 iMatix Corporation * * * * ------------------ GPL Licensed Source Code ------------------ * * iMatix makes this software available under the GNU General * * Public License (GPL) license for open source projects. For * * details of the GPL license please see www.gnu.org or read the * * file license.gpl provided in this package. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program in the file 'license.gpl'; if * * not, write to the Free Software Foundation, Inc., 59 Temple * * Place - Suite 330, Boston, MA 02111-1307, USA. * * * * You can also license this software under iMatix's General Terms * * of Business (GTB) for commercial projects. If you have not * * explicitly licensed this software under the iMatix GTB you may * * only use it under the terms of the GNU General Public License. * * * * For more information, send an email to info@imatix.com. * * -------------------------------------------------------------- * *===========================================================================*/ #include "prelude.h" /* Universal header file */ #include "sfllist.h" /* List-management functions */ #include "sflmem.h" /* Memory-allocation functions */ #include "sflsymb.h" /* Symbol-table functions */ #include "sfltok.h" /* Token-handling functions */ #include "sfluid.h" /* User/group functions */ #include "sflcons.h" /* Console i/o functions */ #include "sflfile.h" /* File handling functions */ #include "sflprint.h" /* snprintf functions */ #include "sflsock.h" /* Prototypes for functions */ /* Implementation notes * * These functions work on 16-bit Windows, 32-bit Windows, 32-bit UNIX, * 64-bit UNIX, Digital OpenVMS. The size of a socket handle varies from * 16 bits to 64 bits. All native socket functions define a socket handle * as 'int'. However, we need a fixed-length external representation. So, * we define a type, 'sock_t', which is a qbyte (32 bits). Outside this * package, sockets are always a 'sock_t'. Internally, we always use an * (SOCKET) cast when passing s sock_t to a system function like connect(). * If the system does not support sockets we fake them just a little. * * Modifications Oct 7 1998 for Unix by Grant McDorman to * allow running with the program suid root; it will run as the user until * the socket must be opened; at that time, it will briefly switch to root * and then return to the actual user id. */ /* Global variables */ int ip_portbase = 0; /* Base for created services */ Bool ip_nonblock = TRUE; /* Create non-blocking sockets */ qbyte ip_passive = INADDR_ANY; /* IP address for passive connects */ int ip_sockets = 0; /* Number of open sockets */ /* The connect_error_value holds the last recorded error cause after a */ /* connection attempt. */ static int connect_error_value = 0; char *connect_errlist [] = { /* Corresponding error messages */ "No errors", "System does not support sockets", "Host is not known", "Service or port not known", "Protocol not known", "Connection failed on socket()", "Connection failed on connect()", "Port is already used by another server", "Connection failed on listen()" }; /* Internal functions used to create passive and active connections */ #if (defined (DOES_SOCKETS)) static void prepare_socket (sock_t handle); # if (defined (__WINDOWS__)) static int win_error (int rc); # endif #endif /* ---------------------------------------------------------------------[<]- Function: sock_init Synopsis: Initialise the internet protocol. On most systems this is a null call. On some systems this loads dynamic libraries. Returns 0 0 if everything was okay, else returns SOCKET_ERROR. You should call sock_term() when your program ends. ---------------------------------------------------------------------[>]-*/ int sock_init (void) { #if (defined (__WINDOWS__)) WORD wVersionRequested; /* We really want Winsock 1.1 */ WSADATA wsaData; wVersionRequested = 0x0101; /* ... but we'll take 1.1 */ if (WSAStartup (wVersionRequested, &wsaData) == 0) return (0); else return ((int) SOCKET_ERROR); #elif (defined (__UTYPE_BEOS)) /* BeOS numbers sockets from 0 upwards, but this causes havoc with * programs that expect a BSD-style numbering of 1 or higher. We * force compatibility by creating (and wasting) one socket so that * further socket handles are guaranteed >0. */ create_socket ("tcp"); return (0); #elif (defined (DOES_SOCKETS)) return (0); #elif (defined (FAKE_SOCKETS)) return (0); #else connect_error_value = IP_NOSOCKETS; return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: sock_term Synopsis: Shuts-down the internet protocol. On most systems this is a null call. On some systems this unloads dynamic libraries. Returns -1 if there was an error, or 0 if everything was okay. See sock_init(). ---------------------------------------------------------------------[>]-*/ int sock_term (void) { #if (defined (__WINDOWS__)) WSACleanup (); #endif return (0); } /* ---------------------------------------------------------------------[<]- Function: passive_TCP Synopsis: Creates a passive bound TCP socket for the specified service. Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect_error (). This may be one of: IP_NOSOCKETS Sockets not supported on this system IP_BADSERVICE Service cannot be converted to port number IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot create the passive socket IP_BINDERROR Cannot bind to the port IP_LISTENERROR Cannot listen to port
---------------------------------------------------------------------[>]-*/ sock_t passive_TCP ( const char *service, /* Service name or port as string */ int queue_length /* Queue length for listen() */ ) { ASSERT (service && *service); ASSERT (queue_length > 0); return (passive_socket (service, "tcp", queue_length)); } /* ---------------------------------------------------------------------[<]- Function: passive_UDP Synopsis: Creates a passive UDP socket for the specified service. Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect_error (). This may be one of: IP_NOSOCKETS Sockets not supported on this system IP_BADSERVICE Service cannot be converted to port number IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot create the passive socket IP_BINDERROR Cannot bind to the port
---------------------------------------------------------------------[>]-*/ sock_t passive_UDP ( const char *service /* Service name or port as string */ ) { ASSERT (service && *service); return (passive_socket (service, "udp", 0)); } /* ---------------------------------------------------------------------[<]- Function: passive_socket Synopsis: Creates a passive TCP or UDP socket. This function allows a server program to create a master socket, so that connections can be accepted. Used by the passive_TCP and passive_UDP functions. Returns a socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect_error (). This may be one of: IP_NOSOCKETS Sockets not supported on this system IP_BADSERVICE Service cannot be converted to port number IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot create the passive socket IP_BINDERROR Cannot bind to the port IP_LISTENERROR Cannot listen to port
By default, opens a socket on all available IP addresses. You can open the socket on a specific address, by setting the global variable ip_passive to the address (in network order). This variable is reset to INADDR_ANY after each call to passive_socket or one of the functions that calls it. ---------------------------------------------------------------------[>]-*/ sock_t passive_socket ( const char *service, /* Service name or port as string */ const char *protocol, /* Protocol "tcp" or "udp" */ int queue_length /* Queue length for TCP sockets */ ) { #if (defined (DOES_SOCKETS)) struct servent *pse; /* Service information entry */ struct sockaddr_in sin; /* Internet end-point address */ sock_t handle; /* Socket from socket() call */ ASSERT (service && *service); ASSERT (protocol && *protocol); connect_error_value = IP_NOERROR; /* Assume no errors */ memset ((void *) &sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip_passive; ip_passive = INADDR_ANY; /* Reset passive address */ /* To allow privileged operations, if possible */ set_uid_root (); /* Map service name to port number */ pse = getservbyname (service, protocol); if (pse) sin.sin_port = htons ((dbyte) (ntohs (pse-> s_port) + ip_portbase)); else { sin.sin_port = atoi (service); if (sin.sin_port + ip_portbase > 0) sin.sin_port = htons ((dbyte) (sin.sin_port + ip_portbase)); else { connect_error_value = IP_BADSERVICE; set_uid_user (); return (INVALID_SOCKET); } } handle = create_socket (protocol); if (handle == INVALID_SOCKET) /* Cannot create the socket */ { set_uid_user (); return (INVALID_SOCKET); } /* Bind the socket */ if (bind ((SOCKET) handle, (struct sockaddr *) &sin, sizeof (sin)) == SOCKET_ERROR) { connect_error_value = IP_BINDERROR; set_uid_user (); return (INVALID_SOCKET); /* Cannot bind to port */ } set_uid_user (); /* Specify incoming queue length for stream socket */ if (streq (protocol, "tcp") && listen ((SOCKET) handle, queue_length) == SOCKET_ERROR) { connect_error_value = IP_LISTENERROR; return (INVALID_SOCKET); /* Cannot listen on port */ } return (handle); #elif (defined (FAKE_SOCKETS)) return (1); /* Return dummy handle */ #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: create_socket Synopsis: Creates a TCP or UDP socket. The socket is not connected. To use with TCP services you must bind or connect the socket. You can use the socket with UDP services - e.g. read_UDP () - immediately. Returns a socket number or INVALID_SOCKET, in which case you can get the reason for the error by calling connect_error (). This may be one of: IP_NOSOCKETS Sockets not supported on this system IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot create the socket
---------------------------------------------------------------------[>]-*/ sock_t create_socket ( const char *protocol /* Protocol "tcp" or "udp" */ ) { #if (defined (DOES_SOCKETS)) struct protoent *ppe; /* Protocol information entry */ int # if (!defined (__WINDOWS__)) true_value = 1, /* Boolean value for setsockopt() */ # endif sock_type; /* Type of socket we want */ sock_t handle; /* Socket from socket() call */ ASSERT (protocol && *protocol); connect_error_value = IP_NOERROR; /* Assume no errors */ /* Map protocol name to protocol number */ ppe = getprotobyname (protocol); if (ppe == NULL) /* Cannot get protocol entry */ { connect_error_value = IP_BADPROTOCOL; return (INVALID_SOCKET); } /* Use protocol string to choose a socket type */ if (streq (protocol, "udp")) sock_type = SOCK_DGRAM; else sock_type = SOCK_STREAM; /* Allocate a socket */ handle = (sock_t) socket (AF_INET, sock_type, ppe-> p_proto); if (handle == INVALID_SOCKET) /* Cannot create passive socket */ { connect_error_value = IP_SOCKETERROR; return (INVALID_SOCKET); } # if (!defined (__WINDOWS__)) /* On BSD-socket systems we need to do this to allow the server to * restart on a previously-used socket, without an annoying timeout * of several minutes. With winsock the reuseaddr option lets the * server work with an already-used socket (!), so we don't do it. */ setsockopt ((SOCKET) handle, SOL_SOCKET, SO_REUSEADDR, (char *) &true_value, sizeof (true_value)); # endif prepare_socket (handle); /* Ready socket for use */ ip_sockets++; return (handle); #elif (defined (FAKE_SOCKETS)) return (1); /* Return dummy handle */ #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif } #if (defined (DOES_SOCKETS)) /* ------------------------------------------------------------------------- * prepare_socket -- internal * * Does any system-specific work required to prepare a socket for normal * use. In Windows we have to set the socket to nonblocking mode. In * UNIX we do this if the ip_nonblock flag is set. */ static void prepare_socket (sock_t handle) { #if (defined (__WINDOWS__)) u_long command = ip_nonblock? 1: 0; /* Redirect events and set non-blocking mode */ if (handle != INVALID_SOCKET) ioctlsocket ((SOCKET) handle, FIONBIO, &command); #elif (defined (__UTYPE_BEOS)) setsockopt ((SOCKET) handle, SOL_SOCKET, SO_NONBLOCK, (void *) &ip_nonblock, sizeof (ip_nonblock)); #elif (defined (__UNIX__) || defined (__OS2__)) if (ip_nonblock) fcntl ((SOCKET) handle, F_SETFL, O_NONBLOCK | fcntl (handle, F_GETFL, 0)); #endif } #endif /* ---------------------------------------------------------------------[<]- Function: connect_TCP Synopsis: Creates a TCP socket and connects it to a specified host and service. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect_error (). This may be: IP_NOSOCKETS Sockets not supported on this system IP_BADHOST Host is not known IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot open a socket IP_CONNECTERROR Cannot connect socket
The host name may be a full name, NULL or "" meaning the current host, or a dotted-decimal number. The service may be a defined service, e.g. "echo", or a port number, specified as an ASCII string. See connect_socket() for details. Single-threaded clients may set ip_nonblock to FALSE and block on all read and write operations. They may use select() if they need to be able to time-out during reading/writing. Multi-threaded servers should set ip_nonblock to TRUE, and use select() to multiplex socket access. When ip_nonblock is TRUE, connect calls will return immediately, and the server should use select() to wait until the socket is ready for writing. On some systems (early Linux?), the select() call will fail in this situation. If you compile with -DBLOCKING_CONNECT, connects are done synchronously in all cases. Examples: sock_t handle; handle = connect_TCP ("", "8080"); handle = connect_TCP (NULL, "echo"); handle = connect_TCP ("www.imatix.com", "http"); ---------------------------------------------------------------------[>]-*/ sock_t connect_TCP ( const char *host, /* Host name */ const char *service /* Service name */ ) { ASSERT (service && *service); return (connect_socket (host, /* We have a host name */ service, /* We have a service name */ "tcp", /* Protocol is TCP */ NULL, /* No prepared address */ 3, 0)); /* 3 retries, no waiting */ } /* ---------------------------------------------------------------------[<]- Function: connect_UDP Synopsis: Creates a UDP socket and connects it to a specified host and service. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect_error (). This may be: IP_NOSOCKETS Sockets not supported on this system IP_BADHOST Host is not known IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot open a socket IP_CONNECTERROR Cannot connect socket
The host name may be a full name, NULL or "" meaning the current host, or a dotted-decimal number. The service may be a defined service, e.g. "echo", or a port number, specified as an ASCII string. See connect_socket() for details. Single-threaded clients may set ip_nonblock to FALSE and block on all read and write operations. They may use select() if they need to be able to time-out during reading/writing. Multi-threaded servers should set ip_nonblock to TRUE, and use select() to multiplex socket access. When ip_nonblock is TRUE, connect calls will return immediately, and the server should use select() to wait until the socket is ready for writing. On some systems (early Linux?), the select() call will fail in this situation. If you compile with -DBLOCKING_CONNECT, connects are done synchronously in all cases. Examples: sock_t handle; handle = connect_UDP ("", "7"); handle = connect_UDP (NULL, "echo"); handle = connect_UDP ("imatix.com", "echo"); ---------------------------------------------------------------------[>]-*/ sock_t connect_UDP ( const char *host, /* Host name */ const char *service /* Service name */ ) { ASSERT (service && *service); return (connect_socket (host, /* We have a host name */ service, /* We have a service name */ "udp", /* Protocol is UDP */ NULL, /* No prepared address */ 3, 0)); /* 3 retries, no waiting */ } /* ---------------------------------------------------------------------[<]- Function: connect_TCP_fast Synopsis: Creates a TCP socket and connects it to a specified host/port address. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect_error (). This may be: IP_NOSOCKETS Sockets not supported on this system IP_BADHOST Host is not known IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot open a socket IP_CONNECTERROR Cannot connect socket
This function is faster, if you know the host system address and port, than connect_TCP() because no translation is needed. You can get the host/address structure by calling address_end_point() or get_peer_addr(). See connect_socket() for details. ---------------------------------------------------------------------[>]-*/ sock_t connect_TCP_fast ( const struct sockaddr_in *sin /* Socket address structure */ ) { ASSERT (sin); return (connect_socket (NULL, /* No host name */ NULL, /* No service name */ "tcp", /* Protocol is TCP */ sin, /* We have a prepared address */ 1, 0)); /* 1 retry, no waiting */ } /* ---------------------------------------------------------------------[<]- Function: connect_UDP_fast Synopsis: Creates a UDP socket and connects it to a specified host/port address. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect_error (). This may be: IP_NOSOCKETS Sockets not supported on this system IP_BADHOST Host is not known IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot open a socket IP_CONNECTERROR Cannot connect socket
This function is faster, if you know the host system address and port, than connect_UDP() because no translation is needed. You can get the host/address structure by calling address_end_point() or get_peer_addr(). See connect_socket() for details. ---------------------------------------------------------------------[>]-*/ sock_t connect_UDP_fast ( const struct sockaddr_in *sin /* Socket address structure */ ) { ASSERT (sin); return (connect_socket (NULL, /* No host name */ NULL, /* No service name */ "udp", /* Protocol is UDP */ sin, /* We have a prepared address */ 1, 0)); /* 1 retry, no waiting */ } /* ---------------------------------------------------------------------[<]- Function: connect_socket Synopsis: Makes a connection to a remote TCP or UDP port. This allows a client program to start sending information to a server. Used by the connect_TCP and connect_UDP functions. Returns a socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect_error (). This may be one of: IP_NOSOCKETS Sockets not supported on this system IP_BADHOST Host is not known IP_BADPROTOCOL Cannot understand protocol name IP_SOCKETERROR Cannot open a socket IP_CONNECTERROR Cannot connect socket
Always blocks until the connection has been made; i.e. when this function returns you can start to read and write on the socket. The host name may be a full name, NULL or "" meaning the current host, or a dotted-decimal number. The service may be a defined service, e.g. "echo", or a port number, specified as an ASCII string. Alternatively, both these values may be NULL or "", in which case the function uses the host_addr argument to supply an address. If you want to build the host_addr structure yourself, use build_sockaddr(). Single-threaded clients may set ip_nonblock to FALSE and block on all read and write operations. They may use select() if they need to be able to time-out during reading/writing. Multi-threaded servers should set ip_nonblock to TRUE, and use select() to multiplex socket access. When ip_nonblock is TRUE, connect calls will return immediately, and the server should use select() to wait until the socket is ready for writing. On some systems (early Linux?), the select() call will fail in this situation. If you compile with -DBLOCKING_CONNECT, connects are done synchronously in all cases. Examples: struct sockaddr_in host_addr; sock_t handle; build_sockaddr (&host_addr, 32_bit_host, 16_bit_port); handle = connect_socket (NULL, NULL, "tcp", &host_addr, 3, 0); ---------------------------------------------------------------------[>]-*/ sock_t connect_socket ( const char *host, /* Name of host, "" = localhost */ const char *service, /* Service name or port as string */ const char *protocol, /* Protocol "tcp" or "udp" */ const struct sockaddr_in *host_addr, /* Socket address structure */ int retries_left, /* Max. number of retries */ int retry_delay /* Delay between retries */ ) { #if (defined (DOES_SOCKETS)) struct sockaddr_in sin; /* Internet end-point address */ sock_t handle = 0; /* Created socket */ int rc; /* Return code from call */ Bool old_nonblock; /* Create non-blocking sockets */ connect_error_value = IP_NOERROR; /* Assume no errors */ /* Format sockaddr_in port and hostname, and quit if that failed */ if (service && strused (service)) { ASSERT (protocol && *protocol); if (address_end_point (host, service, protocol, &sin)) return (INVALID_SOCKET); } else { ASSERT (host_addr); sin = *host_addr; /* Fast connect requested */ } /* Connect socket and maybe retry a few times... */ old_nonblock = ip_nonblock; # if (defined (BLOCKING_CONNECT)) ip_nonblock = FALSE; /* Block on this socket */ # endif while (retries_left) { handle = create_socket (protocol); if (handle == INVALID_SOCKET) /* Unable to open a socket */ { ip_nonblock = old_nonblock; return (INVALID_SOCKET); } rc = connect ((SOCKET) handle, (struct sockaddr *) &sin, sizeof (sin)); if (rc == 0) break; /* Connected okay */ else { # if (defined (__WINDOWS__)) if (WSAGetLastError () == WSAEWOULDBLOCK) # else if (errno == EINPROGRESS) # endif break; /* Still connecting, but okay */ } /* Retry if we have any attempts left */ close_socket (handle); if (--retries_left == 0) /* Connection failed */ { connect_error_value = IP_CONNECTERROR; ip_nonblock = old_nonblock; return (INVALID_SOCKET); } sleep (retry_delay); } ip_nonblock = old_nonblock; prepare_socket (handle); /* Set final blocking mode */ return (handle); #elif (defined (FAKE_SOCKETS)) return (1); /* Return dummy handle */ #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: connect_to_peer Synopsis: Connects an unconnected TCP or UDP socket to a peer specified by a sockaddr structure. Returns 0 if the connection succeeded, or SOCKET_ERROR if there was a problem. In the latter case you can get the reason for the error by calling sockmsg(). ---------------------------------------------------------------------[>]-*/ int connect_to_peer ( sock_t handle, /* Socket to connect */ const struct sockaddr_in *sin /* Socket address structure */ ) { #if (defined (DOES_SOCKETS)) int rc; /* Return code from call */ Bool old_nonblock; /* Create non-blocking sockets */ ASSERT (sin); old_nonblock = ip_nonblock; # if (defined (BLOCKING_CONNECT)) ip_nonblock = FALSE; /* Block on this socket */ # endif rc = connect ((SOCKET) handle, (struct sockaddr *) sin, sizeof (*sin)); ip_nonblock = old_nonblock; prepare_socket (handle); /* Set final blocking mode */ # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else connect_error_value = IP_NOSOCKETS; return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: address_end_point Synopsis: Formats an address block (struct sockaddr_in) for the specified host and service (port) information. Returns 0 if okay, SOCKET_ERROR if there was an error, in which case you can call connect_error () to get the reason for the error. This may be one of: IP_NOSOCKETS Sockets not supported on this system IP_BADHOST Host is not known
---------------------------------------------------------------------[>]-*/ int address_end_point ( const char *host, /* Name of host, "" = localhost */ const char *service, /* Service name or port as string */ const char *protocol, /* Protocol "tcp" or "udp" */ struct sockaddr_in *sin /* Block for formatted address */ ) { #if (defined (DOES_SOCKETS)) struct hostent *phe; /* Host information entry */ struct servent *pse; /* Service information entry */ char hostname [MAXHOSTNAMELEN + 1]; /* Name of this system */ int feedback = 0; /* Assume everything works */ ASSERT (service && *service); ASSERT (protocol && *protocol); ASSERT (sin); connect_error_value = IP_NOERROR; /* Assume no errors */ memset ((void *) sin, 0, sizeof (*sin)); sin-> sin_family = AF_INET; /* Map service name to a port number */ pse = getservbyname (service, protocol); if (pse) sin-> sin_port = htons ((short) (ntohs (pse-> s_port))); else sin-> sin_port = htons ((short) (atoi (service))); /* Map host name to IP address, allowing for dotted decimal */ if (host && strused (host)) strcpy (hostname, host); else strcpy (hostname, "127.0.0.1"); /* Check if it's a valid IP address first */ sin-> sin_addr.s_addr = inet_addr (hostname); if (sin-> sin_addr.s_addr == INADDR_NONE) { /* Not a dotted address -- try to translate the name */ phe = gethostbyname (hostname); if (phe) memcpy ((void *) &sin-> sin_addr, phe-> h_addr, phe-> h_length); else { /* Cannot map to host */ connect_error_value = IP_BADHOST; feedback = (int) SOCKET_ERROR; } } return (feedback); #else connect_error_value = IP_NOSOCKETS; return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: build_sockaddr Synopsis: Builds a socket address structure from the specified host and port addresses. Does not return any value except the built structure. ---------------------------------------------------------------------[>]-*/ void build_sockaddr ( struct sockaddr_in *sin, /* Socket address structure */ qbyte host, /* 32-bit host address */ dbyte port /* 16-bit port number */ ) { ASSERT (sin); sin-> sin_family = AF_INET; sin-> sin_addr.s_addr = htonl (host); sin-> sin_port = htons (port); } /* ---------------------------------------------------------------------[<]- Function: socket_localaddr Synopsis: Returns a string containing the local host address for the specified connected socket. The string is formatted as a string "n.n.n.n". Returns the address of a static string or a buffer that is overwritten by each call. If sockets are not supported, or there was an error, returns the loopback address "127.0.0.1". ---------------------------------------------------------------------[>]-*/ char * socket_localaddr ( sock_t handle) { #define NTOA_MAX 16 #if (defined (DOES_SOCKETS)) static char localaddr [NTOA_MAX + 1]; /* xxx.xxx.xxx.xxx */ struct sockaddr_in sin; /* Address of local system */ if (get_sock_addr (handle, &sin, NULL, 0)) return ("127.0.0.1"); else { strncpy (localaddr, inet_ntoa (sin.sin_addr), NTOA_MAX); return (localaddr); } #else return ("127.0.0.1"); #endif } /* ---------------------------------------------------------------------[<]- Function: socket_peeraddr Synopsis: Returns a string containing the peer host address for the specified connected socket. The string is formatted as a string "n.n.n.n". Returns the address of a static string or a buffer that is overwritten by each call. If sockets are not supported, or there was an error, returns the loopback address "127.0.0.1". ---------------------------------------------------------------------[>]-*/ char * socket_peeraddr ( sock_t handle) { #if (defined (DOES_SOCKETS)) static char peeraddr [NTOA_MAX + 1]; /* xxx.xxx.xxx.xxx */ struct sockaddr_in sin; /* Address of peer system */ if (get_peer_addr (handle, &sin, NULL, 0)) return ("127.0.0.1"); else { strncpy (peeraddr, inet_ntoa (sin.sin_addr), NTOA_MAX); return (peeraddr); } #else return ("127.0.0.1"); #endif } /* ---------------------------------------------------------------------[<]- Function: socket_nodelay Synopsis: Disables Nagle's algorithm for the specified socket; use this when you want to ensure that data is sent outwards as fast as possible, and when you are certain that Nagle's algorithm is causing a slowdown in performance. Recommended for HTTP, but not recommended for telnet. Returns 0 if okay, SOCKET_ERROR if there was a problem. ---------------------------------------------------------------------[>]-*/ int socket_nodelay ( sock_t handle) { #if (defined (__WINDOWS__)) int true_value = 1; /* Boolean value for setsockopt() */ return (setsockopt ((SOCKET) handle, IPPROTO_TCP, TCP_NODELAY, (char *) &true_value, sizeof (true_value))); #elif (defined (TCP_NODELAY) && defined (SOL_TCP)) int true_value = 1; /* Boolean value for setsockopt() */ return (setsockopt ((SOCKET) handle, SOL_TCP, TCP_NODELAY, (char *) &true_value, sizeof (true_value))); #else return (0); /* Not applicable to this system */ #endif } /* ---------------------------------------------------------------------[<]- Function: socket_is_alive Synopsis: Returns TRUE if the socket is open. Returns FALSE if the socket is no longer accessible. You can use this function to check that a socket has not been closed by the other party, before doing reading or writing. ---------------------------------------------------------------------[>]-*/ Bool socket_is_alive ( sock_t handle) { #if (defined (__UTYPE_BEOS)) /* BeOS 4.5 does not support the getsockopt() function */ int rc; rc = setsockopt ((SOCKET) handle, SOL_SOCKET, SO_NONBLOCK, (void *) &ip_nonblock, sizeof (ip_nonblock)); return (rc == 0); #elif (defined (DOES_SOCKETS)) int socket_type, rc; argsize_t socket_type_size = sizeof (SOCKET); rc = getsockopt ((SOCKET) handle, SOL_SOCKET, SO_TYPE, (char *) &socket_type, &socket_type_size); return (rc == 0); #else return (FALSE); #endif } /* ---------------------------------------------------------------------[<]- Function: socket_error Synopsis: Returns an errno value for the socket, or 0 if no error was outstanding on the socket. This function is useful if you are handling sockets using the select() function: this may return error indicators on sockets, without precision on the type of error. This function will return the precise error number. Errors like EINPROGRESS, EAGAIN, and EWOULDBLOCK can usually be ignored or handled by retrying. ---------------------------------------------------------------------[>]-*/ int socket_error ( sock_t handle) { #if (defined (DOES_SOCKETS)) # if (defined (__UTYPE_BEOS)) return (errno); # else return (getsockopt ((SOCKET) handle, SOL_SOCKET, SO_ERROR, 0, 0)); # endif #else return (0); #endif } /* ---------------------------------------------------------------------[<]- Function: accept_socket Synopsis: Accepts a connection on a specified master socket. If you do not want to wait on this call, use select() to poll the socket until there is an incoming request, then call accept_socket. Returns the number of the new slave socket, or INVALID_SOCKET if there was an error on the accept call. You can handle errors as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block (treat EWOULDBLOCK in the same way). ---------------------------------------------------------------------[>]-*/ sock_t accept_socket ( sock_t master_socket) { #if (defined (DOES_SOCKETS)) sock_t slave_socket; /* Connected slave socket */ struct sockaddr_in sin; /* Address of connecting party */ argsize_t sin_length; /* Length of address */ connect_error_value = IP_NOERROR; /* Assume no errors */ sin_length = (int) sizeof (sin); slave_socket = accept ((SOCKET) master_socket, (struct sockaddr *) &sin, &sin_length); /* On non-Windows systems, accept returns -1 in case of error, which */ /* is the same as INVALID_SOCKET. */ # if (defined (__WINDOWS__)) if (slave_socket == INVALID_SOCKET) { int sock_errno = WSAGetLastError (); if (sock_errno == WSAEWOULDBLOCK || sock_errno == WSAEINPROGRESS) errno = EAGAIN; } # endif if (slave_socket != INVALID_SOCKET) { prepare_socket (slave_socket); ip_sockets++; } return (slave_socket); #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: connect_error Synopsis: Returns the last error code from one of the connection functions. For portability in a multithreaded environment, call immediately after the call to the connection function. ---------------------------------------------------------------------[>]-*/ int connect_error (void) { return (connect_error_value); } /* ---------------------------------------------------------------------[<]- Function: get_sock_addr Synopsis: Builds an address block (struct sockaddr_in) for the local end of the specified connected socket. Returns 0 if okay, SOCKET_ERROR if there was an error. If the name argument is not null, looks-up the host name and returns it. The name is truncated to namesize characters, including a trailing null character. ---------------------------------------------------------------------[>]-*/ int get_sock_addr ( sock_t handle, /* Socket to get address for */ struct sockaddr_in *sin, /* Block for formatted address */ char *name, /* Buffer for host name, or NULL */ int namesize /* Size of host name buffer */ ) { #if (defined (DOES_SOCKETS)) int rc; /* Return code from call */ struct hostent *phe; /* Host information entry */ argsize_t sin_length; /* Length of address */ ASSERT (sin); /* Get address for local connected socket */ sin_length = sizeof (struct sockaddr_in); rc = getsockname ((SOCKET) handle, (struct sockaddr *) sin, &sin_length); /* Translate into host name string, only if wanted */ if (name != NULL && rc == 0) { phe = gethostbyaddr ((char *) &sin-> sin_addr, sizeof (sin-> sin_addr), AF_INET); if (phe) { strncpy (name, phe-> h_name, namesize); name [namesize - 1] = '\0'; } } # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: get_peer_addr Synopsis: Builds an address block (struct sockaddr_in) for the remote end of the specified connected socket. Returns 0 if okay, SOCKET_ERROR if there was an error. If the name argument is not null, looks-up the host name and returns it. The name is truncated to namesize characters, including a trailing null character. ---------------------------------------------------------------------[>]-*/ int get_peer_addr ( sock_t handle, /* Socket to get address for */ struct sockaddr_in *sin, /* Block for formatted address */ char *name, /* Buffer for host name, or NULL */ int namesize /* Size of host name buffer */ ) { #if (defined (DOES_SOCKETS)) int rc; /* Return code from call */ struct hostent *phe; /* Host information entry */ argsize_t sin_length; /* Length of address */ ASSERT (sin); /* Get address for connected socket peer */ sin_length = sizeof (struct sockaddr_in); rc = getpeername ((SOCKET) handle, (struct sockaddr *) sin, &sin_length); /* Translate into host name string, only if wanted */ if (name != NULL && rc == 0) { phe = gethostbyaddr ((char *) &sin-> sin_addr, sizeof (sin-> sin_addr), AF_INET); if (phe) { strncpy (name, phe-> h_name, namesize); name [namesize - 1] = '\0'; } } # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: read_TCP Synopsis: Reads data from the socket. On UNIX, VMS, OS/2, passes through to the standard read function; some other systems have particular ways of accessing sockets. If there is an error on the read this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block, and EPIPE or ECONNRESET which indicate that the socket was closed at the other end. Treat EWOULDBLOCK as EAGAIN. ---------------------------------------------------------------------[>]-*/ int read_TCP ( sock_t handle, /* Socket handle */ void *buffer, /* Buffer to receive data */ size_t length /* Maximum amount of data to read */ ) { #if (defined (DOES_SOCKETS)) # if (defined (__UTYPE_BEOS)) return (recv ((SOCKET) handle, buffer, length, 0)); # elif (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__)) return (read ((SOCKET) handle, buffer, length)); # elif (defined (__WINDOWS__)) int rc; /* Return code from call */ ASSERT (buffer); rc = recv ((SOCKET) handle, buffer, length, 0); return (win_error (rc)); # else # error "No code for function body." # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } #if (defined (__WINDOWS__)) /* ------------------------------------------------------------------------- * win_error -- internal * * For Winsockets only: fetches real error code and sticks it in errno, * if the return code from the last call was SOCKET_ERROR. Returns rc. */ static int win_error (int rc) { if (rc == (int) SOCKET_ERROR) errno = winsock_last_error (); return (rc); } #endif /* ---------------------------------------------------------------------[<]- Function: write_TCP Synopsis: Writes data to the socket. On UNIX, VMS, OS/2, calls the standard write function; some other systems have particular ways of accessing sockets. If there is an error on the write this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block, and EPIPE or ECONNRESET which indicate that the socket was closed at the other end. Treat EWOULDBLOCK as EAGAIN. ---------------------------------------------------------------------[>]-*/ int write_TCP ( sock_t handle, /* Socket handle */ const void *buffer, /* Buffer containing data */ size_t length /* Amount of data to write */ ) { #if (defined (DOES_SOCKETS)) # if (defined (__UTYPE_BEOS)) return (send ((SOCKET) handle, buffer, length, 0)); # elif (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__)) return (write ((SOCKET) handle, buffer, length)); # elif (defined (__WINDOWS__)) int rc; /* Return code from call */ ASSERT (buffer); rc = send ((SOCKET) handle, buffer, length, 0); return (win_error (rc)); # else # error "No code for function body." # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: read_UDP Synopsis: Reads data from a connected or unconnected UDP socket. To prepare a connected UDP socket you call connect_UDP (). This makes a connection to a specific port on a specific host, and returns a socket handle. When you call this function with a null value for the address argument, it assumes you are using a connected UDP socket. To prepare an unconnected UDP socket, call create_socket () with the string "udp" as argument. This returns a sock_t handle that you can use in this function. If you use an unconnected UDP socket you must provide an address structure. The function places the remote host and port in this structure. This lets you reply using write_UDP (). Generally a server can use unconnected sockets, and a client can use connected sockets. You can also format an address for a specific host and port using the address_end_point () function. If there is an error on the read this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block. Treat EWOULDBLOCK as EAGAIN. ---------------------------------------------------------------------[>]-*/ int read_UDP ( sock_t handle, /* Socket handle */ void *buffer, /* Buffer to receive data */ size_t length, /* Maximum amount of data to read */ const struct sockaddr_in *sin /* Block for address, or null */ ) { #if (defined (DOES_SOCKETS)) argsize_t sin_length; /* Length of address */ int flags = 0, /* Flags for call */ rc; /* Return code from call */ ASSERT (buffer); ASSERT (sin); sin_length = (int) sizeof (*sin); if (sin) /* Read from unconnected UDP socket; we accept the address of the */ /* sending party in the sin argument. */ rc = recvfrom ((SOCKET) handle, buffer, length, flags, (struct sockaddr *) sin, &sin_length); else /* Read from a connected UDP socket; we don't need to get the */ /* address, since we already know it. */ rc = recv ((SOCKET) handle, buffer, length, flags); # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: write_UDP Synopsis: Writes data to a connected or unconnected UDP socket. To prepare a connected UDP socket you call connect_UDP (). This makes a connection to a specific port on a specific host, and returns a socket handle. When you call this function with a null value for the address argument, it assumes you are using a connected UDP socket. To prepare an unconnected UDP socket, call create_socket () with the string "udp" as argument. This returns a sock_t handle that you can use in this function. If you use an unconnected UDP socket you must provide an address structure containing a valid host and port. You can get this information from a read_UDP () or through address_end_point (). If there is an error on the write this function returns SOCKET_ERROR. You can handle errors as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block. Treat EWOULDBLOCK as EAGAIN. ---------------------------------------------------------------------[>]-*/ int write_UDP ( sock_t handle, /* Socket handle */ const void *buffer, /* Buffer containing data */ size_t length, /* Amount of data to write */ const struct sockaddr_in *sin /* Address to send to, or null */ ) { #if (defined (DOES_SOCKETS)) int sin_length, /* Length of address */ flags = 0, /* Flags for call */ rc; /* Return code from call */ ASSERT (buffer); ASSERT (sin); sin_length = (int) sizeof (*sin); if (sin) /* Write to unconnected UDP socket; we provide the address of */ /* the receiving party in the sin argument. */ rc = sendto ((SOCKET) handle, buffer, length, flags, (struct sockaddr *) sin, sin_length); else /* Write to a connected UDP socket; we don't need to supply */ /* the address, since we already know it. */ rc = send ((SOCKET) handle, buffer, length, flags); # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: close_socket Synopsis: Closes the socket. On UNIX, VMS, OS/2 calls the standard close function; some other systems have particular ways of accessing sockets. If there is an error on the close this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block. Treat EWOULDBLOCK as EAGAIN. ---------------------------------------------------------------------[>]-*/ int close_socket ( sock_t handle /* Socket handle */ ) { #if (defined (DOES_SOCKETS)) # if (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__)) if (!socket_is_alive (handle)) return (0); ip_sockets--; shutdown (handle, 2); return (close ((SOCKET) handle)); # elif (defined (__WINDOWS__)) int rc; if (!socket_is_alive (handle)) return (0); ip_sockets--; shutdown ((SOCKET) handle, 2); rc = closesocket ((SOCKET) handle); return (win_error (rc)); # else # error "No code for function body." # endif #elif (defined (FAKE_SOCKETS)) return (0); /* Okay, closed */ #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif } /* ---------------------------------------------------------------------[<]- Function: sock_select Synopsis: Performs the standard select() call. Use this in preference to select(), as some systems may not be 100% compatible with BSD sockets, Uses the same arguments as the select() call, and gives the same return codes. If sockets are not supported, always returns 0. ---------------------------------------------------------------------[>]-*/ int sock_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { #if (defined (DOES_SOCKETS)) int rc = 0; /* Return code from select() */ ASSERT (timeout); # if (defined (__UTYPE_BEOS)) /* BeOS only supports the readfds argument */ rc = select (nfds, FD_SETTYPE readfds, NULL, NULL, timeout); if (rc == -1) coprintf ("Error after select(): %s", strerror (errno)); return (rc); # elif (defined (WIN32)) /* Windows occasionally aborts during the select call... */ __try { rc = select (nfds, FD_SETTYPE readfds, FD_SETTYPE writefds, FD_SETTYPE errorfds, timeout); } __except (1) { coprintf ("select() aborted - arguments: %d, %p, %p, %p, %p", nfds, FD_SETTYPE readfds, FD_SETTYPE writefds, FD_SETTYPE errorfds, timeout); } return (rc); # else rc = select (nfds, FD_SETTYPE readfds, FD_SETTYPE writefds, FD_SETTYPE errorfds, timeout); return (rc); # endif #else return (0); #endif } /* ---------------------------------------------------------------------[<]- Function: get_hostname Synopsis: Returns a string containing the local hostname. The returned string is in a static area. Only performs the local hostname lookup one time; the returned value is cached for later repeated calls to this function. If sockets are not supported, returns the value "localhost". ---------------------------------------------------------------------[>]-*/ char * get_hostname (void) { #if (defined (DOES_SOCKETS)) static char host_name [LINE_MAX + 1] = ""; if (strnull (host_name)) if (gethostname (host_name, LINE_MAX)) strcpy (host_name, "localhost"); return (host_name); #else return ("localhost"); #endif } /* ---------------------------------------------------------------------[<]- Function: get_hostaddr Synopsis: Returns the current the host address as a 4-byte value in host format (not network format). Returns 0x7f000001 (loopback) if sockets are not supported or there was an error getting the current host IP address. If there are several IP addresses on the system, returns one arbitrary address. ---------------------------------------------------------------------[>]-*/ qbyte get_hostaddr (void) { #if (defined (DOES_SOCKETS)) struct hostent *phe; /* Host information entry */ phe = gethostbyname (get_hostname ()); if (phe) return (*(qbyte *) (phe-> h_addr_list [0])); else return (htonl (SOCKET_LOOPBACK)); #else return (htonl (SOCKET_LOOPBACK)); #endif } /* ---------------------------------------------------------------------[<]- Function: get_hostaddrs Synopsis: Returns a table of all host IP addresses. The table ends in a zero address. Each address is a 4-byte value in host format. Returns NULL if there was an error. If sockets are not supported, returns a table with the loopback address (127.0.0.1) and a null address. The caller must free the table using mem_free() when finished using it. ---------------------------------------------------------------------[>]-*/ qbyte * get_hostaddrs (void) { #if (defined (DOES_SOCKETS)) int addr_count; /* How many addresses do we have */ qbyte *addr_table; /* Where we store the addresses */ struct hostent *phe; /* Host information entry */ if ((phe = gethostbyname (get_hostname ())) == NULL) return (NULL); /* Count the addresses */ for (addr_count = 0; phe-> h_addr_list [addr_count]; addr_count++); /* Allocate a table; socket addresses are 4 bytes */ addr_table = mem_alloc (4 * (addr_count + 1)); /* Store the addresses */ for (addr_count = 0; phe-> h_addr_list [addr_count]; addr_count++) addr_table [addr_count] = *(qbyte *) (phe-> h_addr_list [addr_count]); addr_table [addr_count] = 0; return (addr_table); #else qbyte *addr_table; /* Where we store the addresses */ addr_table = mem_alloc (8); /* Addresses are 4 bytes */ addr_table [0] = htonl (SOCKET_LOOPBACK); addr_table [1] = 0; return (addr_table); #endif } /* ---------------------------------------------------------------------[<]- Function: sock_ntoa Synopsis: Converts an IP address in network order to a string in dotted format. The string is stored in a statically-allocated buffer that is overwritten by each call. ---------------------------------------------------------------------[>]-*/ char * sock_ntoa (qbyte address) { static char string [16]; /* xxx.xxx.xxx.xxx */ byte *part; /* Network order is high-low so we can address the bytes in order */ part = (byte *) &address; snprintf (string, sizeof (string), "%d.%d.%d.%d", part [0], part [1], part [2], part [3]); return (string); } /* ---------------------------------------------------------------------[<]- Function: sockmsg Synopsis: Returns a string describing the cause of the last fatal error to occur a socket. Should be called directly after a socket i/o operation; if you do other i/o operations or allow other threads to proceed in the meantime, the returned string may be incorrect. ---------------------------------------------------------------------[>]-*/ const char * sockmsg (void) { #if (defined (__WINDOWS__)) char *message; switch (WSAGetLastError ()) { case WSAEINTR: message = "WSAEINTR"; break; case WSAEBADF: message = "WSAEBADF"; break; case WSAEACCES: message = "WSAEACCES"; break; case WSAEFAULT: message = "WSAEFAULT"; break; case WSAEINVAL: message = "WSAEINVAL"; break; case WSAEMFILE: message = "WSAEMFILE"; break; case WSAEWOULDBLOCK: message = "WSAEWOULDBLOCK"; break; case WSAEINPROGRESS: message = "WSAEINPROGRESS"; break; case WSAEALREADY: message = "WSAEALREADY"; break; case WSAENOTSOCK: message = "WSAENOTSOCK"; break; case WSAEDESTADDRREQ: message = "WSAEDESTADDRREQ"; break; case WSAEMSGSIZE: message = "WSAEMSGSIZE"; break; case WSAEPROTOTYPE: message = "WSAEPROTOTYPE"; break; case WSAENOPROTOOPT: message = "WSAENOPROTOOPT"; break; case WSAEPROTONOSUPPORT: message = "WSAEPROTONOSUPPORT"; break; case WSAESOCKTNOSUPPORT: message = "WSAESOCKTNOSUPPORT"; break; case WSAEOPNOTSUPP: message = "WSAEOPNOTSUPP"; break; case WSAEPFNOSUPPORT: message = "WSAEPFNOSUPPORT"; break; case WSAEAFNOSUPPORT: message = "WSAEAFNOSUPPORT"; break; case WSAEADDRINUSE: message = "WSAEADDRINUSE"; break; case WSAEADDRNOTAVAIL: message = "WSAEADDRNOTAVAIL"; break; case WSAENETDOWN: message = "WSAENETDOWN"; break; case WSAENETUNREACH: message = "WSAENETUNREACH"; break; case WSAENETRESET: message = "WSAENETRESET"; break; case WSAECONNABORTED: message = "WSAECONNABORTED"; break; case WSAECONNRESET: message = "WSAECONNRESET"; break; case WSAENOBUFS: message = "WSAENOBUFS"; break; case WSAEISCONN: message = "WSAEISCONN"; break; case WSAENOTCONN: message = "WSAENOTCONN"; break; case WSAESHUTDOWN: message = "WSAESHUTDOWN"; break; case WSAETOOMANYREFS: message = "WSAETOOMANYREFS"; break; case WSAETIMEDOUT: message = "WSAETIMEDOUT"; break; case WSAECONNREFUSED: message = "WSAECONNREFUSED"; break; case WSAELOOP: message = "WSAELOOP"; break; case WSAENAMETOOLONG: message = "WSAENAMETOOLONG"; break; case WSAEHOSTDOWN: message = "WSAEHOSTDOWN"; break; case WSAEHOSTUNREACH: message = "WSAEHOSTUNREACH"; break; case WSAENOTEMPTY: message = "WSAENOTEMPTY"; break; case WSAEPROCLIM: message = "WSAEPROCLIM"; break; case WSAEUSERS: message = "WSAEUSERS"; break; case WSAEDQUOT: message = "WSAEDQUOT"; break; case WSAESTALE: message = "WSAESTALE"; break; case WSAEREMOTE: message = "WSAEREMOTE"; break; case WSAEDISCON: message = "WSAEDISCON"; break; case WSASYSNOTREADY: message = "WSASYSNOTREADY"; break; case WSAVERNOTSUPPORTED: message = "WSAVERNOTSUPPORTED"; break; case WSANOTINITIALISED: message = "WSANOTINITIALISED"; break; default: message = "No error"; } return (message); #else return (strerror (errno)); #endif } #if (defined (__WINDOWS__)) /* ---------------------------------------------------------------------[<]- Function: winsock_last_error Synopsis: Convert a winsock error into a errno value. ---------------------------------------------------------------------[>]-*/ int winsock_last_error (void) { int error = 0; switch (WSAGetLastError ()) { case WSAEINTR: error = EINTR; break; case WSAEBADF: error = EBADF; break; case WSAEWOULDBLOCK: error = EAGAIN; break; case WSAEINPROGRESS: error = EAGAIN; break; case WSAENETDOWN: error = EAGAIN; break; case WSAECONNRESET: error = ECONNRESET; break; case WSAECONNABORTED: error = EPIPE; break; case WSAESHUTDOWN: error = ECONNRESET; break; case WSAEINVAL: error = EPIPE; break; # if defined (WIN32) default: error = GetLastError (); # else default: error = errno; # endif } return (error); } #endif /* ---------------------------------------------------------------------[<]- Function: socket_is_permitted Synopsis: Compares the specified address with a mask and returns TRUE if the address matches the mask, or FALSE if it does not. The address is formatted as a string "xxx.xxx.xxx.xxx". The mask is formatted as zero or more patterns, delimited by whitespace or commas. A pattern is an address string, with zero or more of the last components replaced by '*'. The pattern may also be prefixed by '!' to indicate exclusion. This is an example of a mask: "127.0.0.1, 253.34.*, !253.35.*". This mask allows all addresses: "*". To get the string address for a remote socket, use socket_peer_address(). ---------------------------------------------------------------------[>]-*/ Bool socket_is_permitted (const char *address, const char *mask) { char *addrptr, /* Pointer into address */ *maskptr; /* Pointer into mask */ Bool negate, /* If !pattern */ feedback = FALSE; /* False unless matched */ ASSERT (address); ASSERT (mask); maskptr = (char *) mask; while (*maskptr) { while (isspace (*maskptr) || *maskptr == ',') maskptr++; /* Get negation if necessary */ if (*maskptr == '!') { negate = TRUE; maskptr++; } else negate = FALSE; /* Compare pattern with address up to the end of the pattern */ for (addrptr = (char *) address; *addrptr; addrptr++) { if (*maskptr == '*') /* Matched address up to * */ return (!negate); /* So either accepted or failed */ else if (*maskptr == '\0') /* Did not match address */ return (negate); /* so fail unless negated */ else if (*addrptr != *maskptr) /* Some difference */ break; /* so stop comparing */ maskptr++; } if (*addrptr == '\0' /* Matched exact address? */ && (*maskptr == '\0' || isspace (*maskptr) || *maskptr == ',')) return (!negate); /* Either accepted or failed */ until (*maskptr == '\0' || isspace (*maskptr) || *maskptr == ',') maskptr++; /* Skip to end of this pattern */ } return (feedback); } /* ---------------------------------------------------------------------[<]- Function: get_host_file Synopsis: returns the full path name of the host lookup file, if provided by the OS, and found. If not found, returns "hosts". The returned string is held in a static area of memory that may be overwritten by each call. ---------------------------------------------------------------------[>]-*/ char * get_host_file (void) { #if (defined (WIN32)) static OSVERSIONINFO version_info; static char name [LINE_MAX + 1]; strclr (name); GetWindowsDirectory (name, LINE_MAX); version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&version_info)) /* On Windows NT the hosts file is well-hidden; on Win95 it's * more visible */ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) strcat (name, "\\system32\\drivers\\etc\\hosts"); else strcat (name, "\\hosts"); return (name); #elif (defined (__UNIX__)) return ("/etc/hosts"); #elif (defined (__VMS__)) return ("/etc/hosts"); /* Not correct -- needs more work */ #elif (defined (__OS2__)) /* Under OS/2 the hosts information is stored in the "hosts" file which * is in the directory pointed at by the %ETC% environment variable. * If that environment variable is not set, then TCP/IP support is not * properly installed. In that instance we return "/mptn/etc/hosts" * (the likely value on OS/2 Warp 3 Connect and OS/2 Warp 4) and hope * for the best. */ /* A static array is used only because the other versions use a static * array. If the resulting file name will not fit in the space allowed * then "/mtpn/etc/hosts" is used as before. */ static char name [LINE_MAX + 1]; char *etcenv = NULL; etcenv = getenv ("ETC"); if (etcenv != NULL && strlen (etcenv) < (LINE_MAX - 6)) { /* We've already checked it will all fit. */ strcpy (name, etcenv); strcat (name, "/hosts"); return (name); } else return ("/mptn/etc/hosts"); #else return ("hosts"); #endif } /* ---------------------------------------------------------------------[<]- Function: get_name_server Synopsis: gets the addresses of the DNS servers defined in the TCP/IP configuration. The addresses are returned in a user-provided struct sockaddr_in array. The maximum number of addresses in this array is supplied as the ns_max argument. Return the number of address found. ---------------------------------------------------------------------[>]-*/ int get_name_server (struct sockaddr_in *ns_address, int ns_max) { int ns_count = 0; /* Number of servers that we found */ #if (defined (WIN32)) static OSVERSIONINFO version_info; HKEY hkey; /* Handle to returned reg. key */ static char registry_value [LINE_MAX + 1]; /* DNS server info from registry */ long size = LINE_MAX; /* Max. size of returned value */ DWORD type; char *key, **address_list = NULL; int address_nbr; /* Look in registry; this sometimes works, but not always */ version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&version_info) && version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; else key = "SYSTEM\\CurrentControlSet\\Services\\Vxd\\Mstcp\\Parameters"; if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS && RegQueryValueEx (hkey, "NameServer", NULL, (LPDWORD) &type, (LPBYTE) registry_value, (LPDWORD) &size) == ERROR_SUCCESS) { address_list = tok_split (registry_value); for (address_nbr = 0; address_list [address_nbr]; address_nbr++) { if (ns_count >= ns_max) break; ns_address [ns_count].sin_family = AF_INET; ns_address [ns_count].sin_port = htons (DNS_PORT); ns_address [ns_count].sin_addr.s_addr = inet_addr (address_list [address_nbr]); ns_count++; } tok_free (address_list); RegCloseKey (hkey); } #elif (defined (__UNIX__)) static char buffer [LINE_MAX + 1], address [16]; FILE *resolver; int rc; resolver = file_open ("/etc/resolv.conf", 'r'); if (resolver) { while (file_read (resolver, buffer)) { rc = sscanf (buffer, "nameserver %s", address); if (rc > 0 && rc != EOF) { if (ns_count >= ns_max) break; ns_address [ns_count].sin_family = AF_INET; ns_address [ns_count].sin_port = htons (DNS_PORT); ns_address [ns_count].sin_addr.s_addr = inet_addr (address); ns_count++; } } file_close (resolver); } #elif (defined (__OS2__)) static char buffer [LINE_MAX + 1], address [16]; char *etcenv = NULL, *filename = NULL; FILE *resolver = NULL; int rc; /* Under OS/2 the file controlling the resolver is stored in the */ /* directory pointed at by the ETC environment variable. It is called */ /* resolv2 or resolv (I *think* that is the order of preference), so we */ /* try those two file names in that order. */ /* If the ETC environment variable is not set we try the /mptn/etc */ /* directory since that is a likely default location for it. */ etcenv = getenv ("ETC"); if (etcenv) { filename = mem_alloc (strlen(etcenv) + 10); if (!filename) return 0; /* Cannot allocate memory for filename */ strcpy (filename, etcenv); strcat (filename, "/resolv2"); resolver = file_open (filename, 'r'); if (! resolver) { /* Not available under that filename, let's try the other one */ strcpy (filename, etcenv); strcat (filename, "/resolv"); resolver = file_open (filename, 'r'); } mem_free (filename); } else { /* No environment variable around, try using the defaults */ resolver = file_open ("/mptn/etc/resolv2", 'r'); if (! resolver) resolver = file_open ("/mptn/etc/resolv", 'r'); } if (resolver) { while (file_read (resolver, buffer)) { rc = sscanf (buffer, "nameserver %s", address); if (rc > 0 && rc != EOF) { if (ns_count >= ns_max) break; ns_address [ns_count].sin_family = AF_INET; ns_address [ns_count].sin_port = htons (DNS_PORT); ns_address [ns_count].sin_addr.s_addr = inet_addr (address); ns_count++; } } file_close (resolver); } #endif return (ns_count); } /* We fake some BSD functions that are missing on some systems */ #if (defined (__VMS__) || defined (__UTYPE_BEOS)) struct protoent *getprotobyname (const char *protocol) { static struct protoent proto = { NULL, NULL, 0 }; if (streq (protocol, "udp")) proto.p_proto = IPPROTO_UDP; else if (streq (protocol, "tcp")) proto.p_proto = IPPROTO_TCP; else if (streq (protocol, "icmp")) proto.p_proto = IPPROTO_ICMP; return (&proto); } struct protoent *getprotobynumber (int protocol) { static struct protoent proto = { NULL, NULL, 0 }; if (protocol == IPPROTO_UDP) proto.p_name = "udp"; else if (protocol == IPPROTO_TCP) proto.p_name = "tcp"; else if (protocol == IPPROTO_ICMP) proto.p_name = "icmp"; return (&proto); } struct servent *getservbyport (int port, const char *protocol) { static char fmtnumber [9]; static struct servent service = { NULL, NULL, 0, NULL }; sprintf (fmtnumber, "%d", port); service.s_name = fmtnumber; return (&service); } #endif