void connect_to_remote(struct remote_context *remote) { remote->stage = XSTAGE_CONNECT; remote->connect_req.data = remote; int rc = uv_tcp_connect(&remote->connect_req, &remote->handle.tcp, &server_addr, remote_connect_cb); if (rc) { logger_log(LOG_ERR, "connect to server error: %s", uv_strerror(rc)); request_ack(remote->client, S5_REP_NETWORK_UNREACHABLE); } }
static void remote_timer_expire(uv_timer_t *handle) { struct remote_context *remote = handle->data; struct client_context *client = remote->client; if (verbose) { char addrbuf[INET6_ADDRSTRLEN + 1] = {0}; uint16_t port = ip_name(&client->addr, addrbuf, sizeof addrbuf); if (client->stage < XSTAGE_FORWARD) { logger_log(LOG_WARNING, "%s:%d connection timeout", addrbuf, port); } else { logger_log(LOG_WARNING, "%s:%d <-> %s connection timeout", addrbuf, port, client->target_addr); } } assert(client->stage != XSTAGE_TERMINATE); request_ack(client, S5_REP_TTL_EXPIRED); }
static void remote_connect_cb(uv_connect_t *req, int status) { struct remote_context *remote = (struct remote_context *)req->data; struct client_context *client = remote->client; if (status == 0) { remote->stage = XSTAGE_FORWARD; reset_timer(remote); receive_from_client(client); receive_from_remote(remote); } else { if (status != UV_ECANCELED) { logger_log(LOG_ERR, "connect to server failed: %s", uv_strerror(status)); request_ack(client, S5_REP_HOST_UNREACHABLE); } } }
static void request_start(struct client_context *client, char *req_buf) { struct socks5_request *req = (struct socks5_request *)req_buf; struct remote_context *remote = client->remote; assert(remote->stage == XSTAGE_FORWARD); client->cmd = req->cmd; if (req->cmd != S5_CMD_CONNECT && req->cmd != S5_CMD_UDP_ASSOCIATE) { logger_log(LOG_ERR, "unsupported cmd: 0x%02x", req->cmd); request_ack(client, S5_REP_CMD_NOT_SUPPORTED); return; } if (req->cmd == S5_CMD_UDP_ASSOCIATE) { request_ack(client, S5_REP_SUCCESSED); return; } char buf[260] = {0}; size_t buflen; uint16_t portlen = 2; /* * * xsocks request * +------+----------+----------+ * | ATYP | BND.ADDR | BND.PORT | * +------+----------+----------+ * | 1 | Variable | 2 | * +------+----------+----------+ * */ if (req->atyp == ATYP_IPV4) { size_t in_addr_len = sizeof(struct in_addr); buflen = sizeof(struct xsocks_request) + in_addr_len + portlen; buf[0] = ATYP_IPV4; memcpy(buf + 1, req->addr, in_addr_len); memcpy(buf + 1 + in_addr_len, req->addr + in_addr_len, portlen); uv_inet_ntop(AF_INET, (const void *)(req->addr), client->target_addr, INET_ADDRSTRLEN); uint16_t port = read_size((uint8_t*)(req->addr + in_addr_len)); sprintf(client->target_addr, "%s:%u", client->target_addr, port); } else if (req->atyp == ATYP_HOST) { uint8_t namelen = *(uint8_t *)(req->addr); if (namelen > 0xFF) { logger_log(LOG_ERR, "unsupported address type: 0x%02x", req->atyp); request_ack(client, S5_REP_ADDRESS_TYPE_NOT_SUPPORTED); return; } buflen = sizeof(struct xsocks_request) + 1 + namelen + portlen; buf[0] = ATYP_HOST; memcpy(buf + 1, req->addr, 1 + namelen); memcpy(buf + 1 + 1 + namelen, req->addr + 1 + namelen, portlen); memcpy(client->target_addr, req->addr + 1, namelen); uint16_t port = read_size((uint8_t*)(req->addr + 1 + namelen)); sprintf(client->target_addr, "%s:%u", client->target_addr, port); } else if (req->atyp == ATYP_IPV6) { size_t in6_addr_len = sizeof(struct in6_addr); buflen = sizeof(struct xsocks_request) + in6_addr_len + portlen; buf[0] = ATYP_IPV6; memcpy(buf + 1, req->addr, in6_addr_len); memcpy(buf + 1 + in6_addr_len, req->addr + in6_addr_len, portlen); uv_inet_ntop(AF_INET6, (const void *)(req->addr), client->target_addr, INET_ADDRSTRLEN); uint16_t port = read_size((uint8_t*)(req->addr + in6_addr_len)); sprintf(client->target_addr, "%s:%u", client->target_addr, port); } else { logger_log(LOG_ERR, "unsupported address type: 0x%02x", req->atyp); request_ack(client, S5_REP_ADDRESS_TYPE_NOT_SUPPORTED); return; } request_ack(client, S5_REP_SUCCESSED); // TODO: handle UDP ASSOCIATE if (req->cmd == S5_CMD_CONNECT) { if (verbose) { logger_log(LOG_INFO, "connect to %s", client->target_addr); } int clen = buflen + PRIMITIVE_BYTES; uint8_t *c = client->buf + HEADER_BYTES; int rc = crypto_encrypt(c, (uint8_t *)buf, buflen); if (!rc) { forward_to_remote(remote, c, clen); } } }