1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 * Copyright (C) 2014 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 * This test test tds_convert_stream which is supposed to be the main
22 * character conversion routine
23 *
24 * Check that error are reported to error handler if found and tds
25 * is not NULL.
26 *
27 * Check that conversions works with NULL tds.
28 *
29 * Check all types of errors (EILSEQ, EINVAL, E2BIG).
30 *
31 * Check that error are capture in middle and on end of stream.
32 */
33
34 #include "common.h"
35 #include <freetds/iconv.h>
36 #include <freetds/stream.h>
37
38 #if HAVE_UNISTD_H
39 #undef getpid
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42
43 #if HAVE_STDLIB_H
44 #include <stdlib.h>
45 #endif /* HAVE_STDLIB_H */
46
47 #include <assert.h>
48
49 /* test tds_bcp_fread */
50
51 static char buf[4096+80];
52 static char buf_out[4096+80];
53
54 static int last_errno = 0;
55
56 static TDSRET
convert(TDSSOCKET * tds,TDSICONV * conv,TDS_ICONV_DIRECTION direction,const char * from,size_t from_len,char * dest,size_t * dest_len)57 convert(TDSSOCKET *tds, TDSICONV *conv, TDS_ICONV_DIRECTION direction,
58 const char *from, size_t from_len, char *dest, size_t *dest_len)
59 {
60 /* copy to make valgrind test fail on memory problems */
61 char *in = tds_new(char, from_len);
62 char *out = tds_new(char, *dest_len);
63 int res;
64 TDSSTATICINSTREAM r;
65 TDSSTATICOUTSTREAM w;
66
67 assert(in && out);
68
69 memcpy(in, from, from_len);
70 tds_staticin_stream_init(&r, in, from_len);
71 tds_staticout_stream_init(&w, out, *dest_len);
72 last_errno = 0;
73 res = tds_convert_stream(tds, conv, direction, &r.stream, &w.stream);
74 last_errno = errno;
75 memcpy(dest, out, *dest_len - w.stream.buf_len);
76 *dest_len = *dest_len - w.stream.buf_len;
77
78 free(in);
79 free(out);
80 return res;
81 }
82
83 enum Odd {
84 ODD_NONE = 0,
85 ODD_NORMAL,
86 ODD_INVALID,
87 ODD_INCOMPLETE,
88 ODD_NUM_VALUES
89 };
90
91 static const char *odd_names[] = {
92 "None", "Normal", "Invalid", "Incomplete"
93 };
94
95 static int
add_odd(char * buf,int * pos,enum Odd type)96 add_odd(char *buf, int *pos, enum Odd type)
97 {
98 const unsigned char x = 0xa0;
99
100 switch (type) {
101 case ODD_NONE:
102 return 0;
103
104 case ODD_NORMAL:
105 buf[*pos] = 0xC0 + (x >> 6);
106 ++*pos;
107 buf[*pos] = 0x80 + (x & 0x3f);
108 ++*pos;
109 return 0;
110
111 case ODD_INVALID:
112 buf[*pos] = 0xff;
113 ++*pos;
114 return EILSEQ;
115
116 case ODD_INCOMPLETE:
117 buf[*pos] = 0xC2;
118 ++*pos;
119 return EINVAL;
120
121 default:
122 assert(0);
123 }
124 return 0;
125 }
126
127 static void
add_odd2(char * buf,int * pos,enum Odd type)128 add_odd2(char *buf, int *pos, enum Odd type)
129 {
130 const unsigned char x = 0xa0;
131
132 switch (type) {
133 case ODD_NONE:
134 return;
135 case ODD_NORMAL:
136 buf[*pos] = x;
137 ++*pos;
138 break;
139 case ODD_INVALID:
140 break;
141 case ODD_INCOMPLETE:
142 break;
143 default:
144 assert(0);
145 }
146 }
147
148 static int captured_errno = 0;
149
150 static int
err_handler(const TDSCONTEXT * tds_ctx,TDSSOCKET * tds,TDSMESSAGE * msg)151 err_handler(const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, TDSMESSAGE * msg)
152 {
153 int old_err = captured_errno;
154 if (msg->msgno == TDSEICONVAVAIL) {
155 captured_errno = EINVAL;
156 } else if (strstr(msg->message, "could not be converted")) {
157 captured_errno = EILSEQ;
158 } else if (msg->msgno == TDSEICONVIU) {
159 captured_errno = E2BIG;
160 } else {
161 fprintf(stderr, "Unexpected error: %s\n", msg->message);
162 exit(1);
163 }
164 assert(old_err == 0 || old_err == captured_errno);
165 return TDS_INT_CANCEL;
166 }
167
168 static TDSICONV * conv = NULL;
169 static int pos_type = 0;
170
171 static void
test(TDSSOCKET * tds,enum Odd odd_type)172 test(TDSSOCKET *tds, enum Odd odd_type)
173 {
174 int i, l;
175
176 captured_errno = 0;
177
178 /* do not complete incomplete */
179 if (odd_type == ODD_INCOMPLETE && pos_type > 0)
180 return;
181
182 printf("test pos %d type %s\n", pos_type, odd_names[odd_type]);
183
184 for (i = 0; i < 4096+20; ++i) {
185 size_t out_len;
186 TDSRET res;
187 int err;
188
189 if (i == 34)
190 i = 4096-20;
191
192 l = i;
193 memset(buf, 'a', sizeof(buf));
194 switch (pos_type) {
195 case 0: /* end */
196 err = add_odd(buf, &l, odd_type);
197 break;
198 case 1: /* start */
199 l = 0;
200 err = add_odd(buf, &l, odd_type);
201 if (l > i) continue;
202 l = i;
203 break;
204 case 2: /* middle */
205 err = add_odd(buf, &l, odd_type);
206 l = 4096+30;
207 break;
208 default:
209 exit(1);
210 return;
211 }
212
213 /* convert it */
214 out_len = sizeof(buf_out);
215 res = convert(tds, conv, to_server, buf, l, buf_out, &out_len);
216 printf("i %d res %d out_len %u errno %d captured_errno %d\n",
217 i, (int) res, (unsigned int) out_len, last_errno, captured_errno);
218
219 /* test */
220 l = i;
221 memset(buf, 'a', sizeof(buf));
222 switch (pos_type) {
223 case 0: /* end */
224 add_odd2(buf, &l, odd_type);
225 break;
226 case 1: /* start */
227 l = 0;
228 add_odd2(buf, &l, odd_type);
229 l = i;
230 if (odd_type == ODD_NORMAL) l = i-1;
231 if (odd_type == ODD_INVALID) l = 0;
232 break;
233 case 2: /* middle */
234 add_odd2(buf, &l, odd_type);
235 l = 4096+30;
236 if (odd_type == ODD_NORMAL) --l;
237 if (odd_type == ODD_INVALID) l = i;
238 break;
239 }
240
241
242 if (err) {
243 assert(last_errno == err);
244 assert(TDS_FAILED(res));
245 assert(!tds || captured_errno == last_errno);
246 } else {
247 assert(TDS_SUCCEED(res));
248 assert(captured_errno == 0);
249 }
250 if (out_len != l) {
251 fprintf(stderr, "out %u bytes expected %d\n",
252 (unsigned int) out_len, l);
253 exit(1);
254 }
255 assert(memcmp(buf_out, buf, l) == 0);
256 }
257 }
258
259 static void
big_test(TDSSOCKET * tds)260 big_test(TDSSOCKET *tds)
261 {
262 int i, l;
263 const int limit = 1025;
264
265 captured_errno = 0;
266
267 printf("E2BIG test\n");
268
269 for (i = 0; i < 4096+20; ++i) {
270 size_t out_len;
271 TDSRET res;
272 int err;
273
274 if (i == 32)
275 i = 490;
276
277 l = i;
278 memset(buf, 0xa0, sizeof(buf));
279
280 /* convert it */
281 out_len = limit;
282 res = convert(tds, conv, to_client, buf, l, buf_out, &out_len);
283 printf("i %d res %d out_len %u errno %d captured_errno %d\n",
284 i, (int) res, (unsigned int) out_len, last_errno, captured_errno);
285 err = l * 2 > limit ? E2BIG : 0;
286
287 if (err) {
288 assert(last_errno == err);
289 assert(TDS_FAILED(res));
290 assert(!tds || captured_errno == last_errno);
291 } else {
292 assert(TDS_SUCCEED(res));
293 assert(captured_errno == 0);
294 }
295 if (out_len != i*2 && i*2 <= limit) {
296 fprintf(stderr, "out %u bytes expected %d\n",
297 (unsigned int) out_len, i*2);
298 exit(1);
299 }
300 }
301 }
302
303
304 int
main(int argc,char ** argv)305 main(int argc, char **argv)
306 {
307 int i;
308 TDSCONTEXT *ctx = tds_alloc_context(NULL);
309 TDSSOCKET *tds = tds_alloc_socket(ctx, 512);
310 const char *tdsdump;
311
312 setbuf(stdout, NULL);
313 setbuf(stderr, NULL);
314
315 ctx->err_handler = err_handler;
316
317 /* allow dumps, we don't have a connection here */
318 tdsdump = getenv("TDSDUMP");
319 if (tdsdump)
320 tdsdump_open(tdsdump);
321
322 if (!ctx || !tds) {
323 fprintf(stderr, "Error creating socket!\n");
324 return 1;
325 }
326
327 tds_iconv_open(tds->conn, "ISO-8859-1", 0);
328
329 conv = tds_iconv_get(tds->conn, "UTF-8", "ISO-8859-1");
330 if (conv == NULL) {
331 fprintf(stderr, "Error creating conversion, giving up!\n");
332 return 1;
333 }
334
335 for (i = 0; i < ODD_NUM_VALUES*3*2; ++i) {
336 int n = i;
337 enum Odd odd_type = (enum Odd) (n % ODD_NUM_VALUES);
338 n /= ODD_NUM_VALUES;
339 pos_type = n % 3; n /= 3;
340
341 test(n ? tds : NULL, odd_type);
342 }
343
344 /* not try E2BIG error */
345 big_test(NULL);
346 big_test(tds);
347
348 tds_free_socket(tds);
349 tds_free_context(ctx);
350 return 0;
351 }
352