static void server_recv_cb (EV_P_ ev_io *w, int revents) { struct server_ctx *server_recv_ctx = (struct server_ctx *)w; struct server *server = server_recv_ctx->server; struct remote *remote = NULL; int len = server->buf_len; char **buf = &server->buf; ev_timer_again(EV_A_ &server->recv_ctx->watcher); if (server->stage != 0) { remote = server->remote; buf = &remote->buf; len = 0; } ssize_t r = recv(server->fd, *buf + len, BUF_SIZE - len, 0); if (r == 0) { // connection closed if (verbose) { LOGD("server_recv close the connection"); } close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); return; } else if (r == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // no data // continue to wait for recv return; } else { ERROR("server recv"); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); return; } } // handle incomplete header if (server->stage == 0) { r += server->buf_len; if (r <= enc_get_iv_len()) { // wait for more if (verbose) { LOGD("imcomplete header: %zu", r); } server->buf_len = r; return; } else { server->buf_len = 0; } } *buf = ss_decrypt(BUF_SIZE, *buf, &r, server->d_ctx); if (*buf == NULL) { LOGE("invalid password or cipher"); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); return; } // handshake and transmit data if (server->stage == 5) { int s = send(remote->fd, remote->buf, r, 0); if (s == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // no data, wait for send remote->buf_len = r; remote->buf_idx = 0; ev_io_stop(EV_A_ &server_recv_ctx->io); ev_io_start(EV_A_ &remote->send_ctx->io); } else { ERROR("server_recv_send"); close_and_free_remote(EV_A_ remote); close_and_free_server(EV_A_ server); } } else if (s < r) { remote->buf_len = r - s; remote->buf_idx = s; ev_io_stop(EV_A_ &server_recv_ctx->io); ev_io_start(EV_A_ &remote->send_ctx->io); } return; } else if (server->stage == 0) { /* * Shadowsocks Protocol: * * +------+----------+----------+ * | ATYP | DST.ADDR | DST.PORT | * +------+----------+----------+ * | 1 | Variable | 2 | * +------+----------+----------+ */ int offset = 0; char atyp = server->buf[offset++]; char host[256] = {0}; char port[64] = {0}; // get remote addr and port if (atyp == 1) { // IP V4 size_t in_addr_len = sizeof(struct in_addr); if (r > in_addr_len) { inet_ntop(AF_INET, (const void *)(server->buf + offset), host, INET_ADDRSTRLEN); offset += in_addr_len; } } else if (atyp == 3) { // Domain name uint8_t name_len = *(uint8_t *)(server->buf + offset); if (name_len < r && name_len < 255 && name_len > 0) { memcpy(host, server->buf + offset + 1, name_len); offset += name_len + 1; } } else if (atyp == 4) { // IP V6 size_t in6_addr_len = sizeof(struct in6_addr); if (r > in6_addr_len) { inet_ntop(AF_INET6, (const void*)(server->buf + offset), host, INET6_ADDRSTRLEN); offset += in6_addr_len; } } if (offset == 1) { LOGE("invalid header with addr type %d", atyp); close_and_free_server(EV_A_ server); return; } sprintf(port, "%d", ntohs(*(uint16_t *)(server->buf + offset))); offset += 2; if (verbose) { LOGD("connect to: %s:%s", host, port); } struct addrinfo hints; asyncns_query_t *query; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; query = asyncns_getaddrinfo(server->listen_ctx->asyncns, host, port, &hints); if (query == NULL) { ERROR("asyncns_getaddrinfo"); close_and_free_server(EV_A_ server); return; } asyncns_setuserdata(server->listen_ctx->asyncns, query, server); // XXX: should handle buffer carefully if (r > offset) { server->buf_len = r - offset; server->buf_idx = offset; } server->stage = 4; server->query = query; ev_io_stop(EV_A_ &server_recv_ctx->io); return; } // should not reach here FATAL("server context error."); }
static void server_recv_cb(EV_P_ ev_io *w, int revents) { struct server_ctx *server_ctx = (struct server_ctx *)w; struct sockaddr_storage src_addr; char *buf = malloc(BUF_SIZE); socklen_t src_addr_len = sizeof(struct sockaddr_storage); unsigned int offset = 0; ssize_t buf_len = recvfrom(server_ctx->fd, buf, BUF_SIZE, 0, (struct sockaddr *)&src_addr, &src_addr_len); if (buf_len == -1) { // error on recv // simply drop that packet if (verbose) { ERROR("[udp] server_recvfrom"); } goto CLEAN_UP; } if (verbose) { LOGD("[udp] server receive a packet."); } #ifdef UDPRELAY_REMOTE buf = ss_decrypt_all(BUF_SIZE, buf, &buf_len, server_ctx->method); if (buf == NULL) { if (verbose) { ERROR("[udp] server_ss_decrypt_all"); } goto CLEAN_UP; } #endif #ifdef UDPRELAY_LOCAL #ifndef UDPRELAY_TUNNEL uint8_t frag = *(uint8_t *)(buf + 2); offset += 3; #endif #endif /* * * SOCKS5 UDP Request * +----+------+------+----------+----------+----------+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | * +----+------+------+----------+----------+----------+ * | 2 | 1 | 1 | Variable | 2 | Variable | * +----+------+------+----------+----------+----------+ * * SOCKS5 UDP Response * +----+------+------+----------+----------+----------+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | * +----+------+------+----------+----------+----------+ * | 2 | 1 | 1 | Variable | 2 | Variable | * +----+------+------+----------+----------+----------+ * * shadowsocks UDP Request (before encrypted) * +------+----------+----------+----------+ * | ATYP | DST.ADDR | DST.PORT | DATA | * +------+----------+----------+----------+ * | 1 | Variable | 2 | Variable | * +------+----------+----------+----------+ * * shadowsocks UDP Response (before encrypted) * +------+----------+----------+----------+ * | ATYP | DST.ADDR | DST.PORT | DATA | * +------+----------+----------+----------+ * | 1 | Variable | 2 | Variable | * +------+----------+----------+----------+ * * shadowsocks UDP Request and Response (after encrypted) * +-------+--------------+ * | IV | PAYLOAD | * +-------+--------------+ * | Fixed | Variable | * +-------+--------------+ * */ #ifdef UDPRELAY_TUNNEL char addr_header[256] = { 0 }; char * host = server_ctx->tunnel_addr.host; char * port = server_ctx->tunnel_addr.port; int host_len = strlen(host); uint16_t port_num = (uint16_t)atoi(port); uint16_t port_net_num = htons(port_num); int addr_header_len = 2 + host_len + 2; // initialize the addr header addr_header[0] = 3; addr_header[1] = host_len; memcpy(addr_header + 2, host, host_len); memcpy(addr_header + 2 + host_len, &port_net_num, 2); // reconstruct the buffer char *tmp = malloc(buf_len + addr_header_len); memcpy(tmp, addr_header, addr_header_len); memcpy(tmp + addr_header_len, buf, buf_len); free(buf); buf = tmp; buf_len += addr_header_len; #else char host[256] = { 0 }; char port[64] = { 0 }; int addr_header_len = parse_udprealy_header(buf + offset, buf_len - offset, host, port); if (addr_header_len == 0) { // error in parse header goto CLEAN_UP; } char *addr_header = buf + offset; #endif char *key = hash_key(addr_header, addr_header_len, &src_addr); struct cache *conn_cache = server_ctx->conn_cache; struct remote_ctx *remote_ctx = NULL; cache_lookup(conn_cache, key, (void *)&remote_ctx); if (remote_ctx != NULL) { if (memcmp(&src_addr, &remote_ctx->src_addr, sizeof(src_addr)) || strcmp(addr_header, remote_ctx->addr_header) != 0) { remote_ctx = NULL; } } if (remote_ctx == NULL) { if (verbose) { LOGD("[udp] cache missed: %s:%s <-> %s", host, port, get_addr_str((struct sockaddr *)&src_addr)); } } else { if (verbose) { LOGD("[udp] cache hit: %s:%s <-> %s", host, port, get_addr_str((struct sockaddr *)&src_addr)); } } #ifdef UDPRELAY_LOCAL #ifndef UDPRELAY_TUNNEL if (frag) { LOGE("[udp] drop a message since frag is not 0, but %d", frag); goto CLEAN_UP; } #endif if (remote_ctx == NULL) { struct addrinfo hints; struct addrinfo *result; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ hints.ai_socktype = SOCK_DGRAM; /* We want a UDP socket */ int s = getaddrinfo(server_ctx->remote_host, server_ctx->remote_port, &hints, &result); if (s != 0 || result == NULL) { LOGE("[udp] getaddrinfo: %s", gai_strerror(s)); goto CLEAN_UP; } // Bind to any port int remotefd = create_remote_socket(result->ai_family == AF_INET6); if (remotefd < 0) { ERROR("[udp] udprelay bind() error.."); // remember to free addrinfo freeaddrinfo(result); goto CLEAN_UP; } setnonblocking(remotefd); #ifdef SO_NOSIGPIPE set_nosigpipe(remotefd); #endif #ifdef SET_INTERFACE if (server_ctx->iface) { setinterface(remotefd, server_ctx->iface); } #endif // Init remote_ctx remote_ctx = new_remote(remotefd, server_ctx); remote_ctx->src_addr = src_addr; remote_ctx->dst_addr = *((struct sockaddr_storage *)result->ai_addr); remote_ctx->addr_header_len = addr_header_len; memcpy(remote_ctx->addr_header, addr_header, addr_header_len); // Add to conn cache cache_insert(conn_cache, key, (void *)remote_ctx); // Start remote io ev_io_start(EV_A_ & remote_ctx->io); // clean up freeaddrinfo(result); } if (offset > 0) { buf_len -= offset; memmove(buf, buf + offset, buf_len); } buf = ss_encrypt_all(BUF_SIZE, buf, &buf_len, server_ctx->method); size_t addr_len = sizeof(struct sockaddr_in); if (remote_ctx->dst_addr.ss_family == AF_INET6) { addr_len = sizeof(struct sockaddr_in6); } int s = sendto(remote_ctx->fd, buf, buf_len, 0, (struct sockaddr *)&remote_ctx->dst_addr, addr_len); if (s == -1) { ERROR("[udp] sendto_remote"); } #else if (remote_ctx == NULL) { struct addrinfo hints; asyncns_query_t *query; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; query = asyncns_getaddrinfo(server_ctx->asyncns, host, port, &hints); if (query == NULL) { ERROR("[udp] asyncns_getaddrinfo"); goto CLEAN_UP; } struct query_ctx *query_ctx = new_query_ctx(query, buf + addr_header_len, buf_len - addr_header_len); query_ctx->server_ctx = server_ctx; query_ctx->addr_header_len = addr_header_len; query_ctx->src_addr = src_addr; memcpy(query_ctx->addr_header, addr_header, addr_header_len); asyncns_setuserdata(server_ctx->asyncns, query, query_ctx); } else { size_t addr_len = sizeof(struct sockaddr_in); if (remote_ctx->dst_addr.ss_family == AF_INET6) { addr_len = sizeof(struct sockaddr_in6); } int s = sendto(remote_ctx->fd, buf + addr_header_len, buf_len - addr_header_len, 0, (struct sockaddr *)&remote_ctx->dst_addr, addr_len); if (s == -1) { ERROR("[udp] sendto_remote"); } } #endif CLEAN_UP: free(buf); }