Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Brian Bruns
3 : * Copyright (C) 2005-2014 Frediano Ziglio
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : /**
22 : * \file
23 : * \brief Grab data from TDS packets
24 : */
25 :
26 : #include <config.h>
27 :
28 : #if HAVE_ERRNO_H
29 : #include <errno.h>
30 : #endif /* HAVE_ERRNO_H */
31 :
32 : #if HAVE_STDLIB_H
33 : #include <stdlib.h>
34 : #endif /* HAVE_STDLIB_H */
35 :
36 : #if HAVE_STRING_H
37 : #include <string.h>
38 : #endif /* HAVE_STRING_H */
39 :
40 : #if HAVE_UNISTD_H
41 : #include <unistd.h>
42 : #endif /* HAVE_UNISTD_H */
43 :
44 : #include <assert.h>
45 :
46 : #include <freetds/tds.h>
47 : #include <freetds/iconv.h>
48 : #include <freetds/bytes.h>
49 : #include <freetds/stream.h>
50 : #include <freetds/utils/string.h>
51 : #include <freetds/checks.h>
52 :
53 : static size_t read_and_convert(TDSSOCKET * tds, TDSICONV * char_conv,
54 : size_t * wire_size, char *outbuf, size_t outbytesleft);
55 :
56 : /**
57 : * \ingroup libtds
58 : * \defgroup network Network functions
59 : * Functions for reading or writing from network.
60 : */
61 :
62 : /**
63 : * \addtogroup network
64 : * @{
65 : */
66 :
67 : /**
68 : * Return a single byte from the input buffer
69 : * \tds
70 : */
71 : unsigned char
72 1209242 : tds_get_byte(TDSSOCKET * tds)
73 : {
74 2482004 : while (tds->in_pos >= tds->in_len) {
75 63558 : if (tds_read_packet(tds) < 0)
76 : return 0;
77 : }
78 1209204 : return tds->in_buf[tds->in_pos++];
79 : }
80 :
81 : /**
82 : * Unget will always work as long as you don't call it twice in a row. It
83 : * it may work if you call it multiple times as long as you don't backup
84 : * over the beginning of network packet boundary which can occur anywhere in
85 : * the token stream.
86 : * \tds
87 : */
88 : void
89 49790 : tds_unget_byte(TDSSOCKET * tds)
90 : {
91 : /* this is a one trick pony...don't call it twice */
92 49790 : tds->in_pos--;
93 49790 : }
94 :
95 : /**
96 : * Reads a byte from the TDS stream without removing it
97 : * \tds
98 : */
99 : unsigned char
100 36469 : tds_peek(TDSSOCKET * tds)
101 : {
102 36469 : unsigned char result = tds_get_byte(tds);
103 36469 : if (tds->in_pos > 0)
104 36469 : --tds->in_pos;
105 36469 : return result;
106 : } /* tds_peek() */
107 :
108 :
109 : /**
110 : * Get an int16 from the server.
111 : */
112 : TDS_USMALLINT
113 769300 : tds_get_usmallint(TDSSOCKET * tds)
114 : {
115 : TDS_USMALLINT bytes[1];
116 :
117 769300 : tds_get_n(tds, &bytes, 2);
118 769300 : return (TDS_USMALLINT) TDS_GET_A2LE(&bytes);
119 : }
120 :
121 :
122 : /**
123 : * Get an int32 from the server.
124 : * \tds
125 : */
126 : TDS_UINT
127 199708 : tds_get_uint(TDSSOCKET * tds)
128 : {
129 : TDS_UINT bytes;
130 :
131 199708 : tds_get_n(tds, &bytes, 4);
132 199708 : return TDS_GET_A4LE(&bytes);
133 : }
134 :
135 : /**
136 : * Get an uint64 from the server.
137 : * \tds
138 : */
139 : TDS_UINT8
140 32963 : tds_get_uint8(TDSSOCKET * tds)
141 : {
142 : TDS_UINT h;
143 : TDS_UINT l;
144 : TDS_UINT bytes[2];
145 :
146 32963 : tds_get_n(tds, bytes, 8);
147 32963 : l = TDS_GET_A4LE(bytes);
148 32963 : h = TDS_GET_A4LE(bytes+1);
149 32963 : return (((TDS_UINT8) h) << 32) | l;
150 : }
151 :
152 : /**
153 : * Fetch a string from the wire.
154 : * Output string is NOT null terminated.
155 : * If TDS version is 7 or 8 read unicode string and convert it.
156 : * This function should be use to read server default encoding strings like
157 : * columns name, table names, etc, not for data (use tds_get_char_data instead)
158 : * @return bytes written to \a dest
159 : * @param tds connection information
160 : * @param string_len length of string to read from wire
161 : * (in server characters, bytes for tds4-tds5, ucs2 for tds7+)
162 : * @param dest destination buffer, if NULL string is read and discarded
163 : * @param dest_size destination buffer size, in bytes
164 : */
165 : size_t
166 145593 : tds_get_string(TDSSOCKET * tds, size_t string_len, char *dest, size_t dest_size)
167 : {
168 145593 : size_t wire_bytes = string_len;
169 145593 : unsigned conv = client2server_chardata;
170 :
171 145593 : if (IS_TDS7_PLUS(tds->conn)) {
172 95424 : wire_bytes *= 2u;
173 95424 : conv = client2ucs2;
174 : }
175 :
176 145593 : if (dest == NULL) {
177 22 : tds_get_n(tds, NULL, wire_bytes);
178 22 : return string_len;
179 : }
180 :
181 145571 : return read_and_convert(tds, tds->conn->char_convs[conv], &wire_bytes, dest, dest_size);
182 : }
183 :
184 : /**
185 : * Fetch character data the wire.
186 : * Output is NOT null terminated.
187 : * If \a char_conv is not NULL, convert data accordingly.
188 : * \param tds state information for the socket and the TDS protocol
189 : * \param row_buffer destination buffer in current_row. Can't be NULL
190 : * \param wire_size size to read from wire (in bytes)
191 : * \param curcol column information
192 : * \return TDS_SUCCESS or TDS_FAIL (probably memory error on text data)
193 : */
194 : TDSRET
195 382283 : tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol)
196 : {
197 : size_t in_left;
198 :
199 382283 : assert(curcol->char_conv);
200 :
201 : /*
202 : * row_buffer is a column buffer, allocated when the column's metadata are processed
203 : * and reused for each row.
204 : */
205 :
206 : /* silly case, empty string */
207 382283 : if (wire_size == 0) {
208 66 : curcol->column_cur_size = 0;
209 66 : return TDS_SUCCESS;
210 : }
211 :
212 382217 : in_left = curcol->column_size;
213 382217 : curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, row_buffer, in_left);
214 382217 : if (TDS_UNLIKELY(wire_size > 0)) {
215 0 : tds_get_n(tds, NULL, wire_size);
216 0 : tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n",
217 : (unsigned int) wire_size, curcol->column_cur_size);
218 : return TDS_FAIL;
219 : }
220 : return TDS_SUCCESS;
221 : }
222 :
223 : /**
224 : * Get N bytes from the buffer and return them in the already allocated space
225 : * given to us. We ASSUME that the person calling this function has done the
226 : * bounds checking for us since they know how many bytes they want here.
227 : * dest of NULL means we just want to eat the bytes. (tetherow@nol.org)
228 : */
229 : bool
230 2366450 : tds_get_n(TDSSOCKET * tds, void *dest, size_t need)
231 : {
232 : for (;;) {
233 2371861 : unsigned int have = tds->in_len - tds->in_pos;
234 :
235 2371861 : if (need <= have)
236 : break;
237 : /* We need more than is in the buffer, copy what is there */
238 5411 : if (dest != NULL) {
239 5411 : memcpy((char *) dest, tds->in_buf + tds->in_pos, have);
240 5411 : dest = (char *) dest + have;
241 : }
242 5411 : need -= have;
243 5411 : if (TDS_UNLIKELY(tds_read_packet(tds) < 0))
244 : return false;
245 : }
246 2366450 : if (need > 0) {
247 : /* get the remainder if there is any */
248 2013756 : if (dest != NULL) {
249 1977900 : memcpy((char *) dest, tds->in_buf + tds->in_pos, need);
250 : }
251 2013756 : tds->in_pos += need;
252 : }
253 : return true;
254 : }
255 :
256 : /**
257 : * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary
258 : * is not aligned with the character boundary. In that event, it will return an error, and
259 : * some number of bytes (less than a character) will remain in the tail end of temp[]. They are
260 : * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read.
261 : * \tds
262 : * \param char_conv conversion structure
263 : * \param[out] wire_size size readed from wire
264 : * \param outbuf buffer to write to
265 : * \param outbytesleft buffer length
266 : * \return bytes readed
267 : */
268 : static size_t
269 527788 : read_and_convert(TDSSOCKET * tds, TDSICONV * char_conv, size_t * wire_size, char *outbuf,
270 : size_t outbytesleft)
271 : {
272 : int res;
273 : TDSDATAINSTREAM r;
274 : TDSSTATICOUTSTREAM w;
275 :
276 527788 : tds_datain_stream_init(&r, tds, *wire_size);
277 527788 : tds_staticout_stream_init(&w, outbuf, outbytesleft);
278 :
279 527788 : res = tds_convert_stream(tds, char_conv, to_client, &r.stream, &w.stream);
280 527788 : *wire_size = r.wire_size;
281 527788 : return (char *) w.stream.buffer - outbuf;
282 : }
283 :
284 : /**
285 : * Reads a string from wire and put in a DSTR.
286 : * On error we read the bytes from the wire anyway.
287 : * \tds
288 : * \param[out] s output string
289 : * \param[in] len string length (in characters)
290 : * \return string or NULL on error
291 : */
292 : DSTR*
293 83552 : tds_dstr_get(TDSSOCKET * tds, DSTR * s, size_t len)
294 : {
295 : size_t out_len;
296 :
297 83552 : CHECK_TDS_EXTRA(tds);
298 :
299 : /* assure sufficient space for every conversion */
300 83552 : if (TDS_UNLIKELY(!tds_dstr_alloc(s, len * 4))) {
301 0 : tds_get_n(tds, NULL, len);
302 0 : return NULL;
303 : }
304 :
305 167104 : out_len = tds_get_string(tds, len, tds_dstr_buf(s), len * 4);
306 83552 : tds_dstr_setlen(s, out_len);
307 83552 : return s;
308 : }
309 :
310 : /** @} */
|