Mercurial > hg > nnchat
diff libnnchat.c @ 352:b54c8545dcb0
Overhaul network code a bit, add initial implementation of SOCKS4/4A proxy support -- which may not work yet, it is untested.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 23 Jun 2011 06:28:40 +0300 |
parents | 8e509d6546d3 |
children | c01e42fc9adb |
line wrap: on
line diff
--- a/libnnchat.c Thu Jun 23 04:39:10 2011 +0300 +++ b/libnnchat.c Thu Jun 23 06:28:40 2011 +0300 @@ -53,8 +53,6 @@ void nn_conn_err(nn_conn_t *conn, const char *fmt, ...) { - conn->err = TRUE; - if (conn->errfunc) { va_list ap; va_start(ap, fmt); @@ -74,42 +72,153 @@ } } +static const char *nn_proxy_types[] = { + "none", + "SOCKS 4", + "SOCKS 4a", + NULL +}; -nn_conn_t *nn_conn_open(struct in_addr *addr, const int port) +nn_conn_t * nn_conn_open( + void (*errfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap), + void (*msgfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap), + int ptype, int pport, struct in_addr *paddr, + struct in_addr *addr, const int port, const char *host) { nn_conn_t *conn = th_calloc(1, sizeof(nn_conn_t)); struct sockaddr_in dest; - + static const char *userid = "James Bond"; + if (conn == NULL) return NULL; - dest.sin_family = AF_INET; - dest.sin_port = htons(port); - dest.sin_addr = *addr; + /* Initialize data */ + conn->errfunc = errfunc; + conn->msgfunc = msgfunc; + + conn->proxy.type = ptype; + conn->proxy.port = pport; + if (paddr != NULL) + conn->proxy.addr = *paddr; + + conn->port = port; + conn->addr = *addr; + + if (ptype == NN_PROXY_SOCKS4A && host == NULL) { + nn_conn_err(conn, "Host string NULL and proxy type SOCKS 4a specified. Using IP address instead.\n"); + conn->host = th_strdup(inet_ntoa(dest.sin_addr)); + } else + conn->host = th_strdup(host); - nn_conn_msg(conn, "Connecting to %s:%d ...\n", - inet_ntoa(dest.sin_addr), port); + + /* Prepare for connection */ + if (ptype > NN_PROXY_NONE && ptype < NN_PROXY_LAST) { + dest.sin_family = AF_INET; + dest.sin_port = htons(pport); + dest.sin_addr = conn->proxy.addr; + + nn_conn_msg(conn, "Connecting to %s proxy %s:%d ...\n", + nn_proxy_types[ptype], + inet_ntoa(dest.sin_addr), port); + } else { + dest.sin_family = AF_INET; + dest.sin_port = htons(port); + dest.sin_addr = *addr; + + nn_conn_msg(conn, "Connecting to %s:%d ...\n", + inet_ntoa(dest.sin_addr), port); + } if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) { - nn_conn_err(conn, "Could not open socket: %s\n", strerror(errno)); - conn->status = NN_CONN_CLOSED; - return conn; + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "Could not open socket: %s\n", nn_get_socket_errstr(conn->err)); + goto error; } nn_conn_msg(conn, "Using socket %d.\n", conn->socket); if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) { - nn_conn_err(conn, "Could not connect: %s\n", strerror(errno)); - conn->status = NN_CONN_CLOSED; - return conn; + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "Could not connect: %s\n", nn_get_socket_errstr(conn->err)); + goto error; } - conn->port = port; - conn->address = *addr; FD_ZERO(&(conn->sockfds)); FD_SET(conn->socket, &(conn->sockfds)); + + /* Proxy-specific setup */ + if (ptype == NN_PROXY_SOCKS4 || ptype == NN_PROXY_SOCKS4A) { + struct nn_socks_t *socksh; + size_t bufsiz = sizeof(struct nn_socks_t) + strlen(userid) + 1 + strlen(conn->host) + 1; + char *ptr, *buf; + int tries, status = -1; + + ptr = buf = th_malloc(bufsiz); + if (buf == NULL) { + conn->err = -1; + nn_conn_err(conn, "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); + goto error; + } + + /* Create SOCKS 4/4A request */ + nn_conn_msg(conn, "Initializing proxy negotiation.\n"); + socksh = (struct nn_socks_t *) buf; + socksh->version = (ptype == NN_PROXY_SOCKS4) ? 4 : 5; + socksh->command = SOCKS_CMD_CONNECT; + socksh->port = htons(port); + if (ptype == NN_PROXY_SOCKS4A) + socksh->addr = htonl(0x00000032); + else + socksh->addr = dest.sin_addr.s_addr; + ptr += sizeof(struct nn_socks_t); + + strcpy(ptr, userid); + ptr += strlen(userid) + 1; + + strcpy(ptr, conn->host); + + /* Send request */ + if (!nn_conn_send_buf(conn, buf, bufsiz)) { + th_free(buf); + nn_conn_err(conn, "Error sending SOCKS proxy request.\n"); + goto error; + } + th_free(buf); + + /* Wait for SOCKS server to reply */ + for (tries = 0; tries < 20; tries++) { + status = nn_conn_pull(conn); + if (status <= 0) break; + } + + /* Check results */ + if (status == 0) { + struct nn_socks_res_t *res = (struct nn_socks_res_t *) &(conn->buf); + if (res->nb != 0) { + nn_conn_err(conn, "Invalid SOCKS server reply, does not begin with NUL byte (%d).\n", res->nb); + goto error; + } + if (res->result != 0x5a) { + char *s = NULL; + switch (res->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 error response"; break; + } + nn_conn_err(conn, "SOCKS setup failed, 0x%02x: %s.\n", res->result, s); + goto error; + } + } + else + goto error; + } + conn->status = NN_CONN_OPEN; - + return conn; + +error: + conn->status = NN_CONN_CLOSED; return conn; } @@ -143,7 +252,8 @@ ssize_t bufSent; bufSent = send(conn->socket, bufPtr, bufLeft, 0); if (bufSent < 0) { - nn_conn_err(conn, "nn_conn_send_buf() failed: %s", strerror(errno)); + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "nn_conn_send_buf() failed: %s", nn_get_socket_errstr(conn->err)); return FALSE; } bufLeft -= bufSent; @@ -154,7 +264,7 @@ } -BOOL nn_conn_pull(nn_conn_t *conn) +int nn_conn_pull(nn_conn_t *conn) { int result; struct timeval socktv; @@ -171,19 +281,18 @@ tmpfds = conn->sockfds; if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) { - int err = nn_get_socket_errno(); - if (err != EINTR) { + conn->err = nn_get_socket_errno(); + if (conn->err != EINTR) { nn_conn_err(conn, "Error occured in select(%d, sockfds): %d, %s\n", - socket, err, nn_get_socket_errstr(err)); + socket, conn->err, nn_get_socket_errstr(conn->err)); return -1; } } else if (FD_ISSET(conn->socket, &tmpfds)) { conn->got = recv(conn->socket, conn->ptr, NN_CONNBUF_SIZE, 0); - if (conn->got < 0) { - int res = nn_get_socket_errno(); - nn_conn_err(conn, "Error in recv: %d, %s\n", res, nn_get_socket_errstr(res)); + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "Error in recv: %d, %s\n", conn->err, nn_get_socket_errstr(conn->err)); return -2; } else if (conn->got == 0) { nn_conn_err(conn, "Server closed connection.\n"); @@ -933,5 +1042,3 @@ th_free(tuple->str); th_free(tuple); } - -