📄 dbxodbc.c
字号:
DBMS_FLAG_BINARYBLOBS : DBMS_FLAG_NONE;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Database Access Routines *
* *
****************************************************************************/
/* Perform a transaction which 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;
/* If we're aborting a transaction, roll it back, re-eanble autocommit,
and clean up */
if( updateType == DBMS_UPDATE_ABORT )
{
pSQLTransact( dbmsInfo->hEnv, dbmsInfo->hDbc, SQL_ROLLBACK );
pSQLSetConnectOption( dbmsInfo->hDbc, SQL_AUTOCOMMIT, 1 );
pSQLFreeStmt( 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 )
pSQLSetConnectOption( 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 )
pSQLAllocStmt( 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 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 )
{
struct tm *timeInfo = gmtime( &boundDate );
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;
pSQLBindParameter( 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 );
pSQLBindParameter( 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 = pSQLExecute( dbmsInfo->hStmt );
else
{
char query[ MAX_SQL_QUERY_SIZE ];
convertQuery( dbmsInfo, query, command );
retCode = pSQLExecDirect( 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 */
pSQLParamData( dbmsInfo->hStmt, &pToken );
retCode = pSQLPutData( dbmsInfo->hStmt, ( PTR ) boundData,
boundDataLength );
if( retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO )
retCode = pSQLParamData( 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 we actually
changed something */
if( command != NULL && !strnicmp( command, "DELETE", 6 ) )
{
SDWORD rowCount;
pSQLRowCount( 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 */
retCode = pSQLTransact( dbmsInfo->hEnv, dbmsInfo->hDbc, /* VC++ braindamage */
( UWORD ) ( cryptStatusError( status ) ? \
SQL_ROLLBACK : SQL_COMMIT ) );
pSQLSetConnectOption( 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 )
{
pSQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
dbmsInfo->hStmt = NULL;
}
return( status );
}
/* Perform a transaction which 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 = pSQLFetch( 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 = pSQLGetData( 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 non-ASCII-encoded buffer */
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;
/* 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 the
SQLFreeStmt() doesn't return an error code to tell is something
was still happening */
pSQLCancel( dbmsInfo->hStmt );
pSQLFreeStmt( 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 );
pSQLFreeStmt( 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 */
pSQLAllocStmt( 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 the database
doesn't start sucking across huge amounts of data when it's
not necessary */
pSQLSetConnectOption( dbmsInfo->hStmt, SQL_MAX_ROWS, 1 );
pSQLSetStmtOption( dbmsInfo->hStmt, SQL_CONCURRENCY,
SQL_CONCUR_READ_ONLY );
/* Bind in any necessary parameters to the hstmt */
if( boundDate != 0 )
{
struct tm *timeInfo = gmtime( &boundDate );
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;
pSQLBindParameter( 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 = pSQLExecDirect( 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 );
pSQLFreeStmt( dbmsInfo->hStmt, SQL_DROP );
dbmsInfo->hStmt = NULL;
return( status );
}
if( queryType != DBMS_QUERY_START )
{
pSQLFreeStmt( 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 )
{
*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"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -