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 10 : init_connect(void)
37 : {
38 10 : CHKAllocEnv(&odbc_env, "S");
39 10 : SQLSetEnvAttr(odbc_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (SQL_OV_ODBC3), SQL_IS_UINTEGER);
40 10 : CHKAllocConnect(&odbc_conn, "S");
41 10 : }
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 10 : TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
48 : {
49 10 : 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 10 : memset(&sin, 0, sizeof(sin));
56 10 : len = sizeof(sin);
57 :
58 10 : fd.fd = s;
59 10 : fd.events = POLLIN;
60 10 : fd.revents = 0;
61 10 : if (poll(&fd, 1, 30000) <= 0) {
62 0 : fprintf(stderr, "poll: %d\n", sock_errno);
63 0 : exit(1);
64 : }
65 :
66 10 : if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) {
67 0 : perror("accept");
68 0 : exit(1);
69 : }
70 10 : tds_socket_set_nodelay(sock);
71 10 : tds_mutex_lock(&mtx);
72 10 : fake_sock = sock;
73 10 : tds_mutex_unlock(&mtx);
74 10 : CLOSESOCKET(s);
75 :
76 : for (;;) {
77 : int len;
78 :
79 30 : fd.fd = sock;
80 30 : fd.events = POLLIN;
81 30 : fd.revents = 0;
82 30 : 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 30 : len = READSOCKET(sock, buf, sizeof(buf));
89 30 : if (len == 0)
90 : break;
91 30 : if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS)
92 : break;
93 : }
94 10 : return TDS_THREAD_RESULT(0);
95 : }
96 :
97 10 : TEST_MAIN()
98 : {
99 : SQLTCHAR tmp[2048];
100 : char conn[128];
101 : SQLTCHAR sqlstate[6];
102 : SQLSMALLINT len;
103 : int port;
104 : time_t start_time, end_time;
105 :
106 10 : tds_socket_init();
107 :
108 10 : if (tds_mutex_init(&mtx))
109 : return 1;
110 :
111 10 : if (odbc_read_login_info())
112 0 : exit(1);
113 :
114 : /*
115 : * prepare our odbcinst.ini
116 : * it is better to do it before connecting because unixODBC caches INIs
117 : * the name must be odbcinst.ini because unixODBC accepts only this name
118 : */
119 10 : if (common_pwd.driver[0]) {
120 10 : FILE *f = fopen("odbcinst.ini", "w");
121 :
122 10 : if (f) {
123 10 : fprintf(f, "[FreeTDS]\nDriver = %s\n", common_pwd.driver);
124 10 : fclose(f);
125 : /* force iODBC */
126 10 : setenv("ODBCINSTINI", "./odbcinst.ini", 1);
127 10 : setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
128 : /* force unixODBC (only directory) */
129 10 : setenv("ODBCSYSINI", ".", 1);
130 : }
131 : }
132 :
133 : /* this test requires version 7.0, avoid to override externally */
134 10 : setenv("TDSVER", "7.0", 1);
135 10 : unsetenv("TDSPORT");
136 :
137 16 : for (port = 12340; port < 12350; ++port)
138 16 : if (init_fake_server(port))
139 : break;
140 10 : if (port == 12350) {
141 0 : fprintf(stderr, "Cannot bind to a port\n");
142 0 : return 1;
143 : }
144 10 : printf("Fake server bound at port %d\n", port);
145 :
146 10 : init_connect();
147 10 : CHKSetConnectAttr(SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER) 25, sizeof(SQLINTEGER), "SI");
148 10 : CHKSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) 10, SQL_IS_UINTEGER, "SI");
149 :
150 : /* this is expected to work with unixODBC */
151 10 : printf("try to connect to our port just to check connection timeout\n");
152 10 : sprintf(conn, "DRIVER=FreeTDS;SERVER=127.0.0.1;Port=%d;TDS_Version=7.0;UID=test;PWD=test;DATABASE=tempdb;", port);
153 10 : start_time = time(NULL);
154 10 : CHKDriverConnect(NULL, T(conn), SQL_NTS, tmp, TDS_VECTOR_SIZE(tmp), &len, SQL_DRIVER_NOPROMPT, "E");
155 10 : end_time = time(NULL);
156 :
157 10 : memset(sqlstate, 'X', sizeof(sqlstate));
158 10 : tmp[0] = 0;
159 10 : CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, sqlstate, NULL, tmp, TDS_VECTOR_SIZE(tmp), NULL, "SI");
160 10 : odbc_disconnect();
161 10 : tds_mutex_lock(&mtx);
162 10 : CLOSESOCKET(fake_sock);
163 10 : tds_mutex_unlock(&mtx);
164 20 : tds_thread_join(fake_thread, NULL);
165 :
166 10 : printf("Message: %s - %s\n", C(sqlstate), C(tmp));
167 10 : if (strcmp(C(sqlstate), "HYT00") || !strstr(C(tmp), "Timeout")) {
168 0 : fprintf(stderr, "Invalid timeout message\n");
169 0 : return 1;
170 : }
171 10 : if (end_time - start_time < 10 || end_time - start_time > 16) {
172 0 : fprintf(stderr, "Unexpected connect timeout (%d)\n", (int) (end_time - start_time));
173 0 : return 1;
174 : }
175 :
176 10 : printf("Done.\n");
177 10 : ODBC_FREE();
178 10 : return 0;
179 : }
180 :
181 : #else /* !TDS_HAVE_MUTEX */
182 : TEST_MAIN()
183 : {
184 : printf("Not possible for this platform.\n");
185 : odbc_test_skipped();
186 : return 0;
187 : }
188 : #endif
|