LCOV - code coverage report
Current view: top level - src/odbc/unittests - timeout3.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 67 80 83.8 %
Date: 2025-01-18 11:50:39 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Test connection timeout
       3             :  */
       4             : 
       5             : #include "common.h"
       6             : 
       7             : #if HAVE_UNISTD_H
       8             : #include <unistd.h>
       9             : #endif /* HAVE_UNISTD_H */
      10             : 
      11             : #include <freetds/time.h>
      12             : 
      13             : #if HAVE_ERRNO_H
      14             : #include <errno.h>
      15             : #endif /* HAVE_ERRNO_H */
      16             : 
      17             : #if HAVE_SYS_SOCKET_H
      18             : #include <sys/socket.h>
      19             : #endif /* HAVE_SYS_SOCKET_H */
      20             : 
      21             : #if HAVE_NETINET_IN_H
      22             : #include <netinet/in.h>
      23             : #endif /* HAVE_NETINET_IN_H */
      24             : 
      25             : #include <freetds/tds.h>
      26             : #include <freetds/replacements.h>
      27             : #include <freetds/utils.h>
      28             : 
      29             : #include "fake_thread.h"
      30             : 
      31             : #if TDS_HAVE_MUTEX
      32             : 
      33             : static void init_connect(void);
      34             : 
      35             : static void
      36           8 : init_connect(void)
      37             : {
      38           8 :         CHKAllocEnv(&odbc_env, "S");
      39           8 :         SQLSetEnvAttr(odbc_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (SQL_OV_ODBC3), SQL_IS_UINTEGER);
      40           8 :         CHKAllocConnect(&odbc_conn, "S");
      41           8 : }
      42             : 
      43             : static tds_mutex mtx;
      44             : static TDS_SYS_SOCKET fake_sock;
      45             : 
      46             : /* accept a socket and read data as much as you can */
      47           8 : TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
      48             : {
      49           8 :         TDS_SYS_SOCKET s = TDS_PTR2INT(arg), sock;
      50             :         socklen_t len;
      51             :         char buf[128];
      52             :         struct sockaddr_in sin;
      53             :         struct pollfd fd;
      54             : 
      55           8 :         memset(&sin, 0, sizeof(sin));
      56           8 :         len = sizeof(sin);
      57             : 
      58           8 :         fd.fd = s;
      59           8 :         fd.events = POLLIN;
      60           8 :         fd.revents = 0;
      61           8 :         if (poll(&fd, 1, 30000) <= 0) {
      62           0 :                 fprintf(stderr, "poll: %d\n", sock_errno);
      63           0 :                 exit(1);
      64             :         }
      65             : 
      66           8 :         if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) {
      67           0 :                 perror("accept");
      68           0 :                 exit(1);
      69             :         }
      70           8 :         tds_socket_set_nodelay(sock);
      71           8 :         tds_mutex_lock(&mtx);
      72           8 :         fake_sock = sock;
      73           8 :         tds_mutex_unlock(&mtx);
      74           8 :         CLOSESOCKET(s);
      75             : 
      76             :         for (;;) {
      77             :                 int len;
      78             : 
      79          24 :                 fd.fd = sock;
      80          24 :                 fd.events = POLLIN;
      81          24 :                 fd.revents = 0;
      82          24 :                 if (poll(&fd, 1, 30000) <= 0) {
      83           0 :                         fprintf(stderr, "poll: %d\n", sock_errno);
      84           0 :                         exit(1);
      85             :                 }
      86             : 
      87             :                 /* just read and discard */
      88          24 :                 len = READSOCKET(sock, buf, sizeof(buf));
      89          24 :                 if (len == 0)
      90             :                         break;
      91          24 :                 if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS)
      92             :                         break;
      93             :         }
      94           8 :         return TDS_THREAD_RESULT(0);
      95             : }
      96             : 
      97             : int
      98           8 : main(void)
      99             : {
     100             :         SQLTCHAR tmp[2048];
     101             :         char conn[128];
     102             :         SQLTCHAR sqlstate[6];
     103             :         SQLSMALLINT len;
     104             :         int port;
     105             :         time_t start_time, end_time;
     106             : 
     107           8 :         tds_socket_init();
     108             : 
     109           8 :         if (tds_mutex_init(&mtx))
     110             :                 return 1;
     111             : 
     112           8 :         if (odbc_read_login_info())
     113           0 :                 exit(1);
     114             : 
     115             :         /*
     116             :          * prepare our odbcinst.ini 
     117             :          * it is better to do it before connecting because unixODBC caches INIs
     118             :          * the name must be odbcinst.ini because unixODBC accepts only this name
     119             :          */
     120           8 :         if (odbc_driver[0]) {
     121           8 :                 FILE *f = fopen("odbcinst.ini", "w");
     122             : 
     123           8 :                 if (f) {
     124           8 :                         fprintf(f, "[FreeTDS]\nDriver = %s\n", odbc_driver);
     125           8 :                         fclose(f);
     126             :                         /* force iODBC */
     127           8 :                         setenv("ODBCINSTINI", "./odbcinst.ini", 1);
     128           8 :                         setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
     129             :                         /* force unixODBC (only directory) */
     130           8 :                         setenv("ODBCSYSINI", ".", 1);
     131             :                 }
     132             :         }
     133             : 
     134             :         /* this test requires version 7.0, avoid to override externally */
     135           8 :         setenv("TDSVER", "7.0", 1);
     136           8 :         unsetenv("TDSPORT");
     137             : 
     138           9 :         for (port = 12340; port < 12350; ++port)
     139           9 :                 if (init_fake_server(port))
     140             :                         break;
     141           8 :         if (port == 12350) {
     142           0 :                 fprintf(stderr, "Cannot bind to a port\n");
     143           0 :                 return 1;
     144             :         }
     145           8 :         printf("Fake server bound at port %d\n", port);
     146             : 
     147           8 :         init_connect();
     148           8 :         CHKSetConnectAttr(SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) 25, sizeof(SQLINTEGER), "SI");
     149           8 :         CHKSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) 10, SQL_IS_UINTEGER, "SI");
     150             : 
     151             :         /* this is expected to work with unixODBC */
     152           8 :         printf("try to connect to our port just to check connection timeout\n");
     153           8 :         sprintf(conn, "DRIVER=FreeTDS;SERVER=127.0.0.1;Port=%d;TDS_Version=7.0;UID=test;PWD=test;DATABASE=tempdb;", port);
     154           8 :         start_time = time(NULL);
     155           8 :         CHKDriverConnect(NULL, T(conn), SQL_NTS, tmp, TDS_VECTOR_SIZE(tmp), &len, SQL_DRIVER_NOPROMPT, "E");
     156           8 :         end_time = time(NULL);
     157             : 
     158           8 :         memset(sqlstate, 'X', sizeof(sqlstate));
     159           8 :         tmp[0] = 0;
     160           8 :         CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, sqlstate, NULL, tmp, TDS_VECTOR_SIZE(tmp), NULL, "SI");
     161           8 :         odbc_disconnect();
     162           8 :         tds_mutex_lock(&mtx);
     163           8 :         CLOSESOCKET(fake_sock);
     164           8 :         tds_mutex_unlock(&mtx);
     165          16 :         tds_thread_join(fake_thread, NULL);
     166             : 
     167           8 :         printf("Message: %s - %s\n", C(sqlstate), C(tmp));
     168           8 :         if (strcmp(C(sqlstate), "HYT00") || !strstr(C(tmp), "Timeout")) {
     169           0 :                 fprintf(stderr, "Invalid timeout message\n");
     170           0 :                 return 1;
     171             :         }
     172           8 :         if (end_time - start_time < 10 || end_time - start_time > 16) {
     173           0 :                 fprintf(stderr, "Unexpected connect timeout (%d)\n", (int) (end_time - start_time));
     174           0 :                 return 1;
     175             :         }
     176             : 
     177           8 :         printf("Done.\n");
     178           8 :         ODBC_FREE();
     179           8 :         return 0;
     180             : }
     181             : 
     182             : #else   /* !TDS_HAVE_MUTEX */
     183             : int
     184             : main(void)
     185             : {
     186             :         printf("Not possible for this platform.\n");
     187             :         odbc_test_skipped();
     188             :         return 0;
     189             : }
     190             : #endif

Generated by: LCOV version 1.13