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 <common/test_assert.h>
48
49 /* test tds_iconv_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 = malloc(from_len ? from_len : 1);
62 char *out = malloc(*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 };
89
90 static const char *odd_names[] = {
91 "None", "Normal", "Invalid", "Incomplete"
92 };
93
94 static int
add_odd(char * buf,int * pos,enum Odd type)95 add_odd(char *buf, int *pos, enum Odd type)
96 {
97 const unsigned char x = 0xa0;
98
99 switch (type) {
100 case ODD_NONE:
101 return 0;
102
103 case ODD_NORMAL:
104 buf[*pos] = (char) (0xC0 + (x >> 6));
105 ++*pos;
106 buf[*pos] = (char) (0x80 + (x & 0x3f));
107 ++*pos;
108 return 0;
109
110 case ODD_INVALID:
111 buf[*pos] = 0xff;
112 ++*pos;
113 return EILSEQ;
114
115 case ODD_INCOMPLETE:
116 buf[*pos] = 0xC2;
117 ++*pos;
118 return EINVAL;
119
120 default:
121 assert(0);
122 }
123 return 0;
124 }
125
126 static void
add_odd2(char * buf,int * pos,enum Odd type)127 add_odd2(char *buf, int *pos, enum Odd type)
128 {
129 const unsigned char x = 0xa0;
130
131 switch (type) {
132 case ODD_NONE:
133 return;
134 case ODD_NORMAL:
135 buf[*pos] = x;
136 ++*pos;
137 break;
138 case ODD_INVALID:
139 break;
140 case ODD_INCOMPLETE:
141 break;
142 default:
143 assert(0);
144 }
145 }
146
147 static int captured_errno = 0;
148
149 static int
err_handler(const TDSCONTEXT * tds_ctx,TDSSOCKET * tds,TDSMESSAGE * msg)150 err_handler(const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, TDSMESSAGE * msg)
151 {
152 int old_err = captured_errno;
153 if (msg->msgno == TDSEICONVAVAIL) {
154 captured_errno = EINVAL;
155 } else if (strstr(msg->message, "could not be converted")) {
156 captured_errno = EILSEQ;
157 } else if (msg->msgno == TDSEICONVIU) {
158 captured_errno = E2BIG;
159 } else {
160 fprintf(stderr, "Unexpected error: %s\n", msg->message);
161 exit(1);
162 }
163 assert(old_err == 0 || old_err == captured_errno);
164 return TDS_INT_CANCEL;
165 }
166
167 static TDSICONV * conv = NULL;
168 static int pos_type = 0;
169
170 static void
test(TDSSOCKET * tds,enum Odd odd_type)171 test(TDSSOCKET *tds, enum Odd odd_type)
172 {
173 int i, l;
174
175 captured_errno = 0;
176
177 /* do not complete incomplete */
178 if (odd_type == ODD_INCOMPLETE && pos_type > 0)
179 return;
180
181 printf("test pos %d type %s\n", pos_type, odd_names[odd_type]);
182
183 for (i = 0; i < 4096+20; ++i) {
184 size_t out_len;
185 TDSRET res;
186 int err;
187
188 if (i == 34)
189 i = 4096-20;
190
191 l = i;
192 memset(buf, 'a', sizeof(buf));
193 switch (pos_type) {
194 case 0: /* end */
195 err = add_odd(buf, &l, odd_type);
196 break;
197 case 1: /* start */
198 l = 0;
199 err = add_odd(buf, &l, odd_type);
200 if (l > i) continue;
201 l = i;
202 break;
203 case 2: /* middle */
204 err = add_odd(buf, &l, odd_type);
205 l = 4096+30;
206 break;
207 default:
208 exit(1);
209 return;
210 }
211
212 /* convert it */
213 out_len = sizeof(buf_out);
214 res = convert(tds, conv, to_server, buf, l, buf_out, &out_len);
215 printf("i %d res %d out_len %u errno %d captured_errno %d\n",
216 i, (int) res, (unsigned int) out_len, last_errno, captured_errno);
217
218 /* test */
219 l = i;
220 memset(buf, 'a', sizeof(buf));
221 switch (pos_type) {
222 case 0: /* end */
223 add_odd2(buf, &l, odd_type);
224 break;
225 case 1: /* start */
226 l = 0;
227 add_odd2(buf, &l, odd_type);
228 l = i;
229 if (odd_type == ODD_NORMAL) l = i-1;
230 if (odd_type == ODD_INVALID) l = 0;
231 break;
232 case 2: /* middle */
233 add_odd2(buf, &l, odd_type);
234 l = 4096+30;
235 if (odd_type == ODD_NORMAL) --l;
236 if (odd_type == ODD_INVALID) l = i;
237 break;
238 }
239
240
241 if (err) {
242 assert(last_errno == err);
243 assert(TDS_FAILED(res));
244 assert(!tds || captured_errno == last_errno);
245 } else {
246 assert(TDS_SUCCEED(res));
247 assert(captured_errno == 0);
248 }
249 if (out_len != l) {
250 fprintf(stderr, "out %u bytes expected %d\n",
251 (unsigned int) out_len, l);
252 exit(1);
253 }
254 assert(memcmp(buf_out, buf, l) == 0);
255 }
256 }
257
258 static void
big_test(TDSSOCKET * tds)259 big_test(TDSSOCKET *tds)
260 {
261 int i, l;
262 const int limit = 1025;
263
264 captured_errno = 0;
265
266 printf("E2BIG test\n");
267
268 for (i = 0; i < 4096+20; ++i) {
269 size_t out_len;
270 TDSRET res;
271 int err;
272
273 if (i == 32)
274 i = 490;
275
276 l = i;
277 memset(buf, 0xa0, sizeof(buf));
278
279 /* convert it */
280 out_len = limit;
281 res = convert(tds, conv, to_client, buf, l, buf_out, &out_len);
282 printf("i %d res %d out_len %u errno %d captured_errno %d\n",
283 i, (int) res, (unsigned int) out_len, last_errno, captured_errno);
284 err = l * 2 > limit ? E2BIG : 0;
285
286 if (err) {
287 assert(last_errno == err);
288 assert(TDS_FAILED(res));
289 assert(!tds || captured_errno == last_errno);
290 } else {
291 assert(TDS_SUCCEED(res));
292 assert(captured_errno == 0);
293 }
294 if (out_len != i*2 && i*2 <= limit) {
295 fprintf(stderr, "out %u bytes expected %d\n",
296 (unsigned int) out_len, i*2);
297 exit(1);
298 }
299 }
300 }
301
302
303 int
main(int argc,char ** argv)304 main(int argc, char **argv)
305 {
306 int i;
307 TDSCONTEXT *ctx = tds_alloc_context(NULL);
308 TDSSOCKET *tds = tds_alloc_socket(ctx, 512);
309 const char *tdsdump;
310
311 setbuf(stdout, NULL);
312 setbuf(stderr, NULL);
313
314 ctx->err_handler = err_handler;
315
316 /* allow dumps, we don't have a connection here */
317 tdsdump = getenv("TDSDUMP");
318 if (tdsdump)
319 tdsdump_open(tdsdump);
320
321 if (!ctx || !tds) {
322 fprintf(stderr, "Error creating socket!\n");
323 return 1;
324 }
325
326 tds_iconv_open(tds->conn, "ISO-8859-1", 0);
327
328 conv = tds_iconv_get(tds->conn, "UTF-8", "ISO-8859-1");
329 if (conv == NULL) {
330 fprintf(stderr, "Error creating conversion, giving up!\n");
331 return 1;
332 }
333
334 for (i = 0; i < 4*3*2; ++i) {
335 int n = i;
336 int odd_type = n % 4; n /= 4;
337 pos_type = n % 3; n /= 3;
338
339 test(n ? tds : NULL, odd_type);
340 }
341
342 /* not try E2BIG error */
343 big_test(NULL);
344 big_test(tds);
345
346 tds_free_socket(tds);
347 tds_free_context(ctx);
348 return 0;
349 }
350