Line data Source code
1 : #include "common.h"
2 :
3 : #if HAVE_UNISTD_H
4 : #include <unistd.h>
5 : #endif /* HAVE_UNISTD_H */
6 :
7 : #include <freetds/time.h>
8 :
9 : #if HAVE_ERRNO_H
10 : #include <errno.h>
11 : #endif /* HAVE_ERRNO_H */
12 :
13 : #if HAVE_SYS_SOCKET_H
14 : #include <sys/socket.h>
15 : #endif /* HAVE_SYS_SOCKET_H */
16 :
17 : #if HAVE_SYS_STAT_H
18 : #include <sys/stat.h>
19 : #endif /* HAVE_SYS_STAT_H */
20 :
21 : #if HAVE_SYS_IOCTL_H
22 : #include <sys/ioctl.h>
23 : #endif /* HAVE_SYS_IOCTL_H */
24 :
25 : #if HAVE_SYS_WAIT_H
26 : #include <sys/wait.h>
27 : #endif /* HAVE_SYS_WAIT_H */
28 :
29 : #if HAVE_NETINET_IN_H
30 : #include <netinet/in.h>
31 : #endif /* HAVE_NETINET_IN_H */
32 :
33 : #if (!defined(TDS_NO_THREADSAFE) && HAVE_ALARM && HAVE_FSTAT && defined(S_IFSOCK)) || defined(_WIN32)
34 :
35 : #include <ctype.h>
36 :
37 : #include <freetds/tds.h>
38 : #include <freetds/utils.h>
39 :
40 : #include "fake_thread.h"
41 :
42 : /* this crazy test tests that we do not send too much prepare ... */
43 :
44 : static tds_mutex mtx;
45 :
46 : typedef union {
47 : struct sockaddr sa;
48 : struct sockaddr_in sin;
49 : char dummy[256];
50 : } long_sockaddr;
51 :
52 : static long_sockaddr remote_addr;
53 : static socklen_t remote_addr_len;
54 :
55 : #ifdef _WIN32
56 : #define alarm(n) do { ; } while(0)
57 : #endif
58 :
59 : static void
60 569 : write_all(TDS_SYS_SOCKET s, const void *buf, size_t len)
61 : {
62 : int res, l;
63 : fd_set fds_write;
64 :
65 1707 : for (; len > 0;) {
66 569 : FD_ZERO(&fds_write);
67 569 : FD_SET(s, &fds_write);
68 :
69 569 : res = select(s + 1, NULL, &fds_write, NULL, NULL);
70 569 : if (res <= 0) {
71 0 : if (errno == EINTR)
72 0 : continue;
73 0 : perror("select");
74 0 : exit(1);
75 : }
76 :
77 569 : l = WRITESOCKET(s, buf, len);
78 569 : if (l <= 0) {
79 0 : perror("write socket");
80 0 : exit(1);
81 : }
82 569 : buf = ((const char *) buf) + l;
83 569 : len -= l;
84 : }
85 569 : }
86 :
87 : static unsigned int inserts = 0;
88 : static char insert_buf[256] = "";
89 :
90 : static void
91 301 : count_insert(const char* buf, size_t len)
92 : {
93 : static const char search[] = "insert into";
94 : static unsigned char prev = 'x';
95 : char *p;
96 : unsigned char c;
97 : size_t insert_len;
98 :
99 : /* add to buffer */
100 24124 : for (p = strchr(insert_buf, 0); len > 0; --len, ++buf) {
101 23823 : c = (unsigned char) buf[0];
102 23823 : if (prev == 0 || c != 0)
103 20808 : *p++ = (c >= ' ' && c < 128) ? tolower(c) : '_';
104 23823 : prev = c;
105 : }
106 301 : *p = 0;
107 :
108 : /* check it */
109 608 : while ((p=strstr(insert_buf, search)) != NULL) {
110 6 : tds_mutex_lock(&mtx);
111 6 : ++inserts;
112 6 : tds_mutex_unlock(&mtx);
113 : /* do not find again */
114 6 : p[0] = '_';
115 : }
116 :
117 : /* avoid buffer too long */
118 301 : insert_len = strlen(insert_buf);
119 301 : if (insert_len > sizeof(search)) {
120 301 : p = insert_buf + insert_len - sizeof(search);
121 301 : memmove(insert_buf, p, strlen(p) + 1);
122 : }
123 301 : }
124 :
125 : static unsigned int round_trips = 0;
126 : static enum { sending, receiving } flow = sending;
127 :
128 8 : TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
129 : {
130 8 : TDS_SYS_SOCKET s = TDS_PTR2INT(arg), server_sock;
131 : socklen_t sock_len;
132 : int len;
133 : char buf[128];
134 : struct sockaddr_in sin;
135 : fd_set fds_read, fds_write, fds_error;
136 8 : TDS_SYS_SOCKET max_fd = 0;
137 : TDS_SYS_SOCKET fake_sock;
138 :
139 8 : memset(&sin, 0, sizeof(sin));
140 8 : sock_len = sizeof(sin);
141 8 : alarm(30);
142 8 : fprintf(stderr, "waiting connect...\n");
143 8 : if ((fake_sock = tds_accept(s, (struct sockaddr *) &sin, &sock_len)) < 0) {
144 0 : perror("accept");
145 0 : exit(1);
146 : }
147 8 : tds_socket_set_nodelay(fake_sock);
148 8 : CLOSESOCKET(s);
149 :
150 8 : if (TDS_IS_SOCKET_INVALID(server_sock = socket(remote_addr.sa.sa_family, SOCK_STREAM, 0))) {
151 0 : perror("socket");
152 0 : exit(1);
153 : }
154 :
155 8 : fprintf(stderr, "connecting to server...\n");
156 8 : if (remote_addr.sa.sa_family == AF_INET) {
157 8 : fprintf(stderr, "connecting to %x:%d\n", (unsigned int) remote_addr.sin.sin_addr.s_addr,
158 8 : ntohs(remote_addr.sin.sin_port));
159 : }
160 8 : if (connect(server_sock, &remote_addr.sa, remote_addr_len)) {
161 0 : perror("connect");
162 0 : exit(1);
163 : }
164 8 : alarm(0);
165 :
166 8 : if (fake_sock > max_fd) max_fd = fake_sock;
167 8 : if (server_sock > max_fd) max_fd = server_sock;
168 :
169 : for (;;) {
170 : int res;
171 :
172 577 : FD_ZERO(&fds_read);
173 577 : FD_SET(fake_sock, &fds_read);
174 577 : FD_SET(server_sock, &fds_read);
175 :
176 577 : FD_ZERO(&fds_write);
177 :
178 577 : FD_ZERO(&fds_error);
179 577 : FD_SET(fake_sock, &fds_error);
180 577 : FD_SET(server_sock, &fds_error);
181 :
182 577 : alarm(30);
183 577 : res = select(max_fd + 1, &fds_read, &fds_write, &fds_error, NULL);
184 577 : alarm(0);
185 577 : if (res < 0) {
186 0 : if (sock_errno == TDSSOCK_EINTR)
187 0 : continue;
188 0 : perror("select");
189 0 : exit(1);
190 : }
191 :
192 577 : if (FD_ISSET(fake_sock, &fds_error) || FD_ISSET(server_sock, &fds_error)) {
193 0 : fprintf(stderr, "error in select\n");
194 0 : exit(1);
195 : }
196 :
197 : /* just read and forward */
198 577 : if (FD_ISSET(fake_sock, &fds_read)) {
199 307 : if (flow != sending) {
200 218 : tds_mutex_lock(&mtx);
201 218 : ++round_trips;
202 218 : tds_mutex_unlock(&mtx);
203 : }
204 307 : flow = sending;
205 :
206 307 : len = READSOCKET(fake_sock, buf, sizeof(buf));
207 307 : if (len == 0) {
208 6 : fprintf(stderr, "client connection closed\n");
209 6 : break;
210 : }
211 301 : if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) {
212 0 : fprintf(stderr, "read client error %d\n", sock_errno);
213 0 : break;
214 : }
215 301 : count_insert(buf, len);
216 301 : write_all(server_sock, buf, len);
217 : }
218 :
219 571 : if (FD_ISSET(server_sock, &fds_read)) {
220 270 : if (flow != receiving) {
221 220 : tds_mutex_lock(&mtx);
222 220 : ++round_trips;
223 220 : tds_mutex_unlock(&mtx);
224 : }
225 270 : flow = receiving;
226 :
227 270 : len = READSOCKET(server_sock, buf, sizeof(buf));
228 270 : if (len == 0) {
229 2 : fprintf(stderr, "server connection closed\n");
230 2 : break;
231 : }
232 268 : if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) {
233 0 : fprintf(stderr, "read server error %d\n", sock_errno);
234 0 : break;
235 : }
236 268 : write_all(fake_sock, buf, len);
237 : }
238 : }
239 8 : CLOSESOCKET(fake_sock);
240 8 : CLOSESOCKET(server_sock);
241 8 : return TDS_THREAD_RESULT(0);
242 : }
243 :
244 : int
245 8 : main(void)
246 : {
247 8 : SQLLEN sql_nts = SQL_NTS;
248 : const char *query;
249 8 : SQLINTEGER id = 0;
250 : char string[64];
251 : TDS_SYS_SOCKET last_socket;
252 : int port;
253 8 : const int num_inserts = 20;
254 : int is_freetds;
255 :
256 8 : tds_socket_init();
257 :
258 8 : if (tds_mutex_init(&mtx))
259 : return 1;
260 :
261 8 : odbc_mark_sockets_opened();
262 :
263 8 : odbc_connect();
264 :
265 : /*
266 : * this does not work if server is not connected with socket
267 : * (ie ms driver connected locally)
268 : */
269 8 : last_socket = odbc_find_last_socket();
270 8 : if (TDS_IS_SOCKET_INVALID(last_socket)) {
271 0 : fprintf(stderr, "Error finding last socket opened\n");
272 0 : return 1;
273 : }
274 :
275 8 : remote_addr_len = sizeof(remote_addr);
276 8 : if (tds_getpeername(last_socket, &remote_addr.sa, &remote_addr_len)) {
277 0 : fprintf(stderr, "Unable to get remote address %d\n", sock_errno);
278 0 : return 1;
279 : }
280 :
281 8 : is_freetds = odbc_driver_is_freetds();
282 8 : odbc_disconnect();
283 :
284 : /* init fake server, behave like a proxy */
285 8 : for (port = 12340; port < 12350; ++port)
286 8 : if (init_fake_server(port))
287 : break;
288 8 : if (port == 12350) {
289 0 : fprintf(stderr, "Cannot bind to a port\n");
290 0 : return 1;
291 : }
292 8 : printf("Fake server bound at port %d\n", port);
293 :
294 : /* override connections */
295 8 : if (is_freetds) {
296 8 : setenv("TDSHOST", "127.0.0.1", 1);
297 8 : sprintf(string, "%d", port);
298 8 : setenv("TDSPORT", string, 1);
299 :
300 8 : odbc_connect();
301 : } else {
302 : char tmp[2048];
303 : SQLSMALLINT len;
304 :
305 0 : CHKAllocEnv(&odbc_env, "S");
306 0 : CHKAllocConnect(&odbc_conn, "S");
307 0 : sprintf(tmp, "DRIVER={SQL Server};SERVER=127.0.0.1,%d;UID=%s;PWD=%s;DATABASE=%s;Network=DBMSSOCN;", port, odbc_user, odbc_password, odbc_database);
308 0 : printf("connection string: %s\n", tmp);
309 0 : CHKDriverConnect(NULL, T(tmp), SQL_NTS, (SQLTCHAR *) tmp, sizeof(tmp)/sizeof(SQLTCHAR), &len, SQL_DRIVER_NOPROMPT, "SI");
310 0 : CHKAllocStmt(&odbc_stmt, "S");
311 : }
312 :
313 : /* real test */
314 8 : odbc_command("CREATE TABLE #test(i int, c varchar(40))");
315 :
316 8 : odbc_reset_statement();
317 :
318 : /* do not take into account connection statistics */
319 8 : tds_mutex_lock(&mtx);
320 8 : round_trips = 0;
321 8 : inserts = 0;
322 8 : tds_mutex_unlock(&mtx);
323 :
324 8 : query = "insert into #test values (?, ?)";
325 :
326 8 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI");
327 8 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI");
328 :
329 8 : CHKPrepare(T(query), SQL_NTS, "SI");
330 8 : tds_mutex_lock(&mtx);
331 8 : printf("%u round trips %u inserts\n", round_trips, inserts);
332 8 : tds_mutex_unlock(&mtx);
333 :
334 168 : for (id = 0; id < num_inserts; id++) {
335 160 : sprintf(string, "This is a test (%d)", (int) id);
336 160 : CHKExecute("SI");
337 160 : CHKFreeStmt(SQL_CLOSE, "S");
338 : }
339 :
340 8 : tds_mutex_lock(&mtx);
341 8 : printf("%u round trips %u inserts\n", round_trips, inserts);
342 8 : tds_mutex_unlock(&mtx);
343 8 : odbc_reset_statement();
344 :
345 8 : tds_mutex_lock(&mtx);
346 8 : if (inserts > 1 || round_trips > (unsigned) (num_inserts * 2 + 6)) {
347 0 : fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts);
348 0 : tds_mutex_unlock(&mtx);
349 0 : return 1;
350 : }
351 8 : printf("%u round trips %u inserts\n", round_trips, inserts);
352 8 : tds_mutex_unlock(&mtx);
353 :
354 : #ifdef ENABLE_DEVELOPING
355 : /* check for SQL_RESET_PARAMS */
356 : tds_mutex_lock(&mtx);
357 : round_trips = 0;
358 : inserts = 0;
359 : tds_mutex_unlock(&mtx);
360 :
361 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI");
362 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI");
363 :
364 : CHKPrepare((SQLCHAR *) query, SQL_NTS, "SI");
365 : tds_mutex_lock(&mtx);
366 : printf("%u round trips %u inserts\n", round_trips, inserts);
367 : tds_mutex_unlock(&mtx);
368 :
369 : for (id = 0; id < num_inserts; id++) {
370 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI");
371 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI");
372 :
373 : sprintf(string, "This is a test (%d)", (int) id);
374 : CHKExecute("SI");
375 : CHKFreeStmt(SQL_RESET_PARAMS, "S");
376 : }
377 :
378 : tds_mutex_lock(&mtx);
379 : printf("%u round trips %u inserts\n", round_trips, inserts);
380 : tds_mutex_unlock(&mtx);
381 : odbc_reset_statement();
382 :
383 : tds_mutex_lock(&mtx);
384 : if (inserts > 1 || round_trips > num_inserts * 2 + 6) {
385 : fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts);
386 : tds_mutex_unlock(&mtx);
387 : return 1;
388 : }
389 : printf("%u round trips %u inserts\n", round_trips, inserts);
390 : tds_mutex_unlock(&mtx);
391 : #endif
392 :
393 8 : odbc_disconnect();
394 :
395 8 : alarm(10);
396 16 : tds_thread_join(fake_thread, NULL);
397 :
398 8 : return 0;
399 : }
400 :
401 : #else
402 : int
403 : main(void)
404 : {
405 : printf("Not possible for this platform.\n");
406 : odbc_test_skipped();
407 : return 0;
408 : }
409 : #endif
410 :
|