Example #1
0
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);
}
Example #2
0
/**
 * 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;
}
Example #3
0
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;
}
Example #4
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; 
}
Example #5
0
/**
 * \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;
}
Example #6
0
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;
}
Example #7
0
/**
 * 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;
}
Example #8
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)
{
	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;
}