Mercurial > hg > th-libs
comparison th_network.c @ 102:6ca407bfbeaf
New TCP network module, rather simple and only useful for client applications now.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 21 Jun 2014 02:41:29 +0300 |
parents | |
children | f7bec3f7181d |
comparison
equal
deleted
inserted
replaced
101:a8f732601fdc | 102:6ca407bfbeaf |
---|---|
1 /* | |
2 * Simple TCP network connection handling | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
4 * (C) Copyright 2013-2014 Tecnic Software productions (TNSP) | |
5 * | |
6 * Please read file 'COPYING' for information on license and distribution. | |
7 */ | |
8 #include "th_network.h" | |
9 #include "th_string.h" | |
10 #include <errno.h> | |
11 | |
12 | |
13 static BOOL th_network_inited = FALSE; | |
14 static qlist_t *th_conn_list = NULL; | |
15 | |
16 | |
17 enum | |
18 { | |
19 TH_SOCKS5_AUTH_NONE = 0, | |
20 TH_SOCKS5_AUTH_USER = 2, | |
21 }; | |
22 | |
23 | |
24 struct th_socks4_t | |
25 { | |
26 uint8_t version; | |
27 uint8_t command; | |
28 in_port_t port; | |
29 in_addr_t addr; | |
30 } __attribute__((__packed__)); | |
31 | |
32 | |
33 struct th_socks4_res_t | |
34 { | |
35 uint8_t nb; | |
36 uint8_t result; | |
37 in_port_t port; | |
38 in_addr_t addr; | |
39 } __attribute__((__packed__)); | |
40 | |
41 | |
42 static const char *th_proxy_types[] = | |
43 { | |
44 "none", | |
45 "SOCKS 4", | |
46 "SOCKS 4a", | |
47 "SOCKS 5", | |
48 NULL | |
49 }; | |
50 | |
51 | |
52 static int th_get_socket_errno(void) | |
53 { | |
54 return errno; | |
55 } | |
56 | |
57 | |
58 void th_conn_err(th_conn_t *conn, int err, const char *fmt, ...) | |
59 { | |
60 if (conn->errfunc != NULL) | |
61 { | |
62 char *msg; | |
63 va_list ap; | |
64 va_start(ap, fmt); | |
65 msg = th_strdup_vprintf(fmt, ap); | |
66 va_end(ap); | |
67 | |
68 conn->errfunc(conn, err, msg); | |
69 th_free(msg); | |
70 } | |
71 } | |
72 | |
73 | |
74 void th_conn_msg(th_conn_t *conn, int loglevel, const char *fmt, ...) | |
75 { | |
76 if (conn->msgfunc != NULL) | |
77 { | |
78 char *msg; | |
79 va_list ap; | |
80 va_start(ap, fmt); | |
81 msg = th_strdup_vprintf(fmt, ap); | |
82 va_end(ap); | |
83 | |
84 conn->msgfunc(conn, loglevel, msg); | |
85 th_free(msg); | |
86 } | |
87 } | |
88 | |
89 | |
90 struct hostent *th_resolve_host(th_conn_t *conn, const char *name) | |
91 { | |
92 struct hostent *res = gethostbyname(name); | |
93 | |
94 if (res == NULL) | |
95 { | |
96 th_conn_err(conn, h_errno, | |
97 "Could not resolve hostname '%s': %s\n", | |
98 name, th_error_str(h_errno)); | |
99 } | |
100 else | |
101 { | |
102 th_conn_msg(conn, THLOG_INFO, "True hostname for %s is %s\n", | |
103 name, res->h_name); | |
104 } | |
105 | |
106 return res; | |
107 } | |
108 | |
109 | |
110 th_conn_t * th_conn_new( | |
111 void (*errfunc)(th_conn_t *conn, int err, const char *msg), | |
112 void (*msgfunc)(th_conn_t *conn, int loglevel, const char *msg)) | |
113 { | |
114 th_conn_t *conn = th_malloc0(sizeof(th_conn_t)); | |
115 | |
116 if (conn == NULL) | |
117 return NULL; | |
118 | |
119 conn->errfunc = errfunc; | |
120 conn->msgfunc = msgfunc; | |
121 | |
122 return conn; | |
123 } | |
124 | |
125 | |
126 static BOOL th_get_addr(struct in_addr *addr, struct hostent *hst) | |
127 { | |
128 if (hst != NULL) | |
129 { | |
130 *addr = *(struct in_addr *) (hst->h_addr); | |
131 return TRUE; | |
132 } | |
133 else | |
134 { | |
135 addr->s_addr = 0; | |
136 return FALSE; | |
137 } | |
138 } | |
139 | |
140 | |
141 int th_conn_set_proxy(th_conn_t *conn, int type, int port, const char *host, int auth_type) | |
142 { | |
143 if (conn == NULL) | |
144 return -1; | |
145 | |
146 conn->proxy.type = type; | |
147 conn->proxy.port = port; | |
148 conn->proxy.auth_type = auth_type; | |
149 | |
150 th_free(conn->proxy.host); | |
151 conn->proxy.host = th_strdup(host); | |
152 | |
153 if (host != NULL) | |
154 { | |
155 conn->proxy.hst = th_resolve_host(conn, host); | |
156 th_get_addr(&(conn->proxy.addr), conn->proxy.hst); | |
157 } | |
158 else | |
159 return -2; | |
160 | |
161 return 0; | |
162 } | |
163 | |
164 | |
165 int th_conn_set_proxy_auth_user(th_conn_t *conn, const char *userid, const char *passwd) | |
166 { | |
167 if (conn == NULL) | |
168 return -1; | |
169 | |
170 th_free(conn->proxy.userid); | |
171 conn->proxy.userid = th_strdup(userid); | |
172 | |
173 th_free(conn->proxy.passwd); | |
174 conn->proxy.passwd = th_strdup(passwd); | |
175 | |
176 return 0; | |
177 } | |
178 | |
179 | |
180 static BOOL th_conn_proxy_wait(th_conn_t *conn) | |
181 { | |
182 int status, tries; | |
183 | |
184 for (status = tries = 1; tries <= 20 && status > 0; tries++) | |
185 { | |
186 #ifdef __WIN32 | |
187 Sleep(50); | |
188 #else | |
189 usleep(50000); | |
190 #endif | |
191 th_conn_reset(conn); | |
192 status = th_conn_pull(conn); | |
193 } | |
194 | |
195 if (status < 0) | |
196 { | |
197 th_conn_err(conn, status, "Proxy negotiation failed at try %d with network error: %d\n", | |
198 tries, status); | |
199 } | |
200 else | |
201 th_conn_err(conn, THERR_TIMED_OUT, "Proxy negotiation timed out.\n"); | |
202 | |
203 return status == FALSE; | |
204 } | |
205 | |
206 | |
207 static BOOL th_conn_proxy_send(th_conn_t *conn, void *buf, size_t bufsiz) | |
208 { | |
209 BOOL ret; | |
210 th_conn_reset(conn); | |
211 ret = th_conn_send_buf(conn, buf, bufsiz); | |
212 th_free(buf); | |
213 return ret; | |
214 } | |
215 | |
216 | |
217 static int th_conn_socks4_negotiate(th_conn_t *conn, const int port, const char *host) | |
218 { | |
219 struct th_socks4_res_t *sockres; | |
220 struct th_socks4_t *socksh; | |
221 size_t bufsiz; | |
222 uint8_t *ptr, *buf; | |
223 int err = THERR_INIT_FAIL; | |
224 | |
225 (void) host; | |
226 | |
227 th_conn_msg(conn, THLOG_INFO, "Initializing SOCKS 4/a proxy negotiation.\n"); | |
228 | |
229 bufsiz = sizeof(struct th_socks4_t) + strlen(conn->proxy.userid) + 1; | |
230 if (conn->proxy.type == TH_PROXY_SOCKS4A) | |
231 bufsiz += strlen(conn->host) + 1; | |
232 | |
233 if ((ptr = buf = th_malloc(bufsiz)) == NULL) | |
234 { | |
235 err = THERR_MALLOC; | |
236 th_conn_err(conn, err, | |
237 "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); | |
238 return err; | |
239 } | |
240 | |
241 // Create SOCKS 4 handshake | |
242 socksh = (struct th_socks4_t *) buf; | |
243 socksh->version = 4; | |
244 socksh->command = TH_PROXY_CMD_CONNECT; | |
245 socksh->port = htons(port); | |
246 socksh->addr = (conn->proxy.type == TH_PROXY_SOCKS4A) ? htonl(0x00000032) : conn->addr.s_addr; | |
247 ptr += sizeof(struct th_socks4_t); | |
248 | |
249 strcpy((char *) ptr, conn->proxy.userid); | |
250 | |
251 if (conn->proxy.type == TH_PROXY_SOCKS4A) | |
252 { | |
253 ptr += strlen(conn->proxy.userid) + 1; | |
254 strcpy((char *)ptr, conn->host); | |
255 } | |
256 | |
257 // Send request | |
258 if (!th_conn_proxy_send(conn, buf, bufsiz)) | |
259 return FALSE; | |
260 | |
261 // Wait for SOCKS server to reply | |
262 if (th_conn_proxy_wait(conn) != 0) | |
263 return FALSE; | |
264 | |
265 sockres = (struct th_socks4_res_t *) &(conn->buf); | |
266 if (sockres->nb != 0) | |
267 { | |
268 th_conn_err(conn, THERR_INIT_FAIL, | |
269 "Invalid SOCKS 4 server reply, does not begin with NUL byte (%d).\n", sockres->nb); | |
270 return FALSE; | |
271 } | |
272 if (sockres->result != 0x5a) | |
273 { | |
274 const char *s = NULL; | |
275 switch (sockres->result) | |
276 { | |
277 case 0x5b: s = "Request rejected or failed"; break; | |
278 case 0x5c: s = "Request failed because client is not running identd (or not reachable from the server)"; break; | |
279 case 0x5d: s = "Request failed because client's identd could not confirm the user ID string in the request"; break; | |
280 default: s = "Unknown SOCKS 4 error response"; break; | |
281 } | |
282 th_conn_err(conn, THERR_INIT_FAIL, | |
283 "SOCKS 4 setup failed, 0x%02x: %s.\n", | |
284 sockres->result, s); | |
285 return FALSE; | |
286 } | |
287 | |
288 return TRUE; | |
289 } | |
290 | |
291 | |
292 static int th_conn_socks5_negotiate(th_conn_t *conn, const int port, const char *host) | |
293 { | |
294 size_t bufsiz, userid_len = 0, passwd_len = 0; | |
295 uint8_t *ptr, *buf; | |
296 int avail, auth; | |
297 | |
298 th_conn_msg(conn, THLOG_INFO, "Initializing SOCKS 5 proxy negotiation.\n"); | |
299 | |
300 switch (conn->proxy.auth_type) | |
301 { | |
302 case TH_PROXY_AUTH_NONE: | |
303 avail = 1; | |
304 break; | |
305 | |
306 case TH_PROXY_AUTH_USER: | |
307 avail = 2; | |
308 if (conn->proxy.userid == NULL || conn->proxy.passwd == NULL) | |
309 { | |
310 th_conn_err(conn, THERR_INVALID_DATA, | |
311 "SOCKS 5 user authentication chosen, but no user/pass set.\n"); | |
312 return FALSE; | |
313 } | |
314 | |
315 userid_len = strlen(conn->proxy.userid); | |
316 passwd_len = strlen(conn->proxy.passwd); | |
317 if (userid_len > 255 || passwd_len > 255) | |
318 { | |
319 th_conn_err(conn, THERR_INVALID_DATA, | |
320 "SOCKS 5 proxy userid or password is too long.\n"); | |
321 return FALSE; | |
322 } | |
323 break; | |
324 | |
325 default: | |
326 th_conn_err(conn, THERR_NOT_SUPPORTED, | |
327 "Unsupported proxy authentication method %d.\n", | |
328 conn->proxy.auth_type); | |
329 return FALSE; | |
330 } | |
331 | |
332 bufsiz = 2 + avail; | |
333 if ((ptr = buf = th_malloc(bufsiz)) == NULL) | |
334 { | |
335 th_conn_err(conn, THERR_MALLOC, | |
336 "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); | |
337 return FALSE; | |
338 } | |
339 | |
340 // Form handshake packet | |
341 *ptr++ = 0x05; // Protocol version | |
342 *ptr++ = avail; // # of available auth methods | |
343 switch (conn->proxy.auth_type) | |
344 { | |
345 case TH_PROXY_AUTH_NONE: | |
346 *ptr++ = TH_SOCKS5_AUTH_NONE; | |
347 | |
348 case TH_PROXY_AUTH_USER: | |
349 *ptr++ = TH_SOCKS5_AUTH_USER; | |
350 break; | |
351 } | |
352 | |
353 // Send request | |
354 if (!th_conn_proxy_send(conn, buf, bufsiz)) | |
355 return FALSE; | |
356 | |
357 // Wait for SOCKS server to reply | |
358 if (th_conn_proxy_wait(conn) != 0) | |
359 return FALSE; | |
360 | |
361 ptr = (uint8_t *) conn->buf; | |
362 if (*ptr != 0x05) | |
363 { | |
364 th_conn_err(conn, THERR_INVALID_DATA, | |
365 "Invalid SOCKS 5 server reply, does not begin with protocol version byte (%d).\n", *ptr); | |
366 return FALSE; | |
367 } | |
368 ptr++; | |
369 auth = *ptr; | |
370 | |
371 if (auth == 0xff) | |
372 { | |
373 th_conn_err(conn, THERR_NOT_SUPPORTED, | |
374 "No authentication method could be negotiated with the server.\n"); | |
375 return FALSE; | |
376 } | |
377 | |
378 if (auth == TH_SOCKS5_AUTH_USER) | |
379 { | |
380 // Attempt user/pass authentication (RFC 1929) | |
381 bufsiz = 1 + 1 + 1 + userid_len + passwd_len; | |
382 if ((ptr = buf = th_malloc(bufsiz)) == NULL) | |
383 { | |
384 th_conn_err(conn, THERR_MALLOC, | |
385 "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); | |
386 return FALSE; | |
387 } | |
388 | |
389 *ptr++ = 0x01; | |
390 | |
391 *ptr++ = userid_len; | |
392 memcpy(ptr, conn->proxy.userid, userid_len); | |
393 ptr += userid_len; | |
394 | |
395 *ptr++ = passwd_len; | |
396 memcpy(ptr, conn->proxy.passwd, passwd_len); | |
397 ptr += passwd_len; | |
398 | |
399 // Send request | |
400 if (!th_conn_proxy_send(conn, buf, bufsiz)) | |
401 return FALSE; | |
402 | |
403 // Wait for SOCKS server to reply | |
404 if (th_conn_proxy_wait(conn) != 0) | |
405 return FALSE; | |
406 | |
407 ptr = (uint8_t *) conn->buf; | |
408 if (*ptr != 0x01) | |
409 { | |
410 th_conn_err(conn, THERR_INVALID_DATA, | |
411 "Invalid SOCKS 5 server reply, does not begin with protocol version byte (%d).\n", *ptr); | |
412 return FALSE; | |
413 } | |
414 ptr++; | |
415 if (*ptr != 0) | |
416 { | |
417 th_conn_err(conn, THERR_AUTH_FAILED, | |
418 "SOCKS 5 proxy user/pass authentication failed! Code %d.\n", *ptr); | |
419 return FALSE; | |
420 } | |
421 } | |
422 else | |
423 if (auth != TH_SOCKS5_AUTH_NONE) | |
424 { | |
425 th_conn_err(conn, THERR_NOT_SUPPORTED, | |
426 "Proxy server chose an unsupported SOCKS 5 authentication method %d.\n", | |
427 auth); | |
428 return FALSE; | |
429 } | |
430 | |
431 | |
432 #if 0 | |
433 // Form client connection request packet | |
434 bufsiz = 4; | |
435 | |
436 if ((ptr = buf = th_malloc(bufsiz)) == NULL) | |
437 { | |
438 th_conn_err(conn, THERR_MALLOC, | |
439 "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); | |
440 return FALSE; | |
441 } | |
442 | |
443 *ptr++ = 0x05; // Protocol version | |
444 *ptr++ = cmd; | |
445 *ptr++ = 0x00; | |
446 *ptr++ = SOCKS5_ADDR_IPV4; | |
447 | |
448 memcpy(ptr, conn->addr.s_addr, sizeof(conn->addr.s_addr)); | |
449 ptr += sizeof(conn->addr.s_addr); | |
450 tmpPort = htons(port); | |
451 memcpy(ptr, tmpPort, sizeof(tmpPort)); | |
452 | |
453 | |
454 // Send request | |
455 if (!th_conn_proxy_send(conn, buf, bufsiz)) | |
456 return FALSE; | |
457 | |
458 // Wait for SOCKS server to reply | |
459 if (th_conn_proxy_wait(conn) != 0) | |
460 return FALSE; | |
461 | |
462 ptr = (uint8_t *) conn->buf; | |
463 if (*ptr != 0x05) | |
464 { | |
465 th_conn_err(conn, "Invalid SOCKS 5 server reply, does not begin with protocol version byte (%d).\n", *ptr); | |
466 return FALSE; | |
467 } | |
468 ptr++; | |
469 if (*ptr != 0) | |
470 { | |
471 if (*ptr >= 0 && *ptr < sizeof()) | |
472 th_conn_err(conn, "%s.\n", nn_socks5_results_msgs[*ptr]); | |
473 else | |
474 th_conn_err(conn, ".\n"); | |
475 return FALSE; | |
476 } | |
477 ptr++; | |
478 if (*ptr != 0) | |
479 { | |
480 th_conn_err(conn, ".\n"); | |
481 return FALSE; | |
482 } | |
483 | |
484 #endif | |
485 | |
486 return TRUE; | |
487 } | |
488 | |
489 | |
490 int th_conn_open(th_conn_t *conn, const int port, const char *host) | |
491 { | |
492 struct sockaddr_in dest; | |
493 int err = THERR_INIT_FAIL; | |
494 | |
495 if (conn == NULL) | |
496 return THERR_NULLPTR; | |
497 | |
498 conn->port = port; | |
499 if (host != NULL) | |
500 { | |
501 conn->host = th_strdup(host); | |
502 conn->hst = th_resolve_host(conn, host); | |
503 } | |
504 | |
505 th_get_addr(&(conn->addr), conn->hst); | |
506 | |
507 // Prepare for connection | |
508 dest.sin_family = AF_INET; | |
509 | |
510 if (conn->proxy.type > TH_PROXY_NONE && conn->proxy.type < TH_PROXY_LAST) | |
511 { | |
512 // If using a proxy, we connect to the proxy server | |
513 dest.sin_port = htons(conn->proxy.port); | |
514 dest.sin_addr = conn->proxy.addr; | |
515 | |
516 th_conn_msg(conn, THLOG_INFO, "Connecting to %s proxy %s:%d ...\n", | |
517 th_proxy_types[conn->proxy.type], | |
518 inet_ntoa(conn->proxy.addr), conn->proxy.port); | |
519 } | |
520 else | |
521 { | |
522 dest.sin_port = htons(conn->port); | |
523 dest.sin_addr = conn->addr; | |
524 | |
525 th_conn_msg(conn, THLOG_INFO, "Connecting to %s:%d ...\n", | |
526 inet_ntoa(conn->addr), conn->port); | |
527 } | |
528 | |
529 if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) | |
530 { | |
531 err = th_errno_to_error(th_get_socket_errno()); | |
532 th_conn_err(conn, err, "Could not open socket: %s\n", th_error_str(err)); | |
533 goto error; | |
534 } | |
535 | |
536 if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) | |
537 { | |
538 err = th_errno_to_error(th_get_socket_errno()); | |
539 th_conn_err(conn, err, "Could not connect: %s\n", th_error_str(err)); | |
540 goto error; | |
541 } | |
542 | |
543 FD_ZERO(&(conn->sockfds)); | |
544 FD_SET(conn->socket, &(conn->sockfds)); | |
545 | |
546 // Proxy-specific setup | |
547 switch (conn->proxy.type) | |
548 { | |
549 case TH_PROXY_SOCKS4: | |
550 case TH_PROXY_SOCKS4A: | |
551 if (!th_conn_socks4_negotiate(conn, port, host)) | |
552 goto error; | |
553 th_conn_msg(conn, THLOG_INFO, "SOCKS 4 connection established!\n"); | |
554 break; | |
555 | |
556 case TH_PROXY_SOCKS5: | |
557 if (!th_conn_socks5_negotiate(conn, port, host)) | |
558 goto error; | |
559 th_conn_msg(conn, THLOG_INFO, "SOCKS 5 connection established!\n"); | |
560 break; | |
561 } | |
562 | |
563 th_conn_reset(conn); | |
564 conn->status = TH_CONN_OPEN; | |
565 | |
566 // Insert to connection list | |
567 conn->node = th_llist_append(&th_conn_list, conn); | |
568 | |
569 return THERR_OK; | |
570 | |
571 error: | |
572 th_conn_close(conn); | |
573 return err; | |
574 } | |
575 | |
576 | |
577 int th_conn_close(th_conn_t *conn) | |
578 { | |
579 if (conn == NULL) | |
580 return -1; | |
581 | |
582 if (conn->socket >= 0) | |
583 { | |
584 #ifdef __WIN32 | |
585 closesocket(conn->socket); | |
586 #else | |
587 close(conn->socket); | |
588 #endif | |
589 conn->socket = -1; | |
590 } | |
591 | |
592 conn->status = TH_CONN_CLOSED; | |
593 return 0; | |
594 } | |
595 | |
596 | |
597 static void th_conn_free_nodelete(th_conn_t *conn) | |
598 { | |
599 th_conn_close(conn); | |
600 th_free(conn->host); | |
601 th_free(conn->proxy.host); | |
602 th_free(conn); | |
603 } | |
604 | |
605 | |
606 void th_conn_free(th_conn_t *conn) | |
607 { | |
608 if (conn != NULL) | |
609 { | |
610 // Remove from linked list | |
611 if (conn->node != NULL) | |
612 th_llist_delete_node_fast(&th_conn_list, conn->node); | |
613 | |
614 // Free connection data | |
615 th_conn_free_nodelete(conn); | |
616 } | |
617 } | |
618 | |
619 | |
620 BOOL th_conn_send_buf(th_conn_t *conn, const char *buf, const size_t len) | |
621 { | |
622 size_t bufLeft = len; | |
623 const char *bufPtr = buf; | |
624 | |
625 while (bufLeft > 0) | |
626 { | |
627 ssize_t bufSent; | |
628 bufSent = send(conn->socket, bufPtr, bufLeft, 0); | |
629 if (bufSent < 0) | |
630 { | |
631 int err = th_errno_to_error(th_get_socket_errno()); | |
632 th_conn_err(conn, err, "th_conn_send_buf() failed: %s", th_error_str(err)); | |
633 return FALSE; | |
634 } | |
635 bufLeft -= bufSent; | |
636 bufPtr += bufSent; | |
637 } | |
638 | |
639 return TRUE; | |
640 } | |
641 | |
642 | |
643 void th_conn_reset(th_conn_t *conn) | |
644 { | |
645 if (conn != NULL) | |
646 { | |
647 conn->ptr = conn->in_ptr = conn->buf; | |
648 conn->got_bytes = conn->total_bytes = 0; | |
649 } | |
650 } | |
651 | |
652 | |
653 int th_conn_pull(th_conn_t *conn) | |
654 { | |
655 int result; | |
656 struct timeval socktv; | |
657 fd_set tmpfds; | |
658 | |
659 if (conn == NULL) | |
660 return TH_CONN_ERROR; | |
661 | |
662 // Shift the input buffer | |
663 if (conn->ptr > conn->buf) | |
664 { | |
665 size_t left = conn->in_ptr - conn->ptr; | |
666 if (left > 0) | |
667 { | |
668 size_t moved = conn->ptr - conn->buf; | |
669 memmove(conn->buf, conn->ptr, left); | |
670 conn->ptr = conn->buf; | |
671 conn->in_ptr -= moved; | |
672 conn->total_bytes -= moved; | |
673 } | |
674 else | |
675 th_conn_reset(conn); | |
676 } | |
677 | |
678 // Check for incoming data | |
679 socktv.tv_sec = 0; | |
680 socktv.tv_usec = TH_DELAY_USEC; | |
681 tmpfds = conn->sockfds; | |
682 | |
683 if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) | |
684 { | |
685 int err = th_get_socket_errno(); | |
686 if (err != EINTR) | |
687 { | |
688 err = th_errno_to_error(err); | |
689 th_conn_err(conn, err, "Error occured in select(%d, sockfds): %s\n", | |
690 socket, th_error_str(err)); | |
691 return TH_CONN_ERROR; | |
692 } | |
693 } | |
694 else if (FD_ISSET(conn->socket, &tmpfds)) | |
695 { | |
696 conn->got_bytes = recv(conn->socket, conn->in_ptr, TH_CONNBUF_SIZE - conn->total_bytes, 0); | |
697 if (conn->got_bytes < 0) | |
698 { | |
699 int err = th_errno_to_error(th_get_socket_errno()); | |
700 th_conn_err(conn, err, "Error in recv: %s\n", th_error_str(err)); | |
701 return TH_CONN_ERROR; | |
702 } | |
703 else if (conn->got_bytes == 0) | |
704 { | |
705 th_conn_err(conn, ECONNABORTED, "Server closed connection.\n"); | |
706 conn->status = TH_CONN_CLOSED; | |
707 return TH_CONN_CLOSED; | |
708 } | |
709 else | |
710 { | |
711 conn->total_bytes += conn->got_bytes; | |
712 conn->in_ptr += conn->got_bytes; | |
713 return TH_CONN_DATA_AVAIL; | |
714 } | |
715 } | |
716 | |
717 return TH_CONN_NO_DATA; | |
718 } | |
719 | |
720 | |
721 BOOL th_conn_check(th_conn_t *conn) | |
722 { | |
723 if (conn == NULL) | |
724 return FALSE; | |
725 | |
726 return conn->err == 0 && conn->status == TH_CONN_OPEN; | |
727 } | |
728 | |
729 | |
730 int th_network_init(void) | |
731 { | |
732 #ifdef __WIN32 | |
733 // Initialize WinSock, if needed | |
734 WSADATA wsaData; | |
735 int err = WSAStartup(0x0101, &wsaData); | |
736 if (err != 0) | |
737 { | |
738 THERR("Could not initialize WinSock library (err=%d).\n", err); | |
739 return THERR_INIT_FAIL; | |
740 } | |
741 #endif | |
742 | |
743 th_network_inited = TRUE; | |
744 | |
745 th_conn_list = NULL; | |
746 | |
747 return THERR_OK; | |
748 } | |
749 | |
750 | |
751 void th_network_close(void) | |
752 { | |
753 if (th_network_inited) | |
754 { | |
755 // Close connections | |
756 qlist_t *curr = th_conn_list; | |
757 while (curr != NULL) | |
758 { | |
759 qlist_t *next = curr->next; | |
760 th_conn_free_nodelete(curr->data); | |
761 curr = next; | |
762 } | |
763 | |
764 #ifdef __WIN32 | |
765 WSACleanup(); | |
766 #endif | |
767 } | |
768 | |
769 th_network_inited = FALSE; | |
770 } | |
771 | |
772 | |
773 BOOL th_conn_buf_check(th_conn_t *conn, size_t n) | |
774 { | |
775 return conn && (conn->ptr + n <= conn->in_ptr); | |
776 } | |
777 | |
778 | |
779 BOOL th_conn_buf_skip(th_conn_t *conn, size_t n) | |
780 { | |
781 if (th_conn_buf_check(conn, n)) | |
782 { | |
783 conn->ptr += n; | |
784 return TRUE; | |
785 } | |
786 else | |
787 return FALSE; | |
788 } | |
789 | |
790 | |
791 int th_conn_buf_strncmp(th_conn_t *conn, const char *str, const size_t n) | |
792 { | |
793 int ret; | |
794 if (!th_conn_buf_check(conn, n)) | |
795 return -1; | |
796 | |
797 if ((ret = strncmp(conn->ptr, str, n)) == 0) | |
798 { | |
799 conn->ptr += n; | |
800 return 0; | |
801 } | |
802 else | |
803 return ret; | |
804 } | |
805 | |
806 | |
807 int th_conn_buf_strcmp(th_conn_t *conn, const char *str) | |
808 { | |
809 return th_conn_buf_strncmp(conn, str, strlen(str)); | |
810 } | |
811 | |
812 | |
813 char *th_conn_buf_strstr(th_conn_t *conn, const char *str) | |
814 { | |
815 char *pos; | |
816 size_t n = strlen(str); | |
817 | |
818 if (th_conn_buf_check(conn, n) && ((pos = strstr(conn->ptr, str)) != NULL)) | |
819 { | |
820 conn->ptr = pos + n; | |
821 return pos; | |
822 } | |
823 else | |
824 return NULL; | |
825 } | |
826 | |
827 | |
828 void th_conn_dump_buffer(FILE *f, th_conn_t *conn) | |
829 { | |
830 char *p; | |
831 size_t offs, left; | |
832 | |
833 fprintf(f, | |
834 "\n--------------------------------------------------------------\n" | |
835 "err=%d, status=%d, got_bytes=%d, total_bytes=%d\n" | |
836 "buf=0x%p, in_ptr=0x%04x, ptr=0x%04x\n", | |
837 conn->err, conn->status, conn->got_bytes, conn->total_bytes, | |
838 conn->buf, conn->in_ptr - conn->buf, conn->ptr - conn->buf); | |
839 | |
840 // Dump buffer contents as a hexdump | |
841 for (offs = 0, left = conn->total_bytes, p = conn->buf; p < conn->in_ptr;) | |
842 { | |
843 char buf[TH_DUMP_BYTES + 1]; | |
844 size_t bufoffs, amount = left < TH_DUMP_BYTES ? left : TH_DUMP_BYTES; | |
845 left -= amount; | |
846 | |
847 // Dump offs | xx xx xx xx | and fill string | |
848 fprintf(f, "%04x | ", offs); | |
849 for (bufoffs = 0; bufoffs < amount; offs++, bufoffs++, p++) | |
850 { | |
851 fprintf(f, "%02x ", *p); | |
852 buf[bufoffs] = th_isprint(*p) ? *p : '.'; | |
853 } | |
854 buf[bufoffs] = 0; | |
855 | |
856 // Add padding | |
857 for (; bufoffs < TH_DUMP_BYTES; bufoffs++) | |
858 fprintf(f, " "); | |
859 | |
860 // Print string | |
861 fprintf(f, "| %s\n", buf); | |
862 } | |
863 } |