diff options
Diffstat (limited to 'src/rexmpp_socks.c')
-rw-r--r-- | src/rexmpp_socks.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/rexmpp_socks.c b/src/rexmpp_socks.c new file mode 100644 index 0000000..48046bb --- /dev/null +++ b/src/rexmpp_socks.c @@ -0,0 +1,121 @@ +#include <memory.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> +#include <stdint.h> +#include <arpa/inet.h> + +#include "rexmpp_socks.h" + +enum socks_err +rexmpp_socks_proceed (rexmpp_socks_t *s) +{ + ssize_t ret; + if (s->io_state == REXMPP_SOCKS_WRITING) { + ssize_t ret = send(s->fd, s->buf + s->buf_sent, + s->buf_len - s->buf_sent, 0); + if (ret > 0) { + s->buf_sent += ret; + if (s->buf_len == s->buf_sent) { + s->buf_len = 0; + s->io_state = REXMPP_SOCKS_READING; + } + } else if (errno == EAGAIN) { + return REXMPP_SOCKS_E_AGAIN; + } else { + return REXMPP_SOCKS_E_TCP; + } + } else if (s->io_state == REXMPP_SOCKS_READING) { + ret = recv(s->fd, s->buf + s->buf_len, + REXMPP_SOCKS_BUF_LEN - s->buf_len, 0); + if (ret > 0) { + s->buf_len += ret; + if (s->buf[0] != 5) { + return REXMPP_SOCKS_E_VERSION; + } + if (s->buf_len >= 2) { + s->socks_error = s->buf[1]; + } + if (s->stage == REXMPP_SOCKS_AUTH) { + if (s->buf_len > 2) { + return REXMPP_SOCKS_E_REPLY; + } + if (s->buf_len == 2) { + if (s->socks_error != 0) { + return REXMPP_SOCKS_E_SOCKS; + } + /* It's okay to not authenticate, now we send a command. */ + s->buf[0] = 5; /* SOCKS version 5 */ + s->buf[1] = 1; /* Connect */ + s->buf[2] = 0; /* Reserved */ + s->buf[3] = 3; /* Domain name (todo: IP addresses) */ + size_t len = strlen(s->host); + s->buf[4] = len; + memcpy(s->buf + 5, s->host, len); + uint16_t port = htons(s->port); + memcpy(s->buf + 5 + len, &port, 2); + s->buf_len = 7 + len; + s->buf_sent = 0; + s->stage = REXMPP_SOCKS_CMD; + s->io_state = REXMPP_SOCKS_WRITING; + return rexmpp_socks_proceed(s); + } + } else if (s->stage == REXMPP_SOCKS_CMD) { + if (s->buf_len >= 5) { + size_t full_len = 6; + if (s->buf[3] == 1) { /* IPv4 */ + full_len += 4; + } else if (s->buf[3] == 3) { /* Domain name */ + full_len += s->buf[4] + 1; + } else if (s->buf[3] == 4) { /* IPv6 */ + full_len += 16; + } else { + return REXMPP_SOCKS_E_REPLY; + } + if (s->buf_len > full_len) { + return REXMPP_SOCKS_E_REPLY; + } + if (s->buf_len == full_len) { + if (s->socks_error != 0) { + return REXMPP_SOCKS_E_SOCKS; + } + /* We're done. */ + s->stage = REXMPP_SOCKS_DONE; + return REXMPP_SOCKS_CONNECTED; + } + } + } + } else if (errno == EAGAIN) { + return REXMPP_SOCKS_E_AGAIN; + } else { + return REXMPP_SOCKS_E_TCP; + } + } + return REXMPP_SOCKS_E_AGAIN; +} + +enum socks_err +rexmpp_socks_init (rexmpp_socks_t *s, + int fd, + const char *host, + uint16_t port) +{ + s->fd = fd; + s->host = host; + s->port = port; + s->socks_error = 0; + + if (strlen(host) > 255) { + return REXMPP_SOCKS_E_HOST; + } + + /* Request authentication. */ + s->stage = REXMPP_SOCKS_AUTH; + s->io_state = REXMPP_SOCKS_WRITING; + s->buf[0] = 5; /* SOCKS version 5 */ + s->buf[1] = 1; /* 1 supported method */ + s->buf[2] = 0; /* "no authentication required" */ + s->buf_len = 3; + s->buf_sent = 0; + return rexmpp_socks_proceed(s); +} |