📄 net_tcp.c
字号:
/* Clean up the socket pool state information */
endSocketPool();
if( hTCP != NULL_INSTANCE )
{
#ifdef __WINDOWS__
/* Wipe the Sheets Afterwards and Cleanup */
WSACleanup();
#endif /* __WINDOWS__ */
DynamicUnload( hTCP );
}
hTCP = NULL_INSTANCE;
}
/* Return the status of the network interface */
BOOLEAN networkingOK( void )
{
return( hTCP != NULL_INSTANCE ? TRUE : FALSE );
}
#else
void netInitTCP( void )
{
STATIC_FN int initSocketPool( void );
#ifdef __SCO_VERSION__
void ( *disp )( int );
/* Work around the broken SCO/UnixWare signal-handling, which sometimes
sends a nonblocking socket a SIGIO (thus killing the process) when
waiting in a select(). Since SIGIO is an alias for SIGPOLL, SCO
eventually reports this as a "polling alarm" */
disp = signal( SIGIO, SIG_IGN );
if( disp != SIG_DFL && disp != SIG_IGN )
{
/* There was already a SIGIO handler present, reinstate the old
handler and warn the user that they need to take special steps
to manage this problem. This is ugly, but it's better than
leaving a difficult-to-find problem where the user's SIGIO
handler is pre-empted by cryptlib */
signal( SIGIO, disp );
printf( "Conflicting SIGIO handling detected in UnixWare socket "
"bug workaround,\nfile " __FILE__ ", line %d.\n", __LINE__ );
}
#endif /* UnixWare/SCO */
/* Set up the socket pool state information */
initSocketPool();
}
void netEndTCP( void )
{
STATIC_FN void endSocketPool( void );
/* Clean up the socket pool state information */
endSocketPool();
#ifdef __SCO_VERSION__
signal( SIGIO, SIG_DFL );
#endif /* UnixWare/SCO */
}
BOOLEAN networkingOK( void )
{
return( TRUE );
}
#endif /* Windows and Unixen without socket support in libc (ie Slowaris) */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Map of common error codes to strings */
typedef struct { const int errorCode; const char *errorString; } SOCKETERROR_INFO;
#ifdef __WINDOWS__
static const SOCKETERROR_INFO socketErrorInfo[] = {
WSAECONNREFUSED, "WSAECONNREFUSED: The attempt to connect was rejected",
WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL: The remote address is not a valid "
"address",
WSAECONNABORTED, "WSAECONNABORTED: Connection was terminated due to a "
"time-out or other failure",
WSAECONNRESET, "WSAECONNRESET: Connection was reset by the remote host "
"executing a close",
WSAEHOSTUNREACH, "WSAEHOSTUNREACH: Remote host cannot be reached from "
"this host at this time",
WSAEMSGSIZE, "WSAEMSGSIZE: Message is larger than the maximum supported "
"by the underlying transport",
WSAENETDOWN, "WSAENETDOWN: The network subsystem has failed",
WSAENETRESET, "WSAENETRESET: Connection was broken due to keep-alive "
"detecting a failure while operation was in progress",
WSAENETUNREACH, "WSAENETUNREACH: Network cannot be reached from this "
"host at this time",
WSAENOBUFS, "WSAENOBUFS: No buffer space available",
WSAENOTCONN, "WSAENOTCONN: Socket is not connected",
WSAETIMEDOUT, "WSAETIMEDOUT: Function timed out before completion",
WSAHOST_NOT_FOUND, "WSAHOST_NOT_FOUND: Host not found",
WSATRY_AGAIN, "WSATRY_AGAIN: Host not found (non-authoritative)",
WSANO_DATA, "WSANO_DATA: Valid name, no data record of requested type",
CRYPT_ERROR, NULL
};
#define hostErrorInfo socketErrorInfo /* Winsock uses unified error codes */
#define TIMEOUT_ERROR WSAETIMEDOUT /* Code for timeout error */
#else
static const SOCKETERROR_INFO socketErrorInfo[] = {
EADDRNOTAVAIL, "EADDRNOTAVAIL: Specified address is not available from "
"the local machine",
ECONNREFUSED, "ECONNREFUSED: Attempt to connect was rejected",
ECONNRESET, "ECONNRESET: Connection was forcibly closed by remote host",
EINTR, "EINTR: Function was interrupted by a signal",
EMFILE, "EMFILE: Per-process descriptor table is full",
EMSGSIZE, "EMSGSIZE: Message is too large to be sent all at once",
ENETUNREACH, "ENETUNREACH: No route to the network or host is present",
ENOBUFS, "ENOBUFS: Insufficient system resources available to complete "
"the call",
ENOTCONN, "ENOTCONN: Socket is not connected",
ETIMEDOUT, "ETIMEDOUT: Function timed out before completion",
HOST_NOT_FOUND, "HOST_NOT_FOUND: Not an official hostname or alias",
NO_ADDRESS, "NO_ADDRESS: Name is valid but does not have an IP address "
"at the name server",
TRY_AGAIN, "TRY_AGAIN: Local server did not receive a response from an "
"authoritative server",
CRYPT_ERROR, NULL
};
#define TIMEOUT_ERROR ETIMEDOUT /* Code for timeout error */
static const SOCKETERROR_INFO hostErrorInfo[] = {
HOST_NOT_FOUND, "HOST_NOT_FOUND: Host not found",
NO_ADDRESS, "NO_ADDRESS: No address record available for this name",
CRYPT_ERROR, NULL
};
#endif /* System-specific socket error codes */
/* Get and set the low-level error information from a socket- and host-
lookup-based error */
static int mapError( STREAM *stream, const SOCKETERROR_INFO *errorInfo,
const int errorCode, const int status )
{
int i;
*stream->errorMessage = '\0';
for( i = 0; errorInfo[ i ].errorCode != CRYPT_ERROR; i++ )
if( errorInfo[ i ].errorCode == stream->errorCode )
{
strcpy( stream->errorMessage, errorInfo[ i ].errorString );
break;
}
return( status );
}
static int getSocketError( STREAM *stream, const int status )
{
/* Get the low-level error code and map it to an error string if
possible */
stream->errorCode = getErrorCode();
return( mapError( stream, socketErrorInfo, stream->errorCode,
status ) );
}
static int getHostError( STREAM *stream, const int status )
{
/* Get the low-level error code and map it to an error string if
possible */
stream->errorCode = getHostErrorCode();
return( mapError( stream, hostErrorInfo, stream->errorCode,
status ) );
}
static int setSocketError( STREAM *stream, const char *errorMessage,
const int status )
{
stream->errorCode = 0;
strcpy( stream->errorMessage, errorMessage );
return( status );
}
/* Get a host's IP address */
int getIPAddress( STREAM *stream, BYTE *ipAddress, const char *name )
{
struct hostent *pHostent;
/* If it's a dotted address, convert it to in_addr form and return it */
if( isdigit( *name ) )
{
BYTE *addrPtr = ipAddress;
in_addr_t address = inet_addr( name );
if( isBadAddress( address ) )
return( setSocketError( stream, "Bad IP address",
CRYPT_ERROR_OPEN ) );
mputLong( addrPtr, address );
return( CRYPT_OK );
}
/* It's a host name, convert it to the in_addr form */
pHostent = gethostbyname( name );
if( pHostent == NULL || pHostent->h_length != 4 )
return( getHostError( stream, CRYPT_ERROR_OPEN ) );
memcpy( ipAddress, pHostent->h_addr_list[ 0 ], 4 );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Network Socket Manager *
* *
****************************************************************************/
/* cryptlib's separation kernel causes some problems with objects which use
sockets both because it doesn't allow sharing of sockets (which is a
problem because the Unix server programming model assumes that a single
process will listen on a socket and fork off children to handle incoming
connections, in fact the accept() function more or less forces you to do
this whether you want to or not) and because when a thread is blocked in
an object waiting on a socket there's no way to unblock it apart from
killing the thread. In order to work around this we maintain a socket
pool which serves two functions:
- Maintains a list of sockets which an object is listening on to allow a
listening socket to be reused rather than having to listen on a
socket and close it as soon as an incoming connection is made in
order to switch to the connected socket.
- Allows sockets to be closed from another thread, which results in any
objects waiting on them being woken up and exiting.
For now we limit the socket pool to a maximum of 256 sockets both as a
safety feature to protect against runaway apps from and because cryptlib
was never designed to function as a high-volume server application. If
necessary this can be changed to dynamically expand the pool size in the
same way that the kernel dynamically expands its object table */
#define SOCKETPOOL_SIZE 256
typedef struct {
SOCKET netSocket; /* Socket handle */
int port; /* Port which socket is bound to */
int refCount; /* Reference count for socket */
} SOCKET_INFO;
static SOCKET_INFO *socketInfo;
static const SOCKET_INFO socketInfoTemplate = \
{ INVALID_SOCKET, CRYPT_ERROR, 0 };
/* Initialise and shut down the socket pool */
static int initSocketPool( void )
{
int i;
/* Allocate and clear the socket pool */
if( ( socketInfo = malloc( SOCKETPOOL_SIZE * sizeof( SOCKET_INFO ) ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
memcpy( &socketInfo[ i ], &socketInfoTemplate,
sizeof( SOCKET_INFO ) );
return( CRYPT_OK );
}
static void endSocketPool( void )
{
free( socketInfo );
}
/* Create/add and and remove a socket to/from the pool. The difference
between creating and adding a socket is that newSocket() creates and
adds a completely new socket while addSocket() adds an externally-
created (via accept()) socket */
static int newSocket( SOCKET *newSocket, const int port )
{
SOCKET netSocket;
int i;
/* Clear return value */
*newSocket = INVALID_SOCKET;
enterMutex( MUTEX_SOCKETPOOL );
/* If this is a server socket (ie one bound to a specific port), check
to see whether there's already a socket bound to this port. If there
is, return the existing socket rather than creating a new one */
if( port != CRYPT_UNUSED )
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
if( socketInfo[ i ].port == port )
{
socketInfo[ i ].refCount++;
*newSocket = socketInfo[ i ].netSocket;
exitMutex( MUTEX_SOCKETPOOL );
/* The socket already exists, don't perform any further
initialisation with it */
return( CRYPT_OK );
}
/* Create a new socket entry */
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
if( socketInfo[ i ].port == CRYPT_ERROR )
break;
if( i == SOCKETPOOL_SIZE )
return( CRYPT_ERROR_MEMORY ); /* Should never happen */
if( isBadSocket( netSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) )
return( CRYPT_ERROR_OPEN );
socketInfo[ i ].netSocket = netSocket;
socketInfo[ i ].port = port;
socketInfo[ i ].refCount = 0;
*newSocket = netSocket;
/* If we're creating a new server socket we can't unlock the socket info
yet because we need to bind it to a port before we do anything else
with it. If we were to unlock the socket info, another thread could
perform an accept() on the incompletely set up socket, so we return
with the socket info still locked. When the caller has finished
setting it up, they'll call newSocketDone() to signal that the socket
is ready for use */
if( port != CRYPT_UNUSED )
return( OK_SPECIAL );
exitMutex( MUTEX_SOCKETPOOL );
return( CRYPT_OK );
}
static void newSocketDone( void )
{
/* The caller has finished setting up a new server socket, unlock the
socket info to allow others to access it */
exitMutex( MUTEX_SOCKETPOOL );
}
static int addSocket( const SOCKET netSocket )
{
int i;
enterMutex( MUTEX_SOCKETPOOL );
/* Add an existing socket entry */
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
if( socketInfo[ i ].port == CRYPT_ERROR )
break;
if( i == SOCKETPOOL_SIZE )
return( CRYPT_ERROR_MEMORY ); /* Should never happen */
socketInfo[ i ].netSocket = netSocket;
socketInfo[ i ].port = CRYPT_UNUSED;
socketInfo[ i ].refCount = 0;
exitMutex( MUTEX_SOCKETPOOL );
return( CRYPT_OK );
}
static void deleteSocket( const SOCKET netSocket )
{
int i;
enterMutex( MUTEX_SOCKETPOOL );
/* Find the entry for this socket in the pool. There may not be one
present if the pool has received a shutdown signal and closed all
network sockets, so if we don't find it we exit normally */
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
if( socketInfo[ i ].netSocket == netSocket )
break;
if( i == SOCKETPOOL_SIZE )
{
exitMutex( MUTEX_SOCKETPOOL );
return;
}
/* Decrement the socket's reference count */
socketInfo[ i ].refCount--;
if( socketInfo[ i ].refCount < 0 )
{
/* If the reference count has reached zero, close the socket
and delete the pool entry */
closesocket( socketInfo[ i ].netSocket );
memcpy( &socketInfo[ i ], &socketInfoTemplate,
sizeof( SOCKET_INFO ) );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -