⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dbxodbc.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
	*featureFlags = dbmsInfo->hasBinaryBlobs ? \
					DBMS_HAS_BINARYBLOBS : DBMS_HAS_NONE;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*						 	Database Access Routines						*
*																			*
****************************************************************************/

/* Perform a transaction that updates the database without returning any
   data */

static int performUpdate( DBMS_STATE_INFO *dbmsInfo, const char *command,
						  const void *boundData, const int boundDataLength,
						  const time_t boundDate,
						  const DBMS_UPDATE_TYPE updateType )
	{
	TIMESTAMP_STRUCT timestampInfo;
	UWORD paramNo = 1;
	RETCODE retCode;
	int status = CRYPT_OK;

	assert( isWritePtr( dbmsInfo, DBMS_STATE_INFO ) );

	/* If we're aborting a transaction, roll it back, re-enable autocommit,
	   and clean up */
	if( updateType == DBMS_UPDATE_ABORT )
		{
		SQLTransact( dbmsInfo->hEnv, dbmsInfo->hDbc, SQL_ROLLBACK );
		SQLSetConnectOption( dbmsInfo->hDbc, SQL_AUTOCOMMIT, 1 );
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		dbmsInfo->hStmt = NULL;
		return( CRYPT_OK );
		}

	/* If it's the start of a transaction, turn autocommit off */
	if( updateType == DBMS_UPDATE_BEGIN )
		SQLSetConnectOption( dbmsInfo->hDbc, SQL_AUTOCOMMIT, 0 );

	/* Allocate an hstmt unless we're in the middle of a transaction */
	if( updateType != DBMS_UPDATE_CONTINUE && \
		updateType != DBMS_UPDATE_COMMIT )
		SQLAllocStmt( dbmsInfo->hDbc, &dbmsInfo->hStmt );

	/* Bind in any necessary parameters to the hstmt.  This is unlike the
	   behaviour mentioned in the ODBC documentation, which claims that
	   SQLExecDirect() will return SQL_NEED_DATA if it finds a parameter
	   marker.  Instead, we have to bind the parameters before calling
	   SQLExecDirect() and it reads them from the bound location as required.
	   In addition an older version of the ODBC spec required that the
	   cbColDef value never exceed SQL_MAX_MESSAGE_LENGTH, however this is
	   defined to be 512 bytes, which means that we can't add most certs of 
	   any real complexity or with keys > 1K bits, so we pass in the actual 
	   data length here instead.  This works for all ODBC drivers tested */
	if( boundDate != 0 )
		{
		const struct tm *timeInfo = gmtime( &boundDate );

		/* Sanity check on input parameters */
		if( timeInfo == NULL )
			{
			SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
			dbmsInfo->hStmt = NULL;
			return( CRYPT_ERROR_BADDATA );
			}

		memset( &timestampInfo, 0, sizeof( TIMESTAMP_STRUCT ) );
		timestampInfo.year = timeInfo->tm_year + 1900;
		timestampInfo.month = timeInfo->tm_mon + 1;
		timestampInfo.day = timeInfo->tm_mday;
		timestampInfo.hour = timeInfo->tm_hour;
		timestampInfo.minute = timeInfo->tm_min;
		timestampInfo.second = timeInfo->tm_sec;
		SQLBindParameter( dbmsInfo->hStmt, paramNo++, SQL_PARAM_INPUT,
						  SQL_C_TIMESTAMP, SQL_TIMESTAMP, 0, 0,
						  &timestampInfo, 0, NULL );
		}
	if( boundData != NULL )
		{
		dbmsInfo->cbBlobLength = SQL_LEN_DATA_AT_EXEC( boundDataLength );
		SQLBindParameter( dbmsInfo->hStmt, paramNo++, SQL_PARAM_INPUT,
						  SQL_C_BINARY, dbmsInfo->blobType, boundDataLength, 0,
						  ( PTR ) 6, 0, &dbmsInfo->cbBlobLength );
		}

	/* Execute the command/hStmt as appropriate */
	if( command == NULL )
		retCode = SQLExecute( dbmsInfo->hStmt );
	else
		{
		char query[ MAX_SQL_QUERY_SIZE ];

		convertQuery( dbmsInfo, query, command );
		retCode = SQLExecDirect( dbmsInfo->hStmt, query, SQL_NTS );
		}
	if( retCode == SQL_NEED_DATA )
		{
		PTR pToken;

		/* Add the key data and perform a dummy SQLParamData() call to tell
		   the ODBC driver that we've finished with the operation */
		SQLParamData( dbmsInfo->hStmt, &pToken );
		retCode = SQLPutData( dbmsInfo->hStmt, ( PTR ) boundData,
							  boundDataLength );
		if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
			retCode = SQLParamData( dbmsInfo->hStmt, &pToken );
		}
	if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
		{
		/* If we hit an error at this point we can only exit if we're not
		   finishing a transaction.  If we are, the commit turns into an
		   abort further down */
		status = getErrorInfo( dbmsInfo, SQL_ERRLVL_2, CRYPT_ERROR_WRITE );
		if( updateType != DBMS_UPDATE_COMMIT )
			return( status );
		}
	else
		/* If we're performing a delete, the operation will succeed even
		   though nothing was found to delete so we make sure that we 
		   actually changed something */
		if( command != NULL && !strCompare( command, "DELETE", 6 ) )
			{
			SDWORD rowCount;

			SQLRowCount( dbmsInfo->hStmt, &rowCount );
			if( rowCount <= 0 )
				status = CRYPT_ERROR_NOTFOUND;
			}

	/* If it's the end of a transaction, commit the transaction and turn
	   autocommit on again */
	if( updateType == DBMS_UPDATE_COMMIT )
		{
		RETCODE retCode;

		/* If we've had a failure before this point, abort, otherwise
		   commit.  The UWORD cast is necessary in some development
		   environments (although spurious) */
		retCode = SQLTransact( dbmsInfo->hEnv, dbmsInfo->hDbc,
							   ( UWORD  ) ( cryptStatusError( status ) ? \
											SQL_ROLLBACK : SQL_COMMIT ) );
		SQLSetConnectOption( dbmsInfo->hDbc, SQL_AUTOCOMMIT, 1 );
		if( cryptStatusOK( status ) && \
			( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO ) )
			status = getErrorInfo( dbmsInfo, SQL_ERRLVL_2, CRYPT_ERROR_WRITE );
		}

	/* Clean up, unless we're in the middle of a transaction */
	if( updateType != DBMS_UPDATE_BEGIN && \
		updateType != DBMS_UPDATE_CONTINUE )
		{
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		dbmsInfo->hStmt = NULL;
		}

	return( status );
	}

/* Perform a transaction that returns information */

static RETCODE fetchData( DBMS_STATE_INFO *dbmsInfo, char *data,
						  int *dataLength, const int maxLength,
						  const DBMS_QUERY_TYPE queryType )
	{
	const SWORD dataType = ( dbmsInfo->hasBinaryBlobs ) ? \
						   SQL_C_BINARY : SQL_C_CHAR;
	RETCODE retCode;
	SDWORD length;

	/* Get the results of the transaction */
	retCode = SQLFetch( dbmsInfo->hStmt );
	if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
		return( retCode );

	/* If we're just doing a presence check, we don't bother fetching data */
	if( queryType == DBMS_QUERY_CHECK )
		return( SQL_SUCCESS );

	/* Read the data */
	retCode = SQLGetData( dbmsInfo->hStmt, 1, dataType, data, maxLength, 
						  &length );
	*dataLength = ( int ) length;

	return( retCode );
	}

static int performQuery( DBMS_STATE_INFO *dbmsInfo, const char *command,
						 char *data, int *dataLength, time_t boundDate,
						 const DBMS_QUERY_TYPE queryType )
	{
	/* We have to explicitly set the maximum length indicator because some
	   sources will helpfully zero-pad the data to the maximum indicated size,
	   which is smaller for the binary data */
	const int maxLength = dbmsInfo->hasBinaryBlobs ? \
						  MAX_CERT_SIZE : MAX_QUERY_RESULT_SIZE;
	const BOOLEAN isQuery = ( data == NULL ) ? TRUE : FALSE;
	char query[ MAX_SQL_QUERY_SIZE ];
	TIMESTAMP_STRUCT timestampInfo;
	RETCODE retCode;

	assert( isWritePtr( dbmsInfo, DBMS_STATE_INFO ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );

	/* Clear return value */
	*dataLength = 0;

	/* If we're cancelling a continuing query, clean up and exit */
	if( queryType == DBMS_QUERY_CANCEL )
		{
		/* Cancel any outstanding requests and free the statement handle.
		   The cancel isn't strictly necessary, but it means that the
		   SQLFreeStmt() doesn't return an error code to tell us that 
		   something was still happening */
		SQLCancel( dbmsInfo->hStmt );
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		dbmsInfo->hStmt = NULL;
		return( CRYPT_OK );
		}

	/* If we're in the middle of a continuing query, fetch the next set of
	   results */
	if( queryType == DBMS_QUERY_CONTINUE )
		{
		retCode = fetchData( dbmsInfo, data, dataLength, maxLength,
							 DBMS_QUERY_CONTINUE );
		if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
			{
			int status;

			status = getErrorInfo( dbmsInfo, SQL_ERRLVL_2, CRYPT_ERROR_READ );
			SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
			dbmsInfo->hStmt = NULL;

			/* If we ran out of results we explicitly signal to the caller
			   that the query has completed */
			return( ( status == CRYPT_ERROR_NOTFOUND ) ? \
					CRYPT_ERROR_COMPLETE : CRYPT_ERROR_READ );
			}

		return( CRYPT_OK );
		}

	/* Allocate an hstmt and set the cursor concurrency to read-only */
	SQLAllocStmt( dbmsInfo->hDbc, &dbmsInfo->hStmt );
	if( queryType != DBMS_QUERY_START )
		/* Only return a maximum of a single row in response to a point
		   query.  This is a simple optimisation to ensure that the database
		   doesn't start sucking across huge amounts of data when it's not 
		   necessary */
		SQLSetConnectOption( dbmsInfo->hStmt, SQL_MAX_ROWS, 1 );
	SQLSetStmtOption( dbmsInfo->hStmt, SQL_CONCURRENCY,
					  SQL_CONCUR_READ_ONLY );

	/* Bind in any necessary parameters to the hstmt */
	if( boundDate != 0 )
		{
		struct tm *timeInfo = gmtime( &boundDate );

		/* Sanity check on input parameters */
		if( timeInfo == NULL )
			{
			SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
			dbmsInfo->hStmt = NULL;
			return( CRYPT_ERROR_BADDATA );
			}

		memset( &timestampInfo, 0, sizeof( TIMESTAMP_STRUCT ) );
		timestampInfo.year = timeInfo->tm_year + 1900;
		timestampInfo.month = timeInfo->tm_mon + 1;
		timestampInfo.day = timeInfo->tm_mday;
		timestampInfo.hour = timeInfo->tm_hour;
		timestampInfo.minute = timeInfo->tm_min;
		timestampInfo.second = timeInfo->tm_sec;
		SQLBindParameter( dbmsInfo->hStmt, 1, SQL_PARAM_INPUT,
						  SQL_C_TIMESTAMP, SQL_TIMESTAMP, 0, 0,
						  &timestampInfo, 0, NULL );
		}

	/* Execute the SQL statement */
	convertQuery( dbmsInfo, query, command );
	retCode = SQLExecDirect( dbmsInfo->hStmt, query, SQL_NTS );
	if( queryType != DBMS_QUERY_START && \
		( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO ) )
		retCode = fetchData( dbmsInfo, data, dataLength, maxLength,
							 queryType );

	/* Handle any errors */
	if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
		{
		int status;

		status = getErrorInfo( dbmsInfo, SQL_ERRLVL_2, CRYPT_ERROR_READ );
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		dbmsInfo->hStmt = NULL;
		return( status );
		}
	if( queryType != DBMS_QUERY_START )
		{
		SQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
		dbmsInfo->hStmt = NULL;
		}

	return( CRYPT_OK );
	}

/* Fetch extended error information from the database state info */

static void performErrorQuery( DBMS_STATE_INFO *dbmsInfo, int *errorCode,
							   char *errorMessage )
	{
	assert( isWritePtr( dbmsInfo, DBMS_STATE_INFO ) );
	assert( isWritePtr( errorCode, sizeof( int ) ) );
	assert( isWritePtr( errorMessage, MAX_ERRMSG_SIZE ) );

	*errorCode = dbmsInfo->errorCode;
	strcpy( errorMessage, dbmsInfo->errorMessage );
	}

/* Pull in the shared database RPC routines, renaming the generic dispatch
   function to the ODBC-specific one which is called directly by the
   marshalling code */

#define processCommand( stateInfo, buffer ) \
		odbcProcessCommand( stateInfo, buffer )

#include "dbx_rpc.c"

#endif /* USE_ODBC */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -