LCOV - code coverage report
Current view: top level - src/odbc/unittests - timeout3.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 73 93 78.5 %
Date: 2024-03-23 09:12:27 Functions: 4 4 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             : #if HAVE_POLL_H
      26             : #include <poll.h>
      27             : #endif /* HAVE_POLL_H */
      28             : 
      29             : #include <freetds/tds.h>
      30             : #include <freetds/thread.h>
      31             : #include <freetds/replacements.h>
      32             : 
      33             : #if TDS_HAVE_MUTEX
      34             : 
      35             : static void init_connect(void);
      36             : 
      37             : static void
      38           8 : init_connect(void)
      39             : {
      40           8 :         CHKAllocEnv(&odbc_env, "S");
      41           8 :         SQLSetEnvAttr(odbc_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (SQL_OV_ODBC3), SQL_IS_UINTEGER);
      42           8 :         CHKAllocConnect(&odbc_conn, "S");
      43           8 : }
      44             : 
      45             : static tds_thread fake_thread;
      46             : static tds_mutex mtx;
      47             : static TDS_SYS_SOCKET fake_sock;
      48             : 
      49             : static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg);
      50             : 
      51             : /* build a listening socket to connect to */
      52             : static int
      53           8 : init_fake_server(int ip_port)
      54             : {
      55             :         struct sockaddr_in sin;
      56             :         TDS_SYS_SOCKET s;
      57             :         int err;
      58             : 
      59           8 :         memset(&sin, 0, sizeof(sin));
      60             :         sin.sin_addr.s_addr = INADDR_ANY;
      61           8 :         sin.sin_port = htons((short) ip_port);
      62           8 :         sin.sin_family = AF_INET;
      63             : 
      64           8 :         if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) {
      65           0 :                 perror("socket");
      66           0 :                 exit(1);
      67             :         }
      68           8 :         if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
      69           0 :                 perror("bind");
      70           0 :                 CLOSESOCKET(s);
      71           0 :                 return 1;
      72             :         }
      73           8 :         listen(s, 5);
      74          16 :         err = tds_thread_create(&fake_thread, fake_thread_proc, TDS_INT2PTR(s));
      75           8 :         if (err != 0) {
      76           0 :                 perror("tds_thread_create");
      77           0 :                 exit(1);
      78             :         }
      79             :         return 0;
      80             : }
      81             : 
      82             : /* accept a socket and read data as much as you can */
      83           8 : static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
      84             : {
      85           8 :         TDS_SYS_SOCKET s = TDS_PTR2INT(arg), sock;
      86             :         socklen_t len;
      87             :         char buf[128];
      88             :         struct sockaddr_in sin;
      89             :         struct pollfd fd;
      90             : 
      91           8 :         memset(&sin, 0, sizeof(sin));
      92           8 :         len = sizeof(sin);
      93             : 
      94           8 :         fd.fd = s;
      95           8 :         fd.events = POLLIN;
      96           8 :         fd.revents = 0;
      97           8 :         if (poll(&fd, 1, 30000) <= 0) {
      98           0 :                 fprintf(stderr, "poll: %d\n", sock_errno);
      99           0 :                 exit(1);
     100             :         }
     101             : 
     102           8 :         if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) {
     103           0 :                 perror("accept");
     104           0 :                 exit(1);
     105             :         }
     106           8 :         tds_mutex_lock(&mtx);
     107           8 :         fake_sock = sock;
     108           8 :         tds_mutex_unlock(&mtx);
     109           8 :         CLOSESOCKET(s);
     110             : 
     111             :         for (;;) {
     112             :                 int len;
     113             : 
     114          24 :                 fd.fd = sock;
     115          24 :                 fd.events = POLLIN;
     116          24 :                 fd.revents = 0;
     117          24 :                 if (poll(&fd, 1, 30000) <= 0) {
     118           0 :                         fprintf(stderr, "poll: %d\n", sock_errno);
     119           0 :                         exit(1);
     120             :                 }
     121             : 
     122             :                 /* just read and discard */
     123          24 :                 len = READSOCKET(sock, buf, sizeof(buf));
     124          24 :                 if (len == 0)
     125             :                         break;
     126          24 :                 if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS)
     127             :                         break;
     128             :         }
     129           8 :         return TDS_THREAD_RESULT(0);
     130             : }
     131             : 
     132             : int
     133           8 : main(int argc, char *argv[])
     134             : {
     135             :         SQLTCHAR tmp[2048];
     136             :         char conn[128];
     137             :         SQLTCHAR sqlstate[6];
     138             :         SQLSMALLINT len;
     139             :         int port;
     140             :         time_t start_time, end_time;
     141             : 
     142             : #ifdef _WIN32
     143             :         WSADATA wsaData;
     144             :         WSAStartup(MAKEWORD(2, 2), &wsaData);
     145             : #endif
     146             : 
     147           8 :         if (tds_mutex_init(&mtx))
     148             :                 return 1;
     149             : 
     150           8 :         if (odbc_read_login_info())
     151           0 :                 exit(1);
     152             : 
     153             :         /*
     154             :          * prepare our odbcinst.ini 
     155             :          * it is better to do it before connecting because unixODBC caches INIs
     156             :          * the name must be odbcinst.ini because unixODBC accepts only this name
     157             :          */
     158           8 :         if (odbc_driver[0]) {
     159           8 :                 FILE *f = fopen("odbcinst.ini", "w");
     160             : 
     161           8 :                 if (f) {
     162           8 :                         fprintf(f, "[FreeTDS]\nDriver = %s\n", odbc_driver);
     163           8 :                         fclose(f);
     164             :                         /* force iODBC */
     165           8 :                         setenv("ODBCINSTINI", "./odbcinst.ini", 1);
     166           8 :                         setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
     167             :                         /* force unixODBC (only directory) */
     168           8 :                         setenv("ODBCSYSINI", ".", 1);
     169             :                 }
     170             :         }
     171             : 
     172             :         /* this test requires version 7.0, avoid to override externally */
     173           8 :         setenv("TDSVER", "7.0", 1);
     174             : 
     175           8 :         for (port = 12340; port < 12350; ++port)
     176           8 :                 if (!init_fake_server(port))
     177             :                         break;
     178           8 :         if (port == 12350) {
     179           0 :                 fprintf(stderr, "Cannot bind to a port\n");
     180           0 :                 return 1;
     181             :         }
     182           8 :         printf("Fake server bound at port %d\n", port);
     183             : 
     184           8 :         init_connect();
     185           8 :         CHKSetConnectAttr(SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) 25, sizeof(SQLINTEGER), "SI");
     186           8 :         CHKSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) 10, SQL_IS_UINTEGER, "SI");
     187             : 
     188             :         /* this is expected to work with unixODBC */
     189           8 :         printf("try to connect to our port just to check connection timeout\n");
     190           8 :         sprintf(conn, "DRIVER=FreeTDS;SERVER=127.0.0.1;Port=%d;TDS_Version=7.0;UID=test;PWD=test;DATABASE=tempdb;", port);
     191           8 :         start_time = time(NULL);
     192           8 :         CHKDriverConnect(NULL, T(conn), SQL_NTS, tmp, TDS_VECTOR_SIZE(tmp), &len, SQL_DRIVER_NOPROMPT, "E");
     193           8 :         end_time = time(NULL);
     194             : 
     195           8 :         memset(sqlstate, 'X', sizeof(sqlstate));
     196           8 :         tmp[0] = 0;
     197           8 :         CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, sqlstate, NULL, tmp, TDS_VECTOR_SIZE(tmp), NULL, "SI");
     198           8 :         odbc_disconnect();
     199           8 :         tds_mutex_lock(&mtx);
     200           8 :         CLOSESOCKET(fake_sock);
     201           8 :         tds_mutex_unlock(&mtx);
     202          16 :         tds_thread_join(fake_thread, NULL);
     203             : 
     204           8 :         printf("Message: %s - %s\n", C(sqlstate), C(tmp));
     205           8 :         if (strcmp(C(sqlstate), "HYT00") || !strstr(C(tmp), "Timeout")) {
     206           0 :                 fprintf(stderr, "Invalid timeout message\n");
     207           0 :                 return 1;
     208             :         }
     209           8 :         if (end_time - start_time < 10 || end_time - start_time > 16) {
     210           0 :                 fprintf(stderr, "Unexpected connect timeout (%d)\n", (int) (end_time - start_time));
     211           0 :                 return 1;
     212             :         }
     213             : 
     214           8 :         printf("Done.\n");
     215           8 :         ODBC_FREE();
     216           8 :         return 0;
     217             : }
     218             : 
     219             : #else   /* !TDS_HAVE_MUTEX */
     220             : int
     221             : main(void)
     222             : {
     223             :         printf("Not possible for this platform.\n");
     224             :         odbc_test_skipped();
     225             :         return 0;
     226             : }
     227             : #endif

Generated by: LCOV version 1.13