/** * 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; } }
/** * 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; }
/** * 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; }
/** * 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); }
/** * 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); }
/** * 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); }
/** * 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); }
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); }
/** * 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; }
/** * 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); }
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; }
/** * 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; }
/** * 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; }
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; }
/* * 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); }
/** * 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; } } }