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 : }
|