LCOV - code coverage report
Current view: top level - src/tds/unittests - freeze.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 249 251 99.2 %
Date: 2025-01-18 11:50:39 Functions: 19 19 100.0 %

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

Generated by: LCOV version 1.13