Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2020 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 : * Purpose: test freeze functionality
22 : */
23 : #include "common.h"
24 : #include <assert.h>
25 : #include <freetds/bytes.h>
26 :
27 : #if HAVE_UNISTD_H
28 : #undef getpid
29 : #include <unistd.h>
30 : #endif /* HAVE_UNISTD_H */
31 :
32 : #include <freetds/replacements.h>
33 : #include <freetds/utils.h>
34 :
35 : #ifdef TDS_HAVE_MUTEX
36 : #ifdef _WIN32
37 : #define SHUT_WR SD_SEND
38 : #endif
39 :
40 : typedef struct {
41 : uint8_t *buf;
42 : size_t len;
43 : size_t capacity;
44 : } buffer;
45 :
46 : #define BUF_INITIALIZER { NULL, 0, 0 }
47 :
48 : static void
49 818 : buf_append(buffer *buf, const void *data, size_t data_len)
50 : {
51 818 : size_t min_size = buf->len + data_len;
52 818 : if (min_size > buf->capacity) {
53 662 : buf->capacity *= 2;
54 662 : if (buf->capacity < min_size)
55 386 : buf->capacity = min_size;
56 662 : assert(TDS_RESIZE(buf->buf, buf->capacity) != NULL);
57 : }
58 818 : memcpy(buf->buf + buf->len, data, data_len);
59 818 : buf->len += data_len;
60 818 : }
61 :
62 : static void
63 : buf_free(buffer *buf)
64 : {
65 270 : free(buf->buf);
66 270 : buf->buf = NULL;
67 270 : buf->len = buf->capacity = 0;
68 : }
69 :
70 : static TDSSOCKET *tds = NULL;
71 : static buffer thread_buf = BUF_INITIALIZER;
72 : static TDS_SYS_SOCKET server_socket = INVALID_SOCKET;
73 : static bool was_shutdown = false;
74 :
75 : static void shutdown_server_socket(void);
76 :
77 : #define BLOCK_SIZE (tds->conn->env.block_size)
78 :
79 : /* thread to read data from main thread */
80 135 : static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
81 : {
82 135 : TDS_SYS_SOCKET s = TDS_PTR2INT(arg);
83 : #if ENABLE_ODBC_MARS
84 90 : unsigned seq = 0;
85 90 : unsigned total_len = 0;
86 : TDS72_SMP_HEADER mars;
87 : #endif
88 :
89 218 : for (;;) {
90 : char buf[4096];
91 353 : int len = READSOCKET(s, buf, sizeof(buf));
92 353 : if (len <= 0)
93 : break;
94 218 : buf_append(&thread_buf, buf, len);
95 218 : tdsdump_dump_buf(TDS_DBG_INFO1, "received", buf, len);
96 : #if ENABLE_ODBC_MARS
97 134 : total_len += len;
98 363 : while (tds->conn->mars && total_len >= BLOCK_SIZE + sizeof(mars)) {
99 95 : mars.signature = TDS72_SMP;
100 95 : mars.type = TDS_SMP_ACK;
101 95 : mars.sid = 0;
102 95 : TDS_PUT_A4LE(&mars.size, 16);
103 95 : TDS_PUT_A4LE(&mars.seq, 4);
104 95 : TDS_PUT_A4LE(&mars.wnd, 4 + seq);
105 95 : WRITESOCKET(s, &mars, sizeof(mars));
106 95 : total_len -= BLOCK_SIZE + sizeof(mars);
107 95 : seq++;
108 : }
109 : #endif
110 : }
111 :
112 : /* close socket to cleanup and signal main thread */
113 135 : CLOSESOCKET(s);
114 135 : return TDS_THREAD_RESULT(0);
115 : }
116 :
117 : /* remove all headers (TDS and MARS) and do some checks on them */
118 : static void
119 135 : strip_headers(buffer *buf)
120 : {
121 135 : uint8_t final = 0;
122 135 : uint8_t *p = buf->buf;
123 135 : uint8_t *dst = p;
124 135 : uint8_t *end = buf->buf + buf->len;
125 525 : while (p < end) {
126 : size_t len;
127 :
128 390 : assert(final == 0);
129 390 : if (p[0] == TDS72_SMP) {
130 130 : assert(end - p >= 8); /* to read SMP part */
131 130 : len = TDS_GET_UA4LE(p+4);
132 130 : assert(len >= 16);
133 130 : assert(p + len <= end);
134 130 : p += 16;
135 130 : len -= 16;
136 130 : assert(end - p >= 4); /* to read TDS header part */
137 130 : assert(len == TDS_GET_UA2BE(p+2));
138 : } else {
139 260 : assert(end - p >= 4); /* to read TDS header part */
140 260 : len = TDS_GET_UA2BE(p+2);
141 : }
142 390 : assert(len > 8);
143 390 : assert(p + len <= end);
144 390 : final = p[1];
145 390 : memmove(dst, p + 8, len - 8);
146 390 : dst += len - 8;
147 390 : p += len;
148 : }
149 135 : assert(final == 1 || was_shutdown);
150 135 : buf->len = dst - buf->buf;
151 135 : }
152 :
153 : static buffer buf = BUF_INITIALIZER;
154 :
155 : /* Append some data to buffer and TDS.
156 : * If data is NULL append some random data */
157 : static void
158 375 : append(const void *data, size_t size)
159 : {
160 : uint8_t rand_buf[2048];
161 375 : if (!data) {
162 : size_t n;
163 285 : assert(size <= sizeof(rand_buf));
164 196635 : for (n = 0; n < size; ++n)
165 196635 : rand_buf[n] = rand();
166 : data = rand_buf;
167 : }
168 375 : tds_put_n(tds, data, size);
169 375 : buf_append(&buf, data, size);
170 375 : }
171 :
172 : /* Append a number for buffer only */
173 : static void
174 225 : append_num(uint64_t num, unsigned size)
175 : {
176 : uint8_t num_buf[8];
177 : unsigned n;
178 :
179 225 : assert(size == 1 || size == 2 || size == 4 || size == 8);
180 675 : for (n = 0; n < size; ++n) {
181 675 : num_buf[n] = num & 0xff;
182 675 : num >>= 8;
183 : }
184 225 : assert(num == 0);
185 225 : buf_append(&buf, num_buf, size);
186 225 : }
187 :
188 : /* base tests
189 : * - lengths;
190 : * - nested freeze;
191 : * - some packet wrapping case;
192 : */
193 : static void
194 15 : test1(void)
195 : {
196 : TDSFREEZE outer, inner;
197 : unsigned written, left;
198 :
199 : /* just to not start at 0 */
200 15 : append("test", 4);
201 :
202 : /* test writing data as UTF16 */
203 15 : tds_freeze(tds, &outer, 2);
204 15 : append_num(3, 2);
205 15 : append("a\0b\0c", 6);
206 15 : assert(tds_freeze_written(&outer) == 8);
207 15 : written = tds_freeze_written(&outer) / 2 - 1;
208 15 : tds_freeze_close_len(&outer, written);
209 :
210 : /* nested freeze */
211 15 : tds_freeze(tds, &outer, 0);
212 15 : assert(tds_freeze_written(&outer) == 0);
213 15 : append("test", 4);
214 15 : tds_freeze(tds, &inner, 0);
215 15 : assert(tds_freeze_written(&outer) == 4);
216 15 : assert(tds_freeze_written(&inner) == 0);
217 15 : tds_put_smallint(tds, 1234);
218 15 : append_num(1234, 2);
219 15 : append("test", 4);
220 15 : assert(tds_freeze_written(&outer) == 10);
221 15 : assert(tds_freeze_written(&inner) == 6);
222 15 : append(NULL, 600 - 5);
223 15 : append("hello", 5);
224 15 : tdsdump_log(TDS_DBG_INFO2, "%u\n", (unsigned) tds_freeze_written(&outer));
225 15 : assert(tds_freeze_written(&outer) == 610);
226 15 : assert(tds_freeze_written(&inner) == 606);
227 :
228 : /* check values do not change before and after close */
229 15 : tds_freeze_close(&inner);
230 15 : assert(tds_freeze_written(&outer) == 610);
231 :
232 : /* test wrapping packets */
233 15 : append(NULL, 600 - 5);
234 15 : append("hello", 5);
235 15 : tdsdump_log(TDS_DBG_INFO2, "%u\n", (unsigned) tds_freeze_written(&outer));
236 15 : assert(tds_freeze_written(&outer) == 610 + 600);
237 :
238 : /* append data in the additional part to check it */
239 15 : left = tds->out_buf_max - tds->out_pos;
240 15 : append(NULL, left - 3);
241 15 : tds_put_int(tds, 0x12345678);
242 15 : assert(tds->out_pos > tds->out_buf_max);
243 15 : append_num(0x12345678, 4);
244 :
245 15 : tds_freeze_close(&outer);
246 15 : }
247 :
248 : static void
249 15 : test_len1(void)
250 : {
251 : TDSFREEZE outer;
252 :
253 : /* simple len, small */
254 15 : tds_freeze(tds, &outer, 4);
255 15 : append_num(10, 4);
256 15 : append(NULL, 10);
257 15 : tds_freeze_close(&outer);
258 :
259 : /* simple len, large */
260 15 : tds_freeze(tds, &outer, 2);
261 15 : append_num(1234, 2);
262 15 : append(NULL, 1234);
263 15 : tds_freeze_close(&outer);
264 :
265 : /* simple len, large */
266 15 : tds_freeze(tds, &outer, 4);
267 15 : append_num(1045, 4);
268 15 : append(NULL, 1045);
269 15 : tds_freeze_close(&outer);
270 15 : }
271 :
272 : /* similar to test_len1 with other APIs */
273 : static void
274 15 : test_len2(void)
275 : {
276 15 : TDS_START_LEN_UINT(tds) {
277 15 : append_num(4 + 10 + 2 + 1234 + 4 + 1045, 4);
278 :
279 : /* simple len, small */
280 15 : TDS_START_LEN_UINT(tds) {
281 15 : append_num(10, 4);
282 15 : append(NULL, 10);
283 15 : } TDS_END_LEN
284 :
285 : /* simple len, large */
286 15 : TDS_START_LEN_USMALLINT(tds) {
287 15 : append_num(1234, 2);
288 15 : append(NULL, 1234);
289 15 : } TDS_END_LEN
290 :
291 : /* simple len, large */
292 15 : TDS_START_LEN_UINT(tds) {
293 15 : append_num(1045, 4);
294 15 : append(NULL, 1045);
295 15 : } TDS_END_LEN
296 15 : } TDS_END_LEN
297 15 : }
298 :
299 : /* check if sending packet is failing */
300 : static void
301 30 : test_failure(size_t len)
302 : {
303 : TDSFREEZE outer;
304 : size_t old_len;
305 :
306 30 : append(NULL, 123);
307 30 : tds_freeze(tds, &outer, 2);
308 30 : old_len = buf.len;
309 30 : if (len)
310 15 : append(NULL, len);
311 30 : tds_freeze_abort(&outer);
312 30 : memset(buf.buf + old_len, 0xab, buf.capacity - old_len);
313 30 : buf.len = old_len;
314 30 : }
315 :
316 : static void
317 15 : test_failure1(void)
318 : {
319 15 : test_failure(0);
320 15 : }
321 :
322 : static void
323 15 : test_failure2(void)
324 : {
325 15 : test_failure(BLOCK_SIZE * 3 + 56);
326 15 : }
327 :
328 : /* test if server close connection */
329 : static void
330 15 : test_shutdown(void)
331 : {
332 : TDSFREEZE outer;
333 :
334 15 : append(NULL, BLOCK_SIZE + 17);
335 15 : tds_freeze(tds, &outer, 4);
336 15 : append(NULL, BLOCK_SIZE * 2 + 67);
337 15 : shutdown_server_socket();
338 15 : was_shutdown = true;
339 15 : tds_freeze_close(&outer);
340 15 : buf.len = BLOCK_SIZE - 8;
341 15 : }
342 :
343 : /* test freeze cross packet boundary */
344 : static void
345 30 : test_cross(unsigned num_bytes)
346 : {
347 : TDSFREEZE outer;
348 : unsigned left;
349 :
350 30 : left = tds->out_buf_max - tds->out_pos;
351 30 : append(NULL, left - num_bytes);
352 30 : append_num(1234, 4);
353 30 : tds_put_int(tds, 1234);
354 30 : tds_freeze(tds, &outer, 2);
355 30 : append_num(1045, 2);
356 30 : append(NULL, 1045);
357 30 : tds_freeze_close(&outer);
358 30 : }
359 :
360 : /* this will make the first packet a bit bigger than final */
361 : static void
362 15 : test_cross1(void)
363 : {
364 15 : test_cross(1);
365 15 : }
366 :
367 : /* this will make the first packet exact as big as final */
368 : static void
369 15 : test_cross2(void)
370 : {
371 15 : test_cross(4);
372 15 : }
373 :
374 : /* test freeze just when another packet is finished,
375 : * we should not generate an empty final one */
376 : static void
377 15 : test_end(void)
378 : {
379 : TDSFREEZE outer, inner;
380 : unsigned left;
381 :
382 15 : left = tds->out_buf_max - tds->out_pos;
383 15 : append(NULL, left - 1);
384 15 : tds_freeze(tds, &outer, 0);
385 15 : append_num(123, 1);
386 15 : tds_put_byte(tds, 123);
387 15 : tds_freeze(tds, &inner, 0);
388 15 : tds_freeze_close(&inner);
389 15 : tds_freeze_close(&outer);
390 15 : }
391 :
392 : /* close the socket, force thread to stop also */
393 : static void
394 150 : shutdown_server_socket(void)
395 : {
396 : char sock_buf[32];
397 :
398 150 : shutdown(server_socket, SHUT_WR);
399 361 : while (READSOCKET(server_socket, sock_buf, sizeof(sock_buf)) > 0)
400 61 : continue;
401 150 : }
402 :
403 : static void
404 180 : test(int mars, void (*real_test)(void))
405 : {
406 : TDSCONTEXT *ctx;
407 : TDS_SYS_SOCKET sockets[2];
408 : tds_thread fake_thread;
409 :
410 : #if !ENABLE_ODBC_MARS
411 90 : if (mars)
412 45 : return;
413 : #endif
414 :
415 135 : ctx = tds_alloc_context(NULL);
416 135 : assert(ctx);
417 135 : tds = tds_alloc_socket(ctx, 512);
418 135 : assert(tds);
419 :
420 : /* provide connection to a fake remove server */
421 135 : assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) >= 0);
422 135 : tds_socket_set_nosigpipe(sockets[0], 1);
423 135 : was_shutdown = false;
424 135 : tds->state = TDS_IDLE;
425 135 : tds_set_s(tds, sockets[0]);
426 :
427 270 : if (tds_thread_create(&fake_thread, fake_thread_proc, TDS_INT2PTR(sockets[1])) != 0) {
428 0 : perror("tds_thread_create");
429 0 : exit(1);
430 : }
431 135 : server_socket = sockets[0];
432 :
433 : #if ENABLE_ODBC_MARS
434 90 : if (mars) {
435 45 : tds->conn->mars = 1;
436 45 : assert(tds_realloc_socket(tds, tds->out_buf_max));
437 45 : tds_init_write_buf(tds);
438 : }
439 : #endif
440 :
441 135 : real_test();
442 :
443 135 : tds_flush_packet(tds);
444 :
445 : /* flush all data and wait for other thread to finish cleanly */
446 135 : shutdown_server_socket();
447 270 : tds_thread_join(fake_thread, NULL);
448 :
449 135 : tdsdump_dump_buf(TDS_DBG_INFO1, "sent buffer", buf.buf, buf.len);
450 :
451 135 : strip_headers(&thread_buf);
452 135 : tdsdump_dump_buf(TDS_DBG_INFO1, "thread buffer", thread_buf.buf, thread_buf.len);
453 135 : assert(buf.len == thread_buf.len);
454 135 : assert(memcmp(buf.buf, thread_buf.buf, buf.len) == 0);
455 :
456 135 : tds_free_socket(tds);
457 135 : tds = NULL;
458 135 : tds_free_context(ctx);
459 :
460 135 : buf_free(&buf);
461 135 : buf_free(&thread_buf);
462 90 : }
463 :
464 10 : TEST_MAIN()
465 : {
466 : int mars;
467 :
468 10 : setbuf(stdout, NULL);
469 10 : setbuf(stderr, NULL);
470 :
471 10 : tdsdump_open(tds_dir_getenv(TDS_DIR("TDSDUMP")));
472 :
473 30 : for (mars = 0; mars < 2; ++mars) {
474 20 : test(mars, test1);
475 20 : test(mars, test_len1);
476 20 : test(mars, test_len2);
477 20 : test(mars, test_failure1);
478 20 : test(mars, test_failure2);
479 20 : test(mars, test_shutdown);
480 20 : test(mars, test_cross1);
481 20 : test(mars, test_cross2);
482 20 : test(mars, test_end);
483 : }
484 :
485 10 : return 0;
486 : }
487 : #else /* !TDS_HAVE_MUTEX */
488 : TEST_MAIN()
489 : {
490 : printf("Not possible for this platform.\n");
491 : return 0; /* TODO 77 ? */
492 : }
493 : #endif
|