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