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