Mercurial > hg > th-libs
changeset 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 | a8f732601fdc |
children | f7bec3f7181d |
files | th_network.c th_network.h |
diffstat | 2 files changed, 1018 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/th_network.c Sat Jun 21 02:41:29 2014 +0300 @@ -0,0 +1,863 @@ +/* + * Simple TCP network connection handling + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2013-2014 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include "th_network.h" +#include "th_string.h" +#include <errno.h> + + +static BOOL th_network_inited = FALSE; +static qlist_t *th_conn_list = NULL; + + +enum +{ + TH_SOCKS5_AUTH_NONE = 0, + TH_SOCKS5_AUTH_USER = 2, +}; + + +struct th_socks4_t +{ + uint8_t version; + uint8_t command; + in_port_t port; + in_addr_t addr; +} __attribute__((__packed__)); + + +struct th_socks4_res_t +{ + uint8_t nb; + uint8_t result; + in_port_t port; + in_addr_t addr; +} __attribute__((__packed__)); + + +static const char *th_proxy_types[] = +{ + "none", + "SOCKS 4", + "SOCKS 4a", + "SOCKS 5", + NULL +}; + + +static int th_get_socket_errno(void) +{ + return errno; +} + + +void th_conn_err(th_conn_t *conn, int err, const char *fmt, ...) +{ + if (conn->errfunc != NULL) + { + char *msg; + va_list ap; + va_start(ap, fmt); + msg = th_strdup_vprintf(fmt, ap); + va_end(ap); + + conn->errfunc(conn, err, msg); + th_free(msg); + } +} + + +void th_conn_msg(th_conn_t *conn, int loglevel, const char *fmt, ...) +{ + if (conn->msgfunc != NULL) + { + char *msg; + va_list ap; + va_start(ap, fmt); + msg = th_strdup_vprintf(fmt, ap); + va_end(ap); + + conn->msgfunc(conn, loglevel, msg); + th_free(msg); + } +} + + +struct hostent *th_resolve_host(th_conn_t *conn, const char *name) +{ + struct hostent *res = gethostbyname(name); + + if (res == NULL) + { + th_conn_err(conn, h_errno, + "Could not resolve hostname '%s': %s\n", + name, th_error_str(h_errno)); + } + else + { + th_conn_msg(conn, THLOG_INFO, "True hostname for %s is %s\n", + name, res->h_name); + } + + return res; +} + + +th_conn_t * th_conn_new( + void (*errfunc)(th_conn_t *conn, int err, const char *msg), + void (*msgfunc)(th_conn_t *conn, int loglevel, const char *msg)) +{ + th_conn_t *conn = th_malloc0(sizeof(th_conn_t)); + + if (conn == NULL) + return NULL; + + conn->errfunc = errfunc; + conn->msgfunc = msgfunc; + + return conn; +} + + +static BOOL th_get_addr(struct in_addr *addr, struct hostent *hst) +{ + if (hst != NULL) + { + *addr = *(struct in_addr *) (hst->h_addr); + return TRUE; + } + else + { + addr->s_addr = 0; + return FALSE; + } +} + + +int th_conn_set_proxy(th_conn_t *conn, int type, int port, const char *host, int auth_type) +{ + if (conn == NULL) + return -1; + + conn->proxy.type = type; + conn->proxy.port = port; + conn->proxy.auth_type = auth_type; + + th_free(conn->proxy.host); + conn->proxy.host = th_strdup(host); + + if (host != NULL) + { + conn->proxy.hst = th_resolve_host(conn, host); + th_get_addr(&(conn->proxy.addr), conn->proxy.hst); + } + else + return -2; + + return 0; +} + + +int th_conn_set_proxy_auth_user(th_conn_t *conn, const char *userid, const char *passwd) +{ + if (conn == NULL) + return -1; + + th_free(conn->proxy.userid); + conn->proxy.userid = th_strdup(userid); + + th_free(conn->proxy.passwd); + conn->proxy.passwd = th_strdup(passwd); + + return 0; +} + + +static BOOL th_conn_proxy_wait(th_conn_t *conn) +{ + int status, tries; + + for (status = tries = 1; tries <= 20 && status > 0; tries++) + { +#ifdef __WIN32 + Sleep(50); +#else + usleep(50000); +#endif + th_conn_reset(conn); + status = th_conn_pull(conn); + } + + if (status < 0) + { + th_conn_err(conn, status, "Proxy negotiation failed at try %d with network error: %d\n", + tries, status); + } + else + th_conn_err(conn, THERR_TIMED_OUT, "Proxy negotiation timed out.\n"); + + return status == FALSE; +} + + +static BOOL th_conn_proxy_send(th_conn_t *conn, void *buf, size_t bufsiz) +{ + BOOL ret; + th_conn_reset(conn); + ret = th_conn_send_buf(conn, buf, bufsiz); + th_free(buf); + return ret; +} + + +static int th_conn_socks4_negotiate(th_conn_t *conn, const int port, const char *host) +{ + struct th_socks4_res_t *sockres; + struct th_socks4_t *socksh; + size_t bufsiz; + uint8_t *ptr, *buf; + int err = THERR_INIT_FAIL; + + (void) host; + + th_conn_msg(conn, THLOG_INFO, "Initializing SOCKS 4/a proxy negotiation.\n"); + + bufsiz = sizeof(struct th_socks4_t) + strlen(conn->proxy.userid) + 1; + if (conn->proxy.type == TH_PROXY_SOCKS4A) + bufsiz += strlen(conn->host) + 1; + + if ((ptr = buf = th_malloc(bufsiz)) == NULL) + { + err = THERR_MALLOC; + th_conn_err(conn, err, + "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); + return err; + } + + // Create SOCKS 4 handshake + socksh = (struct th_socks4_t *) buf; + socksh->version = 4; + socksh->command = TH_PROXY_CMD_CONNECT; + socksh->port = htons(port); + socksh->addr = (conn->proxy.type == TH_PROXY_SOCKS4A) ? htonl(0x00000032) : conn->addr.s_addr; + ptr += sizeof(struct th_socks4_t); + + strcpy((char *) ptr, conn->proxy.userid); + + if (conn->proxy.type == TH_PROXY_SOCKS4A) + { + ptr += strlen(conn->proxy.userid) + 1; + strcpy((char *)ptr, conn->host); + } + + // Send request + if (!th_conn_proxy_send(conn, buf, bufsiz)) + return FALSE; + + // Wait for SOCKS server to reply + if (th_conn_proxy_wait(conn) != 0) + return FALSE; + + sockres = (struct th_socks4_res_t *) &(conn->buf); + if (sockres->nb != 0) + { + th_conn_err(conn, THERR_INIT_FAIL, + "Invalid SOCKS 4 server reply, does not begin with NUL byte (%d).\n", sockres->nb); + return FALSE; + } + if (sockres->result != 0x5a) + { + const char *s = NULL; + switch (sockres->result) + { + case 0x5b: s = "Request rejected or failed"; break; + case 0x5c: s = "Request failed because client is not running identd (or not reachable from the server)"; break; + case 0x5d: s = "Request failed because client's identd could not confirm the user ID string in the request"; break; + default: s = "Unknown SOCKS 4 error response"; break; + } + th_conn_err(conn, THERR_INIT_FAIL, + "SOCKS 4 setup failed, 0x%02x: %s.\n", + sockres->result, s); + return FALSE; + } + + return TRUE; +} + + +static int th_conn_socks5_negotiate(th_conn_t *conn, const int port, const char *host) +{ + size_t bufsiz, userid_len = 0, passwd_len = 0; + uint8_t *ptr, *buf; + int avail, auth; + + th_conn_msg(conn, THLOG_INFO, "Initializing SOCKS 5 proxy negotiation.\n"); + + switch (conn->proxy.auth_type) + { + case TH_PROXY_AUTH_NONE: + avail = 1; + break; + + case TH_PROXY_AUTH_USER: + avail = 2; + if (conn->proxy.userid == NULL || conn->proxy.passwd == NULL) + { + th_conn_err(conn, THERR_INVALID_DATA, + "SOCKS 5 user authentication chosen, but no user/pass set.\n"); + return FALSE; + } + + userid_len = strlen(conn->proxy.userid); + passwd_len = strlen(conn->proxy.passwd); + if (userid_len > 255 || passwd_len > 255) + { + th_conn_err(conn, THERR_INVALID_DATA, + "SOCKS 5 proxy userid or password is too long.\n"); + return FALSE; + } + break; + + default: + th_conn_err(conn, THERR_NOT_SUPPORTED, + "Unsupported proxy authentication method %d.\n", + conn->proxy.auth_type); + return FALSE; + } + + bufsiz = 2 + avail; + if ((ptr = buf = th_malloc(bufsiz)) == NULL) + { + th_conn_err(conn, THERR_MALLOC, + "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); + return FALSE; + } + + // Form handshake packet + *ptr++ = 0x05; // Protocol version + *ptr++ = avail; // # of available auth methods + switch (conn->proxy.auth_type) + { + case TH_PROXY_AUTH_NONE: + *ptr++ = TH_SOCKS5_AUTH_NONE; + + case TH_PROXY_AUTH_USER: + *ptr++ = TH_SOCKS5_AUTH_USER; + break; + } + + // Send request + if (!th_conn_proxy_send(conn, buf, bufsiz)) + return FALSE; + + // Wait for SOCKS server to reply + if (th_conn_proxy_wait(conn) != 0) + return FALSE; + + ptr = (uint8_t *) conn->buf; + if (*ptr != 0x05) + { + th_conn_err(conn, THERR_INVALID_DATA, + "Invalid SOCKS 5 server reply, does not begin with protocol version byte (%d).\n", *ptr); + return FALSE; + } + ptr++; + auth = *ptr; + + if (auth == 0xff) + { + th_conn_err(conn, THERR_NOT_SUPPORTED, + "No authentication method could be negotiated with the server.\n"); + return FALSE; + } + + if (auth == TH_SOCKS5_AUTH_USER) + { + // Attempt user/pass authentication (RFC 1929) + bufsiz = 1 + 1 + 1 + userid_len + passwd_len; + if ((ptr = buf = th_malloc(bufsiz)) == NULL) + { + th_conn_err(conn, THERR_MALLOC, + "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); + return FALSE; + } + + *ptr++ = 0x01; + + *ptr++ = userid_len; + memcpy(ptr, conn->proxy.userid, userid_len); + ptr += userid_len; + + *ptr++ = passwd_len; + memcpy(ptr, conn->proxy.passwd, passwd_len); + ptr += passwd_len; + + // Send request + if (!th_conn_proxy_send(conn, buf, bufsiz)) + return FALSE; + + // Wait for SOCKS server to reply + if (th_conn_proxy_wait(conn) != 0) + return FALSE; + + ptr = (uint8_t *) conn->buf; + if (*ptr != 0x01) + { + th_conn_err(conn, THERR_INVALID_DATA, + "Invalid SOCKS 5 server reply, does not begin with protocol version byte (%d).\n", *ptr); + return FALSE; + } + ptr++; + if (*ptr != 0) + { + th_conn_err(conn, THERR_AUTH_FAILED, + "SOCKS 5 proxy user/pass authentication failed! Code %d.\n", *ptr); + return FALSE; + } + } + else + if (auth != TH_SOCKS5_AUTH_NONE) + { + th_conn_err(conn, THERR_NOT_SUPPORTED, + "Proxy server chose an unsupported SOCKS 5 authentication method %d.\n", + auth); + return FALSE; + } + + +#if 0 + // Form client connection request packet + bufsiz = 4; + + if ((ptr = buf = th_malloc(bufsiz)) == NULL) + { + th_conn_err(conn, THERR_MALLOC, + "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); + return FALSE; + } + + *ptr++ = 0x05; // Protocol version + *ptr++ = cmd; + *ptr++ = 0x00; + *ptr++ = SOCKS5_ADDR_IPV4; + + memcpy(ptr, conn->addr.s_addr, sizeof(conn->addr.s_addr)); + ptr += sizeof(conn->addr.s_addr); + tmpPort = htons(port); + memcpy(ptr, tmpPort, sizeof(tmpPort)); + + + // Send request + if (!th_conn_proxy_send(conn, buf, bufsiz)) + return FALSE; + + // Wait for SOCKS server to reply + if (th_conn_proxy_wait(conn) != 0) + return FALSE; + + ptr = (uint8_t *) conn->buf; + if (*ptr != 0x05) + { + th_conn_err(conn, "Invalid SOCKS 5 server reply, does not begin with protocol version byte (%d).\n", *ptr); + return FALSE; + } + ptr++; + if (*ptr != 0) + { + if (*ptr >= 0 && *ptr < sizeof()) + th_conn_err(conn, "%s.\n", nn_socks5_results_msgs[*ptr]); + else + th_conn_err(conn, ".\n"); + return FALSE; + } + ptr++; + if (*ptr != 0) + { + th_conn_err(conn, ".\n"); + return FALSE; + } + +#endif + + return TRUE; +} + + +int th_conn_open(th_conn_t *conn, const int port, const char *host) +{ + struct sockaddr_in dest; + int err = THERR_INIT_FAIL; + + if (conn == NULL) + return THERR_NULLPTR; + + conn->port = port; + if (host != NULL) + { + conn->host = th_strdup(host); + conn->hst = th_resolve_host(conn, host); + } + + th_get_addr(&(conn->addr), conn->hst); + + // Prepare for connection + dest.sin_family = AF_INET; + + if (conn->proxy.type > TH_PROXY_NONE && conn->proxy.type < TH_PROXY_LAST) + { + // If using a proxy, we connect to the proxy server + dest.sin_port = htons(conn->proxy.port); + dest.sin_addr = conn->proxy.addr; + + th_conn_msg(conn, THLOG_INFO, "Connecting to %s proxy %s:%d ...\n", + th_proxy_types[conn->proxy.type], + inet_ntoa(conn->proxy.addr), conn->proxy.port); + } + else + { + dest.sin_port = htons(conn->port); + dest.sin_addr = conn->addr; + + th_conn_msg(conn, THLOG_INFO, "Connecting to %s:%d ...\n", + inet_ntoa(conn->addr), conn->port); + } + + if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) + { + err = th_errno_to_error(th_get_socket_errno()); + th_conn_err(conn, err, "Could not open socket: %s\n", th_error_str(err)); + goto error; + } + + if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) + { + err = th_errno_to_error(th_get_socket_errno()); + th_conn_err(conn, err, "Could not connect: %s\n", th_error_str(err)); + goto error; + } + + FD_ZERO(&(conn->sockfds)); + FD_SET(conn->socket, &(conn->sockfds)); + + // Proxy-specific setup + switch (conn->proxy.type) + { + case TH_PROXY_SOCKS4: + case TH_PROXY_SOCKS4A: + if (!th_conn_socks4_negotiate(conn, port, host)) + goto error; + th_conn_msg(conn, THLOG_INFO, "SOCKS 4 connection established!\n"); + break; + + case TH_PROXY_SOCKS5: + if (!th_conn_socks5_negotiate(conn, port, host)) + goto error; + th_conn_msg(conn, THLOG_INFO, "SOCKS 5 connection established!\n"); + break; + } + + th_conn_reset(conn); + conn->status = TH_CONN_OPEN; + + // Insert to connection list + conn->node = th_llist_append(&th_conn_list, conn); + + return THERR_OK; + +error: + th_conn_close(conn); + return err; +} + + +int th_conn_close(th_conn_t *conn) +{ + if (conn == NULL) + return -1; + + if (conn->socket >= 0) + { +#ifdef __WIN32 + closesocket(conn->socket); +#else + close(conn->socket); +#endif + conn->socket = -1; + } + + conn->status = TH_CONN_CLOSED; + return 0; +} + + +static void th_conn_free_nodelete(th_conn_t *conn) +{ + th_conn_close(conn); + th_free(conn->host); + th_free(conn->proxy.host); + th_free(conn); +} + + +void th_conn_free(th_conn_t *conn) +{ + if (conn != NULL) + { + // Remove from linked list + if (conn->node != NULL) + th_llist_delete_node_fast(&th_conn_list, conn->node); + + // Free connection data + th_conn_free_nodelete(conn); + } +} + + +BOOL th_conn_send_buf(th_conn_t *conn, const char *buf, const size_t len) +{ + size_t bufLeft = len; + const char *bufPtr = buf; + + while (bufLeft > 0) + { + ssize_t bufSent; + bufSent = send(conn->socket, bufPtr, bufLeft, 0); + if (bufSent < 0) + { + int err = th_errno_to_error(th_get_socket_errno()); + th_conn_err(conn, err, "th_conn_send_buf() failed: %s", th_error_str(err)); + return FALSE; + } + bufLeft -= bufSent; + bufPtr += bufSent; + } + + return TRUE; +} + + +void th_conn_reset(th_conn_t *conn) +{ + if (conn != NULL) + { + conn->ptr = conn->in_ptr = conn->buf; + conn->got_bytes = conn->total_bytes = 0; + } +} + + +int th_conn_pull(th_conn_t *conn) +{ + int result; + struct timeval socktv; + fd_set tmpfds; + + if (conn == NULL) + return TH_CONN_ERROR; + + // Shift the input buffer + if (conn->ptr > conn->buf) + { + size_t left = conn->in_ptr - conn->ptr; + if (left > 0) + { + size_t moved = conn->ptr - conn->buf; + memmove(conn->buf, conn->ptr, left); + conn->ptr = conn->buf; + conn->in_ptr -= moved; + conn->total_bytes -= moved; + } + else + th_conn_reset(conn); + } + + // Check for incoming data + socktv.tv_sec = 0; + socktv.tv_usec = TH_DELAY_USEC; + tmpfds = conn->sockfds; + + if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) + { + int err = th_get_socket_errno(); + if (err != EINTR) + { + err = th_errno_to_error(err); + th_conn_err(conn, err, "Error occured in select(%d, sockfds): %s\n", + socket, th_error_str(err)); + return TH_CONN_ERROR; + } + } + else if (FD_ISSET(conn->socket, &tmpfds)) + { + conn->got_bytes = recv(conn->socket, conn->in_ptr, TH_CONNBUF_SIZE - conn->total_bytes, 0); + if (conn->got_bytes < 0) + { + int err = th_errno_to_error(th_get_socket_errno()); + th_conn_err(conn, err, "Error in recv: %s\n", th_error_str(err)); + return TH_CONN_ERROR; + } + else if (conn->got_bytes == 0) + { + th_conn_err(conn, ECONNABORTED, "Server closed connection.\n"); + conn->status = TH_CONN_CLOSED; + return TH_CONN_CLOSED; + } + else + { + conn->total_bytes += conn->got_bytes; + conn->in_ptr += conn->got_bytes; + return TH_CONN_DATA_AVAIL; + } + } + + return TH_CONN_NO_DATA; +} + + +BOOL th_conn_check(th_conn_t *conn) +{ + if (conn == NULL) + return FALSE; + + return conn->err == 0 && conn->status == TH_CONN_OPEN; +} + + +int th_network_init(void) +{ +#ifdef __WIN32 + // Initialize WinSock, if needed + WSADATA wsaData; + int err = WSAStartup(0x0101, &wsaData); + if (err != 0) + { + THERR("Could not initialize WinSock library (err=%d).\n", err); + return THERR_INIT_FAIL; + } +#endif + + th_network_inited = TRUE; + + th_conn_list = NULL; + + return THERR_OK; +} + + +void th_network_close(void) +{ + if (th_network_inited) + { + // Close connections + qlist_t *curr = th_conn_list; + while (curr != NULL) + { + qlist_t *next = curr->next; + th_conn_free_nodelete(curr->data); + curr = next; + } + +#ifdef __WIN32 + WSACleanup(); +#endif + } + + th_network_inited = FALSE; +} + + +BOOL th_conn_buf_check(th_conn_t *conn, size_t n) +{ + return conn && (conn->ptr + n <= conn->in_ptr); +} + + +BOOL th_conn_buf_skip(th_conn_t *conn, size_t n) +{ + if (th_conn_buf_check(conn, n)) + { + conn->ptr += n; + return TRUE; + } + else + return FALSE; +} + + +int th_conn_buf_strncmp(th_conn_t *conn, const char *str, const size_t n) +{ + int ret; + if (!th_conn_buf_check(conn, n)) + return -1; + + if ((ret = strncmp(conn->ptr, str, n)) == 0) + { + conn->ptr += n; + return 0; + } + else + return ret; +} + + +int th_conn_buf_strcmp(th_conn_t *conn, const char *str) +{ + return th_conn_buf_strncmp(conn, str, strlen(str)); +} + + +char *th_conn_buf_strstr(th_conn_t *conn, const char *str) +{ + char *pos; + size_t n = strlen(str); + + if (th_conn_buf_check(conn, n) && ((pos = strstr(conn->ptr, str)) != NULL)) + { + conn->ptr = pos + n; + return pos; + } + else + return NULL; +} + + +void th_conn_dump_buffer(FILE *f, th_conn_t *conn) +{ + char *p; + size_t offs, left; + + fprintf(f, + "\n--------------------------------------------------------------\n" + "err=%d, status=%d, got_bytes=%d, total_bytes=%d\n" + "buf=0x%p, in_ptr=0x%04x, ptr=0x%04x\n", + conn->err, conn->status, conn->got_bytes, conn->total_bytes, + conn->buf, conn->in_ptr - conn->buf, conn->ptr - conn->buf); + + // Dump buffer contents as a hexdump + for (offs = 0, left = conn->total_bytes, p = conn->buf; p < conn->in_ptr;) + { + char buf[TH_DUMP_BYTES + 1]; + size_t bufoffs, amount = left < TH_DUMP_BYTES ? left : TH_DUMP_BYTES; + left -= amount; + + // Dump offs | xx xx xx xx | and fill string + fprintf(f, "%04x | ", offs); + for (bufoffs = 0; bufoffs < amount; offs++, bufoffs++, p++) + { + fprintf(f, "%02x ", *p); + buf[bufoffs] = th_isprint(*p) ? *p : '.'; + } + buf[bufoffs] = 0; + + // Add padding + for (; bufoffs < TH_DUMP_BYTES; bufoffs++) + fprintf(f, " "); + + // Print string + fprintf(f, "| %s\n", buf); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/th_network.h Sat Jun 21 02:41:29 2014 +0300 @@ -0,0 +1,155 @@ +/* + * Simple TCP network connection handling + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2013-2014 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#ifndef TH_NETWORK_H +#define TH_NETWORK_H + +#include <stdio.h> +#include <unistd.h> +#include "th_types.h" +#include "th_util.h" + + +#ifdef __WIN32 +#define __OBJC_BOOL // A nasty hack +#include <windows.h> +#include <winsock.h> +typedef uint16_t in_port_t; +typedef uint32_t in_addr_t; +#else +#include <sys/select.h> +#include <sys/socket.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global defines + */ +#define TH_CONNBUF_SIZE (64 * 1024) +#define TH_DELAY_USEC (15 * 1000) +#define TH_DUMP_BYTES 16 + + +enum +{ + TH_CONN_UNINIT = 0, + TH_CONN_PROXY_NEG, + TH_CONN_OPEN, + TH_CONN_CLOSED, + + TH_CONN_ERROR, + TH_CONN_DATA_AVAIL, + TH_CONN_NO_DATA, +}; + +enum +{ + TH_PROXY_NONE = 0, + TH_PROXY_SOCKS4, + TH_PROXY_SOCKS4A, + TH_PROXY_SOCKS5, + + TH_PROXY_LAST +}; + +enum +{ + TH_PROXY_AUTH_NONE, + TH_PROXY_AUTH_USER, +}; + +enum +{ + TH_PROXY_CMD_CONNECT = 1, + TH_PROXY_CMD_BIND = 2, + TH_PROXY_CMD_ASSOC_UDP = 3, +}; + + +typedef struct _th_conn_t +{ + // Proxy settings and data + struct + { + char *host; + struct hostent *hst; + int type, auth_type; + int port; + struct in_addr addr; + char *userid, *passwd; + } proxy; + + // Target host data + char *host; + struct hostent *hst; + int port; + + // Socket data + int socket; + struct in_addr addr; + fd_set sockfds; + + // Error handling and status message functors + void (*errfunc)(struct _th_conn_t *conn, int err, const char *msg); + void (*msgfunc)(struct _th_conn_t *conn, int loglevel, const char *msg); + + int err; + int status; + + // Data buffer + char buf[TH_CONNBUF_SIZE + 16]; + char *ptr, *in_ptr; + ssize_t got_bytes, total_bytes; + + void *node; +} th_conn_t; + + +int th_network_init(); +void th_network_close(void); + +struct hostent *th_resolve_host(th_conn_t *conn, const char *name); +th_conn_t * th_conn_new( + void (*errfunc)(th_conn_t *conn, int err, const char *msg), + void (*msgfunc)(th_conn_t *conn, int loglevel, const char *msg)); + +void th_conn_err(th_conn_t *conn, int err, const char *fmt, ...); +void th_conn_msg(th_conn_t *conn, int loglevel, const char *fmt, ...); + +int th_conn_set_proxy(th_conn_t *conn, int type, int port, const char *host, int auth_type); +int th_conn_set_proxy_auth_user(th_conn_t *conn, const char *userid, const char *passwd); +int th_conn_open(th_conn_t *conn, const int port, const char *host); +int th_conn_close(th_conn_t *); +void th_conn_free(th_conn_t *); +void th_conn_reset(th_conn_t *); + +int th_conn_pull(th_conn_t *); +BOOL th_conn_send_buf(th_conn_t *, const char *buf, const size_t len); +BOOL th_conn_check(th_conn_t *); + + +BOOL th_conn_buf_check(th_conn_t *conn, size_t n); +BOOL th_conn_buf_skip(th_conn_t *conn, size_t n); +int th_conn_buf_strncmp(th_conn_t *conn, const char *str, const size_t n); +int th_conn_buf_strcmp(th_conn_t *conn, const char *str); +char * th_conn_buf_strstr(th_conn_t *conn, const char *str); + +void th_conn_dump_buffer(FILE *f, th_conn_t *conn); + + +#ifdef __cplusplus +} +#endif +#endif /* TH_NETWORK_H */