diff --git a/src/encrypt.c b/src/encrypt.c index 862db88a..2064735d 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -1462,3 +1462,70 @@ int enc_init(const char *pass, const char *method) return m; } +/* + * Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A table-based + * algorithm would be faster, but for only a few bytes it isn't worth the code + * size. + */ +uint8_t crc8(const void *vptr, int len) +{ + const uint8_t *data = vptr; + unsigned crc = 0; + int i, j; + + for (j = len; j; j--, data++) { + crc ^= (*data << 8); + for(i = 8; i; i--) { + if (crc & 0x8000) + crc ^= (0x1070 << 3); + crc <<= 1; + } + } + + return (uint8_t)(crc >> 8); +} + +int ss_check_crc(char *buf, ssize_t *buf_len, char *crc_buf, ssize_t *crc_idx) +{ + int i, j; + ssize_t blen = *buf_len; + ssize_t cidx = *crc_idx; + + for (i = 0, j = 0; i < blen; i++) { + if (cidx == CRC_BUF_LEN) { + uint8_t c = crc8((const void*)crc_buf, CRC_BUF_LEN); + if (memcmp(&c, buf + i, 1) != 0) return 0; + cidx = 0; + } else { + crc_buf[cidx] = buf[j] = buf[i]; + cidx++; j++; + } + } + *buf_len = j; + *crc_idx = cidx; + return 1; +} + +void ss_gen_crc(char *buf, ssize_t *buf_len, char *crc_buf, ssize_t *crc_idx, int buf_size) +{ + int i, j; + ssize_t blen = *buf_len; + ssize_t cidx = *crc_idx; + int size = max(blen / CRC_BUF_LEN + blen, buf_size); + + if (buf_size < size) { + buf = realloc(buf, size); + } + for (i = 0, j = 0; i < blen; i++, j++) { + if (cidx == CRC_BUF_LEN) { + uint8_t c = crc8((const void*)crc_buf, CRC_BUF_LEN); + memmove(buf + j + 1, buf + j, blen - i); + memcpy(buf + j, &c, 1); + j++; cidx = 0; + } + crc_buf[cidx] = buf[j]; + cidx++; + } + *buf_len = j; + *crc_idx = cidx; +} diff --git a/src/encrypt.h b/src/encrypt.h index 77bf5a5a..16069637 100644 --- a/src/encrypt.h +++ b/src/encrypt.h @@ -147,6 +147,8 @@ typedef struct { #define ONETIMEAUTH_MASK 0xF0 #define ADDRTYPE_MASK 0xF +#define CRC_BUF_LEN 128 + #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) @@ -171,4 +173,7 @@ unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); int ss_onetimeauth(char *auth, char *msg, int msg_len); int ss_onetimeauth_verify(char *auth, char *msg, int msg_len); +int ss_check_crc(char *buf, ssize_t *buf_len, char *crc_buf, ssize_t *crc_idx); +void ss_gen_crc(char *buf, ssize_t *buf_len, char *crc_buf, ssize_t *crc_idx, int buf_size); + #endif // _ENCRYPT_H diff --git a/src/local.c b/src/local.c index 127d3cd5..9d7cfe41 100644 --- a/src/local.c +++ b/src/local.c @@ -244,6 +244,10 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) return; } + if (remote->send_ctx->connected && auth) { + ss_gen_crc(remote->buf, &r, remote->crc_buf, &remote->crc_idx, BUF_SIZE); + } + // insert shadowsocks header if (!remote->direct) { remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, @@ -478,6 +482,11 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } memcpy(remote->buf, ss_addr_to_send, addr_len); + + if (auth) { + ss_gen_crc(buf, &r, remote->crc_buf, &remote->crc_idx, BUF_SIZE); + } + if (r > 0) { memcpy(remote->buf + addr_len, buf, r); } diff --git a/src/local.h b/src/local.h index 27588910..f893b5a6 100644 --- a/src/local.h +++ b/src/local.h @@ -81,6 +81,9 @@ struct remote { struct server *server; struct sockaddr_storage addr; int addr_len; + + ssize_t crc_idx; + char crc_buf[CRC_BUF_LEN]; }; #endif // _LOCAL_H diff --git a/src/redir.c b/src/redir.c index 4c54c4ca..da961f44 100644 --- a/src/redir.c +++ b/src/redir.c @@ -184,7 +184,12 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } } + if (auth) { + ss_gen_crc(remote->buf, &r, remote->crc_buf, &remote->crc_idx, BUF_SIZE); + } + remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, server->e_ctx); + if (remote->buf == NULL) { LOGE("invalid password or cipher"); close_and_free_remote(EV_A_ remote); @@ -193,6 +198,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } int s = send(remote->fd, remote->buf, r, 0); + if (s == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // no data, wait for send @@ -444,6 +450,9 @@ static struct remote * new_remote(int fd, int timeout) { struct remote *remote; remote = malloc(sizeof(struct remote)); + + memset(remote, 0, sizeof(struct remote)); + remote->buf = malloc(BUF_SIZE); remote->recv_ctx = malloc(sizeof(struct remote_ctx)); remote->send_ctx = malloc(sizeof(struct remote_ctx)); diff --git a/src/redir.h b/src/redir.h index 57a7f57c..18e9c99d 100644 --- a/src/redir.h +++ b/src/redir.h @@ -70,6 +70,9 @@ struct remote { struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; + + ssize_t crc_idx; + char crc_buf[CRC_BUF_LEN]; }; #endif // _LOCAL_H diff --git a/src/server.c b/src/server.c index 900bf655..0bd3c9b9 100644 --- a/src/server.c +++ b/src/server.c @@ -495,6 +495,14 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) // handshake and transmit data if (server->stage == 5) { + if (server->auth + && !ss_check_crc(remote->buf, &r, server->crc_buf, &server->crc_idx)) { + LOGE("crc error"); + report_addr(server->fd); + close_and_free_server(EV_A_ server); + close_and_free_remote(EV_A_ remote); + return; + } int s = send(remote->fd, remote->buf, r, 0); if (s == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -648,6 +656,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) return; }; offset += ONETIMEAUTH_BYTES; + server->auth = 1; } if (verbose) { @@ -660,6 +669,14 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) server->buf_idx = offset; } + if (server->auth + && !ss_check_crc(server->buf + server->buf_idx, &server->buf_len, server->crc_buf, &server->crc_idx)) { + LOGE("crc error"); + report_addr(server->fd); + close_and_free_server(EV_A_ server); + return; + } + if (!need_query) { struct remote *remote = connect_to_remote(&info, server); @@ -1061,6 +1078,9 @@ static struct server * new_server(int fd, struct listen_ctx *listener) struct server *server; server = malloc(sizeof(struct server)); + + memset(server, 0, sizeof(struct server)); + server->buf = malloc(BUF_SIZE); server->recv_ctx = malloc(sizeof(struct server_ctx)); server->send_ctx = malloc(sizeof(struct server_ctx)); diff --git a/src/server.h b/src/server.h index 8b5c94dc..10555ee2 100644 --- a/src/server.h +++ b/src/server.h @@ -55,6 +55,11 @@ struct server { ssize_t buf_len; ssize_t buf_idx; char *buf; // server send from, remote recv into + + int auth; + ssize_t crc_idx; + char crc_buf[CRC_BUF_LEN]; + struct enc_ctx *e_ctx; struct enc_ctx *d_ctx; struct server_ctx *recv_ctx; diff --git a/src/tunnel.c b/src/tunnel.c index c3dd2ca0..c186f6c9 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -196,6 +196,10 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } } + if (auth) { + ss_gen_crc(remote->buf, &r, remote->crc_buf, &remote->crc_idx, BUF_SIZE); + } + remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, server->e_ctx); if (remote->buf == NULL) { @@ -485,6 +489,9 @@ static struct remote * new_remote(int fd, int timeout) { struct remote *remote; remote = malloc(sizeof(struct remote)); + + memset(remote, 0, sizeof(struct remote)); + remote->buf = malloc(BUF_SIZE); remote->recv_ctx = malloc(sizeof(struct remote_ctx)); remote->send_ctx = malloc(sizeof(struct remote_ctx)); diff --git a/src/tunnel.h b/src/tunnel.h index 3ac4317e..aca370d0 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -74,6 +74,9 @@ struct remote { struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; + + ssize_t crc_idx; + char crc_buf[CRC_BUF_LEN]; }; #endif // _TUNNEL_H