📄 net.c
字号:
/****************************************************************************
* *
* Network Stream I/O Functions *
* Copyright Peter Gutmann 1993-2005 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "stream.h"
#elif defined( INC_CHILD )
#include "stream.h"
#else
#include "io/stream.h"
#endif /* Compiler-specific includes */
#ifdef USE_TCP
/* Network streams can work on multiple levels. At the lowest level we have
the raw network I/O layer, handled by calling setAccessMethodXXX(), which
hooks up the transport-level I/O functions. If there's a requirement to
replace the built-in network I/O, it can be done by replacing the
functionality at this level.
Layered on top of the transport-level I/O via setStreamLayerXXX() is an
optional higher layer protocol such as HTTP, which is added by calling
the appropriate function to layer the higher-level protocol over the
transport-level I/O. Alternatively, we can use setStreamLayerDirect()
to just pass the call straight down to the transport layer.
In addition to these two layers, the higher level read requires an extra
buffering layer in order to avoid making many calls to the transport-
level I/O function, which is a particular problem for HTTP which has to
take input a character at a time. To avoid this problem, we use the
bufferedRead layer which reads ahead as far as it can and then feeds the
buffered result back to the caller as required. We also need to use write
buffering to avoid potential problems with interactions with some
transport layers, details are given in the comment for the buffered write
function.
The layering looks as follows:
--- httpRead ---+-- bufferedRead ---+--- tcpRead
cmpRead |
+--- clibRead
|
------------------------------------+--- otherRead
--- httpWrite --+-- bufferedWrite --+---- tcpWrite
cmpWrite |
+---- clibWrite
|
------------------------------------+---- otherWrite
When we allocate the readahead/write buffers we try and make them an
optimal size to minimise unnecessary copying and not negatively affect
network I/O. If we make them too big, we'll have to move too much data
around when we partially empty them. If we make them too small, the
buffering effect is suboptimal. Since what we're buffering is PKI
traffic, a 4K buffer should get most messages in one go. This also
matches many network stacks that use 4K I/O buffers, the BSD default */
#define NETWORK_BUFFER_SIZE 4096
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Copy error information from a cryptlib transport-layer session into a
stream */
static int getSessionErrorInfo( STREAM *stream, const int errorStatus )
{
RESOURCE_DATA msgData;
int status;
status = krnlSendMessage( stream->iTransportSession,
IMESSAGE_GETATTRIBUTE, &stream->errorCode,
CRYPT_ATTRIBUTE_INT_ERRORCODE );
if( cryptStatusError( status ) )
stream->errorCode = CRYPT_OK;
setMessageData( &msgData, stream->errorMessage, MAX_ERRMSG_SIZE );
krnlSendMessage( stream->iTransportSession, IMESSAGE_GETATTRIBUTE,
&msgData, CRYPT_ATTRIBUTE_INT_ERRORMESSAGE );
return( errorStatus );
}
/****************************************************************************
* *
* URL Processing Functions *
* *
****************************************************************************/
/* Perform various string-processing operations */
static int strFindCh( const char *str, const int strLen, const char findCh )
{
int i;
for( i = 0; i < strLen; i++ )
if( str[ i ] == findCh )
return( i );
return( -1 );
}
static int strFindStr( const char *str, const int strLen,
const char *findStr, const int findStrLen )
{
const char findCh = *findStr;
int i;
for( i = 0; i < strLen - findStrLen; i++ )
if( str[ i ] == findCh && \
!strCompare( str + i, findStr, findStrLen ) )
return( i );
return( -1 );
}
static int strStripWhitespace( char **newStringPtr, const char *string,
const int stringLen )
{
int startPos, endPos;
/* Skip leading and trailing whitespace */
for( startPos = 0;
startPos < stringLen && string[ startPos ] <= ' ';
startPos++ );
*newStringPtr = ( char * ) string + startPos;
for( endPos = stringLen;
endPos > startPos && string[ endPos - 1 ] <= ' ';
endPos-- );
return( endPos - startPos );
}
/* Parse a URI into <schema>://<host>[:<port>]/<path>[?<query>] components */
static int parseURL( URL_INFO *urlInfo, const char *url, const int urlLen,
const int defaultPort )
{
static const FAR_BSS struct {
const char *schema;
const int schemaLength;
const URL_TYPE type;
} urlSchemaInfo[] = {
{ "http://", 7, URL_TYPE_HTTP },
{ "https://", 8, URL_TYPE_HTTPS },
{ "ssh://", 6, URL_TYPE_SSH },
{ "scp://", 6, URL_TYPE_SSH },
{ "sftp://", 7, URL_TYPE_SSH },
{ "cmp://", 6, URL_TYPE_CMP },
{ "tsp://", 6, URL_TYPE_TSP },
{ NULL, 0, URL_TYPE_NONE }
};
char *strPtr;
int offset, length;
/* Clear return values */
memset( urlInfo, 0, sizeof( URL_INFO ) );
if( defaultPort != CRYPT_UNUSED )
urlInfo->port = defaultPort;
/* Skip leading and trailing whitespace and syntactic sugar */
length = strStripWhitespace( &strPtr, url, urlLen );
if( length <= 0 )
return( CRYPT_ERROR_BADDATA );
if( length >= MAX_URL_SIZE )
return( CRYPT_ERROR_OVERFLOW );
if( ( offset = strFindStr( strPtr, length, "://", 3 ) ) >= 0 )
{
int i;
/* Extract the URI schema */
urlInfo->schema = strPtr;
urlInfo->schemaLen = offset + 3;
length -= offset + 3;
if( length <= 0 )
return( CRYPT_ERROR_BADDATA );
strPtr += offset + 3;
length = strStripWhitespace( &strPtr, strPtr, length );
if( length <= 0 )
return( CRYPT_ERROR_BADDATA );
/* Check whether the schema is one that we recognise */
for( i = 0; urlSchemaInfo[ i ].type != URL_TYPE_NONE; i++ )
if( urlSchemaInfo[ i ].schemaLength == urlInfo->schemaLen && \
!strCompare( urlSchemaInfo[ i ].schema, urlInfo->schema,
urlInfo->schemaLen ) )
break;
urlInfo->type = urlSchemaInfo[ i ].type;
}
/* Check for user info before an '@' sign */
if( ( offset = strFindCh( strPtr, length, '@' ) ) >= 0 )
{
/* Extract the user info */
urlInfo->userInfoLen = \
strStripWhitespace( ( char ** ) &urlInfo->userInfo,
strPtr, offset );
length -= offset + 1;
if( length <= 0 || urlInfo->userInfoLen <= 0 )
return( CRYPT_ERROR_BADDATA );
strPtr += offset + 1;
length = strStripWhitespace( &strPtr, strPtr, length );
if( length <= 0 )
return( CRYPT_ERROR_BADDATA );
}
/* IPv6 addresses use colons in their string representation, RFC 2732
requires that IPv6 addresses in URLs be delimited by square brackets
so if we find one at the start of the URI we treat it as an IPv6
address */
if( *strPtr == '[' && \
( length != 12 || strCompareZ( strPtr, "[Autodetect]" ) ) )
{
/* Strip the leading '[' delimiter */
length = strStripWhitespace( &strPtr, strPtr + 1, length - 1 );
if( length <= 0 )
return( CRYPT_ERROR_BADDATA );
/* Locate the end of the RFC 2732 IPv6 address. Trailing whitespace
will be stripped later */
if( ( offset = strFindCh( strPtr, length, ']' ) ) <= 0 )
return( CRYPT_ERROR_BADDATA );
urlInfo->host = strPtr;
urlInfo->hostLen = offset;
strPtr += offset + 1;
length -= offset + 1;
}
else
{
int offset2;
/* It's a non-IPv6 host name, check whether there's anything
following the name */
urlInfo->host = strPtr;
offset = strFindCh( strPtr, length, ':' );
offset2 = strFindCh( strPtr, length, '/' );
if( offset < 0 )
offset = offset2;
else
{
assert( offset >= 0 );
if( offset2 >= 0 )
offset = min( offset, offset2 );
}
if( offset <= 0 )
{
/* It's a standalone server name, we're done */
urlInfo->hostLen = length;
return( CRYPT_OK );
}
/* There's port/location info following the server name. Trailing
whitespace will be stripped later */
urlInfo->hostLen = offset;
strPtr += offset;
length -= offset;
}
urlInfo->hostLen = strStripWhitespace( ( char ** ) &urlInfo->host,
urlInfo->host, urlInfo->hostLen );
if( urlInfo->hostLen <= 0 )
return( CRYPT_ERROR_BADDATA );
/* If there's nothing beyond the host name, we're done */
if( length <= 0 )
return( CRYPT_OK );
length = strStripWhitespace( &strPtr, strPtr, length );
if( length <= 0 )
return( CRYPT_ERROR_BADDATA );
/* Parse the remainder of the URI into port/location */
if( *strPtr == ':' )
{
char portBuffer[ 16 ];
const int portStrLen = min( length - 1, 15 );
int port;
/* Get the port to connect to. If it's an invalid port we ignore it
and use the default one, which was set earlier */
if( portStrLen <= 0 )
return( CRYPT_ERROR_BADDATA );
memcpy( portBuffer, strPtr + 1, portStrLen );
portBuffer[ portStrLen ] = '\0';
port = aToI( portBuffer );
if( port >= 22 && port < 65535 )
urlInfo->port = port;
}
if( ( offset = strFindCh( strPtr, length, '/' ) ) >= 0 )
{
const int locationLength = length - offset;
if( locationLength <= 0 )
return( CRYPT_ERROR_BADDATA );
urlInfo->locationLen = \
strStripWhitespace( ( char ** ) &urlInfo->location,
strPtr + offset, locationLength );
if( urlInfo->locationLen <= 0 )
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
/* Copy parsed URL info to a stream structure */
static int copyUrlToStream( STREAM *stream, const URL_INFO *urlInfo )
{
if( ( stream->host = clAlloc( "copyUrlToStream", \
urlInfo->hostLen + 1 ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memcpy( stream->host, urlInfo->host, urlInfo->hostLen );
stream->host[ urlInfo->hostLen ] = '\0';
if( urlInfo->location != NULL )
{
if( ( stream->path = \
clAlloc( "copyUrlToStream", urlInfo->locationLen + 1 ) ) == NULL )
{
clFree( "copyUrlToStream", stream->host );
return( CRYPT_ERROR_MEMORY );
}
memcpy( stream->path, urlInfo->location, urlInfo->locationLen );
stream->path[ urlInfo->locationLen ] = '\0';
}
stream->port = urlInfo->port;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Transport-layer Functions *
* *
****************************************************************************/
/* Map the upper-layer I/O functions directly to the transport-layer
equivalent. This is used if we're performing raw I/O without any
intermediate protocol layers or buffering */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -