コード例 #1
0
ファイル: read.c プロジェクト: Distrotech/freetds
/**
 * Fetch a string from the wire.
 * Output string is NOT null terminated.
 * If TDS version is 7 or 8 read unicode string and convert it.
 * This function should be use to read server default encoding strings like 
 * columns name, table names, etc, not for data (use tds_get_char_data instead)
 * @return bytes written to \a dest
 * @param tds  connection information
 * @param string_len length of string to read from wire 
 *        (in server characters, bytes for tds4-tds5, ucs2 for tds7+)
 * @param dest destination buffer, if NULL string is read and discarded
 * @param dest_size destination buffer size, in bytes
 */
int
tds_get_string(TDSSOCKET * tds, int string_len, char *dest, size_t dest_size)
{
	size_t wire_bytes;

	/*
	 * FIX: 02-Jun-2000 by Scott C. Gray (SCG)
	 * Bug to malloc(0) on some platforms.
	 */
	if (string_len == 0) {
		return 0;
	}

	assert(string_len >= 0 && dest_size >= 0);

	wire_bytes = IS_TDS7_PLUS(tds) ? string_len * 2 : string_len;

	if (IS_TDS7_PLUS(tds)) {
		if (dest == NULL) {
			tds_get_n(tds, NULL, (int)wire_bytes);
			return string_len;
		}

		return read_and_convert(tds, tds->char_convs[client2ucs2], &wire_bytes, &dest, &dest_size);
	} else {
		/* FIXME convert to client charset */
		assert(dest_size >= (size_t) string_len);
		tds_get_n(tds, dest, string_len);
		return string_len;
	}
}
コード例 #2
0
ファイル: read.c プロジェクト: DavidRueter/freetds
/**
 * Fetch character data the wire.
 * Output is NOT null terminated.
 * If \a char_conv is not NULL, convert data accordingly.
 * \param tds         state information for the socket and the TDS protocol
 * \param row_buffer  destination buffer in current_row. Can't be NULL
 * \param wire_size   size to read from wire (in bytes)
 * \param curcol      column information
 * \return TDS_SUCCESS or TDS_FAIL (probably memory error on text data)
 */
TDSRET
tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol)
{
	size_t in_left;

	assert(curcol->char_conv);

	/*
	 * row_buffer is a column buffer, allocated when the column's metadata are processed
	 * and reused for each row.
	 */

	/* silly case, empty string */
	if (wire_size == 0) {
		curcol->column_cur_size = 0;
		return TDS_SUCCESS;
	}

	in_left = curcol->column_size;
	curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, row_buffer, in_left);
	if (TDS_UNLIKELY(wire_size > 0)) {
		tds_get_n(tds, NULL, wire_size);
		tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n",
						 (unsigned int) wire_size, curcol->column_cur_size);
		return TDS_FAIL;
	}
	return TDS_SUCCESS;
}
コード例 #3
0
ファイル: read.c プロジェクト: Distrotech/freetds
/**
 * Fetch character data the wire.
 * Output is NOT null terminated.
 * If \a char_conv is not NULL, convert data accordingly.
 * \param tds         state information for the socket and the TDS protocol
 * \param row_buffer  destination buffer in current_row. Can't be NULL
 * \param wire_size   size to read from wire (in bytes)
 * \param curcol      column information
 * \return TDS_SUCCEED or TDS_FAIL (probably memory error on text data)
 * \todo put a TDSICONV structure in every TDSCOLUMN
 */
int
tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol)
{
	size_t in_left;
	TDSBLOB *blob = NULL;
	char *dest = row_buffer;

	if (is_blob_col(curcol)) {
		blob = (TDSBLOB *) row_buffer;
		dest = blob->textvalue;
	}

	/* 
	 * dest is usually a column buffer, allocated when the column's metadata are processed 
	 * and reused for each row.  
	 * For blobs, dest is blob->textvalue, and can be reallocated or freed
	 * TODO: reallocate if blob and no space 
	 */
	 
	/* silly case, empty string */
	if (wire_size == 0) {
		curcol->column_cur_size = 0;
		if (blob)
			TDS_ZERO_FREE(blob->textvalue);
		return TDS_SUCCEED;
	}

	if (curcol->char_conv) {
		/*
		 * TODO The conversion should be selected from curcol and tds version
		 * TDS8/single -> use curcol collation
		 * TDS7/single -> use server single byte
		 * TDS7+/unicode -> use server (always unicode)
		 * TDS5/4.2 -> use server 
		 * TDS5/UTF-8 -> use server
		 * TDS5/UTF-16 -> use UTF-16
		 */
		in_left = blob ? curcol->column_cur_size : curcol->column_size;
		curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, &dest, &in_left);
		if (wire_size > 0) {
			tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n", 
							 (unsigned int) wire_size, curcol->column_cur_size);
			return TDS_FAIL;
		}
	} else {
		curcol->column_cur_size = (TDS_INT)wire_size;
		if (tds_get_n(tds, dest, (int)wire_size) == NULL) {
			tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: failed to read %u from wire. \n",
				    (unsigned int) wire_size);
			return TDS_FAIL;
		}
	}
	return TDS_SUCCEED;
}
コード例 #4
0
ファイル: read.c プロジェクト: Distrotech/freetds
/**
 * Get an int32 from the server.
 */
TDS_INT
tds_get_int(TDSSOCKET * tds)
{
	unsigned char bytes[4];

	tds_get_n(tds, bytes, 4);
#if WORDS_BIGENDIAN
	if (tds->emul_little_endian)
		return (TDS_INT) TDS_GET_A4LE(bytes);
#endif
	return (TDS_INT) TDS_GET_A4(bytes);
}
コード例 #5
0
ファイル: read.c プロジェクト: Distrotech/freetds
/**
 * Get an int16 from the server.
 */
TDS_SMALLINT
tds_get_smallint(TDSSOCKET * tds)
{
	unsigned char bytes[2];

	tds_get_n(tds, bytes, 2);
#if WORDS_BIGENDIAN
	if (tds->emul_little_endian)
		return (TDS_SMALLINT) TDS_GET_A2LE(bytes);
#endif
	return (TDS_SMALLINT) TDS_GET_A2(bytes);
}
コード例 #6
0
ファイル: read.c プロジェクト: DavidRueter/freetds
/**
 * Get an int32 from the server.
 * \tds
 */
TDS_UINT
tds_get_uint(TDSSOCKET * tds)
{
	TDS_UINT bytes;

	tds_get_n(tds, &bytes, 4);
#if WORDS_BIGENDIAN
	if (tds->conn->emul_little_endian)
		return TDS_GET_A4LE(&bytes);
#endif
	return TDS_GET_A4(&bytes);
}
コード例 #7
0
ファイル: read.c プロジェクト: DavidRueter/freetds
/**
 * Get an int16 from the server.
 */
TDS_USMALLINT
tds_get_usmallint(TDSSOCKET * tds)
{
	TDS_USMALLINT bytes[1];

	tds_get_n(tds, &bytes, 2);
#if WORDS_BIGENDIAN
	if (tds->conn->emul_little_endian)
		return (TDS_USMALLINT) TDS_GET_A2LE(&bytes);
#endif
	return (TDS_USMALLINT) TDS_GET_A2(&bytes);
}
コード例 #8
0
static int
tds_sspi_handle_next(TDSSOCKET * tds, struct tds_authentication * tds_auth, size_t len)
{
	SecBuffer in_buf, out_buf;
	SecBufferDesc in_desc, out_desc;
	SECURITY_STATUS status;
	ULONG attrs;
	TimeStamp ts;
	TDS_UCHAR *auth_buf;

	TDSSSPIAUTH *auth = (TDSSSPIAUTH *) tds_auth;

	if (len < 32 || len > NTLMBUF_LEN)
		return TDS_FAIL;

	auth_buf = (TDS_UCHAR *) malloc(len);
	if (!auth_buf)
		return TDS_FAIL;
	tds_get_n(tds, auth_buf, (int)len);

	in_desc.ulVersion  = out_desc.ulVersion  = SECBUFFER_VERSION;
	in_desc.cBuffers   = out_desc.cBuffers   = 1;
	in_desc.pBuffers   = &in_buf;
	out_desc.pBuffers   = &out_buf;

	in_buf.BufferType = SECBUFFER_TOKEN;
	in_buf.pvBuffer   = auth_buf;
	in_buf.cbBuffer   = (ULONG)len;

	out_buf.BufferType = SECBUFFER_TOKEN;
	out_buf.pvBuffer   = auth->tds_auth.packet;
	out_buf.cbBuffer   = NTLMBUF_LEN;

	status = sec_fn->InitializeSecurityContextA(&auth->cred, &auth->cred_ctx, auth->sname,
		ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION,
		0, SECURITY_NETWORK_DREP, &in_desc,
		0, &auth->cred_ctx, &out_desc,
		&attrs, &ts);

	free(auth_buf);

	if (status != SEC_E_OK)
		return TDS_FAIL;
	if (out_buf.cbBuffer == 0)
		return TDS_SUCCEED;

	tds_put_n(tds, auth->tds_auth.packet, out_buf.cbBuffer);

	return tds_flush_packet(tds);
}
コード例 #9
0
ファイル: read.c プロジェクト: DavidRueter/freetds
/**
 * 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;
}
コード例 #10
0
ファイル: read.c プロジェクト: DavidRueter/freetds
/**
 * Fetch a string from the wire.
 * Output string is NOT null terminated.
 * If TDS version is 7 or 8 read unicode string and convert it.
 * This function should be use to read server default encoding strings like 
 * columns name, table names, etc, not for data (use tds_get_char_data instead)
 * @return bytes written to \a dest
 * @param tds  connection information
 * @param string_len length of string to read from wire 
 *        (in server characters, bytes for tds4-tds5, ucs2 for tds7+)
 * @param dest destination buffer, if NULL string is read and discarded
 * @param dest_size destination buffer size, in bytes
 */
size_t
tds_get_string(TDSSOCKET * tds, size_t string_len, char *dest, size_t dest_size)
{
	size_t wire_bytes = string_len;
	unsigned conv = client2server_chardata;

	if (IS_TDS7_PLUS(tds->conn)) {
		wire_bytes *= 2u;
		conv = client2ucs2;
	}

	if (dest == NULL) {
		tds_get_n(tds, NULL, wire_bytes);
		return string_len;
	}

	return read_and_convert(tds, tds->conn->char_convs[conv], &wire_bytes, dest, dest_size);
}
コード例 #11
0
ファイル: read.c プロジェクト: Distrotech/freetds
TDS_INT8
tds_get_int8(TDSSOCKET * tds)
{
	TDS_INT h;
	TDS_UINT l;
	unsigned char bytes[8];

	tds_get_n(tds, bytes, 8);
#if WORDS_BIGENDIAN
	if (tds->emul_little_endian) {
		l = TDS_GET_A4LE(bytes);
		h = (TDS_INT) TDS_GET_A4LE(bytes+4);
	} else {
		h = (TDS_INT) TDS_GET_A4(bytes);
		l = TDS_GET_A4(bytes+4);
	}
#else
	l = TDS_GET_A4(bytes);
	h = (TDS_INT) TDS_GET_A4(bytes+4);
#endif
	return (((TDS_INT8) h) << 32) | l;
}
コード例 #12
0
ファイル: read.c プロジェクト: DavidRueter/freetds
/**
 * Get an uint64 from the server.
 * \tds
 */
TDS_UINT8
tds_get_uint8(TDSSOCKET * tds)
{
	TDS_UINT h;
	TDS_UINT l;
	TDS_UINT bytes[2];

	tds_get_n(tds, bytes, 8);
#if WORDS_BIGENDIAN
	if (tds->conn->emul_little_endian) {
		l = TDS_GET_A4LE(bytes);
		h = TDS_GET_A4LE(bytes+1);
	} else {
		h = TDS_GET_A4(bytes);
		l = TDS_GET_A4(bytes+1);
	}
#else
	l = TDS_GET_A4(bytes);
	h = TDS_GET_A4(bytes+1);
#endif
	return (((TDS_UINT8) h) << 32) | l;
}
コード例 #13
0
ファイル: query.c プロジェクト: angcoch/freetds
/**
 * Read a TDS5 tokenized query.
 */
char *
tds_get_query(TDSSOCKET * tds)
{
	int len;

	if (query_buflen == 0) {
		query_buflen = 1024;
		query = (char *) malloc(query_buflen);
	}
	tds_get_byte(tds);	/* 33 */
	len = tds_get_int(tds);	/* query size +1 */
	tds_get_byte(tds);	/* has args, ignored TODO */
	assert(len >= 1);	/* TODO handle correctly */
	if (len > query_buflen) {
		query_buflen = len;
		query = (char *) realloc(query, query_buflen);
	}
	--len;
	tds_get_n(tds, query, len);
	query[len] = 0;
	return query;
}
コード例 #14
0
ファイル: gssapi.c プロジェクト: DoubleQuantZ/freetds
static TDSRET
tds_gss_handle_next(TDSSOCKET * tds, struct tds_authentication * auth, size_t len)
{
	TDSRET res;
	gss_buffer_desc recv_tok;

	if (((struct tds_gss_auth *) auth)->last_stat != GSS_S_CONTINUE_NEEDED)
		return TDS_FAIL;

	if (auth->packet) {
		OM_uint32 min_stat;
		gss_buffer_desc send_tok;

		send_tok.value = (void *) auth->packet;
		send_tok.length = auth->packet_len;
		gss_release_buffer(&min_stat, &send_tok);
		auth->packet = NULL;
	}

	recv_tok.length = len;
	recv_tok.value = (char* ) malloc(len);
	if (!recv_tok.value)
		return TDS_FAIL;
	tds_get_n(tds, recv_tok.value, len);

	res = tds_gss_continue(tds, (struct tds_gss_auth *) auth, &recv_tok);
	free(recv_tok.value);
	if (TDS_FAILED(res))
		return res;

	if (auth->packet_len) {
		tds->out_flag = TDS7_AUTH;
		tds_put_n(tds, auth->packet, auth->packet_len);
		return tds_flush_packet(tds);
	}
	return TDS_SUCCESS;
}
コード例 #15
0
ファイル: read.c プロジェクト: Distrotech/freetds
/*
 * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary
 * is not aligned with the character boundary.  In that event, it will return an error, and
 * some number of bytes (less than a character) will remain in the tail end of temp[].  They are  
 * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read.
 */
static int
read_and_convert(TDSSOCKET * tds, const TDSICONV * char_conv, size_t * wire_size, char **outbuf,
		 size_t * outbytesleft)
{
	TEMP_INIT(256);
	/*
	 * temp (above) is the "preconversion" buffer, the place where the UCS-2 data 
	 * are parked before converting them to ASCII.  It has to have a size, 
	 * and there's no advantage to allocating dynamically.  
	 * This also avoids any memory allocation error.  
	 */
	const char *bufp;
	size_t bufleft = 0;
	const size_t max_output = *outbytesleft;

	/* cast away const for message suppression sub-structure */
	TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;

	memset(suppress, 0, sizeof(char_conv->suppress));
	
	for (bufp = temp; *wire_size > 0 && *outbytesleft > 0; bufp = temp + bufleft) {
		assert(bufp >= temp);
		/* read a chunk of data */
		bufleft = TEMP_SIZE - bufleft;
		if (bufleft > *wire_size)
			bufleft = *wire_size;
		tds_get_n(tds, (char *) bufp, (int)bufleft);
		*wire_size -= bufleft;
		bufleft += bufp - temp;

		/* Convert chunk and write to dest. */
		bufp = temp; /* always convert from start of buffer */
		suppress->einval = *wire_size > 0; /* EINVAL matters only on the last chunk. */
		if ((size_t)-1 == tds_iconv(tds, char_conv, to_client, &bufp, &bufleft, outbuf, outbytesleft)) {
			tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: tds_iconv returned errno %d\n", errno);
			if (errno != EILSEQ) {
				tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: "
							     "Gave up converting %u bytes due to error %d.\n",
							     (unsigned int) bufleft, errno);
				tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", bufp, bufleft);
			}

			if (bufp == temp) {	/* tds_iconv did not convert anything, avoid infinite loop */
				tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: draining remaining %u bytes.\n",
							     (unsigned int) *wire_size);
				tds_get_n(tds, NULL, (int)(*wire_size)); /* perhaps we should read unconverted data into outbuf? */
				*wire_size = 0;
				break;
			}
				
			if (bufleft) {
				memmove(temp, bufp, bufleft);
			}
		}
	}

	assert(*wire_size == 0 || *outbytesleft == 0);

	TEMP_FREE;
	return (int)(max_output - *outbytesleft);
}
コード例 #16
0
ファイル: query.c プロジェクト: angcoch/freetds
/**
 * Read a query, and return it as an ASCII string with a \0 terminator.  This
 * should work for TDS4, TDS5, and TDS7+ connections.  Also, it converts RPC
 * calls into stored procedure queries, and it skips CANCEL packets.  The query
 * string is returned in a static buffer which is overwritten each time this
 * function is called.
 * \param tds  The socket to read from.
 * \return A query string if successful, or NULL if we either can't read from
 * the socket or we read something that we can't handle.
 */
char *tds_get_generic_query(TDSSOCKET * tds)
{
 	int token, byte;
	int len, more, i, j;

	for (;;) {
		/*
		 * Read a new packet.  We must explicitly read it,
		 * instead of letting functions such as tds_get_byte()
		 * to read it for us, so that we can examine the packet
		 * type via tds->in_flag.
		 */
		if (tds_read_packet(tds) < 0) {
			return NULL;
		}

		/* Queries can arrive in a couple different formats. */
		switch (tds->in_flag) {
		case TDS_RPC:
			/* TODO */
		case TDS_NORMAL: /* TDS5 query packet */
			/* get the token */
			token = tds_get_byte(tds);
			switch (token) {
			case TDS_LANGUAGE_TOKEN:
				/* SQL query */
				len = tds_get_int(tds); /* query size +1 */
				assert(len >= 1);	/* TODO handle */
				tds_get_byte(tds);	/* has args, ignored TODO */
				if (len > query_buflen) {
					query_buflen = len;
					query = (char *) realloc(query, query_buflen);
				}
				--len;
				tds_get_n(tds, query, len);
				query[len] = 0;
				return query;

			case TDS_DBRPC_TOKEN:
				/* RPC call -- make it look like a query */

				/* skip the overall length */
				(void)tds_get_smallint(tds);

				/* get the length of the stored procedure's name */
				len = tds_get_byte(tds) + 1;/* sproc name size +1 */
				if (len > query_buflen) {
					query_buflen = len;
					query = (char *) realloc(query, query_buflen);
				}

				/*
				 * Read the chars of the name.  Skip NUL
				 * bytes, as a cheap way to convert
				 * Unicode to ASCII.  (For TDS7+, the
				 * name is sent in Unicode.)
				 */
				for (i = j  = 0; i < len - 1; i++) {
					byte = tds_get_byte(tds);
					if (byte != '\0')
						query[j++] = byte;
				}
				query[j] = '\0';

				/* TODO: WE DON'T HANDLE PARAMETERS YET! */

				/* eat the rest of the packet */
				while (!tds_lastpacket(tds) && tds_read_packet(tds) > 0) {
				}
				return query;

			default:
				/* unexpected token */

				/* eat the rest of the packet */
				while (!tds_lastpacket(tds) && tds_read_packet(tds) > 0) {
				}
				return NULL;
			}
			break;

		case TDS_QUERY:
			/* TDS4 and TDS7+ fill the whole packet with a query */
			len = 0;
			for (;;) {
				const char *src;

				/* If buffer needs to grow, then grow */
				more = tds->in_len - tds->in_pos;
				src = (char *) (tds->in_buf + tds->in_pos);
				if ((size_t)(len + more + 1) > query_buflen)
				{
					query_buflen = len + more + 1024u;
					query_buflen -= query_buflen % 1024u;
					query = (char *)realloc(query, query_buflen);
				}

				/*
				 * Pull new data into the query buffer.
				 * Ignore NUL bytes -- this is a cheap way
				 * to convert Unicode to Latin-1/ASCII.
				 */
				while (--more >= 0)
				{
					query[len] = *src++;
					if (query[len] != '\0')
						len++;
				}

				/* if more then read it */
				if (tds_lastpacket(tds))
					break;
				if (tds_read_packet(tds) < 0)
					return NULL;
			}

			/* add a NUL to mark the end */
			query[len] = '\0';
			return query;

		case TDS_CANCEL:
			/*
			 * ignore cancel requests -- if we're waiting
			 * for the next query then it's obviously too
			 * late to cancel the previous query.
			 */
			/* TODO it's not too late -- freddy77 */
			break;

		default:
			/* not a query packet */
			return NULL;
		}
	}
}