0
0
mirror of https://github.com/darkk/redsocks.git synced 2025-08-29 05:05:30 +00:00

merge commits from upstream

This commit is contained in:
Bin Jin 2010-12-03 22:02:40 +08:00
commit 9d876181b3
12 changed files with 1212 additions and 235 deletions

View File

@ -3,7 +3,7 @@ CFLAGS=-std=gnu99 -Wall -g -O0
.PHONY: all
all: redsocks
obj = parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o
obj = parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o
src = $(patsubst %.o,%.c,$(obj))
redsocks: $(obj)

2
README
View File

@ -32,6 +32,8 @@ Login/password authentication is supported for SOCKS5/HTTPS connections.
SOCKS4 supports only username, password is ignored. for HTTPS, currently
only Basic and Digest scheme is supported.
Redirect UDP packets via SOCKS5 proxy server.
Redirect any HTTP connection to proxy that does not support transparent
proxying (e.g. old SQUID had broken `acl myport' for such connections).

4
main.c
View File

@ -29,12 +29,12 @@
extern app_subsys redsocks_subsys;
extern app_subsys base_subsys;
// extern app_subsys reddns_subsys;
extern app_subsys redudp_subsys;
app_subsys *subsystems[] = {
&redsocks_subsys,
&base_subsys,
// &reddns_subsys,
&redudp_subsys,
};
static const char *confname = "redsocks.conf";

View File

@ -19,7 +19,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
@ -41,26 +40,6 @@
static void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how);
/** simple fcntl(2) wrapper, provides errno and all logging to caller
* I have to use it in event-driven code because of accept(2) (see NOTES)
* and connect(2) (see ERRORS about EINPROGRESS)
*/
static int fcntl_nonblock(int fd)
{
int error;
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
error = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if (error)
return -1;
return 0;
}
extern relay_subsys http_connect_subsys;
extern relay_subsys http_relay_subsys;
extern relay_subsys socks4_subsys;
@ -73,7 +52,7 @@ static relay_subsys *relay_subsystems[] =
&socks5_subsys,
};
list_head instances = LIST_HEAD_INIT(instances);
static list_head instances = LIST_HEAD_INIT(instances);
static parser_entry redsocks_entries[] =
{
@ -169,9 +148,10 @@ static parser_section redsocks_conf_section =
.onexit = redsocks_onexit
};
void redsocks_log_write(
void redsocks_log_write_plain(
const char *file, int line, const char *func, int do_errno,
redsocks_client *client, int priority, const char *orig_fmt, ...
const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr,
int priority, const char *orig_fmt, ...
) {
int saved_errno = errno;
struct evbuffer *fmt = evbuffer_new();
@ -183,15 +163,15 @@ void redsocks_log_write(
// no return, as I have to call va_start/va_end
}
if (!inet_ntop(client->clientaddr.sin_family, &client->clientaddr.sin_addr, clientaddr_str, sizeof(clientaddr_str)))
if (!inet_ntop(clientaddr->sin_family, &clientaddr->sin_addr, clientaddr_str, sizeof(clientaddr_str)))
strncpy(clientaddr_str, "???", sizeof(clientaddr_str));
if (!inet_ntop(client->destaddr.sin_family, &client->destaddr.sin_addr, destaddr_str, sizeof(destaddr_str)))
if (!inet_ntop(destaddr->sin_family, &destaddr->sin_addr, destaddr_str, sizeof(destaddr_str)))
strncpy(destaddr_str, "???", sizeof(destaddr_str));
if (fmt) {
evbuffer_add_printf(fmt, "[%s:%i->%s:%i]: %s",
clientaddr_str, ntohs(client->clientaddr.sin_port),
destaddr_str, ntohs(client->destaddr.sin_port),
clientaddr_str, ntohs(clientaddr->sin_port),
destaddr_str, ntohs(destaddr->sin_port),
orig_fmt);
}
@ -204,16 +184,6 @@ void redsocks_log_write(
va_end(ap);
}
static time_t redsocks_time(time_t *t)
{
time_t retval;
retval = time(t);
if (retval == ((time_t) -1))
log_errno(LOG_WARNING, "time");
return retval;
}
void redsocks_touch_client(redsocks_client *client)
{
redsocks_time(&client->last_event);
@ -382,15 +352,9 @@ static void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffe
// I assume that -1 is invalid errno value
static int redsocks_socket_geterrno(redsocks_client *client, struct bufferevent *buffev)
{
int error;
int pseudo_errno;
size_t optlen = sizeof(pseudo_errno);
assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write));
error = getsockopt(EVENT_FD(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, (socklen_t*)&optlen);
if (error) {
redsocks_log_errno(client, LOG_ERR, "getsockopt");
int pseudo_errno = red_socket_geterrno(buffev);
if (pseudo_errno == -1) {
redsocks_log_errno(client, LOG_ERR, "red_socket_geterrno");
return -1;
}
return pseudo_errno;
@ -417,14 +381,9 @@ static void redsocks_event_error(struct bufferevent *buffev, short what, void *_
}
else {
errno = redsocks_socket_geterrno(client, buffev);
redsocks_log_errno(client, LOG_NOTICE, "%s error, code %s|%s|%s|%s|%s == %X",
redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str,
buffev == client->relay ? "relay" : "client",
what & EVBUFFER_READ ? "EVBUFFER_READ" : "0",
what & EVBUFFER_WRITE ? "EVBUFFER_WRITE" : "0",
what & EVBUFFER_EOF ? "EVBUFFER_EOF" : "0",
what & EVBUFFER_ERROR ? "EVBUFFER_ERROR" : "0",
what & EVBUFFER_TIMEOUT ? "EVBUFFER_TIMEOUT" : "0",
what);
event_fmt(what));
redsocks_drop_client(client);
}
}
@ -478,27 +437,41 @@ fail:
return retval;
}
void redsocks_write_helper_ex(
int redsocks_write_helper_ex(
struct bufferevent *buffev, redsocks_client *client,
redsocks_message_maker mkmessage, int state, size_t wm_low, size_t wm_high)
{
assert(client);
return redsocks_write_helper_ex_plain(buffev, client, (redsocks_message_maker_plain)mkmessage,
client, state, wm_low, wm_high);
}
int redsocks_write_helper_ex_plain(
struct bufferevent *buffev, redsocks_client *client,
redsocks_message_maker_plain mkmessage, void *p, int state, size_t wm_low, size_t wm_high)
{
int len;
struct evbuffer *buff = NULL;
int drop = 1;
if (mkmessage) {
buff = mkmessage(client);
buff = mkmessage(p);
if (!buff)
goto fail;
len = bufferevent_write_buffer(client->relay, buff);
assert(!client || buffev == client->relay);
len = bufferevent_write_buffer(buffev, buff);
if (len < 0) {
redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
if (client)
redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
else
log_errno(LOG_ERR, "bufferevent_write_buffer");
goto fail;
}
}
client->state = state;
if (client)
client->state = state;
buffev->wm_read.low = wm_low;
buffev->wm_read.high = wm_high;
bufferevent_enable(buffev, EV_READ);
@ -507,35 +480,29 @@ void redsocks_write_helper_ex(
fail:
if (buff)
evbuffer_free(buff);
if (drop)
if (drop && client)
redsocks_drop_client(client);
return drop ? -1 : 0;
}
void redsocks_write_helper(
int redsocks_write_helper(
struct bufferevent *buffev, redsocks_client *client,
redsocks_message_maker mkmessage, int state, size_t wm_only)
{
redsocks_write_helper_ex(buffev, client, mkmessage, state, wm_only, wm_only);
assert(client);
return redsocks_write_helper_ex(buffev, client, mkmessage, state, wm_only, wm_only);
}
static void redsocks_relay_connected(struct bufferevent *buffev, void *_arg)
{
redsocks_client *client = _arg;
int pseudo_errno;
assert(buffev == client->relay);
redsocks_touch_client(client);
pseudo_errno = redsocks_socket_geterrno(client, buffev);
if (pseudo_errno == -1) {
redsocks_log_errno(client, LOG_NOTICE, "redsocks_socket_geterrno");
goto fail;
}
if (pseudo_errno) {
errno = pseudo_errno;
redsocks_log_errno(client, LOG_NOTICE, "connect");
if (!red_is_socket_connected_ok(buffev)) {
redsocks_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok");
goto fail;
}
@ -550,53 +517,12 @@ fail:
void redsocks_connect_relay(redsocks_client *client)
{
int on = 1;
int relay_fd = -1;
int error;
relay_fd = socket(AF_INET, SOCK_STREAM, 0);
if (relay_fd == -1) {
redsocks_log_errno(client, LOG_ERR, "socket");
goto fail;
}
error = fcntl_nonblock(relay_fd);
if (error) {
redsocks_log_errno(client, LOG_ERR, "fcntl");
goto fail;
}
error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
if (error) {
redsocks_log_errno(client, LOG_WARNING, "setsockopt");
goto fail;
}
error = connect(relay_fd, (struct sockaddr*)&client->instance->config.relayaddr, sizeof(client->instance->config.relayaddr));
if (error && errno != EINPROGRESS) {
redsocks_log_errno(client, LOG_NOTICE, "connect");
goto fail;
}
client->relay = bufferevent_new(relay_fd, NULL, redsocks_relay_connected, redsocks_event_error, client);
client->relay = red_connect_relay(&client->instance->config.relayaddr,
redsocks_relay_connected, redsocks_event_error, client);
if (!client->relay) {
redsocks_log_errno(client, LOG_ERR, "bufferevent_new");
goto fail;
redsocks_log_errno(client, LOG_ERR, "red_connect_relay");
redsocks_drop_client(client);
}
relay_fd = -1;
error = bufferevent_enable(client->relay, EV_WRITE); // we wait for connection...
if (error) {
redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
goto fail;
}
return; // OK
fail:
if (relay_fd != -1)
close(relay_fd);
redsocks_drop_client(client);
}
static void redsocks_accept_client(int fd, short what, void *_arg)

View File

@ -53,4 +53,29 @@ redsocks {
// password = "baz";
}
// you can add one more `redsocks' section if you need.
redudp {
// `local_ip' should not be 0.0.0.0 as it's also used for outgoing
// packets that are sent as replies - and it should be fixed
// if we want NAT to work properly.
local_ip = 127.0.0.1;
local_port = 10053;
// `ip' and `port' of socks5 proxy server.
ip = 10.0.0.1;
port = 1080;
login = username;
password = pazzw0rd;
// kernel does not give us this information, so we have to duplicate it
// in both iptables rules and configuration file. By the way, you can
// set `local_ip' to 127.45.67.89 if you need more than 65535 ports to
// forward ;-)
// This limitation may be relaxed in future versions using contrack-tools.
dest_ip = 8.8.8.8;
dest_port = 53;
udp_timeout = 30;
udp_timeout_stream = 180;
}
// you can add more `redsocks' and `redudp' sections if you need.

View File

@ -6,12 +6,6 @@
#include <event.h>
#include "list.h"
#if defined __GNUC__
#define PACKED __attribute__((packed))
#else
#error Unknown compiler, modify types.h for it
#endif
struct redsocks_client_t;
struct redsocks_instance_t;
@ -73,18 +67,32 @@ int sizes_greater_equal(size_t a, size_t b);
int redsocks_read_expected(redsocks_client *client, struct evbuffer *input, void *data, size_comparator comparator, size_t expected);
typedef struct evbuffer* (*redsocks_message_maker)(redsocks_client *client);
typedef struct evbuffer* (*redsocks_message_maker_plain)(void *p);
struct evbuffer *mkevbuffer(void *data, size_t len);
void redsocks_write_helper_ex(
/* Yahoo! This code is ex-plain! :-D */
int redsocks_write_helper_ex_plain(
struct bufferevent *buffev, redsocks_client *client,
redsocks_message_maker_plain mkmessage, void *p, int state, size_t wm_low, size_t wm_high);
int redsocks_write_helper_ex(
struct bufferevent *buffev, redsocks_client *client,
redsocks_message_maker mkmessage, int state, size_t wm_low, size_t wm_high);
void redsocks_write_helper(
int redsocks_write_helper(
struct bufferevent *buffev, redsocks_client *client,
redsocks_message_maker mkmessage, int state, size_t wm_only);
#define redsocks_log_error(client, prio, msg...) redsocks_log_write(__FILE__, __LINE__, __func__, 0, client, prio, ## msg)
#define redsocks_log_errno(client, prio, msg...) redsocks_log_write(__FILE__, __LINE__, __func__, 1, client, prio, ## msg)
void redsocks_log_write(const char *file, int line, const char *func, int do_errno, redsocks_client *client, int priority, const char *fmt, ...);
#define redsocks_log_error(client, prio, msg...) \
redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, &(client)->destaddr, prio, ## msg)
#define redsocks_log_errno(client, prio, msg...) \
redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, &(client)->destaddr, prio, ## msg)
void redsocks_log_write_plain(
const char *file, int line, const char *func, int do_errno,
const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr,
int priority, const char *fmt, ...)
#if defined(__GNUC__)
__attribute__ (( format (printf, 8, 9) ))
#endif
;
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */

749
redudp.c Normal file
View File

@ -0,0 +1,749 @@
/* redsocks - transparent TCP-to-proxy redirector
* Copyright (C) 2007-2008 Leonid Evdokimov <leon@darkk.net.ru>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <assert.h>
#include "list.h"
#include "log.h"
#include "socks5.h"
#include "parser.h"
#include "main.h"
#include "redsocks.h"
#include "redudp.h"
#define redudp_log_error(client, prio, msg...) \
redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, &(client)->instance->config.destaddr, prio, ## msg)
#define redudp_log_errno(client, prio, msg...) \
redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, &(client)->instance->config.destaddr, prio, ## msg)
static void redudp_pkt_from_socks(int fd, short what, void *_arg);
static void redudp_drop_client(redudp_client *client);
static void redudp_fini_instance(redudp_instance *instance);
static int redudp_fini();
typedef struct redudp_expected_assoc_reply_t {
socks5_reply h;
socks5_addr_ipv4 ip;
} PACKED redudp_expected_assoc_reply;
/***********************************************************************
* Helpers
*/
static void redudp_fill_preamble(socks5_udp_preabmle *preamble, redudp_client *client)
{
preamble->reserved = 0;
preamble->frag_no = 0; /* fragmentation is not supported */
preamble->addrtype = socks5_addrtype_ipv4;
preamble->ip.addr = client->instance->config.destaddr.sin_addr.s_addr;
preamble->ip.port = client->instance->config.destaddr.sin_port;
}
static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p)
{
int *do_password = p;
return socks5_mkmethods_plain(*do_password);
}
static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p)
{
redudp_instance *self = p;
return socks5_mkpassword_plain(self->config.login, self->config.password);
}
static struct evbuffer* socks5_mkassociate(void *p)
{
struct sockaddr_in sa;
p = p; /* Make compiler happy */
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa);
}
static int recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr)
{
socklen_t addrlen = sizeof(*inaddr);
ssize_t pktlen;
pktlen = recvfrom(fd, buf, buflen, 0, (struct sockaddr*)inaddr, &addrlen);
if (pktlen == -1) {
log_errno(LOG_WARNING, "recvfrom");
return -1;
}
if (addrlen != sizeof(*inaddr)) {
log_error(LOG_WARNING, "unexpected address length %u instead of %u", addrlen, sizeof(*inaddr));
return -1;
}
if (pktlen >= buflen) {
char buf[INET6_ADDRSTRLEN];
const char *addr = inet_ntop(inaddr->sin_family, &inaddr->sin_addr, buf, sizeof(buf));
log_error(LOG_WARNING, "wow! Truncated udp packet of size %u from %s:%u! impossible! dropping it...",
pktlen, addr ? addr : "?", ntohs(inaddr->sin_port));
return -1;
}
return pktlen;
}
/***********************************************************************
* Logic
*/
static void redudp_drop_client(redudp_client *client)
{
int fd;
redudp_log_error(client, LOG_INFO, "Dropping...");
enqueued_packet *q, *tmp;
if (event_initialized(&client->timeout)) {
if (event_del(&client->timeout) == -1)
redudp_log_errno(client, LOG_ERR, "event_del");
}
if (client->relay) {
fd = EVENT_FD(&client->relay->ev_read);
bufferevent_free(client->relay);
shutdown(fd, SHUT_RDWR);
close(fd);
}
if (event_initialized(&client->udprelay)) {
fd = EVENT_FD(&client->udprelay);
if (event_del(&client->udprelay) == -1)
redudp_log_errno(client, LOG_ERR, "event_del");
close(fd);
}
list_for_each_entry_safe(q, tmp, &client->queue, list) {
list_del(&q->list);
free(q);
}
list_del(&client->list);
free(client);
}
static void redudp_bump_timeout(redudp_client *client)
{
struct timeval tv;
tv.tv_sec = client->instance->config.udp_timeout;
tv.tv_usec = 0;
// TODO: implement udp_timeout_stream
if (event_add(&client->timeout, &tv) != 0) {
redudp_log_error(client, LOG_WARNING, "event_add(&client->timeout, ...)");
redudp_drop_client(client);
}
}
static void redudp_forward_pkt(redudp_client *client, char *buf, size_t pktlen)
{
socks5_udp_preabmle req;
struct msghdr msg;
struct iovec io[2];
ssize_t outgoing, fwdlen = pktlen + sizeof(req);
redudp_fill_preamble(&req, client);
memset(&msg, 0, sizeof(msg));
msg.msg_name = &client->udprelayaddr;
msg.msg_namelen = sizeof(client->udprelayaddr);
msg.msg_iov = io;
msg.msg_iovlen = SIZEOF_ARRAY(io);
io[0].iov_base = &req;
io[0].iov_len = sizeof(req);
io[1].iov_base = buf;
io[1].iov_len = pktlen;
outgoing = sendmsg(EVENT_FD(&client->udprelay), &msg, 0);
if (outgoing == -1) {
redudp_log_errno(client, LOG_WARNING, "sendmsg: Can't forward packet, dropping it");
return;
}
else if (outgoing != fwdlen) {
redudp_log_error(client, LOG_WARNING, "sendmsg: I was sending %u bytes, but only %u were sent.", fwdlen, outgoing);
return;
}
}
static int redudp_enqeue_pkt(redudp_client *client, char *buf, size_t pktlen)
{
enqueued_packet *q = NULL;
redudp_log_error(client, LOG_DEBUG, "<trace>");
if (client->queue_len >= client->instance->config.max_pktqueue) {
redudp_log_error(client, LOG_WARNING, "There are already %u packets in queue. Dropping.",
client->queue_len);
return -1;
}
q = calloc(1, sizeof(enqueued_packet) + pktlen);
if (!q) {
redudp_log_errno(client, LOG_ERR, "Can't enqueue packet: calloc");
return -1;
}
q->len = pktlen;
memcpy(q->data, buf, pktlen);
client->queue_len += 1;
list_add_tail(&q->list, &client->queue);
return 0;
}
static void redudp_flush_queue(redudp_client *client)
{
enqueued_packet *q, *tmp;
redudp_log_error(client, LOG_INFO, "Starting UDP relay");
list_for_each_entry_safe(q, tmp, &client->queue, list) {
redudp_forward_pkt(client, q->data, q->len);
list_del(&q->list);
free(q);
}
client->queue_len = 0;
assert(list_empty(&client->queue));
}
static void redudp_read_assoc_reply(struct bufferevent *buffev, void *_arg)
{
redudp_client *client = _arg;
redudp_expected_assoc_reply reply;
int read = evbuffer_remove(buffev->input, &reply, sizeof(reply));
int fd = -1;
int error;
redudp_log_error(client, LOG_DEBUG, "<trace>");
if (read != sizeof(reply)) {
redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %u",
read, sizeof(reply));
goto fail;
}
if (reply.h.ver != socks5_ver) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver);
goto fail;
}
if (reply.h.status != socks5_status_succeeded) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)",
socks5_status_to_str(reply.h.status), reply.h.status);
goto fail;
}
if (reply.h.addrtype != socks5_addrtype_ipv4) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u",
reply.h.addrtype);
goto fail;
}
client->udprelayaddr.sin_family = AF_INET;
client->udprelayaddr.sin_port = reply.ip.port;
client->udprelayaddr.sin_addr.s_addr = reply.ip.addr;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
redudp_log_errno(client, LOG_ERR, "socket");
goto fail;
}
error = connect(fd, (struct sockaddr*)&client->udprelayaddr, sizeof(client->udprelayaddr));
if (error) {
redudp_log_errno(client, LOG_NOTICE, "connect");
goto fail;
}
event_set(&client->udprelay, fd, EV_READ | EV_PERSIST, redudp_pkt_from_socks, client);
error = event_add(&client->udprelay, NULL);
if (error) {
redudp_log_errno(client, LOG_ERR, "event_add");
goto fail;
}
redudp_flush_queue(client);
// TODO: bufferevent_disable ?
return;
fail:
if (fd != -1)
close(fd);
redudp_drop_client(client);
}
static void redudp_read_auth_reply(struct bufferevent *buffev, void *_arg)
{
redudp_client *client = _arg;
socks5_auth_reply reply;
int read = evbuffer_remove(buffev->input, &reply, sizeof(reply));
int error;
redudp_log_error(client, LOG_DEBUG, "<trace>");
if (read != sizeof(reply)) {
redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %u",
read, sizeof(reply));
goto fail;
}
if (reply.ver != socks5_password_ver || reply.status != socks5_password_passed) {
redudp_log_error(client, LOG_NOTICE, "Socks5 authentication error. Version: %u, error code: %u",
reply.ver, reply.status);
goto fail;
}
error = redsocks_write_helper_ex_plain(
client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */
sizeof(redudp_expected_assoc_reply), sizeof(redudp_expected_assoc_reply));
if (error)
goto fail;
client->relay->readcb = redudp_read_assoc_reply;
return;
fail:
redudp_drop_client(client);
}
static void redudp_read_auth_methods(struct bufferevent *buffev, void *_arg)
{
redudp_client *client = _arg;
int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password);
socks5_method_reply reply;
int read = evbuffer_remove(buffev->input, &reply, sizeof(reply));
const char *error = NULL;
int ierror = 0;
redudp_log_error(client, LOG_DEBUG, "<trace>");
if (read != sizeof(reply)) {
redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %u",
read, sizeof(reply));
goto fail;
}
error = socks5_is_known_auth_method(&reply, do_password);
if (error) {
redudp_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error);
goto fail;
}
else if (reply.method == socks5_auth_none) {
ierror = redsocks_write_helper_ex_plain(
client->relay, NULL, socks5_mkassociate, NULL, 0, /* last two are ignored */
sizeof(redudp_expected_assoc_reply), sizeof(redudp_expected_assoc_reply));
if (ierror)
goto fail;
client->relay->readcb = redudp_read_assoc_reply;
}
else if (reply.method == socks5_auth_password) {
ierror = redsocks_write_helper_ex_plain(
client->relay, NULL, socks5_mkpassword_plain_wrapper, client->instance, 0, /* last one is ignored */
sizeof(socks5_auth_reply), sizeof(socks5_auth_reply));
if (ierror)
goto fail;
client->relay->readcb = redudp_read_auth_reply;
}
return;
fail:
redudp_drop_client(client);
}
static void redudp_relay_connected(struct bufferevent *buffev, void *_arg)
{
redudp_client *client = _arg;
int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password);
int error;
redudp_log_error(client, LOG_DEBUG, "<trace>");
if (!red_is_socket_connected_ok(buffev)) {
redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok");
goto fail;
}
error = redsocks_write_helper_ex_plain(
client->relay, NULL, socks5_mkmethods_plain_wrapper, &do_password, 0 /* does not matter */,
sizeof(socks5_method_reply), sizeof(socks5_method_reply));
if (error)
goto fail;
client->relay->readcb = redudp_read_auth_methods;
client->relay->writecb = 0;
//bufferevent_disable(buffev, EV_WRITE); // I don't want to check for writeability.
return;
fail:
redudp_drop_client(client);
}
static void redudp_relay_error(struct bufferevent *buffev, short what, void *_arg)
{
redudp_client *client = _arg;
// TODO: FIXME: Implement me
redudp_log_error(client, LOG_NOTICE, "redudp_relay_error");
redudp_drop_client(client);
}
static void redudp_timeout(int fd, short what, void *_arg)
{
redudp_client *client = _arg;
redudp_log_error(client, LOG_INFO, "Client timeout. First: %u, last_client: %u, last_relay: %u.",
client->first_event, client->last_client_event, client->last_relay_event);
redudp_drop_client(client);
}
static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, char *buf, size_t pktlen)
{
redudp_client *client = calloc(1, sizeof(*client));
if (!client) {
log_errno(LOG_WARNING, "calloc");
return;
}
INIT_LIST_HEAD(&client->list);
INIT_LIST_HEAD(&client->queue);
client->instance = self;
memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr));
timeout_set(&client->timeout, redudp_timeout, client);
// XXX: self->relay_ss->init(client);
client->relay = red_connect_relay(&client->instance->config.relayaddr,
redudp_relay_connected, redudp_relay_error, client);
if (!client->relay)
goto fail;
if (redsocks_time(&client->first_event) == (time_t)-1)
goto fail;
client->last_client_event = client->first_event;
redudp_bump_timeout(client);
if (redudp_enqeue_pkt(client, buf, pktlen) == -1)
goto fail;
list_add(&client->list, &self->clients);
redudp_log_error(client, LOG_INFO, "got 1st packet from client");
return;
fail:
redudp_drop_client(client);
}
static void redudp_pkt_from_socks(int fd, short what, void *_arg)
{
redudp_client *client = _arg;
union {
char buf[0xFFFF];
socks5_udp_preabmle header;
} pkt;
ssize_t pktlen, fwdlen, outgoing;
struct sockaddr_in udprelayaddr;
assert(fd == EVENT_FD(&client->udprelay));
pktlen = recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr);
if (pktlen == -1) {
redudp_log_errno(client, LOG_WARNING, "recv_udp_pkt");
return;
}
if (memcmp(&udprelayaddr, &client->udprelayaddr, sizeof(udprelayaddr)) != 0) {
char buf[INET6_ADDRSTRLEN];
const char *addr = inet_ntop(udprelayaddr.sin_family, &udprelayaddr.sin_addr, buf, sizeof(buf));
redudp_log_error(client, LOG_NOTICE, "Got packet from unexpected address %s:%u.",
addr ? addr : "?", ntohs(udprelayaddr.sin_port));
return;
}
if (pkt.header.frag_no != 0) {
// FIXME: does anybody need it?
redudp_log_error(client, LOG_WARNING, "Got fragment #%u. Packet fragmentation is not supported!",
pkt.header.frag_no);
return;
}
if (pkt.header.addrtype != socks5_addrtype_ipv4) {
redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).",
pkt.header.addrtype, socks5_addrtype_ipv4);
return;
}
if (pkt.header.ip.port != client->instance->config.destaddr.sin_port ||
pkt.header.ip.addr != client->instance->config.destaddr.sin_addr.s_addr)
{
char buf[INET6_ADDRSTRLEN];
const char *addr = inet_ntop(AF_INET, &pkt.header.ip.addr, buf, sizeof(buf));
redudp_log_error(client, LOG_NOTICE, "Socks5 server relayed packet from unexpected address %s:%u.",
addr ? addr : "?", ntohs(pkt.header.ip.port));
return;
}
redsocks_time(&client->last_relay_event);
redudp_bump_timeout(client);
fwdlen = pktlen - sizeof(pkt.header);
outgoing = sendto(EVENT_FD(&client->instance->listener),
pkt.buf + sizeof(pkt.header), fwdlen, 0,
(struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr));
if (outgoing != fwdlen) {
redudp_log_error(client, LOG_WARNING, "sendto: I was sending %d bytes, but only %d were sent.",
fwdlen, outgoing);
return;
}
}
static void redudp_pkt_from_client(int fd, short what, void *_arg)
{
redudp_instance *self = _arg;
struct sockaddr_in clientaddr;
char buf[0xFFFF]; // UDP packet can't be larger then that
ssize_t pktlen;
redudp_client *tmp, *client = NULL;
assert(fd == EVENT_FD(&self->listener));
pktlen = recv_udp_pkt(fd, buf, sizeof(buf), &clientaddr);
if (pktlen == -1) {
return;
}
// TODO: this lookup may be SLOOOOOW.
list_for_each_entry(tmp, &self->clients, list) {
if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) {
client = tmp;
break;
}
}
if (client) {
redsocks_time(&client->last_client_event);
redudp_bump_timeout(client);
if (event_initialized(&client->udprelay)) {
redudp_forward_pkt(client, buf, pktlen);
}
else {
redudp_enqeue_pkt(client, buf, pktlen);
}
}
else {
redudp_first_pkt_from_client(self, &clientaddr, buf, pktlen);
}
}
/***********************************************************************
* Init / shutdown
*/
static parser_entry redudp_entries[] =
{
{ .key = "local_ip", .type = pt_in_addr },
{ .key = "local_port", .type = pt_uint16 },
{ .key = "ip", .type = pt_in_addr },
{ .key = "port", .type = pt_uint16 },
{ .key = "login", .type = pt_pchar },
{ .key = "password", .type = pt_pchar },
{ .key = "dest_ip", .type = pt_in_addr },
{ .key = "dest_port", .type = pt_uint16 },
{ .key = "udp_timeout", .type = pt_uint16 },
{ .key = "udp_timeout_stream", .type = pt_uint16 },
{ }
};
static list_head instances = LIST_HEAD_INIT(instances);
static int redudp_onenter(parser_section *section)
{
redudp_instance *instance = calloc(1, sizeof(*instance));
if (!instance) {
parser_error(section->context, "Not enough memory");
return -1;
}
INIT_LIST_HEAD(&instance->list);
INIT_LIST_HEAD(&instance->clients);
instance->config.bindaddr.sin_family = AF_INET;
instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
instance->config.relayaddr.sin_family = AF_INET;
instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
instance->config.destaddr.sin_family = AF_INET;
instance->config.destaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
instance->config.max_pktqueue = 5;
instance->config.udp_timeout = 30;
instance->config.udp_timeout_stream = 180;
for (parser_entry *entry = &section->entries[0]; entry->key; entry++)
entry->addr =
(strcmp(entry->key, "local_ip") == 0) ? &instance->config.bindaddr.sin_addr :
(strcmp(entry->key, "local_port") == 0) ? &instance->config.bindaddr.sin_port :
(strcmp(entry->key, "ip") == 0) ? &instance->config.relayaddr.sin_addr :
(strcmp(entry->key, "port") == 0) ? &instance->config.relayaddr.sin_port :
(strcmp(entry->key, "login") == 0) ? &instance->config.login :
(strcmp(entry->key, "password") == 0) ? &instance->config.password :
(strcmp(entry->key, "dest_ip") == 0) ? &instance->config.destaddr.sin_addr :
(strcmp(entry->key, "dest_port") == 0) ? &instance->config.destaddr.sin_port :
(strcmp(entry->key, "max_pktqueue") == 0) ? &instance->config.max_pktqueue :
(strcmp(entry->key, "udp_timeout") == 0) ? &instance->config.udp_timeout:
(strcmp(entry->key, "udp_timeout_stream") == 0) ? &instance->config.udp_timeout_stream :
NULL;
section->data = instance;
return 0;
}
static int redudp_onexit(parser_section *section)
{
redudp_instance *instance = section->data;
section->data = NULL;
for (parser_entry *entry = &section->entries[0]; entry->key; entry++)
entry->addr = NULL;
instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port);
instance->config.relayaddr.sin_port = htons(instance->config.relayaddr.sin_port);
instance->config.destaddr.sin_port = htons(instance->config.destaddr.sin_port);
if (instance->config.udp_timeout_stream < instance->config.udp_timeout) {
parser_error(section->context, "udp_timeout_stream should be not less then udp_timeout");
return -1;
}
list_add(&instance->list, &instances);
return 0;
}
static int redudp_init_instance(redudp_instance *instance)
{
/* FIXME: redudp_fini_instance is called in case of failure, this
* function will remove instance from instances list - result
* looks ugly.
*/
int error;
int fd = -1;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
log_errno(LOG_ERR, "socket");
goto fail;
}
error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr));
if (error) {
log_errno(LOG_ERR, "bind");
goto fail;
}
error = fcntl_nonblock(fd);
if (error) {
log_errno(LOG_ERR, "fcntl");
goto fail;
}
event_set(&instance->listener, fd, EV_READ | EV_PERSIST, redudp_pkt_from_client, instance);
error = event_add(&instance->listener, NULL);
if (error) {
log_errno(LOG_ERR, "event_add");
goto fail;
}
fd = -1;
return 0;
fail:
redudp_fini_instance(instance);
if (fd != -1) {
if (close(fd) != 0)
log_errno(LOG_WARNING, "close");
}
return -1;
}
/* Drops instance completely, freeing its memory and removing from
* instances list.
*/
static void redudp_fini_instance(redudp_instance *instance)
{
if (!list_empty(&instance->clients)) {
redudp_client *tmp, *client = NULL;
log_error(LOG_WARNING, "There are connected clients during shutdown! Disconnecting them.");
list_for_each_entry_safe(client, tmp, &instance->clients, list) {
redudp_drop_client(client);
}
}
if (event_initialized(&instance->listener)) {
if (event_del(&instance->listener) != 0)
log_errno(LOG_WARNING, "event_del");
if (close(EVENT_FD(&instance->listener)) != 0)
log_errno(LOG_WARNING, "close");
memset(&instance->listener, 0, sizeof(instance->listener));
}
list_del(&instance->list);
free(instance->config.login);
free(instance->config.password);
memset(instance, 0, sizeof(*instance));
free(instance);
}
static int redudp_init()
{
redudp_instance *tmp, *instance = NULL;
// TODO: init debug_dumper
list_for_each_entry_safe(instance, tmp, &instances, list) {
if (redudp_init_instance(instance) != 0)
goto fail;
}
return 0;
fail:
redudp_fini();
return -1;
}
static int redudp_fini()
{
redudp_instance *tmp, *instance = NULL;
list_for_each_entry_safe(instance, tmp, &instances, list)
redudp_fini_instance(instance);
return 0;
}
static parser_section redudp_conf_section =
{
.name = "redudp",
.entries = redudp_entries,
.onenter = redudp_onenter,
.onexit = redudp_onexit
};
app_subsys redudp_subsys =
{
.init = redudp_init,
.fini = redudp_fini,
.conf_section = &redudp_conf_section,
};
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */

47
redudp.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef REDUDP_H
#define REDUDP_H
typedef struct redudp_config_t {
struct sockaddr_in bindaddr;
struct sockaddr_in relayaddr;
// TODO: outgoingaddr;
struct sockaddr_in destaddr;
char *login;
char *password;
uint16_t max_pktqueue;
uint16_t udp_timeout;
uint16_t udp_timeout_stream;
} redudp_config;
typedef struct redudp_instance_t {
list_head list;
redudp_config config;
struct event listener;
list_head clients;
} redudp_instance;
typedef struct redudp_client_t {
list_head list;
redudp_instance *instance;
struct sockaddr_in clientaddr;
struct event timeout;
struct bufferevent *relay;
struct event udprelay;
struct sockaddr_in udprelayaddr;
int state; // it's used by bottom layer
time_t first_event;
time_t last_client_event;
time_t last_relay_event;
unsigned int queue_len;
list_head queue;
} redudp_client;
typedef struct enqueued_packet_t {
list_head list;
size_t len;
char data[1];
} enqueued_packet;
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
#endif /* REDUDP_H */

174
socks5.c
View File

@ -21,6 +21,7 @@
#include "utils.h"
#include "log.h"
#include "redsocks.h"
#include "socks5.h"
typedef enum socks5_state_t {
socks5_new,
@ -37,82 +38,6 @@ typedef struct socks5_client_t {
int to_skip; // valid while reading last reply (after main request)
} socks5_client;
typedef struct socks5_method_req_t {
uint8_t ver;
uint8_t num_methods;
uint8_t methods[1]; // at least one
} PACKED socks5_method_req;
typedef struct socks5_method_reply_t {
uint8_t ver;
uint8_t method;
} PACKED socks5_method_reply;
const int socks5_ver = 5;
const int socks5_auth_none = 0x00;
const int socks5_auth_gssapi = 0x01;
const int socks5_auth_password = 0x02;
const int socks5_auth_invalid = 0xFF;
typedef struct socks5_auth_reply_t {
uint8_t ver;
uint8_t status;
} PACKED socks5_auth_reply;
const int socks5_password_ver = 0x01;
const int socks5_password_passed = 0x00;
typedef struct socks5_addr_ipv4_t {
uint32_t addr;
uint16_t port;
} PACKED socks5_addr_ipv4;
typedef struct socks5_addr_domain_t {
uint8_t size;
uint8_t more[1];
/* uint16_t port; */
} PACKED socks5_addr_domain;
typedef struct socks5_addr_ipv6_t {
uint8_t addr[16];
uint16_t port;
} PACKED socks5_addr_ipv6;
typedef struct socks5_req_t {
uint8_t ver;
uint8_t cmd;
uint8_t reserved;
uint8_t addrtype;
/* socks5_addr_* */
} PACKED socks5_req;
typedef struct socks5_reply_t {
uint8_t ver;
uint8_t status;
uint8_t reserved;
uint8_t addrtype;
/* socks5_addr_* */
} PACKED socks5_reply;
const int socks5_reply_maxlen = 512; // as domain name can't be longer than 256 bytes
const int socks5_cmd_connect = 1;
const int socks5_cmd_bind = 2;
const int socks5_cmd_udp_associate = 2;
const int socks5_addrtype_ipv4 = 1;
const int socks5_addrtype_domain = 3;
const int socks5_addrtype_ipv6 = 4;
const int socks5_status_succeeded = 0;
const int socks5_status_server_failure = 1;
const int socks5_status_connection_not_allowed_by_ruleset = 2;
const int socks5_status_Network_unreachable = 3;
const int socks5_status_Host_unreachable = 4;
const int socks5_status_Connection_refused = 5;
const int socks5_status_TTL_expired = 6;
const int socks5_status_Command_not_supported = 7;
const int socks5_status_Address_type_not_supported = 8;
const char *socks5_strstatus[] = {
"ok",
"server failure",
@ -124,6 +49,32 @@ const char *socks5_strstatus[] = {
"command not supported",
"address type not supported",
};
const size_t socks5_strstatus_len = SIZEOF_ARRAY(socks5_strstatus);
const char* socks5_status_to_str(int socks5_status)
{
if (0 <= socks5_status && socks5_status < socks5_strstatus_len) {
return socks5_strstatus[socks5_status];
}
else {
return "";
}
}
int socks5_is_valid_cred(const char *login, const char *password)
{
if (!login || !password)
return 0;
if (strlen(login) > 255) {
log_error(LOG_WARNING, "Socks5 login can't be more than 255 chars, <%s> is too long", login);
return 0;
}
if (strlen(password) > 255) {
log_error(LOG_WARNING, "Socks5 password can't be more than 255 chars, <%s> is too long", password);
return 0;
}
return 1;
}
void socks5_client_init(redsocks_client *client)
{
@ -131,27 +82,25 @@ void socks5_client_init(redsocks_client *client)
const redsocks_config *config = &client->instance->config;
client->state = socks5_new;
socks5->do_password = 0;
if (config->login && config->password) {
if (strlen(config->login) > 255)
redsocks_log_error(client, LOG_WARNING, "Socks5 login can't be more than 255 chars");
else if (strlen(config->password) > 255)
redsocks_log_error(client, LOG_WARNING, "Socks5 password can't be more than 255 chars");
else
socks5->do_password = 1;
}
socks5->do_password = socks5_is_valid_cred(config->login, config->password);
}
static struct evbuffer *socks5_mkmethods(redsocks_client *client)
{
socks5_client *socks5 = (void*)(client + 1);
int len = sizeof(socks5_method_req) + socks5->do_password;
socks5_method_req *req = calloc(1, len);
return socks5_mkmethods_plain(socks5->do_password);
}
struct evbuffer *socks5_mkmethods_plain(int do_password)
{
assert(do_password == 0 || do_password == 1);
int len = sizeof(socks5_method_req) + do_password;
socks5_method_req *req = calloc(1, len);
req->ver = socks5_ver;
req->num_methods = 1 + socks5->do_password;
req->num_methods = 1 + do_password;
req->methods[0] = socks5_auth_none;
if (socks5->do_password)
if (do_password)
req->methods[1] = socks5_auth_password;
struct evbuffer *ret = mkevbuffer(req, len);
@ -161,8 +110,11 @@ static struct evbuffer *socks5_mkmethods(redsocks_client *client)
static struct evbuffer *socks5_mkpassword(redsocks_client *client)
{
const char *login = client->instance->config.login;
const char *password = client->instance->config.password;
return socks5_mkpassword_plain(client->instance->config.login, client->instance->config.password);
}
struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password)
{
size_t ulen = strlen(login);
size_t plen = strlen(password);
size_t length = 1 /* version */ + 1 + ulen + 1 + plen;
@ -176,22 +128,29 @@ static struct evbuffer *socks5_mkpassword(redsocks_client *client)
return mkevbuffer(req, length);
}
static struct evbuffer *socks5_mkconnect(redsocks_client *client)
struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr)
{
struct {
socks5_req head;
socks5_addr_ipv4 ip;
} PACKED req;
assert(destaddr->sin_family == AF_INET);
req.head.ver = socks5_ver;
req.head.cmd = socks5_cmd_connect;
req.head.cmd = socks5_cmd;
req.head.reserved = 0;
req.head.addrtype = socks5_addrtype_ipv4;
req.ip.addr = client->destaddr.sin_addr.s_addr;
req.ip.port = client->destaddr.sin_port;
req.ip.addr = destaddr->sin_addr.s_addr;
req.ip.port = destaddr->sin_port;
return mkevbuffer(&req, sizeof(req));
}
static struct evbuffer *socks5_mkconnect(redsocks_client *client)
{
return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr);
}
static void socks5_write_cb(struct bufferevent *buffev, void *_arg)
{
redsocks_client *client = _arg;
@ -206,15 +165,29 @@ static void socks5_write_cb(struct bufferevent *buffev, void *_arg)
}
}
const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_password)
{
if (reply->ver != socks5_ver)
return "Socks5 server reported unexpected auth methods reply version...";
else if (reply->method == socks5_auth_invalid)
return "Socks5 server refused all our auth methods.";
else if (reply->method != socks5_auth_none && !(reply->method == socks5_auth_password && do_password))
return "Socks5 server requested unexpected auth method...";
else
return NULL;
}
static void socks5_read_auth_methods(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5)
{
socks5_method_reply reply;
const char *error = NULL;
if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0)
return;
if (reply.ver != socks5_ver) {
redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected auth methods reply version...");
error = socks5_is_known_auth_method(&reply, socks5->do_password);
if (error) {
redsocks_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error);
redsocks_drop_client(client);
}
else if (reply.method == socks5_auth_none) {
@ -223,17 +196,12 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, redsocks_client
socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply)
);
}
else if (reply.method == socks5_auth_password && socks5->do_password) {
else if (reply.method == socks5_auth_password) {
redsocks_write_helper(
buffev, client,
socks5_mkpassword, socks5_auth_sent, sizeof(socks5_auth_reply)
);
}
else {
if (reply.method != socks5_auth_invalid)
redsocks_log_error(client, LOG_NOTICE, "Socks5 server requested unexpected auth method...");
redsocks_drop_client(client);
}
}
static void socks5_read_auth_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5)

104
socks5.h Normal file
View File

@ -0,0 +1,104 @@
#ifndef SOCKS5_H
#define SOCKS5_H
#include <stdint.h>
#include "utils.h"
typedef struct socks5_method_req_t {
uint8_t ver;
uint8_t num_methods;
uint8_t methods[1]; // at least one
} PACKED socks5_method_req;
typedef struct socks5_method_reply_t {
uint8_t ver;
uint8_t method;
} PACKED socks5_method_reply;
static const int socks5_ver = 5;
static const int socks5_auth_none = 0x00;
static const int socks5_auth_gssapi = 0x01;
static const int socks5_auth_password = 0x02;
static const int socks5_auth_invalid = 0xFF;
typedef struct socks5_auth_reply_t {
uint8_t ver;
uint8_t status;
} PACKED socks5_auth_reply;
static const int socks5_password_ver = 0x01;
static const int socks5_password_passed = 0x00;
typedef struct socks5_addr_ipv4_t {
uint32_t addr;
uint16_t port;
} PACKED socks5_addr_ipv4;
typedef struct socks5_addr_domain_t {
uint8_t size;
uint8_t more[1];
/* uint16_t port; */
} PACKED socks5_addr_domain;
typedef struct socks5_addr_ipv6_t {
uint8_t addr[16];
uint16_t port;
} PACKED socks5_addr_ipv6;
typedef struct socks5_req_t {
uint8_t ver;
uint8_t cmd;
uint8_t reserved;
uint8_t addrtype;
/* socks5_addr_* */
} PACKED socks5_req;
typedef struct socks5_reply_t {
uint8_t ver;
uint8_t status;
uint8_t reserved;
uint8_t addrtype;
/* socks5_addr_* */
} PACKED socks5_reply;
typedef struct socks5_udp_preabmle_t {
uint16_t reserved;
uint8_t frag_no;
uint8_t addrtype; /* 0x01 for IPv4 */
/* socks5_addr_* */
socks5_addr_ipv4 ip; /* I support only IPv4 at the moment */
} PACKED socks5_udp_preabmle;
static const int socks5_reply_maxlen = 512; // as domain name can't be longer than 256 bytes
static const int socks5_addrtype_ipv4 = 1;
static const int socks5_addrtype_domain = 3;
static const int socks5_addrtype_ipv6 = 4;
static const int socks5_status_succeeded = 0;
static const int socks5_status_server_failure = 1;
static const int socks5_status_connection_not_allowed_by_ruleset = 2;
static const int socks5_status_Network_unreachable = 3;
static const int socks5_status_Host_unreachable = 4;
static const int socks5_status_Connection_refused = 5;
static const int socks5_status_TTL_expired = 6;
static const int socks5_status_Command_not_supported = 7;
static const int socks5_status_Address_type_not_supported = 8;
const char* socks5_status_to_str(int socks5_status);
int socks5_is_valid_cred(const char *login, const char *password);
struct evbuffer *socks5_mkmethods_plain(int do_password);
struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password);
const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_password);
static const int socks5_cmd_connect = 1;
static const int socks5_cmd_bind = 2;
static const int socks5_cmd_udp_associate = 3;
struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr);
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
#endif /* SOCKS5_H */

123
utils.c Normal file
View File

@ -0,0 +1,123 @@
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include "log.h"
#include "utils.h"
time_t redsocks_time(time_t *t)
{
time_t retval;
retval = time(t);
if (retval == ((time_t) -1))
log_errno(LOG_WARNING, "time");
return retval;
}
struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg)
{
struct bufferevent *retval = NULL;
int on = 1;
int relay_fd = -1;
int error;
relay_fd = socket(AF_INET, SOCK_STREAM, 0);
if (relay_fd == -1) {
log_errno(LOG_ERR, "socket");
goto fail;
}
error = fcntl_nonblock(relay_fd);
if (error) {
log_errno(LOG_ERR, "fcntl");
goto fail;
}
error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
if (error) {
log_errno(LOG_WARNING, "setsockopt");
goto fail;
}
error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr));
if (error && errno != EINPROGRESS) {
log_errno(LOG_NOTICE, "connect");
goto fail;
}
retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg);
if (!retval) {
log_errno(LOG_ERR, "bufferevent_new");
goto fail;
}
error = bufferevent_enable(retval, EV_WRITE); // we wait for connection...
if (error) {
log_errno(LOG_ERR, "bufferevent_enable");
goto fail;
}
return retval;
fail:
if (relay_fd != -1)
close(relay_fd);
if (retval)
bufferevent_free(retval);
return NULL;
}
int red_socket_geterrno(struct bufferevent *buffev)
{
int error;
int pseudo_errno;
size_t optlen = sizeof(pseudo_errno);
assert(EVENT_FD(&buffev->ev_read) == EVENT_FD(&buffev->ev_write));
error = getsockopt(EVENT_FD(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen);
if (error) {
log_errno(LOG_ERR, "getsockopt");
return -1;
}
return pseudo_errno;
}
/** simple fcntl(2) wrapper, provides errno and all logging to caller
* I have to use it in event-driven code because of accept(2) (see NOTES)
* and connect(2) (see ERRORS about EINPROGRESS)
*/
int fcntl_nonblock(int fd)
{
int error;
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
error = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if (error)
return -1;
return 0;
}
int red_is_socket_connected_ok(struct bufferevent *buffev)
{
int pseudo_errno = red_socket_geterrno(buffev);
if (pseudo_errno == -1) {
return 0;
}
else if (pseudo_errno) {
errno = pseudo_errno;
log_errno(LOG_NOTICE, "connect");
return 0;
}
else {
return 1;
}
}
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */

25
utils.h
View File

@ -2,12 +2,21 @@
#define UTILS_H_SAT_FEB__2_02_24_05_2008
#include <stddef.h>
#include <time.h>
#include <netinet/in.h>
#include <event.h>
#define SIZEOF_ARRAY(arr) (sizeof(arr) / sizeof(arr[0]))
#define FOREACH(ptr, array) for (ptr = array; ptr < array + SIZEOF_ARRAY(array); ptr++)
#define FOREACH_REV(ptr, array) for (ptr = array + SIZEOF_ARRAY(array) - 1; ptr >= array; ptr--)
#if defined __GNUC__
#define PACKED __attribute__((packed))
#else
#error Unknown compiler, modify utils.h for it
#endif
/**
* container_of - cast a member of a structure out to the containing structure
@ -22,6 +31,22 @@
#define free_null(p) if (!(p)) ; else free (p)
time_t redsocks_time(time_t *t);
struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg);
int red_socket_geterrno(struct bufferevent *buffev);
int red_is_socket_connected_ok(struct bufferevent *buffev);
int fcntl_nonblock(int fd);
#define event_fmt_str "%s|%s|%s|%s|%s|0x%x"
#define event_fmt(what) \
(what) & EVBUFFER_READ ? "EVBUFFER_READ" : "0", \
(what) & EVBUFFER_WRITE ? "EVBUFFER_WRITE" : "0", \
(what) & EVBUFFER_EOF ? "EVBUFFER_EOF" : "0", \
(what) & EVBUFFER_ERROR ? "EVBUFFER_ERROR" : "0", \
(what) & EVBUFFER_TIMEOUT ? "EVBUFFER_TIMEOUT" : "0", \
(what) & ~(EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF|EVBUFFER_ERROR|EVBUFFER_TIMEOUT)
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
#endif /* UTILS_H_SAT_FEB__2_02_24_05_2008 */