/src/sflsock.c
C | 2126 lines | 1216 code | 252 blank | 658 comment | 188 complexity | 4a9dbb63d0825e46972cab6827944732 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause
- /*===========================================================================*
- * *
- * sflsock.c - Socket handling functions *
- * *
- * Copyright (c) 1991-2010 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 a 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 <grant@isgtec.com> 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
- 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) || 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:
- <TABLE>
- 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
- </TABLE>
- ---------------------------------------------------------------------[>]-*/
- 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:
- <TABLE>
- 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
- </TABLE>
- ---------------------------------------------------------------------[>]-*/
- 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:
- <TABLE>
- 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
- </TABLE>
- 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:
- <TABLE>
- IP_NOSOCKETS Sockets not supported on this system
- IP_BADPROTOCOL Cannot understand protocol name
- IP_SOCKETERROR Cannot create the socket
- </TABLE>
- ---------------------------------------------------------------------[>]-*/
- 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:
- <TABLE>
- 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
- </TABLE>
- 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:
- <TABLE>
- 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
- </TABLE>
- 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:
- <TABLE>
- 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
- </TABLE>
- 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:
- <TABLE>
- 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
- </TABLE>
- 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:
- <TABLE>
- 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
- </TABLE>
- 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:
- <TABLE>
- IP_NOSOCKETS Sockets not supported on this system
- IP_BADHOST Host is not known
- </TABLE>
- ---------------------------------------------------------------------[>]-*/
- 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 == (unsigned) INADDR_NONE)
- {
- /* Not a dotted address -- try to translate the name */
- phe = (void *) 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)));
- #elif (defined (TCP_NODELAY) && defined (IPPROTO_TCP))
- int
- true_value = 1; /* Boolean value for setsockopt() */
- return (setsockopt ((SOCKET) handle, IPPROTO_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
- rc;
- rc = socket_error (handle);
- if (rc == 0
- || rc == EINPROGRESS
- || rc == EAGAIN
- || rc == EWOULDBLOCK)
- return TRUE;
- else
- return FALSE;
- #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
- int
- socket_error,
- rc;
- argsize_t
- error_size = sizeof (socket_error);
- rc = getsockopt ((SOCKET) handle, SOL_SOCKET, SO_ERROR,
- (char *) &socket_error, &error_size);
- if (rc)
- errno = rc;
- else
- errno = socket_error;
- return (errno);
- # 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 = (void *)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 = (void *)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."
- return ((int) SOCKET_ERROR); /* Sockets not supported */
- # 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."
- return ((int) SOCKET_ERROR); /* Sockets not supported */
- # 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);
- 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);
- 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 (FAKE_SOCKETS))
- return (0); /* Okay, closed */
- #elif (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."
- return ((int) SOCKET_ERROR); /* Sockets not supported */
- # endif
- #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. This function
- will return 0 (nothing done, no error) if the operating system reports
- the select was interrupted during execution (eg, by a signal). This is
- done to distinguish it from other error conditions (like invalid file
- handles which require more drastic handling). Programs using this
- function should not rely on this function having run until the timeout
- expires if it returns 0. (Instead check to see if the time you were
- waiting for has arrived.)
- ---------------------------------------------------------------------[>]-*/
- 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); Removed: allow no 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 {
- if (readfds->fd_count == 0
- && writefds->fd_count == 0
- && errorfds->fd_count == 0)
- return 0;
- else
- 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);
- if (rc < 0 && errno == EINTR)
- return 0;
- else
- 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
- network order. 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 = (void *)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 = (void *)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);
- }