📄 dbxodbc.c
字号:
*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( ×tampInfo, 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,
×tampInfo, 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( ×tampInfo, 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,
×tampInfo, 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 + -