LCOV - code coverage report
Current view: top level - src/tds/unittests - freeze.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 248 250 99.2 %
Date: 2025-01-18 12:13:41 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             : 
      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             : }

Generated by: LCOV version 1.13