Add HTTP/TLS obfuscating. [SIP001] (#1009)

Add experimental HTTP/TLS obfuscating as an **optional extension** of shadowsocks protocol.

More discussions can be found here: https://github.com/shadowsocks/shadowsocks-org/issues/26

As this feature is still a SIP (Shadowsocks Improvement Proposal), it's very unstable and experimental. So, 

1. Don't enable it unless you know what it is.
2. Be very careful when using it in production environment.
This commit is contained in:
Max Lv
2016-12-27 02:53:25 -06:00
committed by GitHub
parent 9b2b394249
commit 8cb4f81eba
45 changed files with 2214 additions and 419 deletions
+94 -42
View File
@@ -61,6 +61,8 @@
#include "netutils.h"
#include "utils.h"
#include "acl.h"
#include "obfs_http.h"
#include "obfs_tls.h"
#include "server.h"
#ifndef EAGAIN
@@ -113,8 +115,10 @@ static int acl = 0;
static int mode = TCP_ONLY;
static int auth = 0;
static int ipv6first = 0;
static int fast_open = 0;
static obfs_para_t *obfs_para = NULL;
#ifdef HAVE_SETRLIMIT
static int nofile = 0;
#endif
@@ -246,7 +250,7 @@ is_header_complete(const buffer_t *buf)
size_t header_len = 0;
size_t buf_len = buf->len;
char atyp = buf->array[header_len];
char atyp = buf->data[header_len];
// 1 byte for atyp
header_len++;
@@ -259,7 +263,7 @@ is_header_complete(const buffer_t *buf)
// domain len + len of domain
if (buf_len < header_len + 1)
return 0;
uint8_t name_len = *(uint8_t *)(buf->array + header_len);
uint8_t name_len = *(uint8_t *)(buf->data + header_len);
header_len += name_len + 1;
} else if ((atyp & ADDRTYPE_MASK) == 4) {
// IP V6
@@ -312,6 +316,7 @@ set_linger(int fd)
so_linger.l_linger = 0;
setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger);
}
#endif
static void
@@ -552,7 +557,7 @@ connect_to_remote(EV_P_ struct addrinfo *res,
endpoints.sae_dstaddrlen = res->ai_addrlen;
struct iovec iov;
iov.iov_base = server->buf->array + server->buf->idx;
iov.iov_base = server->buf->data + server->buf->idx;
iov.iov_len = server->buf->len;
size_t len;
int s = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT,
@@ -561,7 +566,7 @@ connect_to_remote(EV_P_ struct addrinfo *res,
s = len;
}
#else
ssize_t s = sendto(sockfd, server->buf->array + server->buf->idx,
ssize_t s = sendto(sockfd, server->buf->data + server->buf->idx,
server->buf->len, MSG_FASTOPEN, res->ai_addr,
res->ai_addrlen);
#endif
@@ -626,7 +631,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
return;
}
ssize_t r = recv(server->fd, buf->array + len, BUF_SIZE - len, 0);
ssize_t r = recv(server->fd, buf->data + len, BUF_SIZE - len, 0);
if (r == 0) {
// connection closed
@@ -660,12 +665,31 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
// handle incomplete header part 1
if (server->stage == STAGE_INIT) {
buf->len += r;
if (obfs_para && obfs_para->is_enable(server->obfs)) {
int ret = obfs_para->check_obfs(buf);
if (ret == OBFS_NEED_MORE) {
return;
} else if (ret) {
// obfs is enabled
ret = obfs_para->deobfs_request(buf, BUF_SIZE, server->obfs);
if (ret == OBFS_NEED_MORE)
return;
} else {
obfs_para->disable(server->obfs);
}
}
if (buf->len <= enc_get_iv_len() + 1) {
// wait for more
return;
}
} else {
buf->len = r;
if (obfs_para) {
int ret = obfs_para->deobfs_request(buf, BUF_SIZE, server->obfs);
if (ret) LOGE("invalid obfuscating");
}
}
int err = ss_decrypt(buf, server->d_ctx, BUF_SIZE);
@@ -698,15 +722,15 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
if (server->stage == STAGE_HANDSHAKE) {
size_t header_len = server->header_buf->len;
brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE);
memcpy(server->header_buf->array + header_len,
server->buf->array, server->buf->len);
memcpy(server->header_buf->data + header_len,
server->buf->data, server->buf->len);
server->header_buf->len = server->buf->len + header_len;
int ret = is_header_complete(server->buf);
if (ret == 1) {
brealloc(server->buf, server->header_buf->len, BUF_SIZE);
memcpy(server->buf->array, server->header_buf->array, server->header_buf->len);
memcpy(server->buf->data, server->header_buf->data, server->header_buf->len);
server->buf->len = server->header_buf->len;
bfree(server->header_buf);
ss_free(server->header_buf);
@@ -729,7 +753,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
close_and_free_remote(EV_A_ remote);
return;
}
int s = send(remote->fd, remote->buf->array, remote->buf->len, 0);
int s = send(remote->fd, remote->buf->data, remote->buf->len, 0);
if (s == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// no data, wait for send
@@ -780,7 +804,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
int offset = 0;
int need_query = 0;
char atyp = server->buf->array[offset++];
char atyp = server->buf->data[offset++];
char host[257] = { 0 };
uint16_t port = 0;
struct addrinfo info;
@@ -789,7 +813,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
memset(&storage, 0, sizeof(struct sockaddr_storage));
if (auth || (atyp & ONETIMEAUTH_FLAG)) {
size_t header_len = parse_header_len(atyp, server->buf->array, offset);
size_t header_len = parse_header_len(atyp, server->buf->data, offset);
size_t len = server->buf->len;
if (header_len == 0 || len < offset + header_len + ONETIMEAUTH_BYTES) {
@@ -816,8 +840,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
size_t in_addr_len = sizeof(struct in_addr);
addr->sin_family = AF_INET;
if (server->buf->len >= in_addr_len + 3) {
addr->sin_addr = *(struct in_addr *)(server->buf->array + offset);
dns_ntop(AF_INET, (const void *)(server->buf->array + offset),
addr->sin_addr = *(struct in_addr *)(server->buf->data + offset);
dns_ntop(AF_INET, (const void *)(server->buf->data + offset),
host, INET_ADDRSTRLEN);
offset += in_addr_len;
} else {
@@ -826,7 +850,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
close_and_free_server(EV_A_ server);
return;
}
addr->sin_port = *(uint16_t *)(server->buf->array + offset);
addr->sin_port = *(uint16_t *)(server->buf->data + offset);
info.ai_family = AF_INET;
info.ai_socktype = SOCK_STREAM;
info.ai_protocol = IPPROTO_TCP;
@@ -834,9 +858,9 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
info.ai_addr = (struct sockaddr *)addr;
} else if ((atyp & ADDRTYPE_MASK) == 3) {
// Domain name
uint8_t name_len = *(uint8_t *)(server->buf->array + offset);
uint8_t name_len = *(uint8_t *)(server->buf->data + offset);
if (name_len + 4 <= server->buf->len) {
memcpy(host, server->buf->array + offset + 1, name_len);
memcpy(host, server->buf->data + offset + 1, name_len);
offset += name_len + 1;
} else {
LOGE("invalid name length: %d", name_len);
@@ -857,7 +881,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
if (ip.version == 4) {
struct sockaddr_in *addr = (struct sockaddr_in *)&storage;
dns_pton(AF_INET, host, &(addr->sin_addr));
addr->sin_port = *(uint16_t *)(server->buf->array + offset);
addr->sin_port = *(uint16_t *)(server->buf->data + offset);
addr->sin_family = AF_INET;
info.ai_family = AF_INET;
info.ai_addrlen = sizeof(struct sockaddr_in);
@@ -865,7 +889,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
} else if (ip.version == 6) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage;
dns_pton(AF_INET6, host, &(addr->sin6_addr));
addr->sin6_port = *(uint16_t *)(server->buf->array + offset);
addr->sin6_port = *(uint16_t *)(server->buf->data + offset);
addr->sin6_family = AF_INET6;
info.ai_family = AF_INET6;
info.ai_addrlen = sizeof(struct sockaddr_in6);
@@ -886,8 +910,8 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
size_t in6_addr_len = sizeof(struct in6_addr);
addr->sin6_family = AF_INET6;
if (server->buf->len >= in6_addr_len + 3) {
addr->sin6_addr = *(struct in6_addr *)(server->buf->array + offset);
dns_ntop(AF_INET6, (const void *)(server->buf->array + offset),
addr->sin6_addr = *(struct in6_addr *)(server->buf->data + offset);
dns_ntop(AF_INET6, (const void *)(server->buf->data + offset),
host, INET6_ADDRSTRLEN);
offset += in6_addr_len;
} else {
@@ -896,7 +920,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
close_and_free_server(EV_A_ server);
return;
}
addr->sin6_port = *(uint16_t *)(server->buf->array + offset);
addr->sin6_port = *(uint16_t *)(server->buf->data + offset);
info.ai_family = AF_INET6;
info.ai_socktype = SOCK_STREAM;
info.ai_protocol = IPPROTO_TCP;
@@ -911,7 +935,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
return;
}
port = (*(uint16_t *)(server->buf->array + offset));
port = (*(uint16_t *)(server->buf->data + offset));
offset += 2;
@@ -925,7 +949,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
return;
} else {
server->buf->len -= offset;
memmove(server->buf->array, server->buf->array + offset, server->buf->len);
memmove(server->buf->data, server->buf->data + offset, server->buf->len);
}
if (verbose) {
@@ -943,7 +967,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
}
if (!need_query) {
remote_t *remote = connect_to_remote(EV_A_ &info, server);
remote_t *remote = connect_to_remote(EV_A_ & info, server);
if (remote == NULL) {
LOGE("connect error");
@@ -955,7 +979,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
// XXX: should handle buffer carefully
if (server->buf->len > 0) {
memcpy(remote->buf->array, server->buf->array, server->buf->len);
memcpy(remote->buf->data, server->buf->data, server->buf->len);
remote->buf->len = server->buf->len;
remote->buf->idx = 0;
server->buf->len = 0;
@@ -1008,7 +1032,7 @@ server_send_cb(EV_P_ ev_io *w, int revents)
return;
} else {
// has data to send
ssize_t s = send(server->fd, server->buf->array + server->buf->idx,
ssize_t s = send(server->fd, server->buf->data + server->buf->idx,
server->buf->len, 0);
if (s == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -1114,7 +1138,7 @@ server_resolve_cb(struct sockaddr *addr, void *data)
info.ai_addrlen = sizeof(struct sockaddr_in6);
}
remote_t *remote = connect_to_remote(EV_A_ &info, server);
remote_t *remote = connect_to_remote(EV_A_ & info, server);
if (remote == NULL) {
close_and_free_server(EV_A_ server);
@@ -1124,7 +1148,7 @@ server_resolve_cb(struct sockaddr *addr, void *data)
// XXX: should handle buffer carefully
if (server->buf->len > 0) {
memcpy(remote->buf->array, server->buf->array + server->buf->idx,
memcpy(remote->buf->data, server->buf->data + server->buf->idx,
server->buf->len);
remote->buf->len = server->buf->len;
remote->buf->idx = 0;
@@ -1153,7 +1177,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents)
ev_timer_again(EV_A_ & server->recv_ctx->watcher);
ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0);
ssize_t r = recv(remote->fd, server->buf->data, BUF_SIZE, 0);
if (r == 0) {
// connection closed
@@ -1188,7 +1212,11 @@ remote_recv_cb(EV_P_ ev_io *w, int revents)
return;
}
int s = send(server->fd, server->buf->array, server->buf->len, 0);
if (obfs_para) {
obfs_para->obfs_response(server->buf, BUF_SIZE, server->obfs);
}
int s = send(server->fd, server->buf->data, server->buf->len, 0);
if (s == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -1271,7 +1299,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents)
return;
} else {
// has data to send
ssize_t s = send(remote->fd, remote->buf->array + remote->buf->idx,
ssize_t s = send(remote->fd, remote->buf->data + remote->buf->idx,
remote->buf->len, 0);
if (s == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -1317,9 +1345,9 @@ new_remote(int fd)
remote_t *remote = ss_malloc(sizeof(remote_t));
memset(remote, 0, sizeof(remote_t));
remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t));
remote->send_ctx = ss_malloc(sizeof(remote_ctx_t));
remote->buf = ss_malloc(sizeof(buffer_t));
remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t));
remote->send_ctx = ss_malloc(sizeof(remote_ctx_t));
remote->buf = ss_malloc(sizeof(buffer_t));
balloc(remote->buf, BUF_SIZE);
memset(remote->recv_ctx, 0, sizeof(remote_ctx_t));
memset(remote->send_ctx, 0, sizeof(remote_ctx_t));
@@ -1378,10 +1406,10 @@ new_server(int fd, listen_ctx_t *listener)
memset(server, 0, sizeof(server_t));
server->recv_ctx = ss_malloc(sizeof(server_ctx_t));
server->send_ctx = ss_malloc(sizeof(server_ctx_t));
server->buf = ss_malloc(sizeof(buffer_t));
server->header_buf = ss_malloc(sizeof(buffer_t));
server->recv_ctx = ss_malloc(sizeof(server_ctx_t));
server->send_ctx = ss_malloc(sizeof(server_ctx_t));
server->buf = ss_malloc(sizeof(buffer_t));
server->header_buf = ss_malloc(sizeof(buffer_t));
memset(server->recv_ctx, 0, sizeof(server_ctx_t));
memset(server->send_ctx, 0, sizeof(server_ctx_t));
balloc(server->buf, BUF_SIZE);
@@ -1396,6 +1424,11 @@ new_server(int fd, listen_ctx_t *listener)
server->listen_ctx = listener;
server->remote = NULL;
if (obfs_para != NULL) {
server->obfs = (obfs_t *)ss_malloc(sizeof(obfs_t));
memset(server->obfs, 0, sizeof(obfs_t));
}
if (listener->method) {
server->e_ctx = ss_malloc(sizeof(enc_ctx_t));
server->d_ctx = ss_malloc(sizeof(enc_ctx_t));
@@ -1429,6 +1462,12 @@ free_server(server_t *server)
{
cork_dllist_remove(&server->entries);
if (server->obfs != NULL) {
bfree(server->obfs->buf);
if (server->obfs->extra != NULL)
ss_free(server->obfs->extra);
ss_free(server->obfs);
}
if (server->chunk != NULL) {
if (server->chunk->buf != NULL) {
bfree(server->chunk->buf);
@@ -1571,6 +1610,7 @@ main(int argc, char **argv)
{ "manager-address", required_argument, 0, 0 },
{ "mtu", required_argument, 0, 0 },
{ "help", no_argument, 0, 0 },
{ "obfs", required_argument, 0, 0 },
#ifdef __linux__
{ "mptcp", no_argument, 0, 0 },
{ "firewall", no_argument, 0, 0 },
@@ -1600,9 +1640,15 @@ main(int argc, char **argv)
usage();
exit(EXIT_SUCCESS);
} else if (option_index == 5) {
if (strcmp(optarg, obfs_http->name) == 0)
obfs_para = obfs_http;
else if (strcmp(optarg, obfs_tls->name) == 0)
obfs_para = obfs_tls;
LOGI("obfuscating enabled");
} else if (option_index == 6) {
mptcp = 1;
LOGI("enable multipath TCP");
} else if (option_index == 6) {
} else if (option_index == 7) {
firewall = 1;
LOGI("enable firewall rules");
}
@@ -1709,6 +1755,12 @@ main(int argc, char **argv)
if (user == NULL) {
user = conf->user;
}
if (obfs_para == NULL && conf->obfs != NULL) {
if (strcmp(conf->obfs, obfs_http->name) == 0)
obfs_para = obfs_http;
else if (strcmp(conf->obfs, obfs_tls->name) == 0)
obfs_para = obfs_tls;
}
if (auth == 0) {
auth = conf->auth;
}
@@ -1890,12 +1942,12 @@ main(int argc, char **argv)
ev_timer_start(EV_DEFAULT, &block_list_watcher);
// setuid
if (user != NULL && ! run_as(user)) {
if (user != NULL && !run_as(user)) {
FATAL("failed to switch user");
}
#ifndef __MINGW32__
if (geteuid() == 0){
if (geteuid() == 0) {
LOGI("running from root user");
} else if (firewall) {
LOGE("firewall setup requires running from root user");