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