static void tds_check_cancel(TDSCONNECTION *conn) { TDSSOCKET *tds; int rc; if (!tds_connection_signaled(conn)) return; do { unsigned n = 0; rc = TDS_SUCCESS; tds_mutex_lock(&conn->list_mtx); /* Here we scan all list searching for sessions that should send cancel packets */ for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(tds=conn->sessions[n]) && tds->in_cancel == 1) { /* send cancel */ tds->in_cancel = 2; tds_mutex_unlock(&conn->list_mtx); rc = tds_append_cancel(tds); tds_mutex_lock(&conn->list_mtx); if (rc != TDS_SUCCESS) break; } tds_mutex_unlock(&conn->list_mtx); /* for all failed */ /* this must be done outside loop cause it can alter list */ /* this must be done unlocked cause it can lock again */ if (rc != TDS_SUCCESS) tds_close_socket(tds); } while(rc != TDS_SUCCESS); }
static void count_insert(const char* buf, size_t len) { static const char search[] = "insert into"; static unsigned char prev = 'x'; char *p; unsigned char c; size_t insert_len; /* add to buffer */ for (p = strchr(insert_buf, 0); len > 0; --len, ++buf) { c = (unsigned char) buf[0]; if (prev == 0 || c != 0) *p++ = (c >= ' ' && c < 128) ? tolower(c) : '_'; prev = c; } *p = 0; /* check it */ while ((p=strstr(insert_buf, search)) != NULL) { tds_mutex_lock(&mtx); ++inserts; tds_mutex_unlock(&mtx); /* do not find again */ p[0] = '_'; } /* avoid buffer too long */ insert_len = strlen(insert_buf); if (insert_len > sizeof(search)) { p = insert_buf + insert_len - sizeof(search); memmove(insert_buf, p, strlen(p) + 1); } }
/** * Close current socket * for last socket close entire connection * for MARS send FIN request */ void tds_close_socket(TDSSOCKET * tds) { if (!IS_TDSDEAD(tds)) { #if ENABLE_ODBC_MARS TDSCONNECTION *conn = tds->conn; unsigned n = 0, count = 0; tds_mutex_lock(&conn->list_mtx); for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(conn->sessions[n])) ++count; if (count > 1) tds_append_fin(tds); tds_mutex_unlock(&conn->list_mtx); if (count <= 1) { tds_disconnect(tds); tds_connection_close(conn); } else { tds_set_state(tds, TDS_DEAD); } #else tds_disconnect(tds); if (CLOSESOCKET(tds_get_s(tds)) == -1) tdserror(tds_get_ctx(tds), tds, TDSECLOS, sock_errno); tds_set_s(tds, INVALID_SOCKET); tds_set_state(tds, TDS_DEAD); #endif } }
static void tds_alloc_new_sid(TDSSOCKET *tds) { int sid = -1; TDSCONNECTION *conn = tds->conn; TDSSOCKET **s; tds_mutex_lock(&conn->list_mtx); tds->sid = -1; for (sid = 0; sid < conn->num_sessions; ++sid) if (!conn->sessions[sid]) break; if (sid == conn->num_sessions) { /* extend array */ s = (TDSSOCKET **) TDS_RESIZE(conn->sessions, sid+64); if (!s) goto error; memset(s + conn->num_sessions, 0, sizeof(*s) * 64); conn->num_sessions += 64; } conn->sessions[sid] = tds; tds->sid = sid; error: tds_mutex_unlock(&conn->list_mtx); }
static TDSRET tds_update_recv_wnd(TDSSOCKET *tds, TDS_UINT new_recv_wnd) { TDS72_SMP_HEADER *mars; TDSPACKET *packet; if (!tds->conn->mars || tds->sid < 0) return TDS_SUCCESS; packet = tds_get_packet(tds->conn, sizeof(*mars)); if (!packet) return TDS_FAIL; /* TODO check result */ packet->len = sizeof(*mars); packet->sid = tds->sid; mars = (TDS72_SMP_HEADER *) packet->buf; mars->signature = TDS72_SMP; mars->type = TDS_SMP_ACK; TDS_PUT_A2LE(&mars->sid, tds->sid); mars->size = TDS_HOST4LE(16); TDS_PUT_A4LE(&mars->seq, tds->send_seq); tds->recv_wnd = new_recv_wnd; TDS_PUT_A4LE(&mars->wnd, tds->recv_wnd); tds_mutex_lock(&tds->conn->list_mtx); tds_append_packet(&tds->conn->send_packets, packet); tds_mutex_unlock(&tds->conn->list_mtx); return TDS_SUCCESS; }
/* get packet from the cache */ static TDSPACKET * tds_get_packet(TDSCONNECTION *conn, unsigned len) { TDSPACKET *packet, *to_free = NULL; tds_mutex_lock(&conn->list_mtx); while ((packet = conn->packet_cache) != NULL) { --conn->num_cached_packets; conn->packet_cache = packet->next; /* return it */ if (packet->capacity >= len) { TDS_MARK_UNDEFINED(packet->buf, packet->capacity); packet->next = NULL; packet->len = 0; packet->sid = 0; break; } /* discard packet if too small */ packet->next = to_free; to_free = packet; } tds_mutex_unlock(&conn->list_mtx); if (to_free) tds_free_packets(to_free); if (!packet) packet = tds_alloc_packet(NULL, len); return packet; }
int main(void) { tds_condition cond; tds_thread th; void *res; check(tds_cond_init(&cond), "failed initializing condition"); tds_mutex_lock(&mtx); check(tds_thread_create(&th, signal_proc, &cond) != 0, "error creating thread"); sleep(1); check(tds_cond_wait(&cond, &mtx), "failed waiting condition"); check(tds_thread_join(th, &res) != 0, "error waiting thread"); check(ptr2int(res) != 0, "error signaling condition"); tds_mutex_unlock(&mtx); check(tds_cond_destroy(&cond), "failed destroying condition"); return 0; }
static int tds_connection_put_packet(TDSSOCKET *tds, TDSPACKET *packet) { TDSCONNECTION *conn = tds->conn; if (TDS_UNLIKELY(!packet)) { tds_close_socket(tds); return TDS_FAIL; } tds->out_pos = 0; tds_mutex_lock(&conn->list_mtx); for (;;) { int wait_res; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Write attempt when state is TDS_DEAD"); break; } /* limit packet sending looking at sequence/window */ if (tds->send_seq <= tds->send_wnd) { /* append packet */ tds_append_packet(&conn->send_packets, packet); packet = NULL; } /* network ok ? process network */ if (!conn->in_net_tds) { tds_connection_network(conn, tds, packet ? 0 : 1); if (packet) continue; /* FIXME we are not sure we sent the packet !!! */ break; } /* signal thread processing network to handle our packet */ /* TODO check result */ tds_wakeup_send(&conn->wakeup, 0); /* wait local condition */ wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout); if (wait_res == ETIMEDOUT && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) { tds_mutex_unlock(&conn->list_mtx); tds_close_socket(tds); tds_free_packets(packet); return TDS_FAIL; } } tds_mutex_unlock(&conn->list_mtx); if (TDS_UNLIKELY(packet)) { tds_free_packets(packet); return TDS_FAIL; } if (IS_TDSDEAD(tds)) return TDS_FAIL; return TDS_SUCCESS; }
void pool_event_add(TDS_POOL *pool, TDS_POOL_EVENT *ev, TDS_POOL_EXECUTE execute) { tds_mutex_lock(&pool->events_mtx); ev->execute = execute; ev->next = pool->events; pool->events = ev; tds_mutex_unlock(&pool->events_mtx); WRITESOCKET(pool->event_fd, "x", 1); }
static TDS_THREAD_PROC_DECLARE(signal_proc, arg) { tds_condition *cond = (tds_condition *) arg; tds_mutex_lock(&mtx); if (tds_cond_signal(cond)) { tds_mutex_unlock(&mtx); /* failure */ return int2ptr(1); } tds_mutex_unlock(&mtx); /* success */ return int2ptr(0); }
static void Test(int use_threads, int return_data) { tds_thread wait_thread; #if !HAVE_ALARM if (!use_threads) return; #endif printf("testing with %s\n", use_threads ? "threads" : "signals"); printf(">> Wait 5 minutes...\n"); if (!use_threads) { alarm(4); signal(SIGALRM, sigalrm_handler); } else { int err; exit_thread = 0; err = tds_thread_create(&wait_thread, wait_thread_proc, NULL); if (err != 0) { perror("tds_thread_create"); exit(1); } } if (!return_data) CHKExecDirect(T("WAITFOR DELAY '000:05:00'"), SQL_NTS, "E"); else odbc_command2("SELECT MAX(p1.k) FROM tab1 p1, tab1 p2, tab1 p3, tab1 p4", "E"); tds_mutex_lock(&mtx); exit_thread = 1; tds_mutex_unlock(&mtx); if (!use_threads) { alarm(0); } else { tds_thread_join(wait_thread, NULL); } getErrorInfo(SQL_HANDLE_STMT, odbc_stmt); if (strcmp(C(sqlstate), "HY008") != 0) { fprintf(stderr, "Unexpected sql state returned\n"); odbc_disconnect(); exit(1); } odbc_reset_statement(); odbc_command("SELECT name FROM sysobjects WHERE 0=1"); }
/* accept a socket and read data as much as you can */ static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg) { TDS_SYS_SOCKET s = ptr2int(arg), sock; socklen_t len; char buf[128]; struct sockaddr_in sin; struct pollfd fd; memset(&sin, 0, sizeof(sin)); len = sizeof(sin); fd.fd = s; fd.events = POLLIN; fd.revents = 0; if (poll(&fd, 1, 30000) <= 0) { perror("poll"); exit(1); } if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) { perror("accept"); exit(1); } tds_mutex_lock(&mtx); fake_sock = sock; tds_mutex_unlock(&mtx); CLOSESOCKET(s); for (;;) { int len; fd.fd = sock; fd.events = POLLIN; fd.revents = 0; if (poll(&fd, 1, 30000) <= 0) { perror("poll"); exit(1); } /* just read and discard */ len = READSOCKET(sock, buf, sizeof(buf)); if (len == 0) break; if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) break; } return NULL; }
void tds_connection_close(TDSCONNECTION *conn) { unsigned n = 0; if (!TDS_IS_SOCKET_INVALID(conn->s)) { /* TODO check error ?? how to return it ?? */ CLOSESOCKET(conn->s); conn->s = INVALID_SOCKET; } tds_mutex_lock(&conn->list_mtx); for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(conn->sessions[n])) tds_set_state(conn->sessions[n], TDS_DEAD); tds_mutex_unlock(&conn->list_mtx); }
static SSL_CTX * tds_init_openssl(void) { const SSL_METHOD *meth; if (!tls_initialized) { tds_mutex_lock(&tls_mutex); if (!tls_initialized) { SSL_library_init(); tls_initialized = 1; } tds_mutex_unlock(&tls_mutex); } meth = TLSv1_client_method (); if (meth == NULL) return NULL; return SSL_CTX_new (meth); }
int main(void) { tds_condition cond; tds_thread th; void *res; check(tds_cond_init(&cond), "failed initializing condition"); tds_mutex_lock(&mtx); check(tds_thread_create(&th, signal_proc, &cond) != 0, "error creating thread"); tds_sleep_ms(100); check(tds_cond_wait(&cond, &mtx), "failed waiting condition"); res = &th; check(tds_thread_join(th, &res) != 0, "error waiting thread"); check(ptr2int(res) != 0, "error signaling condition"); /* under Windows mutex are recursive */ #ifndef _WIN32 check(tds_mutex_trylock(&mtx) == 0, "mutex should be locked"); #endif /* check timed version */ check(tds_cond_timedwait(&cond, &mtx, 1) == 0, "should not succeed to wait condition"); check(tds_thread_create(&th, signal_proc, &cond) != 0, "error creating thread"); check(tds_cond_timedwait(&cond, &mtx, 1), "error on timed waiting condition"); res = &th; check(tds_thread_join(th, &res) != 0, "error waiting thread"); check(ptr2int(res) != 0, "error signaling condition"); tds_mutex_unlock(&mtx); check(tds_cond_destroy(&cond), "failed destroying condition"); return 0; }
static void pool_process_events(TDS_POOL *pool) { TDS_POOL_EVENT *events, *next; /* detach events from pool */ tds_mutex_lock(&pool->events_mtx); events = pool->events; pool->events = NULL; tds_mutex_unlock(&pool->events_mtx); /* process them */ while (events) { next = events->next; events->next = NULL; events->execute(events); free(events); events = next; } }
int tds_append_cancel(TDSSOCKET *tds) { unsigned char buf[8]; TDSPACKET *packet; buf[0] = TDS_CANCEL; buf[1] = 1; TDS_PUT_A2BE(buf+2, 8); TDS_PUT_A4(buf+4, 0); if (IS_TDS7_PLUS(tds->conn) && !tds->login) buf[6] = 0x01; packet = tds_build_packet(tds, buf, 8); if (!packet) return TDS_FAIL; tds_mutex_lock(&tds->conn->list_mtx); tds_append_packet(&tds->conn->send_packets, packet); tds_mutex_unlock(&tds->conn->list_mtx); return TDS_SUCCESS; }
static TDS_THREAD_PROC_DECLARE(wait_thread_proc, arg) { int n; tds_sleep_s(4); printf(">>>> SQLCancel() ...\n"); CHKCancel("S"); printf(">>>> ... SQLCancel done\n"); for (n = 0; n < 4; ++n) { tds_sleep_s(1); tds_mutex_lock(&mtx); if (exit_thread) { tds_mutex_unlock(&mtx); return NULL; } tds_mutex_unlock(&mtx); } exit_forced(0); return NULL; }
int main(int argc, char *argv[]) { SQLTCHAR tmp[2048]; char conn[128]; SQLTCHAR sqlstate[6]; SQLSMALLINT len; int port; time_t start_time, end_time; #ifdef _WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif if (tds_mutex_init(&mtx)) return 1; if (odbc_read_login_info()) exit(1); /* * prepare our odbcinst.ini * is better to do it before connect cause uniODBC cache INIs * the name must be odbcinst.ini cause unixODBC accept only this name */ if (odbc_driver[0]) { FILE *f = fopen("odbcinst.ini", "w"); if (f) { fprintf(f, "[FreeTDS]\nDriver = %s\n", odbc_driver); fclose(f); /* force iODBC */ setenv("ODBCINSTINI", "./odbcinst.ini", 1); setenv("SYSODBCINSTINI", "./odbcinst.ini", 1); /* force unixODBC (only directory) */ setenv("ODBCSYSINI", ".", 1); } } for (port = 12340; port < 12350; ++port) if (!init_fake_server(port)) break; if (port == 12350) { fprintf(stderr, "Cannot bind to a port\n"); return 1; } printf("Fake server bound at port %d\n", port); init_connect(); CHKSetConnectAttr(SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) 10, sizeof(SQLINTEGER), "SI"); CHKSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) 10, SQL_IS_UINTEGER, "SI"); /* this is expected to work with unixODBC */ printf("try to connect to our port just to check connection timeout\n"); sprintf(conn, "DRIVER=FreeTDS;SERVER=127.0.0.1;Port=%d;TDS_Version=7.0;UID=test;PWD=test;DATABASE=tempdb;", port); start_time = time(NULL); CHKDriverConnect(NULL, T(conn), SQL_NTS, tmp, ODBC_VECTOR_SIZE(tmp), &len, SQL_DRIVER_NOPROMPT, "E"); end_time = time(NULL); memset(sqlstate, 'X', sizeof(sqlstate)); tmp[0] = 0; CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, sqlstate, NULL, tmp, ODBC_VECTOR_SIZE(tmp), NULL, "SI"); odbc_disconnect(); tds_mutex_lock(&mtx); CLOSESOCKET(fake_sock); tds_mutex_unlock(&mtx); tds_thread_join(fake_thread, NULL); printf("Message: %s - %s\n", C(sqlstate), C(tmp)); if (strcmp(C(sqlstate), "HYT00") || !strstr(C(tmp), "Timeout")) { fprintf(stderr, "Invalid timeout message\n"); return 1; } if (end_time - start_time < 10 || end_time - start_time > 16) { fprintf(stderr, "Unexpected connect timeout (%d)\n", (int) (end_time - start_time)); return 1; } printf("Done.\n"); ODBC_FREE(); return 0; }
static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg) { TDS_SYS_SOCKET s = ptr2int(arg), server_sock; socklen_t sock_len; int len; char buf[128]; struct sockaddr_in sin; fd_set fds_read, fds_write, fds_error; TDS_SYS_SOCKET max_fd = 0; memset(&sin, 0, sizeof(sin)); sock_len = sizeof(sin); alarm(30); fprintf(stderr, "waiting connect...\n"); if ((fake_sock = tds_accept(s, (struct sockaddr *) &sin, &sock_len)) < 0) { perror("accept"); exit(1); } CLOSESOCKET(s); if (TDS_IS_SOCKET_INVALID(server_sock = socket(remote_addr.sa.sa_family, SOCK_STREAM, 0))) { perror("socket"); exit(1); } fprintf(stderr, "connecting to server...\n"); if (remote_addr.sa.sa_family == AF_INET) { fprintf(stderr, "connecting to %x:%d\n", remote_addr.sin.sin_addr.s_addr, ntohs(remote_addr.sin.sin_port)); } if (connect(server_sock, &remote_addr.sa, remote_addr_len)) { perror("connect"); exit(1); } alarm(0); if (fake_sock > max_fd) max_fd = fake_sock; if (server_sock > max_fd) max_fd = server_sock; for (;;) { int res; FD_ZERO(&fds_read); FD_SET(fake_sock, &fds_read); FD_SET(server_sock, &fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error); FD_SET(fake_sock, &fds_error); FD_SET(server_sock, &fds_error); alarm(30); res = select(max_fd + 1, &fds_read, &fds_write, &fds_error, NULL); alarm(0); if (res < 0) { if (sock_errno == TDSSOCK_EINTR) continue; perror("select"); exit(1); } if (FD_ISSET(fake_sock, &fds_error) || FD_ISSET(server_sock, &fds_error)) { fprintf(stderr, "error in select\n"); exit(1); } /* just read and forward */ if (FD_ISSET(fake_sock, &fds_read)) { if (flow != sending) { tds_mutex_lock(&mtx); ++round_trips; tds_mutex_unlock(&mtx); } flow = sending; len = READSOCKET(fake_sock, buf, sizeof(buf)); if (len == 0) { fprintf(stderr, "client connection closed\n"); break; } if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) { fprintf(stderr, "read client error %d\n", sock_errno); break; } count_insert(buf, len); write_all(server_sock, buf, len); } if (FD_ISSET(server_sock, &fds_read)) { if (flow != receiving) { tds_mutex_lock(&mtx); ++round_trips; tds_mutex_unlock(&mtx); } flow = receiving; len = READSOCKET(server_sock, buf, sizeof(buf)); if (len == 0) { fprintf(stderr, "server connection closed\n"); break; } if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) { fprintf(stderr, "read server error %d\n", sock_errno); break; } write_all(fake_sock, buf, len); } } CLOSESOCKET(fake_sock); CLOSESOCKET(server_sock); return NULL; }
int main(int argc, char **argv) { SQLLEN sql_nts = SQL_NTS; const char *query; SQLINTEGER id = 0; char string[64]; TDS_SYS_SOCKET last_socket; int port; const int num_inserts = 20; int is_freetds; #ifdef _WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif if (tds_mutex_init(&mtx)) return 1; odbc_mark_sockets_opened(); odbc_connect(); /* * this does not work if server is not connected with socket * (ie ms driver connected locally) */ last_socket = odbc_find_last_socket(); if (TDS_IS_SOCKET_INVALID(last_socket)) { fprintf(stderr, "Error finding last socket opened\n"); return 1; } remote_addr_len = sizeof(remote_addr); if (tds_getpeername(last_socket, &remote_addr.sa, &remote_addr_len)) { fprintf(stderr, "Unable to get remote address\n"); return 1; } is_freetds = odbc_driver_is_freetds(); odbc_disconnect(); /* init fake server, behave like a proxy */ for (port = 12340; port < 12350; ++port) if (!init_fake_server(port)) break; if (port == 12350) { fprintf(stderr, "Cannot bind to a port\n"); return 1; } printf("Fake server bound at port %d\n", port); /* override connections */ if (is_freetds) { setenv("TDSHOST", "127.0.0.1", 1); sprintf(string, "%d", port); setenv("TDSPORT", string, 1); odbc_connect(); } else { char tmp[2048]; SQLSMALLINT len; CHKAllocEnv(&odbc_env, "S"); CHKAllocConnect(&odbc_conn, "S"); sprintf(tmp, "DRIVER={SQL Server};SERVER=127.0.0.1,%d;UID=%s;PWD=%s;DATABASE=%s;Network=DBMSSOCN;", port, odbc_user, odbc_password, odbc_database); printf("connection string: %s\n", tmp); CHKDriverConnect(NULL, T(tmp), SQL_NTS, (SQLTCHAR *) tmp, sizeof(tmp)/sizeof(SQLTCHAR), &len, SQL_DRIVER_NOPROMPT, "SI"); CHKAllocStmt(&odbc_stmt, "S"); } /* real test */ odbc_command("CREATE TABLE #test(i int, c varchar(40))"); odbc_reset_statement(); /* do not take into account connection statistics */ tds_mutex_lock(&mtx); round_trips = 0; inserts = 0; tds_mutex_unlock(&mtx); query = "insert into #test values (?, ?)"; CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI"); CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI"); CHKPrepare(T(query), SQL_NTS, "SI"); tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); for (id = 0; id < num_inserts; id++) { sprintf(string, "This is a test (%d)", (int) id); CHKExecute("SI"); CHKFreeStmt(SQL_CLOSE, "S"); } tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); odbc_reset_statement(); tds_mutex_lock(&mtx); if (inserts > 1 || round_trips > (unsigned) (num_inserts * 2 + 6)) { fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts); tds_mutex_unlock(&mtx); return 1; } printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); #ifdef ENABLE_DEVELOPING /* check for SQL_RESET_PARAMS */ tds_mutex_lock(&mtx); round_trips = 0; inserts = 0; tds_mutex_unlock(&mtx); CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI"); CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI"); CHKPrepare((SQLCHAR *) query, SQL_NTS, "SI"); tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); for (id = 0; id < num_inserts; id++) { CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI"); CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI"); sprintf(string, "This is a test (%d)", (int) id); CHKExecute("SI"); CHKFreeStmt(SQL_RESET_PARAMS, "S"); } tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); odbc_reset_statement(); tds_mutex_lock(&mtx); if (inserts > 1 || round_trips > num_inserts * 2 + 6) { fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts); tds_mutex_unlock(&mtx); return 1; } printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); #endif odbc_disconnect(); alarm(10); tds_thread_join(fake_thread, NULL); return 0; }
/** * Read in one 'packet' from the server. This is a wrapped outer packet of * the protocol (they bundle result packets into chunks and wrap them at * what appears to be 512 bytes regardless of how that breaks internal packet * up. (tetherow\@nol.org) * @return bytes read or -1 on failure */ int tds_read_packet(TDSSOCKET * tds) { #if ENABLE_ODBC_MARS TDSCONNECTION *conn = tds->conn; tds_mutex_lock(&conn->list_mtx); for (;;) { int wait_res; TDSPACKET **p_packet; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD\n"); break; } /* if there is a packet for me return it */ for (p_packet = &conn->packets; *p_packet; p_packet = &(*p_packet)->next) if ((*p_packet)->sid == tds->sid) break; if (*p_packet) { size_t hdr_size; /* remove our packet from list */ TDSPACKET *packet = *p_packet; *p_packet = packet->next; tds_packet_cache_add(conn, tds->recv_packet); tds_mutex_unlock(&conn->list_mtx); packet->next = NULL; tds->recv_packet = packet; hdr_size = packet->buf[0] == TDS72_SMP ? sizeof(TDS72_SMP_HEADER) : 0; tds->in_buf = packet->buf + hdr_size; tds->in_len = packet->len - hdr_size; tds->in_pos = 8; tds->in_flag = tds->in_buf[0]; /* send acknowledge if needed */ if (tds->recv_seq + 2 >= tds->recv_wnd) tds_update_recv_wnd(tds, tds->recv_seq + 4); return tds->in_len; } /* network ok ? process network */ if (!conn->in_net_tds) { tds_connection_network(conn, tds, 0); continue; } /* wait local condition */ wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout); if (wait_res == ETIMEDOUT && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) { tds_mutex_unlock(&conn->list_mtx); tds_close_socket(tds); return -1; } } tds_mutex_unlock(&conn->list_mtx); return -1; #else /* !ENABLE_ODBC_MARS */ unsigned char *pkt = tds->in_buf, *p, *end; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD"); return -1; } tds->in_len = 0; tds->in_pos = 0; for (p = pkt, end = p+8; p < end;) { int len = tds_connection_read(tds, p, end - p); if (len <= 0) { tds_close_socket(tds); return -1; } p += len; if (p - pkt >= 4) { unsigned pktlen = TDS_GET_A2BE(pkt+2); /* packet must at least contains header */ if (TDS_UNLIKELY(pktlen < 8)) { tds_close_socket(tds); return -1; } if (TDS_UNLIKELY(pktlen > tds->recv_packet->capacity)) { TDSPACKET *packet = tds_realloc_packet(tds->recv_packet, pktlen); if (TDS_UNLIKELY(!packet)) { tds_close_socket(tds); return -1; } tds->recv_packet = packet; pkt = packet->buf; p = pkt + (p-tds->in_buf); tds->in_buf = pkt; } end = pkt + pktlen; } } /* set the received packet type flag */ tds->in_flag = pkt[0]; /* Set the length and pos (not sure what pos is used for now */ tds->in_len = p - pkt; tds->in_pos = 8; tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len); return tds->in_len; #endif /* !ENABLE_ODBC_MARS */ }
static void tds_connection_network(TDSCONNECTION *conn, TDSSOCKET *tds, int send) { assert(!conn->in_net_tds); conn->in_net_tds = tds; tds_mutex_unlock(&conn->list_mtx); for (;;) { /* wait packets or update */ int rc = tds_select(tds, conn->send_packets ? TDSSELREAD|TDSSELWRITE : TDSSELREAD, tds->query_timeout); if (rc < 0) { /* FIXME better error report */ tds_connection_close(conn); break; } /* change notify */ /* TODO async */ if (!rc) { /* timeout */ tdsdump_log(TDS_DBG_INFO1, "timeout\n"); switch (rc = tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: continue; default: case TDS_INT_CANCEL: tds_close_socket(tds); } break; } /* * we must write first to catch write errors as * write errors, not as read */ /* something to send */ if (conn->send_packets && (rc & POLLOUT) != 0) { TDSSOCKET *s; short sid = tds_packet_write(conn); if (sid == tds->sid) break; /* return to caller */ tds_mutex_lock(&conn->list_mtx); if (sid >= 0 && sid < conn->num_sessions) { s = conn->sessions[sid]; if (TDSSOCKET_VALID(s)) tds_cond_signal(&s->packet_cond); } tds_mutex_unlock(&conn->list_mtx); /* avoid using a possible closed connection */ continue; } /* received */ if (rc & POLLIN) { TDSPACKET *packet; TDSSOCKET *s; /* try to read a packet */ tds_packet_read(conn, tds); packet = conn->recv_packet; if (!packet || conn->recv_pos < packet->len) continue; conn->recv_packet = NULL; conn->recv_pos = 0; tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", packet->buf, packet->len); tds_mutex_lock(&conn->list_mtx); if (packet->sid >= 0 && packet->sid < conn->num_sessions) { s = conn->sessions[packet->sid]; if (TDSSOCKET_VALID(s)) { /* append to correct session */ if (packet->buf[0] == TDS72_SMP && packet->buf[1] != TDS_SMP_DATA) tds_packet_cache_add(conn, packet); else tds_append_packet(&conn->packets, packet); packet = NULL; /* notify */ tds_cond_signal(&s->packet_cond); } } tds_mutex_unlock(&conn->list_mtx); tds_free_packets(packet); /* if we are receiving return the packet */ if (!send) break; } } tds_mutex_lock(&conn->list_mtx); conn->in_net_tds = NULL; }
/* read partial packet */ static void tds_packet_read(TDSCONNECTION *conn, TDSSOCKET *tds) { TDSPACKET *packet = conn->recv_packet; int len; /* allocate some space to read data */ if (!packet) { conn->recv_packet = packet = tds_get_packet(conn, MAX(conn->env.block_size + sizeof(TDS72_SMP_HEADER), 512)); if (!packet) goto Memory_Error; TDS_MARK_UNDEFINED(packet->buf, packet->capacity); conn->recv_pos = 0; packet->len = 8; } assert(conn->recv_pos < packet->len && packet->len <= packet->capacity); len = tds_connection_read(tds, packet->buf + conn->recv_pos, packet->len - conn->recv_pos); if (len < 0) goto Severe_Error; conn->recv_pos += len; assert(conn->recv_pos <= packet->len && packet->len <= packet->capacity); /* handle SMP */ if (conn->recv_pos > 0 && packet->buf[0] == TDS72_SMP) { TDS72_SMP_HEADER mars_header; short sid; TDSSOCKET *tds; TDS_UINT size; if (conn->recv_pos < 16) { packet->len = 16; return; } memcpy(&mars_header, packet->buf, sizeof(mars_header)); tdsdump_dump_buf(TDS_DBG_HEADER, "Received MARS header", &mars_header, sizeof(mars_header)); sid = TDS_GET_A2LE(&mars_header.sid); /* FIXME this is done even by caller !! */ tds = NULL; tds_mutex_lock(&conn->list_mtx); if (sid >= 0 && sid < conn->num_sessions) tds = conn->sessions[sid]; tds_mutex_unlock(&conn->list_mtx); packet->sid = sid; if (tds == BUSY_SOCKET) { if (mars_header.type != TDS_SMP_FIN) { tdsdump_log(TDS_DBG_ERROR, "Received MARS with no session (%d)\n", sid); goto Severe_Error; } /* check if was just a "zombie" socket */ tds_mutex_lock(&conn->list_mtx); conn->sessions[sid] = NULL; tds_mutex_unlock(&conn->list_mtx); /* reset packet to initial state to reuse it */ packet->len = 8; conn->recv_pos = 0; return; } if (!tds) { /* server sent a unknown packet, close connection */ goto Severe_Error; } tds->send_wnd = TDS_GET_A4LE(&mars_header.wnd); size = TDS_GET_A4LE(&mars_header.size); if (mars_header.type == TDS_SMP_ACK) { if (size != sizeof(mars_header)) goto Severe_Error; } else if (mars_header.type == TDS_SMP_DATA) { if (size < 0x18 || size > 0xffffu + sizeof(mars_header)) goto Severe_Error; /* avoid recursive SMP */ if (conn->recv_pos > 16 && packet->buf[16] == TDS72_SMP) goto Severe_Error; /* TODO is possible to put 2 TDS packet inside a single DATA ?? */ if (conn->recv_pos >= 20 && TDS_GET_A2BE(&packet->buf[18]) != size - 16) goto Severe_Error; tds->recv_seq = TDS_GET_A4LE(&mars_header.seq); /* * does not sent ACK here cause this would lead to memory waste * if session is not able to handle all that packets */ } else if (mars_header.type == TDS_SMP_FIN) { if (size != sizeof(mars_header)) goto Severe_Error; /* this socket shold now not start another session */ // tds_set_state(tds, TDS_DEAD); // tds->sid = -1; } else goto Severe_Error; if (mars_header.type != TDS_SMP_DATA) return; if (packet->len < size) { packet = tds_realloc_packet(packet, size); if (!packet) goto Memory_Error; conn->recv_packet = packet; } packet->len = size; return; } assert(conn->recv_pos <= packet->len && packet->len <= packet->capacity); /* normal packet */ if (conn->recv_pos >= 8) { len = TDS_GET_A2BE(&packet->buf[2]); if (len < 8) goto Severe_Error; if (packet->len < len) { packet = tds_realloc_packet(packet, len); if (!packet) goto Memory_Error; conn->recv_packet = packet; } packet->len = len; } return; Memory_Error: Severe_Error: tds_connection_close(conn); tds_free_packets(packet); conn->recv_packet = NULL; }
TDSRET tds_ssl_init(TDSSOCKET *tds) { gnutls_session_t session; gnutls_certificate_credentials_t xcred; int ret; const char *tls_msg; xcred = NULL; session = NULL; tls_msg = "initializing tls"; if (!tls_initialized) { ret = 0; tds_mutex_lock(&tls_mutex); if (!tls_initialized) { tds_gcry_init(); ret = gnutls_global_init(); if (ret == 0) tls_initialized = 1; } tds_mutex_unlock(&tls_mutex); if (ret != 0) goto cleanup; } if (tds_write_dump && tls_initialized < 2) { gnutls_global_set_log_level(11); gnutls_global_set_log_function(tds_tls_log); tls_initialized = 2; } tls_msg = "allocating credentials"; ret = gnutls_certificate_allocate_credentials(&xcred); if (ret != 0) goto cleanup; if (!tds_dstr_isempty(&tds->login->cafile)) { tls_msg = "loading CA file"; if (strcasecmp(tds_dstr_cstr(&tds->login->cafile), "system") == 0) ret = gnutls_certificate_set_x509_system_trust(xcred); else ret = gnutls_certificate_set_x509_trust_file(xcred, tds_dstr_cstr(&tds->login->cafile), GNUTLS_X509_FMT_PEM); if (ret <= 0) goto cleanup; if (!tds_dstr_isempty(&tds->login->crlfile)) { tls_msg = "loading CRL file"; ret = gnutls_certificate_set_x509_crl_file(xcred, tds_dstr_cstr(&tds->login->crlfile), GNUTLS_X509_FMT_PEM); if (ret <= 0) goto cleanup; } #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION gnutls_certificate_set_verify_function(xcred, tds_verify_certificate); #endif } /* Initialize TLS session */ tls_msg = "initializing session"; ret = gnutls_init(&session, GNUTLS_CLIENT); if (ret != 0) goto cleanup; gnutls_transport_set_ptr(session, tds); gnutls_transport_set_pull_function(session, tds_pull_func_login); gnutls_transport_set_push_function(session, tds_push_func_login); /* NOTE: there functions return int however they cannot fail */ /* use default priorities... */ gnutls_set_default_priority(session); /* ... but overwrite some */ ret = gnutls_priority_set_direct (session, "NORMAL:%COMPAT:-VERS-SSL3.0", NULL); if (ret != 0) goto cleanup; /* mssql does not like padding too much */ #ifdef HAVE_GNUTLS_RECORD_DISABLE_PADDING gnutls_record_disable_padding(session); #endif /* put the anonymous credentials to the current session */ tls_msg = "setting credential"; ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); if (ret != 0) goto cleanup; /* Perform the TLS handshake */ tls_msg = "handshake"; ret = gnutls_handshake (session); if (ret != 0) goto cleanup; #ifndef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION ret = tds_verify_certificate(session); if (ret != 0) goto cleanup; #endif tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n"); gnutls_transport_set_ptr(session, tds->conn); gnutls_transport_set_pull_function(session, tds_pull_func); gnutls_transport_set_push_function(session, tds_push_func); tds->conn->tls_session = session; tds->conn->tls_credentials = xcred; return TDS_SUCCESS; cleanup: if (session) gnutls_deinit(session); if (xcred) gnutls_certificate_free_credentials(xcred); tdsdump_log(TDS_DBG_ERROR, "%s failed: %s\n", tls_msg, gnutls_strerror (ret)); return TDS_FAIL; }