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