psqlodbc.git / summary / log / commit / refs
commit fc4df26bb85ab91e6d3a47dbd002f96290ac258c
Author: Hiroshi Inoue <inoue@tpf.co.jp>
Date: Sat Oct 27 01:14:00 2007 +0000
The version is now 8.2.0501.
1. Correct the COLUMN_LENGTH return value of SQLColumns() for varchar/bpchar
type columns (Unicode driver).
2. Change to not return database name if case of MS Query.
3. The first cut to use Windows SSPI. The trial to use Schannel service
for SSL support.
4. Be more careful about <for locking clause> in UseDeclareFetch mode.
Add missing? "for read only" clause for read only queries for 8.3
or later servers for safety.
connection.c | 246 +++++++++----
connection.h | 6 +
convert.c | 95 +++--
dlg_wingui.c | 2 +-
environ.c | 8 -
info.c | 9 +-
loadlib.c | 8 +
loadlib.h | 3 +
pgtypes.c | 2 +-
psqlodbc.c | 28 +-
qresult.c | 9 +-
socket.c | 79 +++-
socket.h | 6 +
sspisvcs.c | 1168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sspisvcs.h | 25 ++
statement.c | 4 +
version.h | 8 +-
win32.mak | 45 ++-
win64.mak | 19 +-
19 files changed, 1608 insertions(+), 162 deletions(-)
diff --git a/connection.c b/connection.c
index d281ad7..89f3fbc 100644
--- a/connection.c
+++ b/connection.c
@@ -14,13 +14,19 @@
*/
/* Multibyte support Eiji Tokuya 2001-03-15 */
+#ifndef NOT_USE_LIBPQ
#include <libpq-fe.h>
+#endif /* NOT_USE_LIBPQ */
#include "connection.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
-#ifndef WIN32
+#ifdef WIN32
+#ifdef USE_SSPI
+#include "sspisvcs.h"
+#endif /* USE_SSPI */
+#else
#include <errno.h>
#endif /* WIN32 */
@@ -311,23 +317,16 @@ reset_current_schema(ConnectionClass *self)
}
}
+extern int exepgm;
ConnectionClass *
CC_Constructor()
{
- extern int exepgm;
ConnectionClass *rv, *retrv = NULL;
rv = (ConnectionClass *) calloc(sizeof(ConnectionClass), 1);
if (rv != NULL)
{
- // rv->henv = NULL; /* not yet associated with an environment */
-
- // rv->__error_message = NULL;
- // rv->__error_number = 0;
- // rv->sqlstate[0] = '\0';
- // rv->errormsg_created = FALSE;
-
rv->status = CONN_NOT_CONNECTED;
rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */
@@ -352,37 +351,14 @@ CC_Constructor()
#endif /* ODBCVER */
rv->lobj_type = PG_TYPE_LO_UNDEFINED;
-
- // rv->ncursors = 0;
- // rv->ntables = 0;
- // rv->col_info = NULL;
-
- // rv->translation_option = 0;
- // rv->translation_handle = NULL;
- // rv->DataSourceToDriver = NULL;
- // rv->DriverToDataSource = NULL;
rv->driver_version = ODBCVER;
#ifdef WIN32
if (VER_PLATFORM_WIN32_WINDOWS == platformId && rv->driver_version > 0x0300)
rv->driver_version = 0x0300;
#endif /* WIN32 */
- // memset(rv->pg_version, 0, sizeof(rv->pg_version));
- // rv->pg_version_number = .0;
- // rv->pg_version_major = 0;
- // rv->pg_version_minor = 0;
- // rv->ms_jet = 0;
if (1 == exepgm)
rv->ms_jet = 1;
- // rv->unicode = 0;
- // rv->result_uncommitted = 0;
- // rv->schema_support = 0;
rv->isolation = SQL_TXN_READ_COMMITTED;
- // rv->original_client_encoding = NULL;
- // rv->current_client_encoding = NULL;
- // rv->server_encoding = NULL;
- // rv->current_schema = NULL;
- // rv->num_discardp = 0;
- // rv->discardp = NULL;
rv->mb_maxbyte_per_char = 1;
rv->max_identifier_length = -1;
rv->escape_in_literal = ESCAPE_IN_LITERAL;
@@ -1345,6 +1321,7 @@ static char CC_initial_log(ConnectionClass *self, const char *func)
}
static char CC_setenv(ConnectionClass *self);
+#ifndef NOT_USE_LIBPQ
static int LIBPQ_connect(ConnectionClass *self);
static char
LIBPQ_CC_connect(ConnectionClass *self, char password_req, char *salt_para)
@@ -1366,6 +1343,7 @@ LIBPQ_CC_connect(ConnectionClass *self, char password_req, char *salt_para)
return 1;
}
+#endif /* NOT_USE_LIBPQ */
static char
original_CC_connect(ConnectionClass *self, char password_req, char *salt_para)
@@ -1380,8 +1358,11 @@ original_CC_connect(ConnectionClass *self, char password_req, char *salt_para)
char msgbuffer[ERROR_MSG_LENGTH];
char salt[5], notice[512];
CSTR func = "original_CC_connect";
- // char *encoding;
- BOOL startPacketReceived = FALSE;
+ BOOL startPacketReceived = FALSE, anotherVersionRetry;
+#ifdef USE_SSPI
+ int ssl_try_count, ssl_try_no;
+ char ssl_call[2];
+#endif /* USE_SSPI */
mylog("%s: entering...\n", func);
@@ -1395,8 +1376,62 @@ original_CC_connect(ConnectionClass *self, char password_req, char *salt_para)
if (0 == CC_initial_log(self, func))
return 0;
+#ifdef USE_SSPI
+ switch (self->connInfo.sslmode[0])
+ {
+ case 'r':
+ ssl_try_count = 1;
+ ssl_call[0] = 'y';
+ break;
+ case 'p':
+ ssl_try_count = 2;
+ ssl_call[0] = 'y';
+ ssl_call[1] = 'n';
+ break;
+ case 'a':
+ ssl_try_count = 2;
+ ssl_call[0] = 'n';
+ ssl_call[1] = 'y';
+ break;
+ default:
+ ssl_try_count = 0;
+ break;
+ }
+ ssl_try_no = 0;
+#endif /* USE_SSPI */
+ anotherVersionRetry = FALSE;
+
another_version_retry:
+ if (anotherVersionRetry)
+ {
+#ifdef USE_SSPI
+ if (ssl_try_no < ssl_try_count)
+ ssl_try_no++;
+ if (ssl_try_no >= ssl_try_count)
+ {
+#endif /* USE_SSPI */
+ /* retry older version */
+ if (PROTOCOL_62(ci))
+ {
+ CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not construct a socket to the server", func);
+ return 0;
+ }
+ if (PROTOCOL_63(ci))
+ strncpy(ci->protocol, PG62, sizeof(ci->protocol));
+ else if (PROTOCOL_64(ci))
+ strncpy(ci->protocol, PG63, sizeof(ci->protocol));
+ else
+ strncpy(ci->protocol, PG64, sizeof(ci->protocol));
+#ifdef USE_SSPI
+ ssl_try_no = 0;
+ }
+#endif /* USE_SSPI */
+ SOCK_Destructor(sock);
+ self->sock = (SocketClass *) 0;
+ CC_initialize_pg_version(self);
+ anotherVersionRetry = FALSE;
+ }
/*
* If the socket was closed for some reason (like a SQLDisconnect,
* but no SQLFreeConnect then create a socket now.
@@ -1424,6 +1459,50 @@ another_version_retry:
mylog("connection to the server socket succeeded.\n");
inolog("protocol=%s version=%d,%d\n", ci->protocol, self->pg_version_major, self->pg_version_minor);
+#ifdef USE_SSPI
+ if ('y' == ssl_call[ssl_try_no])
+ {
+ struct {
+ Int4 slen;
+ ProtocolVersion pv;
+ } nego_packet;
+ char rnego;
+
+ nego_packet.slen = htonl(sizeof(nego_packet));
+ nego_packet.pv = htonl(PG_NEGOTIATE_SSLMODE);
+ SOCK_put_n_char(sock, (char *) &nego_packet, sizeof(nego_packet));
+ SOCK_flush_output(sock);
+ if (0 != SOCK_get_errcode(sock))
+ {
+ mylog("%s:failed to send a negotiation packet\n", func);
+ goto sockerr_proc;
+ }
+ SOCK_get_n_char(sock, &rnego, 1);
+ if (0 != SOCK_get_errcode(sock))
+ goto sockerr_proc;
+ mylog("nego got '%c'\n", rnego);
+ switch (rnego)
+ {
+ case 'S':
+ break;
+ case 'N':
+ ssl_try_no++;
+ if (ssl_try_no < ssl_try_count &&
+ 'y' != ssl_call[ssl_try_no])
+ break;
+ else
+ goto another_version_retry;
+ default:
+ CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Service negotation failed", func);
+ return 0;
+ }
+ if (!StartupSspiService(sock, SchannelService, NULL))
+ {
+ CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Service negotation failed", func);
+ return 0;
+ }
+ }
+#endif /* USE_SSPI */
if (PROTOCOL_62(ci))
{
sock->reverse = TRUE; /* make put_int and get_int work
@@ -1637,16 +1716,7 @@ inolog("Ekita\n");
return 0;
}
if (retry)
- { /* retry older version */
- if (PROTOCOL_63(ci))
- strncpy(ci->protocol, PG62, sizeof(ci->protocol));
- else if (PROTOCOL_64(ci))
- strncpy(ci->protocol, PG63, sizeof(ci->protocol));
- else
- strncpy(ci->protocol, PG64, sizeof(ci->protocol));
- SOCK_Destructor(sock);
- self->sock = (SocketClass *) 0;
- CC_initialize_pg_version(self);
+ {
goto another_version_retry;
}
@@ -1688,7 +1758,7 @@ sockerr_proc:
{
CC_set_error(self, CONNECTION_NO_SUCH_DATABASE, "The database does not exist on the server\nor user authentication failed.", func);
QR_Destructor(res);
- return 0;
+ return 0;
}
QR_Destructor(res);
@@ -1710,35 +1780,51 @@ inolog("CC_lookup_pg_version\n");
char
CC_connect(ConnectionClass *self, char password_req, char *salt_para)
{
- // StartupPacket sp;
- // StartupPacket6_2 sp62;
- // QResultClass *res;
- // SocketClass *sock;
- ConnInfo *ci = &(self->connInfo);
- // int areq = -1;
- // int beresp;
- // char msgbuffer[ERROR_MSG_LENGTH];
- // char salt[5], notice[512];
- CSTR func = "CC_connect";
- char ret;
- // char *encoding;
- // BOOL startPacketReceived = FALSE;
+ ConnInfo *ci = &(self->connInfo);
+ CSTR func = "CC_connect";
+ char ret;
+#ifndef NOT_USE_LIBPQ
+ BOOL call_libpq = FALSE;
+#endif /* NOT_USE_LIBPQ */
mylog("%s: entering...\n", func);
mylog("sslmode=%s\n", self->connInfo.sslmode);
- if (self->connInfo.sslmode[0] != 'd' ||
- self->connInfo.username[0] == '\0')
+#ifndef NOT_USE_LIBPQ
+ if (self->connInfo.username[0] == '\0')
+ call_libpq = TRUE;
+ else if (self->connInfo.sslmode[0] != 'd')
+#ifdef USE_SSPI
+ {
+ if (0 == (SchannelService & self->svcs_allowed))
+ call_libpq = TRUE;
+ }
+#else
+ call_libpq = TRUE;
+#endif /* USE_SSPI */
+ if (call_libpq)
+ {
ret = LIBPQ_CC_connect(self, password_req, salt_para);
+#ifdef USE_SSPI
+ if (CONN_UNABLE_TO_LOAD_DLL == ret)
+ {
+ self->svcs_allowed |= SchannelService;
+ ret = original_CC_connect(self, password_req, salt_para);
+ }
+#endif /* USE_SSPI */
+ }
else
+#endif /* NOT_USE_LIBPQ */
{
ret = original_CC_connect(self, password_req, salt_para);
+#ifndef NOT_USE_LIBPQ
if (0 == ret && CONN_AUTH_TYPE_UNSUPPORTED == CC_get_errornumber(self))
{
SOCK_Destructor(self->sock);
self->sock = (SocketClass *) 0;
ret = LIBPQ_CC_connect(self, password_req, salt_para);
}
+#endif /* NOT_USE_LIBPQ */
}
if (ret <= 0)
return ret;
@@ -2031,7 +2117,7 @@ static void CC_clear_cursors(ConnectionClass *self, BOOL on_abort)
{
stmt = self->stmts[i];
if (stmt && (res = SC_get_Result(stmt)) &&
- QR_get_cursor(res))
+ (NULL != QR_get_cursor(res)))
{
if ((on_abort && !QR_is_permanent(res)) ||
!QR_is_withhold(res))
@@ -2206,6 +2292,7 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD
/* QR_set_command() dups this string so doesn't need static */
char cmdbuffer[ERROR_MSG_LENGTH + 1];
+ BOOL reduce_round_trip_time = !(flag & IGNORE_ROUND_TRIP);
if (appendq)
{
@@ -2244,6 +2331,28 @@ CC_send_query_append(ConnectionClass *self, const char *query, QueryInfo *qi, UD
return NULL;
}
+ /*
+ * In case the round trip time can be ignored, the query
+ * and the appeneded query would be issued separately.
+ * Otherwise a multiple command query would be issued.
+ */
+ if (appendq && !reduce_round_trip_time)
+ {
+ res = CC_send_query_append(self, query, qi, flag, stmt, NULL);
+ if (QR_command_maybe_successful(res))
+ {
+ cmdres = CC_send_query_append(self, appendq, qi, flag & (~(GO_INTO_TRANSACTION)), stmt, NULL);
+ if (QR_command_maybe_successful(cmdres))
+ res->next = cmdres;
+ else
+ {
+ QR_Destructor(res);
+ res = cmdres;
+ }
+ }
+ return res;
+ }
+
rollback_on_error = (flag & ROLLBACK_ON_ERROR) != 0;
end_with_commit = (flag & END_WITH_COMMIT) != 0;
#define return DONT_CALL_RETURN_FROM_HERE???
@@ -3352,8 +3461,10 @@ CC_send_cancel_request(const ConnectionClass *conn)
if (!sock)
return FALSE;
+#ifndef NOT_USE_LIBPQ
if (sock->via_libpq)
return LIBPQ_send_cancel_request(conn);
+#endif /* NOT_USE_LIBPQ */
/*
* We need to open a temporary connection to the postmaster. Use the
* information saved by connectDB to do this with only kernel calls.
@@ -3441,6 +3552,7 @@ int CC_discard_marked_objects(ConnectionClass *conn)
return 1;
}
+#ifndef NOT_USE_LIBPQ
static int
LIBPQ_connect(ConnectionClass *self)
{
@@ -3474,7 +3586,7 @@ inolog("sock=%p\n", sock);
free(conninfo);
if (!libpqLoaded)
{
- CC_set_error(self, CONN_OPENDB_ERROR, "Couldn't load libpq library", func);
+ CC_set_error(self, CONN_UNABLE_TO_LOAD_DLL, "Couldn't load libpq library", func);
goto cleanup1;
}
sock->via_libpq = TRUE;
@@ -3602,10 +3714,18 @@ LIBPQ_send_cancel_request(const ConnectionClass *conn)
else
return FALSE;
}
+#endif /* NOT_USE_LIBPQ */
const char *CurrCat(const ConnectionClass *conn)
{
- if (conn->schema_support)
+ /*
+ * Returning the database name causes problems in MS Query. It
+ * generates query like: "SELECT DISTINCT a FROM byronnbad3
+ * bad3"
+ */
+ if (2 == exepgm) /* MS Query */
+ return NULL;
+ else if (conn->schema_support)
return conn->connInfo.database;
else
return NULL;
diff --git a/connection.h b/connection.h
index caf4c02..9c46828 100644
--- a/connection.h
+++ b/connection.h
@@ -228,6 +228,7 @@ typedef unsigned int ProtocolVersion;
#define PG_PROTOCOL_64 PG_PROTOCOL(2, 0)
#define PG_PROTOCOL_63 PG_PROTOCOL(1, 0)
#define PG_PROTOCOL_62 PG_PROTOCOL(0, 0)
+#define PG_NEGOTIATE_SSLMODE PG_PROTOCOL(1234, 5679)
/* This startup packet is to support latest Postgres protocol (6.4, 6.3) */
typedef struct _StartupPacket
@@ -307,6 +308,7 @@ typedef struct
signed char cvt_null_date_string;
signed char autocommit_public;
signed char accessible_only;
+ UInt4 extra_opts;
#ifdef _HANDLE_ENLIST_IN_DTC_
signed char xa_opt;
#endif /* _HANDLE_ENLIST_IN_DTC_ */
@@ -450,6 +452,9 @@ struct ConnectionClass_
#endif /* ODBCVER */
pgNAME schemaIns;
pgNAME tableIns;
+#ifdef USE_SSPI
+ UInt4 svcs_allowed;
+#endif /* USE_SSPI */
#if defined(WIN_MULTITHREAD_SUPPORT)
CRITICAL_SECTION cs;
CRITICAL_SECTION slock;
@@ -543,6 +548,7 @@ enum {
,GO_INTO_TRANSACTION = (1L << 2) /* issue begin in advance */
,ROLLBACK_ON_ERROR = (1L << 3) /* rollback the query when an error occurs */
,END_WITH_COMMIT = (1L << 4) /* the query ends with COMMMIT command */
+ ,IGNORE_ROUND_TRIP = (1L << 5) /* the commincation round trip time is considered ignorable */
};
/* CC_on_abort options */
#define NO_TRANS 1L
diff --git a/convert.c b/convert.c
index 3cded81..177d8b9 100644
--- a/convert.c
+++ b/convert.c
@@ -1554,11 +1554,12 @@ inolog("SQL_C_VARBOOKMARK value=%d\n", ival);
*/
#define FLGP_PREPARE_DUMMY_CURSOR 1L
-#define FLGP_CURSOR_CHECK_OK (1L << 1)
+#define FLGP_USING_CURSOR (1L << 1)
#define FLGP_SELECT_INTO (1L << 2)
-#define FLGP_SELECT_FOR_UPDATE (1L << 3)
+#define FLGP_SELECT_FOR_UPDATE_OR_SHARE (1L << 3)
#define FLGP_BUILDING_PREPARE_STATEMENT (1L << 4)
#define FLGP_MULTIPLE_STATEMENT (1L << 5)
+#define FLGP_SELECT_FOR_READONLY (1L << 6)
typedef struct _QueryParse {
const char *statement;
int statement_type;
@@ -2014,19 +2015,42 @@ into_table_from(const char *stmt)
* This isn't really a strict check but ...
*----------
*/
-static BOOL
-table_for_update(const char *stmt, int *endpos)
+static UInt4
+table_for_update_or_share(const char *stmt, int *endpos)
{
const char *wstmt = stmt;
+ int advance;
+ UInt4 flag = 0;
while (isspace((UCHAR) *(++wstmt)));
if (!*wstmt)
- return FALSE;
- if (strnicmp(wstmt, "update", 6))
- return FALSE;
- wstmt += 6;
+ return 0;
+ if (0 == strnicmp(wstmt, "update", advance = 6))
+ flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE;
+ else if (0 == strnicmp(wstmt, "share", advance = 5))
+ flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE;
+ else if (0 == strnicmp(wstmt, "read", advance = 4))
+ flag |= FLGP_SELECT_FOR_READONLY;
+ else
+ return 0;
+ wstmt += advance;
+ if (0 != wstmt[0] && !isspace((UCHAR) wstmt[0]))
+ return 0;
+ else if (0 != (FLGP_SELECT_FOR_READONLY))
+ {
+ if (!isspace((UCHAR) wstmt[0]))
+ return 0;
+ while (isspace((UCHAR) *(++wstmt)));
+ if (!*wstmt)
+ return 0;
+ if (0 != strnicmp(wstmt, "only", advance = 4))
+ return 0;
+ wstmt += advance;
+ }
+ if (0 != wstmt[0] && !isspace((UCHAR) wstmt[0]))
+ return 0;
*endpos = wstmt - stmt;
- return !wstmt[0] || isspace((UCHAR) wstmt[0]);
+ return flag;
}
/*----------
@@ -2120,7 +2144,7 @@ static int
Prepare_and_convert(StatementClass *stmt, QueryParse *qp, QueryBuild *qb)
{
CSTR func = "Prepare_and_convert";
- char *new_statement, *exe_statement = NULL;
+ char *exe_statement = NULL;
int retval;
ConnectionClass *conn = SC_get_conn(stmt);
ConnInfo *ci = &(conn->connInfo);
@@ -2529,7 +2553,7 @@ inolog("type=%d concur=%d\n", stmt->options.cursor_type, stmt->options.scroll_co
sprintf(new_statement, "%sdeclare \"%s\"%s cursor%s for ",
new_statement, SC_cursor_name(stmt), opt_scroll, opt_hold);
qb->npos = strlen(new_statement);
- qp->flags |= FLGP_CURSOR_CHECK_OK;
+ qp->flags |= FLGP_USING_CURSOR;
qp->declare_pos = qb->npos;
}
else if (SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency)
@@ -2556,16 +2580,19 @@ inolog("type=%d concur=%d\n", stmt->options.cursor_type, stmt->options.scroll_co
new_statement = qb->query_statement;
stmt->statement_type = qp->statement_type;
stmt->inaccurate_result = (0 != (qb->flags & FLGB_INACCURATE_RESULT));
+ if (0 == (qp->flags & FLGP_USING_CURSOR))
+ SC_no_fetchcursor(stmt);
+ else if (0 == (qp->flags & (FLGP_SELECT_FOR_UPDATE_OR_SHARE | FLGP_SELECT_FOR_READONLY)) &&
+ PG_VERSION_GE(conn, 8.3))
+ CVT_APPEND_STR(qb, " for read only");
if (0 != (qp->flags & FLGP_SELECT_INTO) ||
- 0 != (qp->flags & FLGP_MULTIPLE_STATEMENT))
+ 0 != (qp->flags & FLGP_MULTIPLE_STATEMENT))
{
SC_no_pre_executable(stmt);
- SC_no_fetchcursor(stmt);
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
}
- if (0 != (qp->flags & FLGP_SELECT_FOR_UPDATE))
+ if (0 != (qp->flags & FLGP_SELECT_FOR_UPDATE_OR_SHARE))
{
- SC_no_fetchcursor(stmt);
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
}
@@ -2634,6 +2661,8 @@ inolog("type=%d concur=%d\n", stmt->options.cursor_type, stmt->options.scroll_co
static void
remove_declare_cursor(QueryBuild *qb, QueryParse *qp)
{
+ qp->flags &= ~FLGP_USING_CURSOR;
+ if (qp->declare_pos <= 0) return;
memmove(qb->query_statement, qb->query_statement + qp->declare_pos, qb->npos - qp->declare_pos);
qb->npos -= qp->declare_pos;
qp->declare_pos = 0;
@@ -2890,7 +2919,7 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb)
* to parse.
*/
qb->dollar_number = 0;
- if (0 != (qp->flags & FLGP_CURSOR_CHECK_OK))
+ if (0 != (qp->flags & FLGP_USING_CURSOR))
{
const char *vp = &(qp->statement[qp->opos + 1]);
@@ -2899,7 +2928,6 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb)
if (*vp) /* multiple statement */
{
qp->flags |= FLGP_MULTIPLE_STATEMENT;
- qp->flags &= ~FLGP_CURSOR_CHECK_OK;
qb->flags &= ~FLGB_KEYSET_DRIVEN;
remove_declare_cursor(qb, qp);
}
@@ -2915,11 +2943,10 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb)
qp->token_save[qp->token_len] = '\0';
if (qp->token_len == 4)
{
- if (0 != (qp->flags & FLGP_CURSOR_CHECK_OK) &&
+ if (0 != (qp->flags & FLGP_USING_CURSOR) &&
into_table_from(&qp->statement[qp->opos - qp->token_len]))
{
qp->flags |= FLGP_SELECT_INTO;
- qp->flags &= ~FLGP_CURSOR_CHECK_OK;
qb->flags &= ~FLGB_KEYSET_DRIVEN;
qp->statement_type = STMT_TYPE_CREATE;
remove_declare_cursor(qb, qp);
@@ -2932,23 +2959,29 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb)
}
else if (qp->token_len == 3)
{
- int endpos;
- if (0 != (qp->flags & FLGP_CURSOR_CHECK_OK) &&
- strnicmp(qp->token_save, "for", 3) == 0 &&
- table_for_update(F_OldPtr(qp), &endpos))
+ if (0 != (qp->flags & FLGP_USING_CURSOR) &&
+ strnicmp(qp->token_save, "for", 3) == 0)
{
- qp->flags |= FLGP_SELECT_FOR_UPDATE;
- qp->flags &= ~FLGP_CURSOR_CHECK_OK;
- if (qp->flags & FLGP_PREPARE_DUMMY_CURSOR)
+ UInt4 flg;
+ int endpos;
+
+ flg = table_for_update_or_share(F_OldPtr(qp), &endpos);
+ if (0 != (FLGP_SELECT_FOR_UPDATE_OR_SHARE & flg))
{
- qb->npos -= 4;
- qp->opos += endpos;
+ if (qp->flags & FLGP_PREPARE_DUMMY_CURSOR)
+ {
+ qb->npos -= 4;
+ qp->opos += endpos;
+ }
+ else
+ {
+ qp->flags |= flg;
+ remove_declare_cursor(qb, qp);
+ }
}
else
- {
- remove_declare_cursor(qb, qp);
- }
+ qp->flags |= flg;
}
}
else if (qp->token_len == 2)
diff --git a/dlg_wingui.c b/dlg_wingui.c
index e9e7899..21a36bd 100644
--- a/dlg_wingui.c
+++ b/dlg_wingui.c
@@ -586,7 +586,7 @@ ds_options2Proc(HWND hdlg,
case IDAPPLY:
case IDPREVPAGE:
ci = (ConnInfo *) GetWindowLongPtr(hdlg, DWLP_USER);
- mylog("IDOK: got ci = %x\n", ci);
+ mylog("IDOK: got ci = %p\n", ci);
/* Readonly */
sprintf(ci->onlyread, "%d", IsDlgButtonChecked(hdlg, DS_READONLY));
diff --git a/environ.c b/environ.c
index e1d8893..ccb6687 100644
--- a/environ.c
+++ b/environ.c
@@ -22,9 +22,7 @@
#include <string.h>
#include "pgapifunc.h"
#ifdef WIN32
-#ifndef _WSASTARTUP_IN_DLLMAIN_
#include <winsock2.h>
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
#endif /* WIN32 */
#include "loadlib.h"
@@ -511,7 +509,6 @@ EN_Constructor(void)
CSTR func = "EN_Constructor";
EnvironmentClass *rv = NULL;
#ifdef WIN32
-#ifndef _WSASTARTUP_IN_DLLMAIN_
WORD wVersionRequested;
WSADATA wsaData;
const int major = 1, minor = 1;
@@ -531,7 +528,6 @@ EN_Constructor(void)
mylog("%s: WSAStartup version=(%d,%d)\n", func,
LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
}
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
#endif /* WIN32 */
rv = (EnvironmentClass *) malloc(sizeof(EnvironmentClass));
@@ -544,13 +540,11 @@ cleanup:
INIT_ENV_CS(rv);
}
#ifdef WIN32
-#ifndef _WSASTARTUP_IN_DLLMAIN_
else
{
mylog("%s: malloc error\n", func);
WSACleanup();
}
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
#endif /* WIN32 */
return rv;
@@ -597,9 +591,7 @@ EN_Destructor(EnvironmentClass *self)
free(self);
#ifdef WIN32
-#ifndef _WSASTARTUP_IN_DLLMAIN_
WSACleanup();
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
#endif
mylog("exit EN_Destructor: rv = %d\n", rv);
#ifdef _MEMORY_DEBUG_
diff --git a/info.c b/info.c
index 5af3b0e..a585516 100644
--- a/info.c
+++ b/info.c
@@ -2194,6 +2194,7 @@ retry_public_schema:
goto cleanup;
}
+#ifdef NOT_USED
result = PGAPI_BindCol(hcol_stmt, 7, SQL_C_LONG,
&field_length, MAX_INFO_STRING, NULL);
if (!SQL_SUCCEEDED(result))
@@ -2201,6 +2202,7 @@ retry_public_schema:
SC_error_copy(stmt, col_stmt, TRUE);
goto cleanup;
}
+#endif /* NOT_USED */
result = PGAPI_BindCol(hcol_stmt, 8, SQL_C_LONG,
&mod_length, MAX_INFO_STRING, NULL);
@@ -2477,11 +2479,12 @@ mylog(" and the data=%s\n", attdef);
mylog("%s: field type is VARCHAR,BPCHAR: field_type = %d, mod_length = %d\n", func, field_type, mod_length);
set_tuplefield_int4(&tuple[COLUMNS_PRECISION], mod_length);
+ field_length = mod_length;
#ifdef UNICODE_SUPPORT
- if (0 < mod_length && ALLOW_WCHAR(conn))
- mod_length *= WCLEN;
+ if (0 < field_length && ALLOW_WCHAR(conn))
+ field_length *= WCLEN;
#endif /* UNICODE_SUPPORT */
- set_tuplefield_int4(&tuple[COLUMNS_LENGTH], mod_length);
+ set_tuplefield_int4(&tuple[COLUMNS_LENGTH], field_length);
#if (ODBCVER >= 0x0300)
set_tuplefield_int4(&tuple[COLUMNS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, field_type, PG_STATIC, PG_STATIC));
#endif /* ODBCVER */
diff --git a/loadlib.c b/loadlib.c
index 0dbfd85..11bda5e 100644
--- a/loadlib.c
+++ b/loadlib.c
@@ -14,15 +14,19 @@
#ifndef WIN32
#include <errno.h>
#endif /* WIN32 */
+#ifndef NOT_USE_LIBPQ
#include <libpq-fe.h>
+#endif /* NOT_USE_LIBPQ */
#include "loadlib.h"
#include "pgenlist.h"
#ifdef WIN32
#ifdef _MSC_VER
#pragma comment(lib, "Delayimp")
+#ifndef NOT_USE_LIBPQ
#pragma comment(lib, "libpq")
#pragma comment(lib, "ssleay32")
+#endif /* NOT_USE_LIBPQ */
#ifdef _HANDLE_ENLIST_IN_DTC_
#ifdef UNICODE_SUPPORT
#pragma comment(lib, "pgenlist")
@@ -33,8 +37,10 @@
// The followings works under VC++6.0 but doesn't work under VC++7.0.
// Please add the equivalent linker options using command line etc.
#if (_MSC_VER == 1200) && defined(DYNAMIC_LOAD) // VC6.0
+#ifndef NOT_USE_LIBPQ
#pragma comment(linker, "/Delayload:libpq.dll")
#pragma comment(linker, "/Delayload:ssleay32.dll")
+#endif /* NOT_USE_LIBPQ */
#ifdef UNICODE_SUPPORT
#pragma comment(linker, "/Delayload:pgenlist.dll")
#else
@@ -187,6 +193,7 @@ void CleanupDelayLoadedDLLs(void)
}
#endif /* _MSC_DELAY_LOAD_IMPORT */
+#ifndef NOT_USE_LIBPQ
void *CALL_PQconnectdb(const char *conninfo, BOOL *libpqLoaded)
{
void *pqconn = NULL;
@@ -223,6 +230,7 @@ void *CALL_PQconnectdb(const char *conninfo, BOOL *libpqLoaded)
#endif /* _MSC_DELAY_LOAD_IMPORT */
return pqconn;
}
+#endif /* NOT_USE_LIBPQ */
#ifdef _HANDLE_ENLIST_IN_DTC_
RETCODE CALL_EnlistInDtc(ConnectionClass *conn, void *pTra, int method)
diff --git a/loadlib.h b/loadlib.h
index c1dadd6..997bac2 100644
--- a/loadlib.h
+++ b/loadlib.h
@@ -15,8 +15,11 @@
#ifdef __cplusplus
extern "C" {
#endif
+
BOOL LIBPQ_check(void);
+#ifndef NOT_USE_LIBPQ
void *CALL_PQconnectdb(const char *conninfo, BOOL *);
+#endif /* NOT_USE_LIBPQ */
#ifdef _HANDLE_ENLIST_IN_DTC_
RETCODE CALL_EnlistInDtc(ConnectionClass *conn, void * pTra, int method);
RETCODE CALL_DtcOnDisconnect(ConnectionClass *);
diff --git a/pgtypes.c b/pgtypes.c
index f89cbb3..2aae4c2 100644
--- a/pgtypes.c
+++ b/pgtypes.c
@@ -1142,7 +1142,7 @@ pgtype_transfer_octet_length(StatementClass *stmt, OID type, int col, int handle
#endif /* UNICODE_SUPPORT */
/* after 7.2 */
if (PG_VERSION_GE(conn, 7.2))
- coef =conn->mb_maxbyte_per_char;
+ coef = conn->mb_maxbyte_per_char;
if (coef < 2 && (conn->connInfo).lf_conversion)
/* CR -> CR/LF */
coef = 2;
diff --git a/psqlodbc.c b/psqlodbc.c
index f2a58b4..d7c3999 100644
--- a/psqlodbc.c
+++ b/psqlodbc.c
@@ -23,9 +23,6 @@
#include "environ.h"
#ifdef WIN32
-#ifdef _WSASTARTUP_IN_DLLMAIN_
-#include <winsock2.h>
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
#include "loadlib.h"
int platformId = 0;
#endif
@@ -109,31 +106,11 @@ HINSTANCE NEAR s_hModule; /* Saved module handle. */
BOOL WINAPI
DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
-#ifdef _WSASTARTUP_IN_DLLMAIN_
- WORD wVersionRequested;
- WSADATA wsaData;
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
-
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
s_hModule = hInst; /* Save for dialog boxes */
-#ifdef _WSASTARTUP_IN_DLLMAIN_
- /* Load the WinSock Library */
- wVersionRequested = MAKEWORD(1, 1);
-
- if (WSAStartup(wVersionRequested, &wsaData))
- return FALSE;
-
- /* Verify that this is the minimum version of WinSock */
- if (LOBYTE(wsaData.wVersion) != 1 ||
- HIBYTE(wsaData.wVersion) != 1)
- {
- WSACleanup();
- return FALSE;
- }
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
if (initialize_global_cs() == 0)
{
char pathname[_MAX_PATH], fname[_MAX_FNAME];
@@ -145,6 +122,8 @@ DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
_splitpath(pathname, NULL, NULL, fname, NULL);
if (stricmp(fname, "msaccess") == 0)
exepgm = 1;
+ else if (strnicmp(fname, "msqry", 5) == 0)
+ exepgm = 2;
}
osversion.dwOSVersionInfoSize = sizeof(osversion);
if (GetVersionEx(&osversion))
@@ -163,9 +142,6 @@ DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
CleanupDelayLoadedDLLs();
/* my(q)log is unavailable from here */
finalize_global_cs();
-#ifdef _WSASTARTUP_IN_DLLMAIN_
- WSACleanup();
-#endif /* _WSASTARTUP_IN_DLLMAIN_ */
return TRUE;
case DLL_THREAD_DETACH:
diff --git a/qresult.c b/qresult.c
index 8bce106..2047c45 100644
--- a/qresult.c
+++ b/qresult.c
@@ -644,6 +644,7 @@ QR_close(QResultClass *self)
mylog("QResult: END transaction on conn=%p\n", conn);
strncat(buf, ";commit", sizeof(buf));
flag |= END_WITH_COMMIT;
+ QR_set_cursor(self, NULL);
}
res = CC_send_query(conn, buf, NULL, flag, NULL);
@@ -1490,7 +1491,13 @@ inolog("QR_read_a_tuple_from_db len=%d\n", len);
if (field_lf >= effective_cols)
buffer = tidoidbuf;
else
- buffer = (char *) malloc(len + 1);
+ {
+ if (buffer = (char *) malloc(len + 1), NULL == buffer)
+ {
+ mylog("failed to allocate %d+1 bytes of buffer\n", len);
+ return FALSE;
+ }
+ }
SOCK_get_n_char(sock, buffer, len);
buffer[len] = '\0';
diff --git a/socket.c b/socket.c
index 226105e..e6e9599 100644
--- a/socket.c
+++ b/socket.c
@@ -12,14 +12,21 @@
*-------
*/
+#ifdef USE_SSPI
+#include "sspisvcs.h"
+#endif /* USE_SSPI */
+#ifndef NOT_USE_LIBPQ
#include <libpq-fe.h>
#include <openssl/ssl.h>
+#endif /* NOT_USE_LIBPQ */
#include "socket.h"
#include "loadlib.h"
#include "connection.h"
-#ifndef WIN32
+#ifdef WIN32
+#include <time.h>
+#else
#include <stdlib.h>
#include <string.h> /* for memset */
#ifdef TIME_WITH_SYS_TIME
@@ -69,9 +76,15 @@ SOCK_Constructor(const ConnectionClass *conn)
if (rv != NULL)
{
rv->socket = (SOCKETFD) -1;
+#ifdef USE_SSPI
+ rv->ssd = NULL;
+ rv->sspisvcs = 0;
+#endif /* USE_SSPI */
+#ifndef NOT_USE_LIBPQ
rv->via_libpq = FALSE;
rv->ssl = NULL;
rv->pqconn = NULL;
+#endif /* NOT_USE_LIBPQ */
rv->pversion = 0;
rv->reslen = 0;
rv->buffer_filled_in = 0;
@@ -103,7 +116,6 @@ SOCK_Constructor(const ConnectionClass *conn)
return rv;
}
-
void
SOCK_Destructor(SocketClass *self)
{
@@ -112,6 +124,7 @@ SOCK_Destructor(SocketClass *self)
return;
if (self->socket != (SOCKETFD) -1)
{
+#ifndef NOT_USE_LIBPQ
if (self->pqconn)
{
if (self->via_libpq)
@@ -124,7 +137,17 @@ SOCK_Destructor(SocketClass *self)
self->ssl = NULL;
}
else
+#endif /* NOT_USE_LIBPQ */
{
+#ifdef USE_SSPI
+ if (self->ssd)
+ {
+ ReleaseSvcSpecData(self);
+ free(self->ssd);
+ self->ssd = NULL;
+ }
+ self->sspisvcs = 0;
+#endif /* USE_SSPI */
SOCK_put_char(self, 'X');
if (PG_PROTOCOL_74 == self->pversion)
SOCK_put_int(self, 4, 4);
@@ -423,13 +446,22 @@ cleanup:
* To handle EWOULDBLOCK etc (mainly for libpq non-blocking connection).
*/
#define MAX_RETRY_COUNT 30
+
static int SOCK_wait_for_ready(SocketClass *sock, BOOL output, int retry_count)
{
int ret, gerrno;
fd_set fds, except_fds;
struct timeval tm;
- BOOL no_timeout = (0 != retry_count && (retry_count < 0 || (!sock->ssl)));
-
+ BOOL no_timeout;
+
+ if (0 == retry_count)
+ no_timeout = FALSE;
+ else if (0 > retry_count)
+ no_timeout = TRUE;
+#ifndef NOT_USE_LIBPQ
+ else if (sock && NULL == sock->ssl)
+ no_timeout = TRUE;
+#endif /* NOT_USE_LIBPQ */
do {
FD_ZERO(&fds);
FD_ZERO(&except_fds);
@@ -440,7 +472,7 @@ static int SOCK_wait_for_ready(SocketClass *sock, BOOL output, int retry_count)
tm.tv_sec = retry_count;
tm.tv_usec = 0;
}
- ret = select((int)sock->socket + 1, output ? NULL : &fds, output ? &fds : NULL, &except_fds, no_timeout ? NULL : &tm);
+ ret = select((int) socket + 1, output ? NULL : &fds, output ? &fds : NULL, &except_fds, no_timeout ? NULL : &tm);
gerrno = SOCK_ERRNO;
} while (ret < 0 && EINTR == gerrno);
if (retry_count < 0)
@@ -448,11 +480,36 @@ static int SOCK_wait_for_ready(SocketClass *sock, BOOL output, int retry_count)
if (0 == ret && retry_count > MAX_RETRY_COUNT)
{
ret = -1;
- SOCK_set_error(sock, output ? SOCKET_WRITE_TIMEOUT : SOCKET_READ_TIMEOUT, "SOCK_wait_for_ready timeout");
+ if (sock)
+ SOCK_set_error(sock, output ? SOCKET_WRITE_TIMEOUT : SOCKET_READ_TIMEOUT, "SOCK_wait_for_ready timeout");
}
return ret;
}
+static int SOCK_SSPI_recv(SocketClass *self, void *buffer, int len)
+{
+#ifdef USE_SSPI
+ if (!self->sspisvcs || !self->ssd)
+ return recv(self->socket, (char *) buffer, len, 0);
+ else
+ return SSPI_recv(self, (char *) buffer, len);
+#endif /* USE_SSPI */
+ return recv(self->socket, (char *) buffer, len, 0);
+}
+
+static int SOCK_SSPI_send(SocketClass *self, const void *buffer, int len)
+{
+#ifdef USE_SSPI
+ CSTR func = "SOCK_SSPI_send";
+
+ if (!self->sspisvcs || !self->ssd)
+ return send(self->socket, (char *) buffer, len, 0);
+ else
+ return SSPI_send(self, buffer, len);
+#endif /* USE_SSPI */
+ return send(self->socket, (char *) buffer, len, 0);
+}
+
#ifdef USE_SSL
/*
* The stuff for SSL.
@@ -738,7 +795,9 @@ SOCK_flush_output(SocketClass *self)
written = SOCK_SSL_send(self, (char *) self->buffer_out + pos, self->buffer_filled_out);
else
#endif /* USE_SSL */
- written = send(self->socket, (char *) self->buffer_out + pos, self->buffer_filled_out, 0);
+ {
+ written = SOCK_SSPI_send(self, (char *) self->buffer_out + pos, self->buffer_filled_out);
+ }
gerrno = SOCK_ERRNO;
if (written < 0)
{
@@ -786,7 +845,7 @@ retry:
self->buffer_filled_in = SOCK_SSL_recv(self, (char *) self->buffer_in, self->buffer_size);
else
#endif /* USE_SSL */
- self->buffer_filled_in = recv(self->socket, (char *) self->buffer_in, self->buffer_size, 0);
+ self->buffer_filled_in = SOCK_SSPI_recv(self, (char *) self->buffer_in, self->buffer_size);
gerrno = SOCK_ERRNO;
mylog("read %d, global_socket_buffersize=%d\n", self->buffer_filled_in, self->buffer_size);
@@ -861,7 +920,9 @@ SOCK_put_next_byte(SocketClass *self, UCHAR next_byte)
bytes_sent = SOCK_SSL_send(self, (char *) self->buffer_out + pos, self->buffer_filled_out);
else
#endif /* USE_SSL */
- bytes_sent = send(self->socket, (char *) self->buffer_out + pos, self->buffer_filled_out, 0);
+ {
+ bytes_sent = SOCK_SSPI_send(self, (char *) self->buffer_out + pos, self->buffer_filled_out);
+ }
gerrno = SOCK_ERRNO;
if (bytes_sent < 0)
{
diff --git a/socket.h b/socket.h
index b53c4ea..e8caae8 100644
--- a/socket.h
+++ b/socket.h
@@ -134,10 +134,16 @@ struct SocketClass_
int errornumber;
int sadr_len;
struct sockaddr_storage sadr_area; /* Used for various connections */
+#ifdef USE_SSPI
+ UInt4 sspisvcs;
+ void *ssd;
+#endif /* USE_SSPI */
+#ifdef USE_SSL
/* SSL stuff */
void *ssl; /* libpq ssl */
void *pqconn; /* libpq PGConn */
BOOL via_libpq; /* using libpq library ? */
+#endif /* USE_SSL */
char reverse; /* used to handle Postgres 6.2 protocol
* (reverse byte order) */
diff --git a/sspisvcs.c b/sspisvcs.c
new file mode 100644
index 0000000..aa7615b
--- /dev/null
+++ b/sspisvcs.c
@@ -0,0 +1,1168 @@
+/*-------
+ * Module: sspi_proc.c
+ *
+ * Description: This module contains functions for low level socket
+ * operations (connecting/reading/writing to the backend)
+ *
+ * Classes: SocketClass (Functions prefix: "SOCK_")
+ *
+ * API functions: none
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#ifdef USE_SSPI
+#define SECURITY_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <security.h>
+#include <sspi.h>
+#pragma comment(lib, "secur32.lib")
+
+#include "sspisvcs.h"
+#include "socket.h"
+
+/*
+ * To handle EWOULDBLOCK etc (mainly for libpq non-blocking connection).
+ */
+#define MAX_RETRY_COUNT 30
+static int Socket_wait_for_ready(SOCKET socket, BOOL output, int retry_count)
+{
+ int ret, gerrno;
+ fd_set fds, except_fds;
+ struct timeval tm;
+ BOOL no_timeout = (retry_count < 0);
+
+ do {
+ FD_ZERO(&fds);
+ FD_ZERO(&except_fds);
+ FD_SET(socket, &fds);
+ FD_SET(socket, &except_fds);
+ if (!no_timeout)
+ {
+ tm.tv_sec = retry_count;
+ tm.tv_usec = 0;
+ }
+ ret = select((int) socket + 1, output ? NULL : &fds, output ? &fds : NULL, &except_fds, no_timeout ? NULL : &tm);
+ gerrno = SOCK_ERRNO;
+ } while (ret < 0 && EINTR == gerrno);
+ if (retry_count < 0)
+ retry_count *= -1;
+ if (0 == ret && retry_count > MAX_RETRY_COUNT)
+ {
+ ret = -1;
+ }
+ return ret;
+}
+
+static int sendall(SOCKET sock, const void *buf, int len)
+{
+ CSTR func = "sendall";
+ int wrtlen, ttllen, reqlen, retry_count;
+
+ retry_count = 0;
+ for (ttllen = 0, reqlen = len; reqlen > 0;)
+ {
+ if (0 > (wrtlen = send(sock, (const char *) buf + ttllen, reqlen, 0)))
+ {
+ int gerrno = SOCK_ERRNO;
+
+ mylog("%s:errno=%d\n", func, gerrno);
+ switch (gerrno)
+ {
+ case EINTR:
+ continue;
+ case EWOULDBLOCK:
+ retry_count++;
+ if (Socket_wait_for_ready(sock, TRUE, retry_count) >= 0)
+ continue;
+ break;
+ case ECONNRESET:
+ return 0;
+ }
+ return SOCKET_ERROR;
+ }
+ ttllen += wrtlen;
+ reqlen -= wrtlen;
+ retry_count = 0;
+ }
+ return ttllen;
+}
+
+static int recvall(SOCKET sock, void *buf, int len)
+{
+ CSTR func = "recvall";
+ int rcvlen, ttllen, reqlen, retry_count = 0;
+
+ for (ttllen = 0, reqlen = len; reqlen > 0;)
+ {
+ if (0 > (rcvlen = recv(sock, (char *) buf + ttllen, reqlen, 0)))
+ {
+ int gerrno = SOCK_ERRNO;
+
+ switch (gerrno)
+ {
+ case EINTR:
+ continue;
+ case EWOULDBLOCK:
+ retry_count++;
+ if (Socket_wait_for_ready(sock, FALSE, retry_count) >= 0)
+ continue;
+ break;
+ case ECONNRESET:
+ return 0;
+ }
+ return -1;
+ }
+ ttllen += rcvlen;
+ reqlen -= rcvlen;
+ retry_count = 0;
+ }
+ return ttllen;
+}
+
+CSTR SCHANNEL = "sChannel";
+static int DoSchannelNegotiation(SocketClass *, const char *opt);
+int StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt)
+{
+ CSTR func = "DoServicelNegotiation";
+
+ switch (svc)
+ {
+ case SchannelService:
+ return DoSchannelNegotiation(self, opt);
+ }
+
+ return -1;
+}
+
+/*
+ * Stuff for Schannel service
+ */
+#include <schannel.h>
+#pragma comment(lib, "crypt32")
+#define UNI_SCHANNEL TEXT("sChannel")
+#define IO_BUFFER_SIZE 0x10000
+
+typedef struct {
+ CredHandle hCred;
+ CtxtHandle hCtxt;
+ PBYTE ioovrbuf;
+ size_t ioovrlen;
+ PBYTE iobuf;
+ size_t iobuflen;
+ size_t ioread;
+} SchannelSpec;
+
+static SECURITY_STATUS CreateSchannelCredentials(LPCTSTR, LPSTR, PCredHandle);
+static SECURITY_STATUS PerformSchannelClientHandshake(SOCKET, PCredHandle, LPSTR, CtxtHandle *, SecBuffer *);
+static SECURITY_STATUS SchannelClientHandshakeLoop(SOCKET, PCredHandle, CtxtHandle *, BOOL, SecBuffer *);
+static void GetNewSchannelClientCredentials(PCredHandle, CtxtHandle *);
+
+static HCERTSTORE hMyCertStore = NULL;
+
+static int DoSchannelNegotiation(SocketClass *self, const char *opt)
+{
+ CSTR func = "DoSchannelNegotiation";
+ SecBuffer ExtraData;
+ BOOL ret = 0, cCreds = FALSE, cCtxt = FALSE;
+ SchannelSpec *ssd = NULL;
+
+ if (NULL == (ssd = calloc(sizeof(SchannelSpec), 1)))
+ {
+ return 0;
+ }
+ if (SEC_E_OK != CreateSchannelCredentials(NULL, NULL, &ssd->hCred))
+ {
+ mylog("%s:CreateSchannel failed\n", func);
+ goto cleanup;
+ }
+ cCreds = TRUE;
+ if (SEC_E_OK != PerformSchannelClientHandshake(self->socket, &ssd->hCred, NULL, &ssd->hCtxt, &ExtraData))
+ {
+ mylog("%s:PerformSchannelClientHandshake failed\n", func);
+ goto cleanup;
+ }
+ cCtxt = TRUE;
+ if (NULL != ExtraData.pvBuffer && 0 != ExtraData.cbBuffer)
+ {
+ ssd->iobuf = malloc(ExtraData.cbBuffer);
+ ssd->iobuflen =
+ ssd->ioread = ExtraData.cbBuffer;
+ memcpy(ssd->iobuf, ExtraData.pvBuffer, ssd->ioread);
+ free(ExtraData.pvBuffer);
+ }
+ ret = TRUE;
+cleanup:
+ if (ret)
+ {
+ self->sspisvcs |= SchannelService;
+ self->ssd = ssd;
+ }
+ else
+ {
+ if (cCreds)
+ FreeCredentialHandle(&ssd->hCred);
+ if (cCtxt)
+ DeleteSecurityContext(&ssd->hCtxt);
+ if (ssd->iobuf)
+ free(ssd->iobuf);
+ free(ssd);
+ }
+ return ret;
+}
+
+static
+SECURITY_STATUS
+CreateSchannelCredentials(
+ LPCTSTR opt, /* in */
+ LPSTR pszUserName, /* in */
+ PCredHandle phCreds) /* out */
+{
+ TimeStamp tsExpiry;
+ SECURITY_STATUS Status;
+ SCHANNEL_CRED SchannelCred;
+
+ DWORD cSupportedAlgs = 0;
+ ALG_ID rgbSupportedAlgs[16];
+ DWORD dwProtocol = SP_PROT_SSL3 | SP_PROT_SSL2;
+ DWORD aiKeyExch = 0;
+
+ PCCERT_CONTEXT pCertContext = NULL;
+
+ /*
+ * If a user name is specified, then attempt to find a client
+ * certificate. Otherwise, just create a NULL credential.
+ */
+
+ if (pszUserName)
+ {
+ /* Find client certificate. Note that this sample just searchs for a
+ * certificate that contains the user name somewhere in the subject name.
+ * A real application should be a bit less casual.
+ */
+ pCertContext = CertFindCertificateInStore(hMyCertStore,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_STR_A,
+ pszUserName,
+ NULL);
+ if (pCertContext == NULL)
+ {
+ mylog("**** Error 0x%p returned by CertFindCertificateInStore\n",
+ GetLastError());
+ return SEC_E_NO_CREDENTIALS;
+ }
+ }
+
+
+ /*
+ * Build Schannel credential structure. Currently, this sample only
+ * specifies the protocol to be used (and optionally the certificate,
+ * of course). Real applications may wish to specify other parameters
+ * as well.
+ */
+
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ if (pCertContext)
+ {
+ SchannelCred.cCreds = 1;
+ SchannelCred.paCred = &pCertContext;
+ }
+
+ SchannelCred.grbitEnabledProtocols = dwProtocol;
+
+ if (aiKeyExch)
+ {
+ rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch;
+ }
+
+ if (cSupportedAlgs)
+ {
+ SchannelCred.cSupportedAlgs = cSupportedAlgs;
+ SchannelCred.palgSupportedAlgs = rgbSupportedAlgs;
+ }
+
+ SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+
+ /* The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
+ * this sample verifies the server certificate manually.
+ * Applications that expect to run on WinNT, Win9x, or WinME
+ * should specify this flag and also manually verify the server
+ * certificate. Applications running on newer versions of Windows can
+ * leave off this flag, in which case the InitializeSecurityContext
+ * function will validate the server certificate automatically.
+ */
+ SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
+
+ /*
+ * Create an SSPI credential.
+ */
+
+ Status = AcquireCredentialsHandle(
+ NULL, /* Name of principal */
+ UNI_SCHANNEL, /* Name of package */
+ SECPKG_CRED_OUTBOUND, /* Flags indicating use */
+ NULL, /* Pointer to logon ID */
+ &SchannelCred, /* Package specific data */
+ NULL, /* Pointer to GetKey() func */
+ NULL, /* Value to pass to GetKey() */
+ phCreds, /* (out) Cred Handle */
+ &tsExpiry); /* (out) Lifetime (optional) */
+ if (Status != SEC_E_OK)
+ {
+ mylog("**** Error 0x%p returned by AcquireCredentialsHandle\n", Status);
+ goto cleanup;
+ }
+
+cleanup:
+
+ /*
+ * Free the certificate context. Schannel has already made its own copy.
+ */
+
+ if(pCertContext)
+ {
+ CertFreeCertificateContext(pCertContext);
+ }
+
+
+ return Status;
+}
+
+static
+SECURITY_STATUS
+PerformSchannelClientHandshake(
+ SOCKET Socket, /* in */
+ PCredHandle phCreds, /* in */
+ LPSTR pszServerName, /* in */
+ CtxtHandle *phContext, /* out */
+ SecBuffer *pExtraData) /* out */
+{
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ /*
+ * Initiate a ClientHello message and generate a token.
+ */
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ scRet = InitializeSecurityContext(
+ phCreds,
+ NULL,
+ pszServerName,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ phContext,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ if (scRet != SEC_I_CONTINUE_NEEDED)
+ {
+ mylog("**** Error %d returned by InitializeSecurityContext (1)\n", scRet);
+ return scRet;
+ }
+
+ /* Send response to server if there is one. */
+ if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+ {
+ cbData = sendall(Socket,
+ OutBuffers[0].pvBuffer,
+ OutBuffers[0].cbBuffer);
+ if (cbData <= 0)
+ {
+ mylog("**** Error %d sending data to server (1)\n", SOCK_ERRNO);
+ FreeContextBuffer(OutBuffers[0].pvBuffer);
+ DeleteSecurityContext(phContext);
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ mylog("%d bytes of handshake data sent\n", cbData);
+
+ /* Free output buffer. */
+ FreeContextBuffer(OutBuffers[0].pvBuffer);
+ OutBuffers[0].pvBuffer = NULL;
+ }
+
+
+ return SchannelClientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData);
+}
+
+static
+SECURITY_STATUS
+SchannelClientHandshakeLoop(
+ SOCKET Socket, /* in */
+ PCredHandle phCreds, /* in */
+ CtxtHandle *phContext, /* i-o */
+ BOOL fDoInitialRead, /* in */
+ SecBuffer *pExtraData) /* out */
+{
+ SecBufferDesc InBuffer;
+ SecBuffer InBuffers[2];
+ SecBufferDesc OutBuffer;
+ SecBuffer OutBuffers[1];
+ DWORD dwSSPIFlags;
+ DWORD dwSSPIOutFlags;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS scRet;
+ DWORD cbData;
+
+ PUCHAR IoBuffer;
+ DWORD cbIoBuffer;
+ BOOL fDoRead;
+ int retry_count;
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ /*
+ * Allocate data buffer.
+ */
+
+ IoBuffer = malloc(IO_BUFFER_SIZE);
+ if (IoBuffer == NULL)
+ {
+ mylog("**** Out of memory (1)\n");
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ cbIoBuffer = 0;
+ fDoRead = fDoInitialRead;
+ /*
+ * Loop until the handshake is finished or an error occurs.
+ */
+
+ retry_count = 0;
+ scRet = SEC_I_CONTINUE_NEEDED;
+ while (scRet == SEC_I_CONTINUE_NEEDED ||
+ scRet == SEC_E_INCOMPLETE_MESSAGE ||
+ scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ /*
+ * Read data from server.
+ */
+ if( 0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ if (fDoRead)
+ {
+ cbData = recv(Socket,
+ IoBuffer + cbIoBuffer,
+ IO_BUFFER_SIZE - cbIoBuffer,
+ 0);
+ if (cbData == SOCKET_ERROR)
+ {
+ int gerrno = SOCK_ERRNO;
+
+ mylog("**** Error %d reading data from server\n", gerrno);
+ switch (gerrno)
+ {
+ case EINTR:
+ continue;
+ case ECONNRESET:
+ break;
+ case EWOULDBLOCK:
+ retry_count++;
+ if (Socket_wait_for_ready(Socket, FALSE, retry_count) >= 0)
+ continue;
+ default:
+ scRet = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+ break;
+ }
+ else if(cbData == 0)
+ {
+ mylog("**** Server unexpectedly disconnected\n");
+ scRet = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+ mylog("%d bytes of handshake data received\n", cbData);
+ cbIoBuffer += cbData;
+ retry_count = 0;
+ }
+ else
+ {
+ fDoRead = TRUE;
+ }
+ }
+ /*
+ * Set up the input buffers. Buffer 0 is used to pass in data
+ * received from the server. Schannel will consume some or all
+ * of this. Leftover data (if any) will be placed in buffer 1 and
+ * given a buffer type of SECBUFFER_EXTRA.
+ */
+ InBuffers[0].pvBuffer = IoBuffer;
+ InBuffers[0].cbBuffer = cbIoBuffer;
+ InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ InBuffers[1].pvBuffer = NULL;
+ InBuffers[1].cbBuffer = 0;
+ InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ InBuffer.cBuffers = 2;
+ InBuffer.pBuffers = InBuffers;
+ InBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Set up the output buffers. These are initialized to NULL
+ * so as to make it less likely we'll attempt to free random
+ * garbage later.
+ */
+
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType= SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+ /*
+ * Call InitializeSecurityContext.
+ */
+
+ scRet = InitializeSecurityContext(phCreds,
+ phContext,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &InBuffer,
+ 0,
+ NULL,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry);
+
+ /*
+ * If InitializeSecurityContext was successful (or if the error was
+ * one of the special extended ones), send the contends of the output
+ * buffer to the server.
+ */
+
+ if( scRet == SEC_E_OK ||
+ scRet == SEC_I_CONTINUE_NEEDED ||
+ FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
+ {
+ if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
+ {
+ cbData = sendall(Socket,
+ OutBuffers[0].pvBuffer,
+ OutBuffers[0].cbBuffer);
+ if (cbData == SOCKET_ERROR || cbData == 0)
+ {
+ mylog("**** Error %d sending data to server (2)\n",
+ SOCK_ERRNO);
+ FreeContextBuffer(OutBuffers[0].pvBuffer);
+ DeleteSecurityContext(phContext);
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ mylog("%d bytes of handshake data sent\n", cbData);
+ /* Free output buffer. */
+ FreeContextBuffer(OutBuffers[0].pvBuffer);
+ OutBuffers[0].pvBuffer = NULL;
+ }
+ }
+
+ /*
+ * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
+ * then we need to read more data from the server and try again.
+ */
+
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ continue;
+ }
+
+ /*
+ * If InitializeSecurityContext returned SEC_E_OK, then the
+ * handshake completed successfully.
+ */
+
+ if(scRet == SEC_E_OK)
+ {
+ /*
+ * If the "extra" buffer contains data, this is encrypted application
+ * protocol layer stuff. It needs to be saved. The application layer
+ * will later decrypt it with DecryptMessage.
+ */
+ mylog("Handshake was successful\n");
+
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ pExtraData->pvBuffer = malloc(InBuffers[1].cbBuffer);
+ if (pExtraData->pvBuffer == NULL)
+ {
+ mylog("**** Out of memory (2)\n");
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ memmove(pExtraData->pvBuffer,
+ IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
+ InBuffers[1].cbBuffer);
+
+ pExtraData->cbBuffer = InBuffers[1].cbBuffer;
+ pExtraData->BufferType = SECBUFFER_TOKEN;
+
+ mylog("%d bytes of app data was bundled with handshake data\n",
+ pExtraData->cbBuffer);
+ }
+ else
+ {
+ pExtraData->pvBuffer = NULL;
+ pExtraData->cbBuffer = 0;
+ pExtraData->BufferType = SECBUFFER_EMPTY;
+ }
+
+ /*
+ * Bail out to quit
+ */
+
+ break;
+ }
+
+ /*
+ * Check for fatal error.
+ */
+
+ if(FAILED(scRet))
+ {
+ mylog("**** Error 0x%p returned by InitializeSecurityContext (2)\n", scRet);
+ break;
+ }
+
+ /*
+ * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
+ * then the server just requested client authentication.
+ */
+
+ if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
+ {
+ /*
+ * Busted. The server has requested client authentication and
+ * the credential we supplied didn't contain a client certificate.
+ *
+
+ *
+ * This function will read the list of trusted certificate
+ * authorities ("issuers") that was received from the server
+ * and attempt to find a suitable client certificate that
+ * was issued by one of these. If this function is successful,
+ * then we will connect using the new certificate. Otherwise,
+ * we will attempt to connect anonymously (using our current
+ * credentials).
+ */
+
+ GetNewSchannelClientCredentials(phCreds, phContext);
+
+ /* Go around again. */
+ fDoRead = FALSE;
+ scRet = SEC_I_CONTINUE_NEEDED;
+ continue;
+ }
+ /*
+ * Copy any leftover data from the "extra" buffer, and go around
+ * again.
+ */
+
+ if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
+ {
+ memmove(IoBuffer,
+ IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
+ InBuffers[1].cbBuffer);
+
+ cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+ else
+ {
+ cbIoBuffer = 0;
+ }
+ }
+
+ /* Delete the security context in the case of a fatal error. */
+ if (FAILED(scRet))
+ {
+ DeleteSecurityContext(phContext);
+ }
+
+ free(IoBuffer);
+
+ return scRet;
+}
+
+static void
+GetNewSchannelClientCredentials(
+ CredHandle *phCreds,
+ CtxtHandle *phContext)
+{
+ SCHANNEL_CRED SchannelCred;
+ CredHandle hCreds;
+ SecPkgContext_IssuerListInfoEx IssuerListInfo;
+ PCCERT_CHAIN_CONTEXT pChainContext;
+ CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
+ PCCERT_CONTEXT pCertContext;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS Status;
+
+ /*
+ * Read list of trusted issuers from schannel.
+ */
+
+ Status = QueryContextAttributes(phContext,
+ SECPKG_ATTR_ISSUER_LIST_EX,
+ (PVOID)&IssuerListInfo);
+ if (Status != SEC_E_OK)
+ {
+ mylog("Error 0x%p querying issuer list info\n", Status);
+ return;
+ }
+
+ /*
+ * Enumerate the client certificates.
+ */
+
+ ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
+
+ FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
+ FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
+ FindByIssuerPara.dwKeySpec = 0;
+ FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers;
+ FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers;
+
+ pChainContext = NULL;
+
+ while (TRUE)
+ {
+ /* Find a certificate chain. */
+ pChainContext = CertFindChainInStore(hMyCertStore,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_CHAIN_FIND_BY_ISSUER,
+ &FindByIssuerPara,
+ pChainContext);
+ if (pChainContext == NULL)
+ {
+ mylog("Error 0x%p finding cert chain\n", GetLastError());
+ break;
+ }
+ mylog("\ncertificate chain found\n");
+
+ /* Get pointer to leaf certificate context. */
+ pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
+
+ ZeroMemory(&SchannelCred, sizeof(SchannelCred));
+ /* Create schannel credential. */
+ SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
+ SchannelCred.cCreds = 1;
+ SchannelCred.paCred = &pCertContext;
+
+ Status = AcquireCredentialsHandle(
+ NULL, /* Name of principal */
+ UNI_SCHANNEL, /* Name of package */
+ SECPKG_CRED_OUTBOUND, /* Flags indicating use */
+ NULL, /* Pointer to logon ID */
+ &SchannelCred, /* Package specific data */
+ NULL, /* Pointer to GetKey() func */
+ NULL, /* Value to pass to GetKey() */
+ &hCreds, /* (out) Cred Handle */
+ &tsExpiry); /* (out) Lifetime (optional) */
+ if (Status != SEC_E_OK)
+ {
+ mylog("**** Error 0x%p returned by AcquireCredentialsHandle\n", Status);
+ continue;
+ }
+ mylog("\nnew schannel credential created\n");
+
+ /* Destroy the old credentials. */
+ FreeCredentialsHandle(phCreds);
+
+ *phCreds = hCreds;
+
+ /*
+ * As you can see, this sample code maintains a single credential
+ * handle, replacing it as necessary. This is a little unusual.
+ *
+ * Many applications maintain a global credential handle that's
+ * anonymous (that is, it doesn't contain a client certificate),
+ * which is used to connect to all servers. If a particular server
+ * should require client authentication, then a new credential
+ * is created for use when connecting to that server. The global
+ * anonymous credential is retained for future connections to
+ * other servers.
+ *
+ * Maintaining a single anonymous credential that's used whenever
+ * possible is most efficient, since creating new credentials all
+ * the time is rather expensive.
+ */
+
+ break;
+ }
+}
+
+int SSPI_recv(SocketClass *self, void *buffer, int len)
+{
+ CSTR func = "SSPI_recv";
+
+ if (0 != (self->sspisvcs & SchannelService))
+ {
+ SECURITY_STATUS scRet;
+ SecBuffer Buffers[4];
+ SecBufferDesc Message;
+ SecBuffer *pDataBuffer;
+ SecBuffer *pExtraBuffer;
+ SecBuffer ExtraBuffer;
+ int i, retry_count, reqlen, rtnlen = -1;
+ PBYTE pbIoBuffer;
+ DWORD cbIoBuffer, cbIoBufferLength;
+
+ DWORD cbData;
+ SchannelSpec *ssd = (SchannelSpec *) self->ssd;
+
+mylog("buflen=%d,%d ovrlen=%d\n", ssd->iobuflen, ssd->ioread, ssd->ioovrlen);
+ if (ssd->ioovrlen > 0)
+ {
+ if (rtnlen = ssd->ioovrlen, rtnlen > len)
+ rtnlen = len;
+ memmove(buffer, ssd->ioovrbuf, rtnlen);
+ if (rtnlen < ssd->ioovrlen)
+ {
+ memmove(ssd->ioovrbuf, ssd->ioovrbuf + rtnlen, ssd->ioovrlen - rtnlen);
+ }
+ ssd->ioovrlen -= rtnlen;
+ return rtnlen;
+ }
+ /*
+ * Read data from server until done.
+ */
+ retry_count = 0;
+ cbIoBufferLength = ((len - 1) / 16 + 1) * 16;
+ pbIoBuffer = ssd->iobuf;
+ cbIoBuffer = ssd->ioread;
+ if (cbIoBuffer > 0)
+ reqlen = 0;
+ else
+ reqlen = len - cbIoBuffer;
+ while (TRUE)
+ {
+ /*
+ * Read some data.
+ */
+mylog("buf=%p read=%d req=%d\n", pbIoBuffer, cbIoBuffer, reqlen);
+ if (reqlen > 0)
+ {
+ if (cbIoBuffer + reqlen > ssd->iobuflen)
+ {
+ void *iobuf;
+
+ cbIoBufferLength = cbIoBuffer + reqlen;
+ iobuf = realloc(ssd->iobuf, cbIoBufferLength);
+ if (NULL == iobuf)
+ {
+ mylog("failed to realloc ssd->iobuf\n");
+ if (ssd->iobuf)
+ {
+ free(ssd->iobuf);
+ ssd->iobuf = NULL;
+ }
+ ssd->iobuflen = 0;
+ return -1;
+ }
+ pbIoBuffer = ssd->iobuf = iobuf;
+ ssd->iobuflen = cbIoBufferLength;
+ }
+ cbData = recv(self->socket,
+ pbIoBuffer + cbIoBuffer,
+ reqlen,
+ 0);
+ if (cbData == SOCKET_ERROR)
+ {
+ int gerrno = SOCK_ERRNO;
+
+ mylog("**** Error %d reading data from server\n", gerrno);
+ switch (gerrno)
+ {
+ case EINTR:
+ continue;
+ case ECONNRESET:
+ break;
+ case EWOULDBLOCK:
+ retry_count++;
+ if (Socket_wait_for_ready(self->socket, FALSE, retry_count) >= 0)
+ continue;
+ default:
+ scRet = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+ break;
+ }
+ else if (cbData == 0)
+ {
+ /* Server disconnected. */
+ if (cbIoBuffer)
+ {
+ mylog("**** Server unexpectedly disconnected\n");
+ scRet = SEC_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+ else
+ {
+ rtnlen = 0;
+ break;
+ }
+ }
+ else
+ {
+ mylog("%d bytes of (encrypted) application data received\n", cbData);
+
+ cbIoBuffer += cbData;
+ reqlen -= cbData;
+ retry_count = 0;
+ }
+ }
+
+ /*
+ * Attempt to decrypt the received data.
+ */
+
+ Buffers[0].pvBuffer = pbIoBuffer;
+ Buffers[0].cbBuffer = cbIoBuffer;
+ Buffers[0].BufferType = SECBUFFER_DATA;
+
+ Buffers[1].BufferType = SECBUFFER_EMPTY;
+ Buffers[2].BufferType = SECBUFFER_EMPTY;
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+
+ scRet = DecryptMessage(&ssd->hCtxt, &Message, 0, NULL);
+
+ if (scRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ /* The input buffer contains only a fragment of an
+ * encrypted record. Loop around and read some more
+ * data.
+ */
+ if (reqlen <= 0)
+ {
+ if (cbIoBuffer < len)
+ reqlen = len - cbIoBuffer;
+ else
+ reqlen = len;
+ }
+ continue;
+ }
+
+ /* Server signalled end of session */
+ if (scRet == SEC_I_CONTEXT_EXPIRED)
+ break;
+
+ if (scRet != SEC_E_OK &&
+ scRet != SEC_I_RENEGOTIATE)
+ {
+ mylog("**** Error 0x%p returned by DecryptMessage\n", scRet);
+ goto cleanup;
+ }
+
+ /* Locate data and (optional) extra buffers. */
+ pDataBuffer = NULL;
+ pExtraBuffer = NULL;
+ for(i = 1; i < 4; i++)
+ {
+ if (pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
+ {
+ pDataBuffer = &Buffers[i];
+ mylog("%p Buffers[%d].BufferType = SECBUFFER_DATA\n", pDataBuffer->pvBuffer, i);
+ }
+ if (pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
+ {
+ pExtraBuffer = &Buffers[i];
+ mylog("%p Buffers[%d].BufferType = SECBUFFER_EXTRA\n", pExtraBuffer->pvBuffer, i);
+ }
+ }
+
+ /* Display or otherwise process the decrypted data. */
+ if (pDataBuffer)
+ {
+ mylog("Decrypted data: %d bytes\n", pDataBuffer->cbBuffer);
+ if (len < pDataBuffer->cbBuffer)
+ {
+ rtnlen = len;
+ memcpy(buffer, pDataBuffer->pvBuffer, rtnlen);
+
+ ssd->ioovrlen = pDataBuffer->cbBuffer - len;
+ ssd->ioovrbuf = realloc(ssd->ioovrbuf, ssd->ioovrlen);
+ memcpy(ssd->ioovrbuf, (const char *) pDataBuffer->pvBuffer + len, ssd->ioovrlen);
+ }
+ else
+ {
+ rtnlen = pDataBuffer->cbBuffer;
+ memcpy(buffer, pDataBuffer->pvBuffer, rtnlen);
+ }
+ }
+
+ /* Move any "extra" data to the input buffer. */
+ if (pExtraBuffer)
+ {
+ mylog("Extra data: %d bytes\n", pExtraBuffer->cbBuffer);
+ pbIoBuffer = ssd->iobuf;
+ cbIoBuffer = pExtraBuffer->cbBuffer;
+ memmove(pbIoBuffer, pExtraBuffer->pvBuffer, cbIoBuffer);
+ }
+ else
+ cbIoBuffer = 0;
+ ssd->ioread = cbIoBuffer;
+
+ if (scRet == SEC_I_RENEGOTIATE)
+ {
+ /* The server wants to perform another handshake
+ * sequence.
+ */
+ mylog("Server requested renegotiate!\n");
+ scRet = SchannelClientHandshakeLoop(
+ self->socket,
+ &ssd->hCred,
+ &ssd->hCtxt,
+ FALSE,
+ &ExtraBuffer);
+ if (scRet != SEC_E_OK)
+ {
+ goto cleanup;
+ }
+
+ /* Move any "extra" data to the input buffer. */
+ if (ExtraBuffer.pvBuffer)
+ {
+ memmove(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
+ cbIoBuffer = ExtraBuffer.cbBuffer;
+ }
+ }
+ break;
+ }
+cleanup:
+ return rtnlen;
+ }
+ else
+ return recv(self->socket, (char *) buffer, len, 0);
+}
+
+int SSPI_send(SocketClass *self, const void *buffer, int len)
+{
+ CSTR func = "SSPI_send";
+
+ if (0 != (self->sspisvcs & SchannelService))
+ {
+ SecPkgContext_StreamSizes sizes;
+ int ttllen, wrtlen, slen;
+ LPVOID lpHead;
+ LPVOID lpMsg;
+ LPVOID lpTrail;
+ SecBuffer sb[4];
+ SecBufferDesc sbd;
+ SchannelSpec *ssd = (SchannelSpec *) self->ssd;
+
+ QueryContextAttributes(&ssd->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sizes);
+ slen = len;
+ ttllen = sizes.cbHeader + len + sizes.cbTrailer;
+ if (ttllen > sizes.cbMaximumMessage)
+ {
+ ttllen = sizes.cbMaximumMessage;
+ slen = ttllen - sizes.cbHeader - sizes.cbTrailer;
+ }
+ lpHead = malloc(ttllen);
+ lpMsg = (char *) lpHead + sizes.cbHeader;
+ memcpy(lpMsg, buffer, slen);
+ lpTrail = (char *) lpMsg + slen;
+
+ sb[0].cbBuffer = sizes.cbHeader;
+ sb[0].pvBuffer = lpHead;
+ sb[0].BufferType = SECBUFFER_STREAM_HEADER;
+ sb[1].cbBuffer = slen;
+ sb[1].pvBuffer = lpMsg;
+ sb[1].BufferType = SECBUFFER_DATA;
+ sb[2].cbBuffer = sizes.cbTrailer;
+ sb[2].pvBuffer = lpTrail;
+ sb[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ /* sb[3].cbBuffer = 16;
+ sb[3].pvBuffer = lpPad; */
+ sb[3].BufferType = SECBUFFER_EMPTY;
+
+ sbd.cBuffers = 4;
+ sbd.pBuffers = sb;
+ sbd.ulVersion = SECBUFFER_VERSION;
+
+ EncryptMessage(&ssd->hCtxt, 0, &sbd, 0);
+
+mylog("EMPTY=%p %d %d\n", sb[3].pvBuffer, sb[3].cbBuffer, sb[3].BufferType);
+ if (wrtlen = sendall(self->socket, lpHead, ttllen), wrtlen < 0)
+ {
+ int gerrno = SOCK_ERRNO;
+
+ free(lpHead);
+ SOCK_ERRNO_SET(gerrno);
+ return -1;
+ }
+
+ free(lpHead);
+ return slen;
+ }
+ else
+ return send(self->socket, (char *) buffer, len, 0);
+}
+
+void ReleaseSvcSpecData(SocketClass *self)
+{
+ if (!self->ssd)
+ return;
+ if (0 != (self->sspisvcs & SchannelService))
+ {
+ SchannelSpec *ssd = (SchannelSpec *) self->ssd;
+
+ if (ssd->iobuf)
+ {
+ free(ssd->iobuf);
+ ssd->iobuf = NULL;
+ }
+ if (ssd->ioovrbuf)
+ {
+ free(ssd->ioovrbuf);
+ ssd->ioovrbuf = NULL;
+ }
+ FreeCredentialHandle(&ssd->hCred);
+ DeleteSecurityContext(&ssd->hCtxt);
+ self->sspisvcs &= (~SchannelService);
+ }
+}
+#endif /* USE_SSPI */
diff --git a/sspisvcs.h b/sspisvcs.h
new file mode 100755
index 0000000..eeb8754
--- /dev/null
+++ b/sspisvcs.h
@@ -0,0 +1,25 @@
+/* File: sspisvcs.h
+ *
+ * Description: See "sspisvcs.c"
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *
+ */
+
+#ifndef __SSPISVCS_H__
+#define __SSPISVCS_H__
+
+#include "socket.h"
+
+/* SSPI Services */
+typedef enum {
+ SchannelService = 1L
+ ,KerberosService = (1L << 1)
+} SSPI_Service;
+
+void ReleaseSvcSpecData(SocketClass *self);
+int StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt);
+int SSPI_recv(SocketClass *self, void *buf, int len);
+int SSPI_send(SocketClass *self, const void *buf, int len);
+
+#endif /* __SSPISVCS_H__ */
diff --git a/statement.c b/statement.c
index 172a7b4..a806d76 100644
--- a/statement.c
+++ b/statement.c
@@ -1818,6 +1818,10 @@ inolog("get_Result=%p\n", res);
sprintf(fetch, "%s " FORMAT_LEN " in \"%s\"", fetch_cmd, qi.row_size, SC_cursor_name(self));
qryi = &qi;
appendq = fetch;
+#ifdef NOT_USED
+ if (0 != (ci->extra_opts & BIT_IGNORE_ROUND_TRIP_TIME))
+ qflag |= IGNORE_ROUND_TRIP;
+#endif /* NOT_USED */
}
res = CC_send_query_append(conn, self->stmt_with_params, qryi, qflag, SC_get_ancestor(self), appendq);
if (SC_is_fetchcursor(self) && QR_command_maybe_successful(res))
diff --git a/version.h b/version.h
index 86941fa..10a1ae7 100644
--- a/version.h
+++ b/version.h
@@ -9,9 +9,9 @@
#ifndef __VERSION_H__
#define __VERSION_H__
-#define POSTGRESDRIVERVERSION "08.02.0500"
-#define POSTGRES_RESOURCE_VERSION "08.02.0500\0"
-#define PG_DRVFILE_VERSION 8,2,05,00
-#define PG_BUILD_VERSION "200709120001"
+#define POSTGRESDRIVERVERSION "08.02.0501"
+#define POSTGRES_RESOURCE_VERSION "08.02.0501\0"
+#define PG_DRVFILE_VERSION 8,2,05,01
+#define PG_BUILD_VERSION "200710280001"
#endif
diff --git a/win32.mak b/win32.mak
index 24edd5d..d20aff2 100644
--- a/win32.mak
+++ b/win32.mak
@@ -49,6 +49,15 @@ PG_LIB=$(PROGRAMFILES)\PostgreSQL\8.2\lib\ms
!MESSAGE Using default PostgreSQL Library directory: $(PG_LIB)
!ENDIF
+!IF "$(LINKMT)" == ""
+LINKMT=MT
+!ENDIF
+!IF "$(LINKMT)" == "MT"
+!MESSAGE Linking static Multithread library
+!ELSE
+!MESSAGE Linking dynamic Multithread library
+!ENDIF
+
!IF "$(SSL_INC)" == ""
SSL_INC=C:\OpenSSL\include
!MESSAGE Using default OpenSSL Include directory: $(SSL_INC)
@@ -59,17 +68,16 @@ SSL_LIB=C:\OpenSSL\lib\VC
!MESSAGE Using default OpenSSL Library directory: $(SSL_LIB)
!ENDIF
-!IF "$(LINKMT)" == ""
-LINKMT=MT
-!ENDIF
-!IF "$(LINKMT)" == "MT"
-!MESSAGE Linking static Multithread library
+!IF "$(USE_LIBPQ)" != "no"
+SSL_DLL = "SSLEAY32.dll"
+ADD_DEFINES = $(ADD_DEFINES) /D "SSL_DLL=\"$(SSL_DLL)\"" /D USE_SSL
!ELSE
-!MESSAGE Linking dynamic Multithread library
+ADD_DEFINES = $(ADD_DEFINES) /D NOT_USE_LIBPQ
!ENDIF
-SSL_DLL = "SSLEAY32.dll"
-ADD_DEFINES = $(ADD_DEFINES) /D "SSL_DLL=\"$(SSL_DLL)\"" /D USE_SSL
+!IF "$(USE_SSPI)" == "yes"
+ADD_DEFINES = $(ADD_DEFINES) /D USE_SSPI
+!ENDIF
!IF "$(ANSI_VERSION)" == "yes"
DTCLIB = pgenlista
@@ -84,7 +92,13 @@ MSDTC=no
VC_FLAGS=/GX /YX
!ELSE
MSVC_VERSION=vc70
-VC07_DELAY_LOAD="/DelayLoad:libpq.dll /DelayLoad:$(SSL_DLL) /DelayLoad:$(DTCDLL) /DELAY:UNLOAD"
+!IF "$(USE_LIBPQ)" != "no"
+VC07_DELAY_LOAD=/DelayLoad:libpq.dll /DelayLoad:$(SSL_DLL)
+!ENDIF
+!IF "$(USE_SSPI)" == "yes"
+VC07_DELAY_LOAD=$(VC07_DELAY_LOAD) /Delayload:secur32.dll /Delayload:crypt32.dll
+!ENDIF
+VC07_DELAY_LOAD=$(VC07_DELAY_LOAD) /delayLoad:$(DTCDLL) /DELAY:UNLOAD
VC_FLAGS=/EHsc
!ENDIF
ADD_DEFINES = $(ADD_DEFINES) /D "DYNAMIC_LOAD"
@@ -194,7 +208,7 @@ CPP_PROJ=/nologo /$(LINKMT) /O2 /D "NDEBUG"
CPP_PROJ=/nologo /$(LINKMT)d /Gm /ZI /Od /RTC1 /D "_DEBUG"
!ENDIF
CPP_PROJ=$(CPP_PROJ) /W3 $(VC_FLAGS) /I "$(PG_INC)" /I "$(SSL_INC)" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "_CRT_SECURE_NO_DEPRECATE" /D "PSQLODBC_EXPORTS" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /Fo"$(INTDIR)"\ /Fd"$(INTDIR)"\ /FD
-
+!MESSAGE CPP_PROJ=$(CPP_PROJ)
.c{$(INTDIR)}.obj::
$(CPP) @<<
$(CPP_PROJ) /c $<
@@ -245,7 +259,7 @@ LIB32=lib.exe
!IF "$(MSDTC)" != "no"
LINK32_FLAGS=$(OUTDIR)\$(DTCLIB).lib
!ENDIF
-LINK32_FLAGS=$(LINK32_FLAGS) kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib winmm.lib /nologo /dll /machine:I386 /def:"$(DEF_FILE)"
+LINK32_FLAGS=$(LINK32_FLAGS) kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib winmm.lib /nologo /dll /machine:I386 /def:$(DEF_FILE)
!IF "$(ANSI_VERSION)" == "yes"
DEF_FILE= "psqlodbca.def"
!ELSE
@@ -256,7 +270,7 @@ LINK32_FLAGS=$(LINK32_FLAGS) /incremental:no
!ELSE
LINK32_FLAGS=$(LINK32_FLAGS) /incremental:yes /debug
!ENDIF
-LINK32_FLAGS=$(LINK32_FLAGS) "$(VC07_DELAY_LOAD)" /libpath:"$(PG_LIB)" /libpath:"$(SSL_LIB)"
+LINK32_FLAGS=$(LINK32_FLAGS) $(VC07_DELAY_LOAD) /libpath:"$(PG_LIB)" /libpath:"$(SSL_LIB)"
LINK32_OBJS= \
"$(INTDIR)\bind.obj" \
@@ -283,6 +297,9 @@ LINK32_OBJS= \
"$(INTDIR)\qresult.obj" \
"$(INTDIR)\results.obj" \
"$(INTDIR)\setup.obj" \
+!IF "$(USE_SSPI)" == "yes"
+ "$(INTDIR)\sspisvcs.obj" \
+!ENDIF
"$(INTDIR)\socket.obj" \
"$(INTDIR)\statement.obj" \
"$(INTDIR)\tuple.obj" \
@@ -304,14 +321,14 @@ LINK32_OBJS= \
"$(INTDIR)\psqlodbc.res"
DTCDEF_FILE= "$(DTCLIB).def"
-LIB_DTCLIBFLAGS=/nologo /machine:I386 /def:"$(DTCDEF_FILE)"
+LIB_DTCLIBFLAGS=/nologo /machine:I386 /def:$(DTCDEF_FILE)
LINK32_DTCFLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib wsock32.lib XOleHlp.lib $(OUTDIR)\$(MAINLIB).lib Delayimp.lib /DelayLoad:XOLEHLP.DLL /nologo /dll /incremental:no /machine:I386
LINK32_DTCOBJS= \
"$(INTDIR)\msdtc_enlist.obj" "$(INTDIR)\xalibname.obj"
XADEF_FILE= "$(XALIB).def"
-LINK32_XAFLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib odbc32.lib odbccp32.lib uuid.lib wsock32.lib /nologo /dll /incremental:no /machine:I386 /def:"$(XADEF_FILE)"
+LINK32_XAFLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib odbc32.lib odbccp32.lib uuid.lib wsock32.lib /nologo /dll /incremental:no /machine:I386 /def:$(XADEF_FILE)
LINK32_XAOBJS= \
"$(INTDIR)\pgxalib.obj"
diff --git a/win64.mak b/win64.mak
index 5046783..98504cd 100755
--- a/win64.mak
+++ b/win64.mak
@@ -57,6 +57,7 @@ PG_LIB="C:\develop\lib\$(CPU)"
!MESSAGE Using default PostgreSQL Library directory: $(PG_LIB)
!ENDIF
+!IF "$(USE_LIBPQ)" != "no"
!IF "$(SSL_INC)" == ""
SSL_INC=C:\OpenSSL\include
!MESSAGE Using default OpenSSL Include directory: $(SSL_INC)
@@ -69,6 +70,13 @@ SSL_LIB="C:\develop\lib\$(CPU)"
SSL_DLL = "SSLEAY32.dll"
ADD_DEFINES = $(ADD_DEFINES) /D "SSL_DLL=\"$(SSL_DLL)\"" /D USE_SSL
+!ELSE
+ADD_DEFINES = $(ADD_DEFINES) /D NOT_USE_LIBPQ
+!ENDIF
+
+!IF "$(USE_SSPI)" == "yes"
+ADD_DEFINES = $(ADD_DEFINES) /D USE_SSPI
+!ENDIF
!IF "$(ANSI_VERSION)" == "yes"
DTCLIB = pgenlista
@@ -81,7 +89,13 @@ DTCDLL = $(DTCLIB).dll
VC07_DELAY_LOAD=
MSDTC=no
!ELSE
-VC07_DELAY_LOAD="/DelayLoad:libpq.dll /DelayLoad:$(SSL_DLL) /DelayLoad:$(DTCDLL) /DELAY:UNLOAD"
+!IF "$(USE_LIBPQ)" != "no"
+VC07_DELAY_LOAD="/DelayLoad:libpq.dll /DelayLoad:$(SSL_DLL)
+!ENDIF
+!IF "$(USE_SSPI)" == "yes"
+VC07_DELAY_LOAD=$(VC07_DELAY_LOAD) /DelayLoad:secur32.dll /Delayload:crypt32.dll
+!ENDIF
+VC07_DELAY_LOAD=$(VC07_DELAY_LOAD) /DelayLoad:$(DTCDLL) /DELAY:UNLOAD"
!ENDIF
ADD_DEFINES = $(ADD_DEFINES) /D "DYNAMIC_LOAD"
@@ -269,6 +283,9 @@ LINK32_OBJS= \
"$(INTDIR)\qresult.obj" \
"$(INTDIR)\results.obj" \
"$(INTDIR)\setup.obj" \
+!IF "$(USE_SSPI)" == "yes"
+ "$(INTDIR)\sspisvcs.obj" \
+!ENDIF
"$(INTDIR)\socket.obj" \
"$(INTDIR)\statement.obj" \
"$(INTDIR)\tuple.obj" \
[parent: be8dd244ed3f]