📄 tcp.c
字号:
warn them about this */
fprintf( stderr, "Warning: Conflicting SIGIO handling detected in "
"UnixWare socket bug\n workaround, file " __FILE__
", line %d. This may cause\n false SIGIO/SIGPOLL "
"errors.\n", __LINE__ );
sigaction( SIGIO, &oact, &act );
}
#endif /* UnixWare/SCO */
/* Set up the socket pool state information */
return( initSocketPool() );
}
void netEndTCP( void )
{
/* Clean up the socket pool state information */
endSocketPool();
#ifdef __SCO_VERSION__
signal( SIGIO, SIG_DFL );
#endif /* UnixWare/SCO */
}
static BOOLEAN transportOKFunction( void )
{
#if defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ )
static BOOLEAN transportOK = FALSE;
if( !transportOK )
{
SOCKET netSocket;
/* If the networking subsystem isn't enabled, attempting any network
operations will return ENOENT (which isn't a normal return code,
but is the least inappropriate thing to return). In order to
check this before we get deep into the networking code, we create
a test socket here to make sure that everything is OK. If the
network transport is unavailable, we re-try each time we're
called in case it's been enabled in the meantime */
if( !isBadSocket( netSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) )
{
closesocket( netSocket );
transportOK = TRUE;
}
}
return( transportOK );
#else
return( TRUE );
#endif /* OS-specific socket availability check */
}
#endif /* __WINDOWS__ */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Map of common error codes to strings. The error code supplied by the
caller is usually used as the return status code, however if a more
specific error code than the default is available it's specified via the
cryptSpecificCode member */
typedef struct {
const int errorCode; /* Native error code */
const int cryptSpecificCode;/* Specific cryptlib error code */
const BOOLEAN isFatal; /* Seriousness level */
const char *errorString; /* Error message */
} SOCKETERROR_INFO;
#ifdef __WINDOWS__
static const FAR_BSS SOCKETERROR_INFO socketErrorInfo[] = {
{ WSAECONNREFUSED, CRYPT_ERROR_PERMISSION, TRUE,
"WSAECONNREFUSED: The attempt to connect was rejected" },
{ WSAEADDRNOTAVAIL, CRYPT_ERROR_NOTFOUND, TRUE,
"WSAEADDRNOTAVAIL: The remote address is not a valid address" },
{ WSAECONNABORTED, CRYPT_OK, TRUE,
"WSAECONNABORTED: Connection was terminated due to a time-out or "
"other failure" },
{ WSAECONNRESET, CRYPT_OK, TRUE,
"WSAECONNRESET: Connection was reset by the remote host executing "
"a close" },
{ WSAEHOSTUNREACH, CRYPT_OK, TRUE,
"WSAEHOSTUNREACH: Remote host cannot be reached from this host at "
"this time" },
{ WSAEMSGSIZE, CRYPT_ERROR_OVERFLOW, FALSE,
"WSAEMSGSIZE: Message is larger than the maximum supported by the "
"underlying transport" },
{ WSAENETDOWN, CRYPT_OK, FALSE,
"WSAENETDOWN: The network subsystem has failed" },
{ WSAENETRESET, CRYPT_OK, FALSE,
"WSAENETRESET: Connection was broken due to keep-alive detecting a "
"failure while operation was in progress" },
{ WSAENETUNREACH, CRYPT_ERROR_NOTAVAIL, FALSE,
"WSAENETUNREACH: Network cannot be reached from this host at this "
"time" },
{ WSAENOBUFS, CRYPT_ERROR_MEMORY, FALSE,
"WSAENOBUFS: No buffer space available" },
{ WSAENOTCONN, CRYPT_OK, TRUE,
"WSAENOTCONN: Socket is not connected" },
{ WSAETIMEDOUT, CRYPT_ERROR_TIMEOUT, FALSE,
"WSAETIMEDOUT: Function timed out before completion" },
{ WSAHOST_NOT_FOUND, CRYPT_ERROR_NOTFOUND, FALSE,
"WSAHOST_NOT_FOUND: Host not found" },
{ WSATRY_AGAIN, CRYPT_OK, FALSE,
"WSATRY_AGAIN: Host not found (non-authoritative)" },
{ WSANO_ADDRESS, CRYPT_OK, FALSE,
"WSANO_ADDRESS: No address record available for this name" },
{ WSANO_DATA, CRYPT_OK, FALSE,
"WSANO_DATA: Valid name, no data record of requested type" },
{ CRYPT_ERROR }
};
#define hostErrorInfo socketErrorInfo /* Winsock uses unified error codes */
#define TIMEOUT_ERROR WSAETIMEDOUT /* Code for timeout error */
#else
static const FAR_BSS SOCKETERROR_INFO socketErrorInfo[] = {
{ EADDRNOTAVAIL, CRYPT_ERROR_NOTFOUND, TRUE,
"EADDRNOTAVAIL: Specified address is not available from the local "
"machine" },
{ ECONNREFUSED, CRYPT_ERROR_PERMISSION, TRUE,
"ECONNREFUSED: Attempt to connect was rejected" },
{ EINTR, CRYPT_OK, FALSE,
"EINTR: Function was interrupted by a signal" },
{ EMFILE, CRYPT_OK, FALSE,
"EMFILE: Per-process descriptor table is full" },
#if !( defined( __PALMOS__ ) || defined( __SYMBIAN32__ ) )
{ ECONNABORTED, CRYPT_OK, TRUE,
"ECONNABORTED: Software caused connection abort" },
#endif /* PalmOS || Symbian OS */
#ifndef __SYMBIAN32__
{ ECONNRESET, CRYPT_OK, TRUE,
"ECONNRESET: Connection was forcibly closed by remote host" },
{ EMSGSIZE, CRYPT_ERROR_OVERFLOW, FALSE,
"EMSGSIZE: Message is too large to be sent all at once" },
{ ENETUNREACH, CRYPT_OK, FALSE,
"ENETUNREACH: No route to the network or host is present" },
{ ENOBUFS, CRYPT_ERROR_MEMORY, FALSE,
"ENOBUFS: Insufficient system resources available to complete the "
"call" },
{ ENOTCONN, CRYPT_OK, TRUE,
"ENOTCONN: Socket is not connected" },
#endif /* Symbian OS */
{ ETIMEDOUT, CRYPT_ERROR_TIMEOUT, FALSE,
"ETIMEDOUT: Function timed out before completion" },
{ HOST_NOT_FOUND, CRYPT_ERROR_NOTFOUND, TRUE,
"HOST_NOT_FOUND: Not an official hostname or alias" },
{ NO_ADDRESS, CRYPT_ERROR_NOTFOUND, TRUE,
"NO_ADDRESS: Name is valid but does not have an IP address at the "
"name server" },
{ TRY_AGAIN, CRYPT_OK, FALSE,
"TRY_AGAIN: Local server did not receive a response from an "
"authoritative server" },
{ CRYPT_ERROR }
};
#define TIMEOUT_ERROR ETIMEDOUT /* Code for timeout error */
static const FAR_BSS SOCKETERROR_INFO hostErrorInfo[] = {
{ HOST_NOT_FOUND, CRYPT_ERROR_NOTFOUND, TRUE,
"HOST_NOT_FOUND: Host not found" },
{ NO_ADDRESS, CRYPT_ERROR_NOTFOUND, TRUE,
"NO_ADDRESS: No address record available for this name" },
{ NO_DATA, CRYPT_ERROR_NOTFOUND, TRUE,
"NO_DATA: Valid name, no data record of requested type" },
{ TRY_AGAIN, CRYPT_OK, FALSE,
"TRY_AGAIN: Local server did not receive a response from an "
"authoritative server" },
{ CRYPT_ERROR }
};
#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,
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 );
if( errorInfo[ i ].cryptSpecificCode != CRYPT_OK )
/* There's a more specific error code than the generic one
that we've been given available, use that instead */
status = errorInfo[ i ].cryptSpecificCode;
if( errorInfo[ i ].isFatal )
/* It's a fatal error, make it persistent for the stream */
stream->status = status;
break;
}
return( status );
}
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, status ) );
}
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, status ) );
}
int setSocketError( STREAM *stream, const char *errorMessage,
const int status, const BOOLEAN isFatal )
{
/* Set a cryptlib-supplied socket error message. Since this doesn't
correspond to any system error, we clear the error code */
stream->errorCode = 0;
strcpy( stream->errorMessage, errorMessage );
if( isFatal )
/* It's a fatal error, make it persistent for the stream */
stream->status = status;
return( status );
}
#if defined( __BEOS__ ) && !defined( BONE_VERSION )
/* BeOS doesn't support checking for anything except readability in select()
and only supports one or two socket options, so we define our own
versions of these functions that no-op out unsupported options */
#undef select /* Restore normal select() around the wrapper */
static int my_select( int socket_range, struct fd_set *read_bits,
struct fd_set *write_bits,
struct fd_set *exception_bits,
struct timeval *timeout )
{
/* BeOS doesn't support nonblocking connects, it always waits about a
minute for the connect and then times out, so it we get a wait on a
connecting socket we report it as being successful by exiting with
the fds as set by the caller and a successful return status */
if( read_bits != NULL && write_bits != NULL )
return( 1 );
/* If we're checking for writeability, the best that we can do is to
always report the socket as writeable. Since the socket is a
blocking socket, the data will (eventually) get written */
if( read_bits == NULL && write_bits != NULL )
{
if( exception_bits != NULL )
FD_ZERO( exception_bits );
return( 1 );
}
/* Since BeOS doesn't support checking for writeability or errors, we
have to clear these values before we call select() so the caller
won't find anything still set when we return */
if( write_bits != NULL )
FD_ZERO( write_bits );
if( exception_bits != NULL )
FD_ZERO( exception_bits );
return( select( socket_range, read_bits, NULL, NULL, timeout ) );
}
#define select( sockets, readFD, writeFD, exceptFD, timeout ) \
my_select( sockets, readFD, writeFD, exceptFD, timeout )
static int my_setsockopt( int socket, int level, int option,
const void *data, uint size )
{
if( option != SO_NONBLOCK && option != SO_REUSEADDR )
return( 0 );
return( setsockopt( socket, level, option, data, size ) );
}
static int my_getsockopt( int socket, int level, int option,
void *data, uint *size )
{
BYTE buffer[ 8 ];
int count;
if( option != SO_ERROR )
return( 0 );
*( ( int * ) data ) = 0; /* Clear return status */
/* It's unclear whether the following setsockopt actually does anything
under BeOS or not. If it fails, the alternative below may work */
#if 1
return( setsockopt( socket, level, option, data, *size ) );
#else
count = recv( socket, buffer, 0, 0 );
printf( "recv( 0 ) = %d, errno = %d.\n", count, errno );
if( count < 0 )
*( ( int * ) data ) = errno;
#endif /* 1 */
}
#endif /* BeOS without BONE */
/****************************************************************************
* *
* Network Socket Manager *
* *
****************************************************************************/
/* cryptlib's separation kernel causes some problems with objects that 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 (actually we could create some sort of lookback socket
and wait for it alongside the listen socket in the pre-accept select wait,
signalling a shutdown by closing the loopback socket, but this starts to
get ugly). In order to work around this we maintain a socket pool that
serves two functions:
- Maintains a list of sockets that 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 (16 in
resource-constrained environments) both as a safety feature to protect
against runaway apps and because cryptlib was never designed to function
as a high-volume server application. If necessary this can be changed to
dynamically expand the socket pool in the same way that the kernel
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -