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 12 : init_fake_server(int ip_port)
54 : {
55 : struct sockaddr_in sin;
56 : TDS_SYS_SOCKET s;
57 : int err;
58 :
59 12 : memset(&sin, 0, sizeof(sin));
60 : sin.sin_addr.s_addr = INADDR_ANY;
61 12 : sin.sin_port = htons((short) ip_port);
62 12 : sin.sin_family = AF_INET;
63 :
64 12 : if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) {
65 0 : perror("socket");
66 0 : exit(1);
67 : }
68 12 : if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
69 4 : perror("bind");
70 4 : CLOSESOCKET(s);
71 4 : 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 12 : for (port = 12340; port < 12350; ++port)
176 12 : 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
|