LCOV - code coverage report
Current view: top level - src/odbc/unittests - login_timeout.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 58 67 86.6 %
Date: 2026-08-16 18:05:36 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Test login timeout if server accepts connection but does not dialog.
       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             : #ifdef _WIN32
      30             : #define SHUT_RDWR SD_BOTH
      31             : #endif
      32             : 
      33             : #include "fake_thread.h"
      34             : 
      35             : static void init_connect(void);
      36             : 
      37             : static void
      38          10 : init_connect(void)
      39             : {
      40          10 :         CHKAllocEnv(&odbc_env, "S");
      41          10 :         SQLSetEnvAttr(odbc_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (SQL_OV_ODBC3), SQL_IS_UINTEGER);
      42          10 :         CHKAllocConnect(&odbc_conn, "S");
      43          10 : }
      44             : 
      45             : static TDS_SYS_SOCKET listen_sock;
      46             : 
      47             : /* accept a socket and read data as much as you can */
      48          10 : TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
      49             : {
      50          10 :         TDS_SYS_SOCKET s = TDS_PTR2INT(arg);
      51          10 :         int accepted = 0;
      52             : 
      53          10 :         listen_sock = s;
      54             : 
      55          20 :         for (;;) {
      56             :                 socklen_t len;
      57             :                 struct sockaddr_in sin;
      58             :                 struct pollfd fd;
      59             :                 TDS_SYS_SOCKET sock;
      60             : 
      61          30 :                 fd.fd = s;
      62          30 :                 fd.events = POLLIN;
      63          30 :                 fd.revents = 0;
      64          30 :                 if (poll(&fd, 1, 30000) <= 0)
      65             :                         break;
      66             : 
      67          30 :                 memset(&sin, 0, sizeof(sin));
      68          30 :                 len = sizeof(sin);
      69          30 :                 if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len)))
      70             :                         break;
      71          20 :                 ++accepted;
      72             :         }
      73          10 :         CLOSESOCKET(s);
      74          10 :         return TDS_THREAD_RESULT(accepted);
      75             : }
      76             : 
      77          10 : TEST_MAIN()
      78             : {
      79             :         SQLTCHAR tmp[2048];
      80             :         char conn[128];
      81             :         SQLTCHAR sqlstate[6];
      82             :         SQLSMALLINT len;
      83             :         int port, accepted;
      84             :         void *res;
      85             :         time_t start_time, end_time;
      86             : 
      87             :         tds_socket_init();
      88             : 
      89          10 :         if (odbc_read_login_info())
      90           0 :                 exit(1);
      91             : 
      92             :         /*
      93             :          * prepare our odbcinst.ini
      94             :          * it is better to do it before connecting because unixODBC caches INIs
      95             :          * the name must be odbcinst.ini because unixODBC accepts only this name
      96             :          */
      97          10 :         if (common_pwd.driver[0]) {
      98          10 :                 FILE *f = fopen("odbcinst.ini", "w");
      99             : 
     100          10 :                 if (f) {
     101          10 :                         fprintf(f, "[FreeTDS]\nDriver = %s\n", common_pwd.driver);
     102          10 :                         fclose(f);
     103             :                         /* force iODBC */
     104          10 :                         setenv("ODBCINSTINI", "./odbcinst.ini", 1);
     105          10 :                         setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
     106             :                         /* force unixODBC (only directory) */
     107          10 :                         setenv("ODBCSYSINI", ".", 1);
     108             :                 }
     109             :         }
     110             : 
     111             :         /* this test requires version "auto", avoid to override externally */
     112          10 :         setenv("TDSVER", "auto", 1);
     113          10 :         unsetenv("TDSPORT");
     114             : 
     115          12 :         for (port = 12340; port < 12350; ++port)
     116          12 :                 if (init_fake_server(port))
     117             :                         break;
     118          10 :         if (port == 12350) {
     119           0 :                 fprintf(stderr, "Cannot bind to a port\n");
     120           0 :                 return 1;
     121             :         }
     122          10 :         printf("Fake server bound at port %d\n", port);
     123             : 
     124          10 :         init_connect();
     125          10 :         CHKSetConnectAttr(SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) 8, sizeof(SQLINTEGER), "SI");
     126          10 :         CHKSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) 2, SQL_IS_UINTEGER, "SI");
     127             : 
     128          10 :         printf("try to connect to our port just to check connection timeout\n");
     129          10 :         sprintf(conn, "DRIVER=FreeTDS;SERVER=127.0.0.1;Port=%d;TDS_Version=auto;UID=test;PWD=test;DATABASE=tempdb;", port);
     130          10 :         start_time = time(NULL);
     131          10 :         CHKDriverConnect(NULL, T(conn), SQL_NTS, tmp, TDS_VECTOR_SIZE(tmp), &len, SQL_DRIVER_NOPROMPT, "E");
     132          10 :         end_time = time(NULL);
     133             : 
     134          10 :         memset(sqlstate, 'X', sizeof(sqlstate));
     135          10 :         tmp[0] = 0;
     136          10 :         CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, sqlstate, NULL, tmp, TDS_VECTOR_SIZE(tmp), NULL, "SI");
     137          10 :         odbc_disconnect();
     138          10 :         shutdown(listen_sock, SHUT_RDWR);
     139          20 :         tds_thread_join(fake_thread, &res);
     140          10 :         accepted = TDS_PTR2INT(res);
     141             : 
     142          10 :         printf("Message: %s - %s\n", C(sqlstate), C(tmp));
     143          10 :         if (strcmp(C(sqlstate), "HYT00") || !strstr(C(tmp), "Timeout")) {
     144           0 :                 fprintf(stderr, "Invalid timeout message\n");
     145           0 :                 return 1;
     146             :         }
     147          10 :         if (end_time - start_time < 9 || end_time - start_time > 10) {
     148           0 :                 fprintf(stderr, "Unexpected connect timeout (%d)\n", (int) (end_time - start_time));
     149           0 :                 return 1;
     150             :         }
     151          10 :         if (accepted < 2 || accepted > 4) {
     152           0 :                 fprintf(stderr, "Unexpected connection attempts (%d)\n", accepted);
     153           0 :                 return 1;
     154             :         }
     155             : 
     156          10 :         printf("Done.\n");
     157          10 :         ODBC_FREE();
     158          10 :         return 0;
     159             : }

Generated by: LCOV version 1.13