Page MenuHomePhabricator (Chris)

sflsock.c
No OneTemporary

Authored By
Unknown
Size
76 KB
Referenced Files
None
Subscribers
None

sflsock.c

/* ----------------------------------------------------------------<Prolog>-
Name: sflsock.c
Title: TCP/IP, UDP/IP socket functions
Package: Standard Function Library (SFL)
Written: 1996/02/03 iMatix SFL project team <sfl@imatix.com>
Revised: 2000/01/19
Notes: The BeOS port of sflsock is incomplete: at last testing,
BeOS 4.5 gave consistent errors on its select() function,
apparently due to timer interrupt signals.
Copyright: Copyright (c) 1996-2000 iMatix Corporation
License: This is free software; you can redistribute it and/or modify
it under the terms of the SFL License Agreement as provided
in the file LICENSE.TXT. This software is distributed in
the hope that it will be useful, but without any warranty.
------------------------------------------------------------------</Prolog>-*/
#include "prelude.h" /* Universal header file */
#include "sfllist.h" /* List-management functions */
#include "sflmem.h" /* Memory-allocation functions */
#include "sflsymb.h" /* Symbol-table functions */
#include "sfltok.h" /* Token-handling functions */
#include "sfluid.h" /* User/group functions */
#include "sflcons.h" /* Console i/o functions */
#include "sflfile.h" /* File handling functions */
#include "sflprint.h" /* snprintf functions */
#include "sflsock.h" /* Prototypes for functions */
/* Implementation notes
*
* These functions work on 16-bit Windows, 32-bit Windows, 32-bit UNIX,
* 64-bit UNIX, Digital OpenVMS. The size of a socket handle varies from
* 16 bits to 64 bits. All native socket functions define a socket handle
* as 'int'. However, we need a fixed-length external representation. So,
* we define a type, 'sock_t', which is a qbyte (32 bits). Outside this
* package, sockets are always a 'sock_t'. Internally, we always use an
* (SOCKET) cast when passing s sock_t to a system function like connect().
* If the system does not support sockets we fake them just a little.
*
* Modifications Oct 7 1998 for Unix by Grant McDorman <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
0 if everything was okay, else returns SOCKET_ERROR. You should call
sock_term() when your program ends.
---------------------------------------------------------------------[>]-*/
int
sock_init (void)
{
#if (defined (__WINDOWS__))
WORD
wVersionRequested; /* We really want Winsock 1.1 */
WSADATA
wsaData;
wVersionRequested = 0x0101; /* ... but we'll take 1.1 */
if (WSAStartup (wVersionRequested, &wsaData) == 0)
return (0);
else
return ((int) SOCKET_ERROR);
#elif (defined (__UTYPE_BEOS))
/* BeOS numbers sockets from 0 upwards, but this causes havoc with
* programs that expect a BSD-style numbering of 1 or higher. We
* force compatibility by creating (and wasting) one socket so that
* further socket handles are guaranteed >0.
*/
create_socket ("tcp");
return (0);
#elif (defined (DOES_SOCKETS))
return (0);
#elif (defined (FAKE_SOCKETS))
return (0);
#else
connect_error_value = IP_NOSOCKETS;
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: sock_term
Synopsis: Shuts-down the internet protocol. On most systems this is a
null call. On some systems this unloads dynamic libraries. Returns -1
if there was an error, or 0 if everything was okay. See sock_init().
---------------------------------------------------------------------[>]-*/
int
sock_term (void)
{
#if (defined (__WINDOWS__))
WSACleanup ();
#endif
return (0);
}
/* ---------------------------------------------------------------------[<]-
Function: passive_TCP
Synopsis: Creates a passive bound TCP socket for the specified service.
Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET,
you can get the reason for the error by calling connect_error (). This
may be one of:
<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 == INADDR_NONE)
{
/* Not a dotted address -- try to translate the name */
phe = gethostbyname (hostname);
if (phe)
memcpy ((void *) &sin-> sin_addr, phe-> h_addr, phe-> h_length);
else
{ /* Cannot map to host */
connect_error_value = IP_BADHOST;
feedback = (int) SOCKET_ERROR;
}
}
return (feedback);
#else
connect_error_value = IP_NOSOCKETS;
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: build_sockaddr
Synopsis:
Builds a socket address structure from the specified host and port
addresses. Does not return any value except the built structure.
---------------------------------------------------------------------[>]-*/
void
build_sockaddr (
struct sockaddr_in *sin, /* Socket address structure */
qbyte host, /* 32-bit host address */
dbyte port /* 16-bit port number */
)
{
ASSERT (sin);
sin-> sin_family = AF_INET;
sin-> sin_addr.s_addr = htonl (host);
sin-> sin_port = htons (port);
}
/* ---------------------------------------------------------------------[<]-
Function: socket_localaddr
Synopsis: Returns a string containing the local host address for the
specified connected socket. The string is formatted as a string
"n.n.n.n". Returns the address of a static string or a buffer that
is overwritten by each call. If sockets are not supported, or there
was an error, returns the loopback address "127.0.0.1".
---------------------------------------------------------------------[>]-*/
char *
socket_localaddr (
sock_t handle)
{
#define NTOA_MAX 16
#if (defined (DOES_SOCKETS))
static char
localaddr [NTOA_MAX + 1]; /* xxx.xxx.xxx.xxx */
struct sockaddr_in
sin; /* Address of local system */
if (get_sock_addr (handle, &sin, NULL, 0))
return ("127.0.0.1");
else
{
strncpy (localaddr, inet_ntoa (sin.sin_addr), NTOA_MAX);
return (localaddr);
}
#else
return ("127.0.0.1");
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: socket_peeraddr
Synopsis: Returns a string containing the peer host address for the
specified connected socket. The string is formatted as a string
"n.n.n.n". Returns the address of a static string or a buffer that
is overwritten by each call. If sockets are not supported, or there
was an error, returns the loopback address "127.0.0.1".
---------------------------------------------------------------------[>]-*/
char *
socket_peeraddr (
sock_t handle)
{
#if (defined (DOES_SOCKETS))
static char
peeraddr [NTOA_MAX + 1]; /* xxx.xxx.xxx.xxx */
struct sockaddr_in
sin; /* Address of peer system */
if (get_peer_addr (handle, &sin, NULL, 0))
return ("127.0.0.1");
else
{
strncpy (peeraddr, inet_ntoa (sin.sin_addr), NTOA_MAX);
return (peeraddr);
}
#else
return ("127.0.0.1");
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: socket_nodelay
Synopsis: Disables Nagle's algorithm for the specified socket; use this
when you want to ensure that data is sent outwards as fast as possible,
and when you are certain that Nagle's algorithm is causing a slowdown in
performance. Recommended for HTTP, but not recommended for telnet.
Returns 0 if okay, SOCKET_ERROR if there was a problem.
---------------------------------------------------------------------[>]-*/
int
socket_nodelay (
sock_t handle)
{
#if (defined (__WINDOWS__))
int
true_value = 1; /* Boolean value for setsockopt() */
return (setsockopt ((SOCKET) handle, IPPROTO_TCP, TCP_NODELAY,
(char *) &true_value, sizeof (true_value)));
#elif (defined (TCP_NODELAY) && defined (SOL_TCP))
int
true_value = 1; /* Boolean value for setsockopt() */
return (setsockopt ((SOCKET) handle, SOL_TCP, TCP_NODELAY,
(char *) &true_value, sizeof (true_value)));
#else
return (0); /* Not applicable to this system */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: socket_is_alive
Synopsis:
Returns TRUE if the socket is open. Returns FALSE if the socket is no
longer accessible. You can use this function to check that a socket has
not been closed by the other party, before doing reading or writing.
---------------------------------------------------------------------[>]-*/
Bool
socket_is_alive (
sock_t handle)
{
#if (defined (__UTYPE_BEOS))
/* BeOS 4.5 does not support the getsockopt() function */
int
rc;
rc = setsockopt ((SOCKET) handle, SOL_SOCKET, SO_NONBLOCK,
(void *) &ip_nonblock, sizeof (ip_nonblock));
return (rc == 0);
#elif (defined (DOES_SOCKETS))
int
socket_type,
rc;
argsize_t
socket_type_size = sizeof (SOCKET);
rc = getsockopt ((SOCKET) handle, SOL_SOCKET, SO_TYPE,
(char *) &socket_type, &socket_type_size);
return (rc == 0);
#else
return (FALSE);
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: socket_error
Synopsis: Returns an errno value for the socket, or 0 if no error was
outstanding on the socket. This function is useful if you are handling
sockets using the select() function: this may return error indicators
on sockets, without precision on the type of error. This function will
return the precise error number. Errors like EINPROGRESS, EAGAIN, and
EWOULDBLOCK can usually be ignored or handled by retrying.
---------------------------------------------------------------------[>]-*/
int
socket_error (
sock_t handle)
{
#if (defined (DOES_SOCKETS))
# if (defined (__UTYPE_BEOS))
return (errno);
# else
return (getsockopt ((SOCKET) handle, SOL_SOCKET, SO_ERROR, 0, 0));
# endif
#else
return (0);
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: accept_socket
Synopsis: Accepts a connection on a specified master socket. If you
do not want to wait on this call, use select() to poll the socket until
there is an incoming request, then call accept_socket. Returns the
number of the new slave socket, or INVALID_SOCKET if there was an error
on the accept call. You can handle errors as fatal except for EAGAIN
which indicates that the operation would cause a non-blocking socket to
block (treat EWOULDBLOCK in the same way).
---------------------------------------------------------------------[>]-*/
sock_t
accept_socket (
sock_t master_socket)
{
#if (defined (DOES_SOCKETS))
sock_t
slave_socket; /* Connected slave socket */
struct sockaddr_in
sin; /* Address of connecting party */
argsize_t
sin_length; /* Length of address */
connect_error_value = IP_NOERROR; /* Assume no errors */
sin_length = (int) sizeof (sin);
slave_socket = accept ((SOCKET) master_socket,
(struct sockaddr *) &sin, &sin_length);
/* On non-Windows systems, accept returns -1 in case of error, which */
/* is the same as INVALID_SOCKET. */
# if (defined (__WINDOWS__))
if (slave_socket == INVALID_SOCKET)
{
int sock_errno = WSAGetLastError ();
if (sock_errno == WSAEWOULDBLOCK || sock_errno == WSAEINPROGRESS)
errno = EAGAIN;
}
# endif
if (slave_socket != INVALID_SOCKET)
{
prepare_socket (slave_socket);
ip_sockets++;
}
return (slave_socket);
#else
connect_error_value = IP_NOSOCKETS;
return (INVALID_SOCKET); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: connect_error
Synopsis:
Returns the last error code from one of the connection functions. For
portability in a multithreaded environment, call immediately after the
call to the connection function.
---------------------------------------------------------------------[>]-*/
int
connect_error (void)
{
return (connect_error_value);
}
/* ---------------------------------------------------------------------[<]-
Function: get_sock_addr
Synopsis: Builds an address block (struct sockaddr_in) for the local
end of the specified connected socket. Returns 0 if okay, SOCKET_ERROR
if there was an error. If the name argument is not null, looks-up the
host name and returns it. The name is truncated to namesize characters,
including a trailing null character.
---------------------------------------------------------------------[>]-*/
int
get_sock_addr (
sock_t handle, /* Socket to get address for */
struct sockaddr_in *sin, /* Block for formatted address */
char *name, /* Buffer for host name, or NULL */
int namesize /* Size of host name buffer */
)
{
#if (defined (DOES_SOCKETS))
int
rc; /* Return code from call */
struct hostent
*phe; /* Host information entry */
argsize_t
sin_length; /* Length of address */
ASSERT (sin);
/* Get address for local connected socket */
sin_length = sizeof (struct sockaddr_in);
rc = getsockname ((SOCKET) handle, (struct sockaddr *) sin, &sin_length);
/* Translate into host name string, only if wanted */
if (name != NULL && rc == 0)
{
phe = gethostbyaddr ((char *) &sin-> sin_addr,
sizeof (sin-> sin_addr), AF_INET);
if (phe)
{
strncpy (name, phe-> h_name, namesize);
name [namesize - 1] = '\0';
}
}
# if (defined (__WINDOWS__))
return (win_error (rc));
# else
return (rc);
# endif
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: get_peer_addr
Synopsis: Builds an address block (struct sockaddr_in) for the remote
end of the specified connected socket. Returns 0 if okay, SOCKET_ERROR
if there was an error. If the name argument is not null, looks-up the
host name and returns it. The name is truncated to namesize characters,
including a trailing null character.
---------------------------------------------------------------------[>]-*/
int
get_peer_addr (
sock_t handle, /* Socket to get address for */
struct sockaddr_in *sin, /* Block for formatted address */
char *name, /* Buffer for host name, or NULL */
int namesize /* Size of host name buffer */
)
{
#if (defined (DOES_SOCKETS))
int
rc; /* Return code from call */
struct hostent
*phe; /* Host information entry */
argsize_t
sin_length; /* Length of address */
ASSERT (sin);
/* Get address for connected socket peer */
sin_length = sizeof (struct sockaddr_in);
rc = getpeername ((SOCKET) handle, (struct sockaddr *) sin, &sin_length);
/* Translate into host name string, only if wanted */
if (name != NULL && rc == 0)
{
phe = gethostbyaddr ((char *) &sin-> sin_addr,
sizeof (sin-> sin_addr), AF_INET);
if (phe)
{
strncpy (name, phe-> h_name, namesize);
name [namesize - 1] = '\0';
}
}
# if (defined (__WINDOWS__))
return (win_error (rc));
# else
return (rc);
# endif
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: read_TCP
Synopsis:
Reads data from the socket. On UNIX, VMS, OS/2, passes through to the
standard read function; some other systems have particular ways of
accessing sockets. If there is an error on the read this function
returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except
for EAGAIN which indicates that the operation would cause a non-blocking
socket to block, and EPIPE or ECONNRESET which indicate that the socket
was closed at the other end. Treat EWOULDBLOCK as EAGAIN.
---------------------------------------------------------------------[>]-*/
int
read_TCP (
sock_t handle, /* Socket handle */
void *buffer, /* Buffer to receive data */
size_t length /* Maximum amount of data to read */
)
{
#if (defined (DOES_SOCKETS))
# if (defined (__UTYPE_BEOS))
return (recv ((SOCKET) handle, buffer, length, 0));
# elif (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__))
return (read ((SOCKET) handle, buffer, length));
# elif (defined (__WINDOWS__))
int
rc; /* Return code from call */
ASSERT (buffer);
rc = recv ((SOCKET) handle, buffer, length, 0);
return (win_error (rc));
# else
# error "No code for function body."
# endif
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
#if (defined (__WINDOWS__))
/* -------------------------------------------------------------------------
* win_error -- internal
*
* For Winsockets only: fetches real error code and sticks it in errno,
* if the return code from the last call was SOCKET_ERROR. Returns rc.
*/
static int
win_error (int rc)
{
if (rc == (int) SOCKET_ERROR)
errno = winsock_last_error ();
return (rc);
}
#endif
/* ---------------------------------------------------------------------[<]-
Function: write_TCP
Synopsis:
Writes data to the socket. On UNIX, VMS, OS/2, calls the standard
write function; some other systems have particular ways of accessing
sockets. If there is an error on the write this function returns
SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for
EAGAIN which indicates that the operation would cause a non-blocking
socket to block, and EPIPE or ECONNRESET which indicate that the socket
was closed at the other end. Treat EWOULDBLOCK as EAGAIN.
---------------------------------------------------------------------[>]-*/
int
write_TCP (
sock_t handle, /* Socket handle */
const void *buffer, /* Buffer containing data */
size_t length /* Amount of data to write */
)
{
#if (defined (DOES_SOCKETS))
# if (defined (__UTYPE_BEOS))
return (send ((SOCKET) handle, buffer, length, 0));
# elif (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__))
return (write ((SOCKET) handle, buffer, length));
# elif (defined (__WINDOWS__))
int
rc; /* Return code from call */
ASSERT (buffer);
rc = send ((SOCKET) handle, buffer, length, 0);
return (win_error (rc));
# else
# error "No code for function body."
# endif
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: read_UDP
Synopsis:
Reads data from a connected or unconnected UDP socket. To prepare a
connected UDP socket you call connect_UDP (). This makes a connection
to a specific port on a specific host, and returns a socket handle.
When you call this function with a null value for the address argument,
it assumes you are using a connected UDP socket.
To prepare an unconnected UDP socket, call create_socket () with the
string "udp" as argument. This returns a sock_t handle that you can
use in this function. If you use an unconnected UDP socket you must
provide an address structure. The function places the remote host and
port in this structure. This lets you reply using write_UDP ().
Generally a server can use unconnected sockets, and a client can use
connected sockets. You can also format an address for a specific host
and port using the address_end_point () function.
If there is an error on the read this function returns SOCKET_ERROR.
You can handle errors (in sockerrno) as fatal except for EAGAIN which
indicates that the operation would cause a non-blocking socket to block.
Treat EWOULDBLOCK as EAGAIN.
---------------------------------------------------------------------[>]-*/
int
read_UDP (
sock_t handle, /* Socket handle */
void *buffer, /* Buffer to receive data */
size_t length, /* Maximum amount of data to read */
const struct sockaddr_in *sin /* Block for address, or null */
)
{
#if (defined (DOES_SOCKETS))
argsize_t
sin_length; /* Length of address */
int
flags = 0, /* Flags for call */
rc; /* Return code from call */
ASSERT (buffer);
ASSERT (sin);
sin_length = (int) sizeof (*sin);
if (sin)
/* Read from unconnected UDP socket; we accept the address of the */
/* sending party in the sin argument. */
rc = recvfrom ((SOCKET) handle, buffer, length, flags,
(struct sockaddr *) sin, &sin_length);
else
/* Read from a connected UDP socket; we don't need to get the */
/* address, since we already know it. */
rc = recv ((SOCKET) handle, buffer, length, flags);
# if (defined (__WINDOWS__))
return (win_error (rc));
# else
return (rc);
# endif
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: write_UDP
Synopsis:
Writes data to a connected or unconnected UDP socket. To prepare a
connected UDP socket you call connect_UDP (). This makes a connection
to a specific port on a specific host, and returns a socket handle.
When you call this function with a null value for the address argument,
it assumes you are using a connected UDP socket.
To prepare an unconnected UDP socket, call create_socket () with the
string "udp" as argument. This returns a sock_t handle that you can
use in this function. If you use an unconnected UDP socket you must
provide an address structure containing a valid host and port. You can
get this information from a read_UDP () or through address_end_point ().
If there is an error on the write this function returns SOCKET_ERROR.
You can handle errors as fatal except for EAGAIN which indicates that
the operation would cause a non-blocking socket to block. Treat
EWOULDBLOCK as EAGAIN.
---------------------------------------------------------------------[>]-*/
int
write_UDP (
sock_t handle, /* Socket handle */
const void *buffer, /* Buffer containing data */
size_t length, /* Amount of data to write */
const struct sockaddr_in *sin /* Address to send to, or null */
)
{
#if (defined (DOES_SOCKETS))
int
sin_length, /* Length of address */
flags = 0, /* Flags for call */
rc; /* Return code from call */
ASSERT (buffer);
ASSERT (sin);
sin_length = (int) sizeof (*sin);
if (sin)
/* Write to unconnected UDP socket; we provide the address of */
/* the receiving party in the sin argument. */
rc = sendto ((SOCKET) handle, buffer, length, flags,
(struct sockaddr *) sin, sin_length);
else
/* Write to a connected UDP socket; we don't need to supply */
/* the address, since we already know it. */
rc = send ((SOCKET) handle, buffer, length, flags);
# if (defined (__WINDOWS__))
return (win_error (rc));
# else
return (rc);
# endif
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: close_socket
Synopsis:
Closes the socket. On UNIX, VMS, OS/2 calls the standard close
function; some other systems have particular ways of accessing sockets.
If there is an error on the close this function returns SOCKET_ERROR.
You can handle errors (in sockerrno) as fatal except for EAGAIN which
indicates that the operation would cause a non-blocking socket to block.
Treat EWOULDBLOCK as EAGAIN.
---------------------------------------------------------------------[>]-*/
int
close_socket (
sock_t handle /* Socket handle */
)
{
#if (defined (DOES_SOCKETS))
# if (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__))
if (!socket_is_alive (handle))
return (0);
ip_sockets--;
shutdown (handle, 2);
return (close ((SOCKET) handle));
# elif (defined (__WINDOWS__))
int
rc;
if (!socket_is_alive (handle))
return (0);
ip_sockets--;
shutdown ((SOCKET) handle, 2);
rc = closesocket ((SOCKET) handle);
return (win_error (rc));
# else
# error "No code for function body."
# endif
#elif (defined (FAKE_SOCKETS))
return (0); /* Okay, closed */
#else
return ((int) SOCKET_ERROR); /* Sockets not supported */
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: sock_select
Synopsis: Performs the standard select() call. Use this in preference
to select(), as some systems may not be 100% compatible with BSD sockets,
Uses the same arguments as the select() call, and gives the same return
codes. If sockets are not supported, always returns 0.
---------------------------------------------------------------------[>]-*/
int
sock_select (int nfds, fd_set *readfds, fd_set *writefds,
fd_set *errorfds, struct timeval *timeout)
{
#if (defined (DOES_SOCKETS))
int
rc = 0; /* Return code from select() */
ASSERT (timeout);
# if (defined (__UTYPE_BEOS))
/* BeOS only supports the readfds argument */
rc = select (nfds, FD_SETTYPE readfds, NULL, NULL, timeout);
if (rc == -1)
coprintf ("Error after select(): %s", strerror (errno));
return (rc);
# elif (defined (WIN32))
/* Windows occasionally aborts during the select call... */
__try {
rc = select (nfds, FD_SETTYPE readfds, FD_SETTYPE writefds,
FD_SETTYPE errorfds, timeout);
}
__except (1) {
coprintf ("select() aborted - arguments: %d, %p, %p, %p, %p",
nfds, FD_SETTYPE readfds, FD_SETTYPE writefds,
FD_SETTYPE errorfds, timeout);
}
return (rc);
# else
rc = select (nfds, FD_SETTYPE readfds, FD_SETTYPE writefds,
FD_SETTYPE errorfds, timeout);
return (rc);
# endif
#else
return (0);
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: get_hostname
Synopsis: Returns a string containing the local hostname. The returned
string is in a static area. Only performs the local hostname lookup one
time; the returned value is cached for later repeated calls to this
function. If sockets are not supported, returns the value "localhost".
---------------------------------------------------------------------[>]-*/
char *
get_hostname (void)
{
#if (defined (DOES_SOCKETS))
static char
host_name [LINE_MAX + 1] = "";
if (strnull (host_name))
if (gethostname (host_name, LINE_MAX))
strcpy (host_name, "localhost");
return (host_name);
#else
return ("localhost");
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: get_hostaddr
Synopsis: Returns the current the host address as a 4-byte value in
host format (not network format). Returns 0x7f000001 (loopback) if
sockets are not supported or there was an error getting the current
host IP address. If there are several IP addresses on the system,
returns one arbitrary address.
---------------------------------------------------------------------[>]-*/
qbyte
get_hostaddr (void)
{
#if (defined (DOES_SOCKETS))
struct hostent
*phe; /* Host information entry */
phe = gethostbyname (get_hostname ());
if (phe)
return (*(qbyte *) (phe-> h_addr_list [0]));
else
return (htonl (SOCKET_LOOPBACK));
#else
return (htonl (SOCKET_LOOPBACK));
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: get_hostaddrs
Synopsis: Returns a table of all host IP addresses. The table ends in
a zero address. Each address is a 4-byte value in host format. Returns
NULL if there was an error. If sockets are not supported, returns a
table with the loopback address (127.0.0.1) and a null address. The
caller must free the table using mem_free() when finished using it.
---------------------------------------------------------------------[>]-*/
qbyte *
get_hostaddrs (void)
{
#if (defined (DOES_SOCKETS))
int
addr_count; /* How many addresses do we have */
qbyte
*addr_table; /* Where we store the addresses */
struct hostent
*phe; /* Host information entry */
if ((phe = gethostbyname (get_hostname ())) == NULL)
return (NULL);
/* Count the addresses */
for (addr_count = 0; phe-> h_addr_list [addr_count]; addr_count++);
/* Allocate a table; socket addresses are 4 bytes */
addr_table = mem_alloc (4 * (addr_count + 1));
/* Store the addresses */
for (addr_count = 0; phe-> h_addr_list [addr_count]; addr_count++)
addr_table [addr_count]
= *(qbyte *) (phe-> h_addr_list [addr_count]);
addr_table [addr_count] = 0;
return (addr_table);
#else
qbyte
*addr_table; /* Where we store the addresses */
addr_table = mem_alloc (8); /* Addresses are 4 bytes */
addr_table [0] = htonl (SOCKET_LOOPBACK);
addr_table [1] = 0;
return (addr_table);
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: sock_ntoa
Synopsis: Converts an IP address in network order to a string in dotted
format. The string is stored in a statically-allocated buffer that is
overwritten by each call.
---------------------------------------------------------------------[>]-*/
char *
sock_ntoa (qbyte address)
{
static char
string [16]; /* xxx.xxx.xxx.xxx */
byte
*part;
/* Network order is high-low so we can address the bytes in order */
part = (byte *) &address;
snprintf (string, sizeof (string),
"%d.%d.%d.%d", part [0], part [1], part [2], part [3]);
return (string);
}
/* ---------------------------------------------------------------------[<]-
Function: sockmsg
Synopsis:
Returns a string describing the cause of the last fatal error to occur
a socket. Should be called directly after a socket i/o operation; if you
do other i/o operations or allow other threads to proceed in the meantime,
the returned string may be incorrect.
---------------------------------------------------------------------[>]-*/
const char *
sockmsg (void)
{
#if (defined (__WINDOWS__))
char
*message;
switch (WSAGetLastError ())
{
case WSAEINTR: message = "WSAEINTR"; break;
case WSAEBADF: message = "WSAEBADF"; break;
case WSAEACCES: message = "WSAEACCES"; break;
case WSAEFAULT: message = "WSAEFAULT"; break;
case WSAEINVAL: message = "WSAEINVAL"; break;
case WSAEMFILE: message = "WSAEMFILE"; break;
case WSAEWOULDBLOCK: message = "WSAEWOULDBLOCK"; break;
case WSAEINPROGRESS: message = "WSAEINPROGRESS"; break;
case WSAEALREADY: message = "WSAEALREADY"; break;
case WSAENOTSOCK: message = "WSAENOTSOCK"; break;
case WSAEDESTADDRREQ: message = "WSAEDESTADDRREQ"; break;
case WSAEMSGSIZE: message = "WSAEMSGSIZE"; break;
case WSAEPROTOTYPE: message = "WSAEPROTOTYPE"; break;
case WSAENOPROTOOPT: message = "WSAENOPROTOOPT"; break;
case WSAEPROTONOSUPPORT: message = "WSAEPROTONOSUPPORT"; break;
case WSAESOCKTNOSUPPORT: message = "WSAESOCKTNOSUPPORT"; break;
case WSAEOPNOTSUPP: message = "WSAEOPNOTSUPP"; break;
case WSAEPFNOSUPPORT: message = "WSAEPFNOSUPPORT"; break;
case WSAEAFNOSUPPORT: message = "WSAEAFNOSUPPORT"; break;
case WSAEADDRINUSE: message = "WSAEADDRINUSE"; break;
case WSAEADDRNOTAVAIL: message = "WSAEADDRNOTAVAIL"; break;
case WSAENETDOWN: message = "WSAENETDOWN"; break;
case WSAENETUNREACH: message = "WSAENETUNREACH"; break;
case WSAENETRESET: message = "WSAENETRESET"; break;
case WSAECONNABORTED: message = "WSAECONNABORTED"; break;
case WSAECONNRESET: message = "WSAECONNRESET"; break;
case WSAENOBUFS: message = "WSAENOBUFS"; break;
case WSAEISCONN: message = "WSAEISCONN"; break;
case WSAENOTCONN: message = "WSAENOTCONN"; break;
case WSAESHUTDOWN: message = "WSAESHUTDOWN"; break;
case WSAETOOMANYREFS: message = "WSAETOOMANYREFS"; break;
case WSAETIMEDOUT: message = "WSAETIMEDOUT"; break;
case WSAECONNREFUSED: message = "WSAECONNREFUSED"; break;
case WSAELOOP: message = "WSAELOOP"; break;
case WSAENAMETOOLONG: message = "WSAENAMETOOLONG"; break;
case WSAEHOSTDOWN: message = "WSAEHOSTDOWN"; break;
case WSAEHOSTUNREACH: message = "WSAEHOSTUNREACH"; break;
case WSAENOTEMPTY: message = "WSAENOTEMPTY"; break;
case WSAEPROCLIM: message = "WSAEPROCLIM"; break;
case WSAEUSERS: message = "WSAEUSERS"; break;
case WSAEDQUOT: message = "WSAEDQUOT"; break;
case WSAESTALE: message = "WSAESTALE"; break;
case WSAEREMOTE: message = "WSAEREMOTE"; break;
case WSAEDISCON: message = "WSAEDISCON"; break;
case WSASYSNOTREADY: message = "WSASYSNOTREADY"; break;
case WSAVERNOTSUPPORTED: message = "WSAVERNOTSUPPORTED"; break;
case WSANOTINITIALISED: message = "WSANOTINITIALISED"; break;
default: message = "No error";
}
return (message);
#else
return (strerror (errno));
#endif
}
#if (defined (__WINDOWS__))
/* ---------------------------------------------------------------------[<]-
Function: winsock_last_error
Synopsis: Convert a winsock error into a errno value.
---------------------------------------------------------------------[>]-*/
int
winsock_last_error (void)
{
int
error = 0;
switch (WSAGetLastError ())
{
case WSAEINTR: error = EINTR; break;
case WSAEBADF: error = EBADF; break;
case WSAEWOULDBLOCK: error = EAGAIN; break;
case WSAEINPROGRESS: error = EAGAIN; break;
case WSAENETDOWN: error = EAGAIN; break;
case WSAECONNRESET: error = ECONNRESET; break;
case WSAECONNABORTED: error = EPIPE; break;
case WSAESHUTDOWN: error = ECONNRESET; break;
case WSAEINVAL: error = EPIPE; break;
# if defined (WIN32)
default: error = GetLastError ();
# else
default: error = errno;
# endif
}
return (error);
}
#endif
/* ---------------------------------------------------------------------[<]-
Function: socket_is_permitted
Synopsis: Compares the specified address with a mask and returns
TRUE if the address matches the mask, or FALSE if it does not. The
address is formatted as a string "xxx.xxx.xxx.xxx". The mask is
formatted as zero or more patterns, delimited by whitespace or commas.
A pattern is an address string, with zero or more of the last
components replaced by '*'. The pattern may also be prefixed by '!'
to indicate exclusion. This is an example of a mask: "127.0.0.1,
253.34.*, !253.35.*". This mask allows all addresses: "*". To get
the string address for a remote socket, use socket_peer_address().
---------------------------------------------------------------------[>]-*/
Bool
socket_is_permitted (const char *address, const char *mask)
{
char
*addrptr, /* Pointer into address */
*maskptr; /* Pointer into mask */
Bool
negate, /* If !pattern */
feedback = FALSE; /* False unless matched */
ASSERT (address);
ASSERT (mask);
maskptr = (char *) mask;
while (*maskptr)
{
while (isspace (*maskptr) || *maskptr == ',')
maskptr++;
/* Get negation if necessary */
if (*maskptr == '!')
{
negate = TRUE;
maskptr++;
}
else
negate = FALSE;
/* Compare pattern with address up to the end of the pattern */
for (addrptr = (char *) address; *addrptr; addrptr++)
{
if (*maskptr == '*') /* Matched address up to * */
return (!negate); /* So either accepted or failed */
else
if (*maskptr == '\0') /* Did not match address */
return (negate); /* so fail unless negated */
else
if (*addrptr != *maskptr) /* Some difference */
break; /* so stop comparing */
maskptr++;
}
if (*addrptr == '\0' /* Matched exact address? */
&& (*maskptr == '\0' || isspace (*maskptr) || *maskptr == ','))
return (!negate); /* Either accepted or failed */
until (*maskptr == '\0' || isspace (*maskptr) || *maskptr == ',')
maskptr++; /* Skip to end of this pattern */
}
return (feedback);
}
/* ---------------------------------------------------------------------[<]-
Function: get_host_file
Synopsis: returns the full path name of the host lookup file, if provided
by the OS, and found. If not found, returns "hosts". The returned string
is held in a static area of memory that may be overwritten by each call.
---------------------------------------------------------------------[>]-*/
char *
get_host_file (void)
{
#if (defined (WIN32))
static OSVERSIONINFO
version_info;
static char
name [LINE_MAX + 1];
strclr (name);
GetWindowsDirectory (name, LINE_MAX);
version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (GetVersionEx (&version_info))
/* On Windows NT the hosts file is well-hidden; on Win95 it's
* more visible */
if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
strcat (name, "\\system32\\drivers\\etc\\hosts");
else
strcat (name, "\\hosts");
return (name);
#elif (defined (__UNIX__))
return ("/etc/hosts");
#elif (defined (__VMS__))
return ("/etc/hosts"); /* Not correct -- needs more work */
#elif (defined (__OS2__))
/* Under OS/2 the hosts information is stored in the "hosts" file which
* is in the directory pointed at by the %ETC% environment variable.
* If that environment variable is not set, then TCP/IP support is not
* properly installed. In that instance we return "/mptn/etc/hosts"
* (the likely value on OS/2 Warp 3 Connect and OS/2 Warp 4) and hope
* for the best.
*/
/* A static array is used only because the other versions use a static
* array. If the resulting file name will not fit in the space allowed
* then "/mtpn/etc/hosts" is used as before.
*/
static char
name [LINE_MAX + 1];
char
*etcenv = NULL;
etcenv = getenv ("ETC");
if (etcenv != NULL && strlen (etcenv) < (LINE_MAX - 6))
{ /* We've already checked it will all fit. */
strcpy (name, etcenv);
strcat (name, "/hosts");
return (name);
}
else
return ("/mptn/etc/hosts");
#else
return ("hosts");
#endif
}
/* ---------------------------------------------------------------------[<]-
Function: get_name_server
Synopsis: gets the addresses of the DNS servers defined in the TCP/IP
configuration. The addresses are returned in a user-provided struct
sockaddr_in array. The maximum number of addresses in this array is
supplied as the ns_max argument. Return the number of address found.
---------------------------------------------------------------------[>]-*/
int
get_name_server (struct sockaddr_in *ns_address, int ns_max)
{
int
ns_count = 0; /* Number of servers that we found */
#if (defined (WIN32))
static OSVERSIONINFO
version_info;
HKEY
hkey; /* Handle to returned reg. key */
static char
registry_value [LINE_MAX + 1]; /* DNS server info from registry */
long
size = LINE_MAX; /* Max. size of returned value */
DWORD
type;
char
*key,
**address_list = NULL;
int
address_nbr;
/* Look in registry; this sometimes works, but not always */
version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (GetVersionEx (&version_info)
&& version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
else
key = "SYSTEM\\CurrentControlSet\\Services\\Vxd\\Mstcp\\Parameters";
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, key, 0,
KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS
&& RegQueryValueEx (hkey, "NameServer", NULL, (LPDWORD) &type,
(LPBYTE) registry_value, (LPDWORD) &size) == ERROR_SUCCESS)
{
address_list = tok_split (registry_value);
for (address_nbr = 0; address_list [address_nbr]; address_nbr++)
{
if (ns_count >= ns_max)
break;
ns_address [ns_count].sin_family = AF_INET;
ns_address [ns_count].sin_port = htons (DNS_PORT);
ns_address [ns_count].sin_addr.s_addr =
inet_addr (address_list [address_nbr]);
ns_count++;
}
tok_free (address_list);
RegCloseKey (hkey);
}
#elif (defined (__UNIX__))
static char
buffer [LINE_MAX + 1],
address [16];
FILE
*resolver;
int
rc;
resolver = file_open ("/etc/resolv.conf", 'r');
if (resolver)
{
while (file_read (resolver, buffer))
{
rc = sscanf (buffer, "nameserver %s", address);
if (rc > 0 && rc != EOF)
{
if (ns_count >= ns_max)
break;
ns_address [ns_count].sin_family = AF_INET;
ns_address [ns_count].sin_port = htons (DNS_PORT);
ns_address [ns_count].sin_addr.s_addr = inet_addr (address);
ns_count++;
}
}
file_close (resolver);
}
#elif (defined (__OS2__))
static char
buffer [LINE_MAX + 1],
address [16];
char
*etcenv = NULL,
*filename = NULL;
FILE
*resolver = NULL;
int
rc;
/* Under OS/2 the file controlling the resolver is stored in the */
/* directory pointed at by the ETC environment variable. It is called */
/* resolv2 or resolv (I *think* that is the order of preference), so we */
/* try those two file names in that order. */
/* If the ETC environment variable is not set we try the /mptn/etc */
/* directory since that is a likely default location for it. */
etcenv = getenv ("ETC");
if (etcenv)
{
filename = mem_alloc (strlen(etcenv) + 10);
if (!filename)
return 0; /* Cannot allocate memory for filename */
strcpy (filename, etcenv);
strcat (filename, "/resolv2");
resolver = file_open (filename, 'r');
if (! resolver)
{ /* Not available under that filename, let's try the other one */
strcpy (filename, etcenv);
strcat (filename, "/resolv");
resolver = file_open (filename, 'r');
}
mem_free (filename);
}
else
{ /* No environment variable around, try using the defaults */
resolver = file_open ("/mptn/etc/resolv2", 'r');
if (! resolver)
resolver = file_open ("/mptn/etc/resolv", 'r');
}
if (resolver)
{
while (file_read (resolver, buffer))
{
rc = sscanf (buffer, "nameserver %s", address);
if (rc > 0 && rc != EOF)
{
if (ns_count >= ns_max)
break;
ns_address [ns_count].sin_family = AF_INET;
ns_address [ns_count].sin_port = htons (DNS_PORT);
ns_address [ns_count].sin_addr.s_addr = inet_addr (address);
ns_count++;
}
}
file_close (resolver);
}
#endif
return (ns_count);
}
/* We fake some BSD functions that are missing on some systems */
#if (defined (__VMS__) || defined (__UTYPE_BEOS))
struct protoent *getprotobyname (const char *protocol)
{
static struct protoent
proto = { NULL, NULL, 0 };
if (streq (protocol, "udp"))
proto.p_proto = IPPROTO_UDP;
else
if (streq (protocol, "tcp"))
proto.p_proto = IPPROTO_TCP;
else
if (streq (protocol, "icmp"))
proto.p_proto = IPPROTO_ICMP;
return (&proto);
}
struct protoent *getprotobynumber (int protocol)
{
static struct protoent
proto = { NULL, NULL, 0 };
if (protocol == IPPROTO_UDP)
proto.p_name = "udp";
else
if (protocol == IPPROTO_TCP)
proto.p_name = "tcp";
else
if (protocol == IPPROTO_ICMP)
proto.p_name = "icmp";
return (&proto);
}
struct servent *getservbyport (int port, const char *protocol)
{
static char
fmtnumber [9];
static struct servent
service = { NULL, NULL, 0, NULL };
sprintf (fmtnumber, "%d", port);
service.s_name = fmtnumber;
return (&service);
}
#endif

File Metadata

Mime Type
text/x-c
Expires
Sun, Nov 15, 9:56 AM (23 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
124237
Default Alt Text
sflsock.c (76 KB)

Event Timeline