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 }