static void unfinished_query_test(TDSSOCKET *tds) { int char_len; char *buf, *p; int i, len; union { TDS_USMALLINT si; TDS_UINT i; TDS_INT8 i8; char buf[8]; } conv; if (IS_TDS72_PLUS(tds->conn)) return; tds_init_write_buf(tds); /* try to build an invalid (unfinished) query split in two packets */ char_len = IS_TDS7_PLUS(tds->conn) ? 2 : 1; buf = calloc(1, tds->out_buf_max + 200); memset(buf, '-', tds->out_buf_max + 200); strcpy(buf + (tds->out_buf_max - 8) / char_len - strlen(select_query) + 1, select_query); memset(strchr(buf, 0), 0, 16); /* convert if needed */ len = strlen(buf); for (i = len; --i >= 0; ) { char c = buf[i]; buf[i * char_len + 0] = c; if (IS_TDS7_PLUS(tds->conn)) buf[i * char_len + 1] = 0; } len *= char_len; /* send the query using tds_put_int8, non allineati */ tds->out_flag = TDS_QUERY; if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING) exit(1); p = buf; memcpy(conv.buf, p, 2); tds_put_smallint(tds, conv.si); p += 2; for (; p < buf + len; p += 8) { CHECK_TDS_EXTRA(tds); memcpy(conv.buf, p, 8); tds_put_int8(tds, conv.i8); } tds_flush_packet(tds); tds_set_state(tds, TDS_PENDING); /* check result was fine */ if (TDS_FAILED(tds_process_simple_query(tds))) { fprintf(stderr, "Error in prepared query\n"); exit(1); } free(buf); }
/** * Reads a string from wire and put in a DSTR. * On error we read the bytes from the wire anyway. * \tds * \param[out] s output string * \param[in] len string length (in characters) * \return string or NULL on error */ DSTR* tds_dstr_get(TDSSOCKET * tds, DSTR * s, size_t len) { size_t out_len; CHECK_TDS_EXTRA(tds); /* assure sufficient space for every conversion */ if (TDS_UNLIKELY(!tds_dstr_alloc(s, len * 4))) { tds_get_n(tds, NULL, len); return NULL; } out_len = tds_get_string(tds, len, tds_dstr_buf(s), len * 4); tds_dstr_setlen(s, out_len); return s; }
int main(int argc, char **argv) { TDSLOGIN *login; TDSSOCKET *tds; int ret; int verbose = 0; TDS_INT8 i8; unsigned limit; fprintf(stdout, "%s: Testing login, logout\n", __FILE__); ret = try_tds_login(&login, &tds, __FILE__, verbose); if (ret != TDS_SUCCESS) { fprintf(stderr, "try_tds_login() failed\n"); return 1; } unfinished_query_test(tds); tds->out_flag = TDS_QUERY; if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING) { return 1; } tds_put_n(tds, "aaa", 3); limit = tds->out_buf_max / 8 + 100; for (i8 = 0; i8 < limit; ++i8) { CHECK_TDS_EXTRA(tds); tds_put_int8(tds, i8); } tds_send_cancel(tds); tds_process_simple_query(tds); try_tds_logout(login, tds, verbose); return 0; }
/** * Set state of TDS connection, with logging and checking. * \param tds state information for the socket and the TDS protocol * \param state the new state of the connection, cf. TDS_STATE. * \return the new state, which might not be \a state. */ TDS_STATE tds_set_state(TDSSOCKET * tds, TDS_STATE state) { const TDS_STATE prior_state = tds->state; static const char state_names[][10] = { "IDLE", "QUERYING", "PENDING", "READING", "DEAD" }; assert(state < TDS_VECTOR_SIZE(state_names)); assert(tds->state < TDS_VECTOR_SIZE(state_names)); if (state == tds->state) return state; switch(state) { /* transition to READING are valid only from PENDING */ case TDS_PENDING: if (tds->state != TDS_READING && tds->state != TDS_QUERYING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } tds->state = state; break; case TDS_READING: if (tds->state != TDS_PENDING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } tds->state = state; break; case TDS_IDLE: if (tds->state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } case TDS_DEAD: tds->state = state; break; case TDS_QUERYING: CHECK_TDS_EXTRA(tds); if (tds->state == TDS_DEAD) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0); break; } else if (tds->state != TDS_IDLE) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSERPND, 0); break; } /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(tds, tds->cur_cursor); tds->cur_cursor = NULL; tds->internal_sp_called = 0; tds->state = state; break; default: assert(0); break; } tdsdump_log(TDS_DBG_ERROR, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]); CHECK_TDS_EXTRA(tds); return tds->state; }
/** * \brief Call the client library's error handler (for library-generated errors only) * * The client library error handler may return: * TDS_INT_CANCEL -- Return TDS_FAIL to the calling function. For TDSETIME, closes the connection first. * TDS_INT_CONTINUE -- For TDSETIME only, retry the network read/write operation. Else invalid. * TDS_INT_TIMEOUT -- For TDSETIME only, send a TDSCANCEL packet. Else invalid. * * These are Sybase semantics, but they serve all purposes. * The application tells the library to quit, fail, retry, or attempt to cancel. In the event of a network timeout, * a failed operation necessarily means the connection becomes unusable, because no cancellation dialog was * concluded with the server. * * It is the client library's duty to call the error handler installed by the application, if any, and to interpret the * installed handler's return code. It may return to this function one of the above codes only. This function will not * check the return code because there's nothing that can be done here except abort. It is merely passed to the * calling function, which will (we hope) DTRT. * * \param tds_ctx points to a TDSCONTEXT structure * \param tds the connection structure, may be NULL if not connected * \param msgno an enumerated libtds msgno, cf. tds.h * \param errnum the OS errno, if it matters, else zero * * \returns client library function's return code */ int tdserror (const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, int msgno, int errnum) { #if 0 static const char int_exit_text[] = "FreeTDS: libtds: exiting because client error handler returned %d for msgno %d\n"; static const char int_invalid_text[] = "%s (%d) received from client library error handler for nontimeout for error %d." " Treating as INT_EXIT\n"; #endif const TDS_ERROR_MESSAGE *err; TDSMESSAGE msg; int rc = TDS_INT_CANCEL; tdsdump_log(TDS_DBG_FUNC, "tdserror(%p, %p, %d, %d)\n", tds_ctx, tds, msgno, errnum); /* look up the error message */ for (err = tds_error_messages; err->msgno; ++err) { if (err->msgno == msgno) break; } CHECK_CONTEXT_EXTRA(tds_ctx); if (tds) CHECK_TDS_EXTRA(tds); if (tds_ctx && tds_ctx->err_handler) { memset(&msg, 0, sizeof(TDSMESSAGE)); msg.msgno = msgno; msg.severity = err->severity; msg.state = -1; msg.server = "OpenClient"; msg.line_number = -1; msg.message = (TDS_CHAR*) err->msgtext; msg.sql_state = tds_alloc_client_sqlstate(msg.msgno); msg.oserr = errnum; /* * Call client library handler. * The client library must return a valid code. It is not checked again here. */ rc = tds_ctx->err_handler(tds_ctx, tds, &msg); tdsdump_log(TDS_DBG_FUNC, "tdserror: client library returned %s(%d)\n", retname(rc), rc); TDS_ZERO_FREE(msg.sql_state); } else { const static char msg[] = "tdserror: client library not called because either " "tds_ctx (%p) or tds_ctx->err_handler is NULL\n"; tdsdump_log(TDS_DBG_FUNC, msg, tds_ctx); } assert(msgno == TDSETIME || rc != TDS_INT_TIMEOUT); /* client library should prevent */ assert(msgno == TDSETIME || rc != TDS_INT_CONTINUE); /* client library should prevent */ if (msgno != TDSETIME && rc != TDS_INT_CANCEL) { tdsdump_log(TDS_DBG_SEVERE, "exit: %s(%d) valid only for TDSETIME\n", retname(rc), rc); rc = TDS_INT_CANCEL; } if (rc == TDS_INT_TIMEOUT) { tds_send_cancel(tds); rc = TDS_INT_CONTINUE; } tdsdump_log(TDS_DBG_FUNC, "tdserror: returning %s(%d)\n", retname(rc), rc); return rc; }
bool pool_packet_read(TDSSOCKET *tds) { int packet_len; int readed, err; tdsdump_log(TDS_DBG_INFO1, "tds in_len %d in_pos %d\n", tds->in_len, tds->in_pos); /* determine how much we should read */ packet_len = 8; if (tds->in_len >= 4) packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (tds->in_len >= packet_len) { tds->in_pos = 0; tds->in_len = 0; packet_len = 8; } tdsdump_log(TDS_DBG_INFO1, "packet_len %d capacity %d\n", packet_len, tds->recv_packet->capacity); assert(packet_len > tds->in_len); assert(packet_len <= tds->recv_packet->capacity); assert(tds->in_len < tds->recv_packet->capacity); readed = read(tds_get_s(tds), &tds->in_buf[tds->in_len], packet_len - tds->in_len); tdsdump_log(TDS_DBG_INFO1, "readed %d\n", readed); if (readed == 0) { /* socket closed */ tds->in_len = 0; return false; } if (readed < 0) { /* error */ err = sock_errno; if (err == EINTR || TDSSOCK_WOULDBLOCK(err)) return true; goto failure; } tds->in_len += readed; if (tds->in_len >= 4) { packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (packet_len < 8) goto failure; tdsdump_log(TDS_DBG_INFO1, "packet_len %d in_len %d after\n", packet_len, tds->in_len); /* resize packet if not enough */ if (packet_len > tds->recv_packet->capacity) { TDSPACKET *packet; packet = tds_realloc_packet(tds->recv_packet, packet_len); if (!packet) goto failure; tds->in_buf = packet->buf; tds->recv_packet = packet; } CHECK_TDS_EXTRA(tds); return tds->in_len < packet_len; } return true; failure: tds->in_len = -1; return false; }
/** * Read part of packet. Function does not block. * @return true if packet is not complete and we must call again, * false on full packet or error. */ bool pool_packet_read(TDSSOCKET *tds) { int packet_len; int readed, err; tdsdump_log(TDS_DBG_INFO1, "tds in_len %d in_pos %d\n", tds->in_len, tds->in_pos); // TODO MARS /* determine packet size */ packet_len = tds->in_len >= 4 ? TDS_GET_A2BE(&tds->in_buf[2]) : 8; if (TDS_UNLIKELY(packet_len < 8)) { tds->in_len = 0; return false; } /* get another packet */ if (tds->in_len >= packet_len) { tds->in_pos = 0; tds->in_len = 0; } for (;;) { /* determine packet size */ packet_len = 8; if (tds->in_len >= 4) { packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (packet_len < 8) break; tdsdump_log(TDS_DBG_INFO1, "packet_len %d in_len %d\n", packet_len, tds->in_len); /* resize packet if not enough */ if (packet_len > tds->recv_packet->capacity) { TDSPACKET *packet; packet = tds_realloc_packet(tds->recv_packet, packet_len); if (!packet) break; tds->in_buf = packet->buf; tds->recv_packet = packet; } CHECK_TDS_EXTRA(tds); if (tds->in_len >= packet_len) return false; } assert(packet_len > tds->in_len); assert(packet_len <= tds->recv_packet->capacity); assert(tds->in_len < tds->recv_packet->capacity); readed = read(tds_get_s(tds), &tds->in_buf[tds->in_len], packet_len - tds->in_len); tdsdump_log(TDS_DBG_INFO1, "readed %d\n", readed); /* socket closed */ if (readed == 0) break; /* error */ if (readed < 0) { err = sock_errno; if (err == EINTR) continue; if (TDSSOCK_WOULDBLOCK(err)) return true; break; } /* got some data */ tds->in_len += readed; } /* failure */ tds->in_len = 0; return false; }
/** * Set state of TDS connection, with logging and checking. * \param tds state information for the socket and the TDS protocol * \param state the new state of the connection, cf. TDS_STATE. * \return the new state, which might not be \a state. */ TDS_STATE tds_set_state(TDSSOCKET * tds, TDS_STATE state) { TDS_STATE prior_state; static const char state_names[][8] = { "IDLE", "WRITING", "SENDING", "PENDING", "READING", "DEAD" }; assert(state < TDS_VECTOR_SIZE(state_names)); assert(tds->state < TDS_VECTOR_SIZE(state_names)); prior_state = tds->state; if (state == prior_state) return state; switch(state) { case TDS_PENDING: if (prior_state == TDS_READING || prior_state == TDS_WRITING) { tds->state = TDS_PENDING; tds_mutex_unlock(&tds->wire_mtx); break; } tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; case TDS_READING: /* transition to READING are valid only from PENDING */ if (tds_mutex_trylock(&tds->wire_mtx)) return tds->state; if (tds->state != TDS_PENDING) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } tds->state = state; break; case TDS_SENDING: if (prior_state != TDS_READING && prior_state != TDS_WRITING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } if (tds->state == TDS_READING) { /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(&tds->cur_cursor); tds_release_cur_dyn(tds); tds->current_op = TDS_OP_NONE; } tds_mutex_unlock(&tds->wire_mtx); tds->state = state; break; case TDS_IDLE: if (prior_state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } case TDS_DEAD: if (prior_state == TDS_READING || prior_state == TDS_WRITING) tds_mutex_unlock(&tds->wire_mtx); tds->state = state; break; case TDS_WRITING: CHECK_TDS_EXTRA(tds); if (tds_mutex_trylock(&tds->wire_mtx)) return tds->state; if (tds->state == TDS_DEAD) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0); break; } else if (tds->state != TDS_IDLE && tds->state != TDS_SENDING) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSERPND, 0); break; } if (tds->state == TDS_IDLE) { /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(&tds->cur_cursor); tds_release_cur_dyn(tds); tds->current_op = TDS_OP_NONE; } tds->state = state; break; default: assert(0); break; } state = tds->state; tdsdump_log(TDS_DBG_ERROR, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]); CHECK_TDS_EXTRA(tds); return state; }