/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
Large files files are truncated, but you can click here to view the full file
- /*===========================================================================*
- * *
- * 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)
- {
- …
Large files files are truncated, but you can click here to view the full file