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