1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 * Copyright (C) 2013 Frediano Ziglio
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 /**
21 * \file
22 * \brief Handle stream of data
23 */
24
25 #include <config.h>
26
27 #if HAVE_ERRNO_H
28 #include <errno.h>
29 #endif /* HAVE_ERRNO_H */
30
31 #if HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif /* HAVE_STDLIB_H */
34
35 #if HAVE_STRING_H
36 #include <string.h>
37 #endif /* HAVE_STRING_H */
38
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42
43 #include <assert.h>
44
45 #include <freetds/tds.h>
46 #include <freetds/iconv.h>
47 #include <freetds/stream.h>
48
49 /** \cond HIDDEN_SYMBOLS */
50 #if ENABLE_EXTRA_CHECKS
51 # define TEMP_INIT(s) const size_t temp_size = s; char* temp = tds_new(char, temp_size)
52 # define TEMP_FREE free(temp);
53 # define TEMP_SIZE temp_size
54 #else
55 # define TEMP_INIT(s) char temp[s]
56 # define TEMP_FREE ;
57 # define TEMP_SIZE sizeof(temp)
58 #endif
59 /** \endcond */
60
61 /**
62 * Reads and writes from a stream converting characters
63 * \tds
64 * \param char_conv conversion structure
65 * \param direction specify conversion to server or from server
66 * \param istream input stream
67 * \param ostream output stream
68 * \return TDS_SUCCESS of TDS_FAIL
69 */
70 TDSRET
tds_convert_stream(TDSSOCKET * tds,TDSICONV * char_conv,TDS_ICONV_DIRECTION direction,TDSINSTREAM * istream,TDSOUTSTREAM * ostream)71 tds_convert_stream(TDSSOCKET * tds, TDSICONV * char_conv, TDS_ICONV_DIRECTION direction,
72 TDSINSTREAM * istream, TDSOUTSTREAM *ostream)
73 {
74 TEMP_INIT(4096);
75 /*
76 * temp (above) is the "preconversion" buffer, the place where the UCS-2 data
77 * are parked before converting them to ASCII. It has to have a size,
78 * and there's no advantage to allocating dynamically.
79 * This also avoids any memory allocation error.
80 */
81 const char *ib;
82 size_t bufleft = 0;
83 TDSRET res = TDS_FAIL;
84
85 /* cast away const for message suppression sub-structure */
86 TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;
87
88 memset(suppress, 0, sizeof(char_conv->suppress));
89 for (ib = temp; ostream->buf_len; ib = temp + bufleft) {
90
91 char *ob;
92 int len, conv_errno;
93 size_t ol;
94
95 assert(ib >= temp);
96
97 /* read a chunk of data */
98 len = istream->read(istream, (char*) ib, TEMP_SIZE - bufleft);
99 if (len < 0)
100 break;
101 if (len == 0 && bufleft == 0) {
102 res = TDS_SUCCESS;
103 break;
104 }
105 bufleft += len;
106
107 /* Convert chunk */
108 ib = temp; /* always convert from start of buffer */
109
110 convert_more:
111 ob = ostream->buffer;
112 ol = ostream->buf_len;
113 /* FIXME not for last */
114 suppress->einval = 1; /* EINVAL matters only on the last chunk. */
115 suppress->e2big = 1;
116 ol = tds_iconv(tds, char_conv, direction, (const char **) &ib, &bufleft, &ob, &ol);
117 conv_errno = errno;
118
119 /* write converted chunk */
120 len = ostream->write(ostream, ob - ostream->buffer);
121 if (TDS_UNLIKELY(len < 0))
122 break;
123
124 if ((size_t) -1 == ol) {
125 tdsdump_log(TDS_DBG_NETWORK, "Error: tds_convert_stream: tds_iconv returned errno %d, conv_errno %d\n", errno, conv_errno);
126 if (conv_errno == E2BIG && ostream->buf_len && bufleft && len)
127 goto convert_more;
128 if (conv_errno != EILSEQ) {
129 tdsdump_log(TDS_DBG_NETWORK, "Error: tds_convert_stream: "
130 "Gave up converting %u bytes due to error %d.\n",
131 (unsigned int) bufleft, errno);
132 tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", ib, bufleft);
133 }
134
135 if (TDS_UNLIKELY(ib == temp)) { /* tds_iconv did not convert anything, avoid infinite loop */
136 tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: some bytes left.\n");
137 res = TDS_FAIL;
138 if (conv_errno == EINVAL && tds)
139 tdserror(tds_get_ctx(tds), tds, TDSEICONVAVAIL, 0);
140 if (conv_errno == E2BIG && tds)
141 tdserror(tds_get_ctx(tds), tds, TDSEICONVIU, 0);
142 errno = conv_errno;
143 break;
144 }
145
146 if (bufleft)
147 memmove(temp, ib, bufleft);
148 }
149 }
150
151 TEMP_FREE;
152 return res;
153 }
154
155 /**
156 * Reads and writes from a stream to another
157 * \tds
158 * \param istream input stream
159 * \param ostream output stream
160 * \return TDS_SUCCESS or TDS_FAIL
161 */
162 TDSRET
tds_copy_stream(TDSINSTREAM * istream,TDSOUTSTREAM * ostream)163 tds_copy_stream(TDSINSTREAM * istream, TDSOUTSTREAM * ostream)
164 {
165 while (ostream->buf_len) {
166 /* read a chunk of data */
167 int len = istream->read(istream, ostream->buffer, ostream->buf_len);
168 if (len == 0)
169 return TDS_SUCCESS;
170 if (TDS_UNLIKELY(len < 0))
171 break;
172
173 /* write chunk */
174 len = ostream->write(ostream, len);
175 if (TDS_UNLIKELY(len < 0))
176 break;
177 }
178 return TDS_FAIL;
179 }
180
181 /**
182 * Reads data from network for input stream
183 */
184 static int
tds_datain_stream_read(TDSINSTREAM * stream,void * ptr,size_t len)185 tds_datain_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
186 {
187 TDSDATAINSTREAM *s = (TDSDATAINSTREAM *) stream;
188 if (len > s->wire_size)
189 len = s->wire_size;
190 if (!tds_get_n(s->tds, ptr, len))
191 return -1;
192 s->wire_size -= len;
193 return len;
194 }
195
196 /**
197 * Initialize a data input stream.
198 * This stream read data from network.
199 * \param stream input stream to initialize
200 * \tds
201 * \param wire_size byte to read
202 */
203 void
tds_datain_stream_init(TDSDATAINSTREAM * stream,TDSSOCKET * tds,size_t wire_size)204 tds_datain_stream_init(TDSDATAINSTREAM * stream, TDSSOCKET * tds, size_t wire_size)
205 {
206 stream->stream.read = tds_datain_stream_read;
207 stream->wire_size = wire_size;
208 stream->tds = tds;
209 }
210
211 /**
212 * Writes data to network for output stream
213 */
214 static int
tds_dataout_stream_write(TDSOUTSTREAM * stream,size_t len)215 tds_dataout_stream_write(TDSOUTSTREAM *stream, size_t len)
216 {
217 TDSDATAOUTSTREAM *s = (TDSDATAOUTSTREAM *) stream;
218 TDSSOCKET *tds = s->tds;
219
220 assert(len <= stream->buf_len);
221 assert(stream->buffer == (char *) tds->out_buf + tds->out_pos);
222 assert(stream->buf_len == tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE);
223
224 tds->out_pos += len;
225 /* this must be strictly test as equal means we send a full packet
226 * and we could be just at the end of packet so server would
227 * wait for another packet with flag != 0
228 */
229 if (tds->out_pos > tds->out_buf_max)
230 tds_write_packet(tds, 0x0);
231 stream->buffer = (char *) tds->out_buf + tds->out_pos;
232 stream->buf_len = tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE;
233 s->written += len;
234 return len;
235 }
236
237 /**
238 * Initialize a data output stream.
239 * This stream writes data to network.
240 * \param stream output stream to initialize
241 * \tds
242 */
243 void
tds_dataout_stream_init(TDSDATAOUTSTREAM * stream,TDSSOCKET * tds)244 tds_dataout_stream_init(TDSDATAOUTSTREAM * stream, TDSSOCKET * tds)
245 {
246 #if TDS_ADDITIONAL_SPACE < 4
247 #error Not supported
248 #endif
249 /*
250 * we use the extra space as we want possible space for converting
251 * a character and cause we don't want to send an exactly entire
252 * packet with 0 flag and then nothing
253 */
254 size_t left = tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE;
255
256 assert(left > 0);
257 stream->stream.write = tds_dataout_stream_write;
258 stream->stream.buffer = (char *) tds->out_buf + tds->out_pos;
259 stream->stream.buf_len = left;
260 stream->written = 0;
261 stream->tds = tds;
262 }
263
264 /**
265 * Reads data from a static allocated buffer
266 */
267 static int
tds_staticin_stream_read(TDSINSTREAM * stream,void * ptr,size_t len)268 tds_staticin_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
269 {
270 TDSSTATICINSTREAM *s = (TDSSTATICINSTREAM *) stream;
271 size_t cp = (len <= s->buf_left) ? len : s->buf_left;
272
273 memcpy(ptr, s->buffer, cp);
274 s->buffer += cp;
275 s->buf_left -= cp;
276 return cp;
277 }
278
279 /**
280 * Initialize an input stream for read from a static allocated buffer
281 * \param stream stream to initialize
282 * \param ptr buffer to read from
283 * \param len buffer size in bytes
284 */
285 void
tds_staticin_stream_init(TDSSTATICINSTREAM * stream,const void * ptr,size_t len)286 tds_staticin_stream_init(TDSSTATICINSTREAM * stream, const void *ptr, size_t len)
287 {
288 stream->stream.read = tds_staticin_stream_read;
289 stream->buffer = (const char *) ptr;
290 stream->buf_left = len;
291 }
292
293
294 /**
295 * Writes data to a static allocated buffer
296 */
297 static int
tds_staticout_stream_write(TDSOUTSTREAM * stream,size_t len)298 tds_staticout_stream_write(TDSOUTSTREAM *stream, size_t len)
299 {
300 assert(stream->buf_len >= len);
301 stream->buffer += len;
302 stream->buf_len -= len;
303 return len;
304 }
305
306 /**
307 * Initialize an output stream for write into a static allocated buffer
308 * \param stream stream to initialize
309 * \param ptr buffer to write to
310 * \param len buffer size in bytes
311 */
312 void
tds_staticout_stream_init(TDSSTATICOUTSTREAM * stream,void * ptr,size_t len)313 tds_staticout_stream_init(TDSSTATICOUTSTREAM * stream, void *ptr, size_t len)
314 {
315 stream->stream.write = tds_staticout_stream_write;
316 stream->stream.buffer = (char *) ptr;
317 stream->stream.buf_len = len;
318 }
319
320 /**
321 * Writes data to a dynamic allocated buffer
322 */
323 static int
tds_dynamic_stream_write(TDSOUTSTREAM * stream,size_t len)324 tds_dynamic_stream_write(TDSOUTSTREAM *stream, size_t len)
325 {
326 TDSDYNAMICSTREAM *s = (TDSDYNAMICSTREAM *) stream;
327 size_t wanted;
328
329 s->size += len;
330 /* grow linearly till some limit then exponentially */
331 if (s->size + 256 > s->allocated) {
332 wanted = s->size + (s->size < 4096 ? 1024 : s->size / 8u);
333 if (TDS_UNLIKELY(!tds_realloc(s->buf, wanted)))
334 return -1;
335 s->allocated = wanted;
336 }
337 assert(s->allocated > s->size);
338 stream->buffer = (char *) *s->buf + s->size;
339 stream->buf_len = s->allocated - s->size;
340 return len;
341 }
342
343 /**
344 * Initialize a dynamic output stream.
345 * This stream write data into a dynamic allocated buffer.
346 * \param stream stream to initialize
347 * \param ptr pointer to pointer to buffer to fill. Buffer
348 * will be extended as needed
349 * \param allocated bytes initialially allocated for the buffer.
350 * Useful to reuse buffers
351 * \return TDS_SUCCESS on success, TDS_FAIL otherwise
352 */
353 TDSRET
tds_dynamic_stream_init(TDSDYNAMICSTREAM * stream,void ** ptr,size_t allocated)354 tds_dynamic_stream_init(TDSDYNAMICSTREAM * stream, void **ptr, size_t allocated)
355 {
356 const size_t initial_size = 1024;
357
358 stream->stream.write = tds_dynamic_stream_write;
359 stream->buf = ptr;
360 if (allocated < initial_size) {
361 free(*ptr);
362 *ptr = NULL;
363 allocated = initial_size;
364 }
365 if (!*ptr) {
366 *ptr = malloc(allocated);
367 if (TDS_UNLIKELY(!*ptr))
368 return TDS_FAIL;
369 }
370 stream->allocated = allocated;
371 stream->size = 0;
372 stream->stream.buffer = (char *) *ptr;
373 stream->stream.buf_len = allocated;
374 return TDS_SUCCESS;
375 }
376
377
378