static void ss_relay_readcb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; struct bufferevent * from = buffev; struct bufferevent * to = client->client; size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->relay); redsocks_touch_client(client); if (client->state == ss_connected) { /* decrypt and forward data to client side */ if (output_size < to->wm_write.high) { if (input_size) decrypt_buffer(client, from, to); if (bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } else { if (bufferevent_disable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); } } else { redsocks_drop_client(client); } }
static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); } else { if (bufferevent_disable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); } }
static void auto_connect_relay(redsocks_client *client) { autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; time_t * acc_time = NULL; time_t now = redsocks_time(NULL); /* use default timeout if timeout is not configured */ if (tv.tv_sec == 0) tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; if (aclient->state == AUTOPROXY_NEW) { acc_time = get_addr_time_in_cache(&client->destaddr); if (acc_time) { if (now - *acc_time < CACHE_ITEM_STALE_SECONDS ) { redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache"); auto_retry(client, 0); return ; } else { /* stale this address in cache */ del_addr_from_cache(&client->destaddr); /* update timeout value for quick detection */ tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS; } } /* connect to target directly without going through proxy */ client->relay = red_connect_relay2(&client->destaddr, auto_relay_connected, auto_event_error, client, &tv); aclient->time_connect_relay = redsocks_time(NULL); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); redsocks_drop_client(client); } } else { redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state); } }
static void auto_connect_relay(redsocks_client *client) { socks5_client *socks5 = (void*)(client + 1); struct timeval tv; tv.tv_sec = CONNECT_TIMEOUT_SECONDS; tv.tv_usec = 0; if (client->state == socks5_pre_detect) { if (is_addr_in_cache(&client->destaddr)) { client->state = socks5_new; /* Connect SOCKS5 */ redsocks_log_error(client, LOG_DEBUG, "Found in cache"); } } client->relay = red_connect_relay2( client->state == socks5_pre_detect ? &client->destaddr : &client->instance->config.relayaddr, auto_relay_connected, auto_event_error, client, client->state == socks5_pre_detect ? &tv: NULL); socks5->time_connect_relay = redsocks_time(NULL); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "auto_connect_relay"); redsocks_drop_client(client); } }
static void ss_relay_writecb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; struct bufferevent * from = client->client; struct bufferevent * to = buffev; size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from)); size_t output_size = evbuffer_get_length(bufferevent_get_output(to)); assert(buffev == client->relay); redsocks_touch_client(client); if (process_shutdown_on_write_(client, from, to)) return; if (client->state == ss_connected) { /* encrypt and forward data received from client side */ if (output_size < to->wm_write.high) { if (input_size) encrypt_buffer(client, from, to); if (!(client->client_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } else { redsocks_drop_client(client); } }
static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { redsocks_client *client = _arg; int saved_errno = errno; assert(buffev == client->relay || buffev == client->client); redsocks_touch_client(client); redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: %x", saved_errno, client->state, what); if (buffev == client->relay) { if ( client->state == socks5_pre_detect && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { /* In case timeout occurs for connecting relay, we try to connect to target with SOCKS5 proxy. It is possible that the connection to target can be set up a bit longer than the timeout value we set. However, it is still better to make connection via proxy. */ auto_retry(client, 1); return; } if (client->state == socks5_pre_detect && saved_errno == ECONNRESET) if (!auto_retry_or_drop(client)) return; if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET ) { if (!auto_retry_or_drop(client)) return; } } if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { struct bufferevent *antiev; if (buffev == client->relay) antiev = client->client; else antiev = client->relay; redsocks_shutdown(client, buffev, SHUT_RD); if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) redsocks_shutdown(client, antiev, SHUT_WR); } else { /* myerrno = red_socket_geterrno(buffev); redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str, buffev == client->relay ? "relay" : "client", event_fmt(what)); */ redsocks_drop_client(client); } }
static void auto_event_error(struct bufferevent *buffev, short what, void *_arg) { redsocks_client *client = _arg; autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; int saved_errno = errno; assert(buffev == client->relay || buffev == client->client); redsocks_touch_client(client); redsocks_log_errno(client, LOG_DEBUG, "%s errno(%d), State: %d, what: " event_fmt_str, buffev == client->client?"client":"relay", saved_errno, aclient->state, event_fmt(what)); if (buffev == client->relay) { if ( aclient->state == AUTOPROXY_NEW && what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT)) { on_connection_blocked(client); /* In case timeout occurs while connecting relay, we try to connect to target via SOCKS5 proxy. It is possible that the connection to target can be set up a bit longer than the timeout value we set. However, it is still better to make connection via proxy. */ auto_retry(client, 1); return; } if (aclient->state == AUTOPROXY_NEW && saved_errno == ECONNRESET) if (!auto_retry_or_drop(client)) return; if (aclient->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) && saved_errno == ECONNRESET ) { if (!auto_retry_or_drop(client)) return; } } if (what == (EVBUFFER_READ|EVBUFFER_EOF)) { struct bufferevent *antiev; if (buffev == client->relay) antiev = client->client; else antiev = client->relay; redsocks_shutdown(client, buffev, SHUT_RD); if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0) redsocks_shutdown(client, antiev, SHUT_WR); } else { redsocks_drop_client(client); } }
static void httpr_connect_relay(redsocks_client *client) { int error; client->client->readcb = httpr_client_read_cb; error = bufferevent_enable(client->client, EV_READ); if (error) { redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); redsocks_drop_client(client); } }
static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; struct bufferevent * from = client->relay; redsocks_touch_client(client); if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { redsocks_shutdown(client, to, SHUT_WR); return; } if (aclient->state == AUTOPROXY_CONNECTED) { if (!aclient->data_recv) aclient->data_recv = EVBUFFER_LENGTH(from->input); } if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } }
static void direct_relay_clientwritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; socks5_client *socks5 = (void*)(client + 1); struct bufferevent * from = client->relay; redsocks_touch_client(client); if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) { redsocks_shutdown(client, to, SHUT_WR); return; } if (client->state == socks5_direct) { if (!socks5->got_data) socks5->got_data = EVBUFFER_LENGTH(from->input); } if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } }
static int direct_connect_relay(redsocks_client *client) { char * interface = client->instance->config.interface; struct timeval tv = {client->instance->config.timeout, 0}; // Allowing binding relay socket to specified IP for outgoing connections client->relay = red_connect_relay(interface, &client->destaddr, NULL, redsocks_relay_connected, redsocks_event_error, client, &tv); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); redsocks_drop_client(client); return -1; } return 0; }
static int direct_connect_relay(redsocks_client *client) { char * interface = client->instance->config.interface; // Allowing binding relay socket to specified IP for outgoing connections if (interface && strlen(interface)) { client->relay = red_connect_relay_if(interface, &client->destaddr, NULL, redsocks_relay_connected, redsocks_event_error, client); } else client->relay = red_connect_relay(&client->destaddr, NULL, redsocks_relay_connected, redsocks_event_error, client); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "red_connect_relay"); redsocks_drop_client(client); return -1; } return 0; }
static int ss_connect_relay(redsocks_client *client) { struct timeval tv; tv.tv_sec = client->instance->config.timeout; tv.tv_usec = 0; /* use default timeout if timeout is not configured */ if (tv.tv_sec == 0) tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; client->relay = red_connect_relay2(&client->instance->config.relayaddr, NULL, ss_relay_connected, redsocks_event_error, client, &tv); if (!client->relay) { redsocks_log_errno(client, LOG_ERR, "ss_connect_relay"); redsocks_drop_client(client); return -1; } return 0; }
static void httpc_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; assert(client->relay == buffev); assert(client->state == httpc_request_sent || client->state == httpc_reply_came); redsocks_touch_client(client); // evbuffer_add() triggers callbacks, so we can't write to client->client // till we know that we're going to ONFAIL_FORWARD_HTTP_ERR. // And the decision is made when all the headers are processed. struct evbuffer* tee = NULL; const bool do_errtee = client->instance->config.on_proxy_fail == ONFAIL_FORWARD_HTTP_ERR; if (client->state == httpc_request_sent) { size_t len = evbuffer_get_length(buffev->input); char *line = redsocks_evbuffer_readline(buffev->input); if (line) { unsigned int code; if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match if (code == 407) { // auth failed http_auth *auth = red_http_auth(client->instance); if (auth->last_auth_query != NULL && auth->last_auth_count == 1) { redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth failed: %s", line); client->state = httpc_no_way; } else if (client->instance->config.login == NULL || client->instance->config.password == NULL) { redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no login/password configured: %s", line); client->state = httpc_no_way; } else { if (do_errtee) tee = evbuffer_new(); char *auth_request = http_auth_request_header(buffev->input, tee); if (!auth_request) { redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no <%s> header found: %s", auth_request_header, line); client->state = httpc_no_way; } else { free(line); if (tee) evbuffer_free(tee); free(auth->last_auth_query); char *ptr = auth_request; ptr += strlen(auth_request_header); while (isspace(*ptr)) ptr++; size_t last_auth_query_len = strlen(ptr) + 1; auth->last_auth_query = calloc(last_auth_query_len, 1); memcpy(auth->last_auth_query, ptr, last_auth_query_len); auth->last_auth_count = 0; free(auth_request); if (bufferevent_disable(client->relay, EV_WRITE)) { redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); return; } /* close relay tunnel */ redsocks_bufferevent_free(client->relay); /* set to initial state*/ client->state = httpc_new; /* and reconnect */ redsocks_connect_relay(client); return; } } } else if (200 <= code && code <= 299) { client->state = httpc_reply_came; } else { redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy error: %s", line); client->state = httpc_no_way; } } else { redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy bad firstline: %s", line); client->state = httpc_no_way; } if (do_errtee && client->state == httpc_no_way) { if (bufferevent_write(client->client, line, strlen(line)) != 0 || bufferevent_write(client->client, "\r\n", 2) != 0) { redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write"); goto fail; } } free(line); } else if (len >= HTTP_HEAD_WM_HIGH) { redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy reply is too long, %zu bytes", len); client->state = httpc_no_way; } } if (do_errtee && client->state == httpc_no_way) { if (tee) { if (bufferevent_write_buffer(client->client, tee) != 0) { redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write_buffer"); goto fail; } } redsocks_shutdown(client, client->client, SHUT_RD); const size_t avail = evbuffer_get_length(client->client->input); if (avail) { if (evbuffer_drain(client->client->input, avail) != 0) { redsocks_log_errno(client, LOG_NOTICE, "evbuffer_drain"); goto fail; } } redsocks_shutdown(client, client->relay, SHUT_WR); client->state = httpc_headers_skipped; } fail: if (tee) { evbuffer_free(tee); } if (client->state == httpc_no_way) { redsocks_drop_client(client); return; } while (client->state == httpc_reply_came) { char *line = redsocks_evbuffer_readline(buffev->input); if (line) { if (strlen(line) == 0) { client->state = httpc_headers_skipped; } free(line); } else { break; } } if (client->state == httpc_headers_skipped) { redsocks_start_relay(client); } }
static void httpr_relay_write_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; httpr_client *httpr = (void*)(client + 1); int len = 0; assert(client->state >= httpr_recv_request_headers); redsocks_touch_client(client); if (client->state == httpr_recv_request_headers) { if (httpr->firstline) { len = bufferevent_write(client->relay, httpr->firstline, strlen(httpr->firstline)); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "bufferevent_write"); redsocks_drop_client(client); return; } } http_auth *auth = (void*)(client->instance + 1); ++auth->last_auth_count; const char *auth_scheme = NULL; char *auth_string = NULL; if (auth->last_auth_query != NULL) { /* find previous auth challange */ if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) { auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password); auth_scheme = "Basic"; } else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0 && httpr->firstline) { /* calculate method & uri */ char *ptr = strchr(httpr->firstline, ' '), *ptr2; char *method = calloc(ptr - httpr->firstline + 1, 1); memcpy(method, httpr->firstline, ptr - httpr->firstline); method[ptr - httpr->firstline] = 0; ptr = strchr(httpr->firstline, '/'); if (!ptr || *++ptr != '/') { free(method); redsocks_log_error(client, LOG_NOTICE, "malformed request came"); redsocks_drop_client(client); return; } if (!(ptr = strchr(++ptr, '/')) || !(ptr2 = strchr(ptr, ' '))) { free(method); redsocks_log_error(client, LOG_NOTICE, "malformed request came"); redsocks_drop_client(client); return; } char *uri = calloc(ptr2 - ptr + 1, 1); memcpy(uri, ptr, ptr2 - ptr); uri[ptr2 - ptr] = 0; /* prepare an random string for cnounce */ char cnounce[17]; snprintf(cnounce, sizeof(cnounce), "%08x%08x", red_randui32(), red_randui32()); auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line client->instance->config.login, client->instance->config.password, //user, pass method, uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce free(method); free(uri); auth_scheme = "Digest"; } } if (auth_string != NULL) { len = 0; len |= bufferevent_write(client->relay, auth_response_header, strlen(auth_response_header)); len |= bufferevent_write(client->relay, " ", 1); len |= bufferevent_write(client->relay, auth_scheme, strlen(auth_scheme)); len |= bufferevent_write(client->relay, " ", 1); len |= bufferevent_write(client->relay, auth_string, strlen(auth_string)); len |= bufferevent_write(client->relay, "\r\n", 2); if (len) { redsocks_log_errno(client, LOG_ERR, "bufferevent_write"); redsocks_drop_client(client); return; } } free(auth_string); len = bufferevent_write(client->relay, httpr->client_buffer.buff, httpr->client_buffer.len); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "bufferevent_write"); redsocks_drop_client(client); return; } client->state = httpr_request_sent; buffev->wm_read.low = 1; buffev->wm_read.high = HTTP_HEAD_WM_HIGH; bufferevent_enable(buffev, EV_READ); } }
static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; httpr_client *httpr = (void*)(client + 1); int dropped = 0; assert(client->state >= httpr_request_sent); redsocks_touch_client(client); httpr_buffer_fini(&httpr->relay_buffer); httpr_buffer_init(&httpr->relay_buffer); if (client->state == httpr_request_sent) { size_t len = EVBUFFER_LENGTH(buffev->input); char *line = redsocks_evbuffer_readline(buffev->input); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); unsigned int code; if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match if (code == 407) { // auth failed http_auth *auth = (void*)(client->instance + 1); if (auth->last_auth_query != NULL && auth->last_auth_count == 1) { redsocks_log_error(client, LOG_NOTICE, "proxy auth failed"); redsocks_drop_client(client); dropped = 1; } else if (client->instance->config.login == NULL || client->instance->config.password == NULL) { redsocks_log_error(client, LOG_NOTICE, "proxy auth required, but no login information provided"); redsocks_drop_client(client); dropped = 1; } else { free(line); char *auth_request = get_auth_request_header(buffev->input); if (!auth_request) { redsocks_log_error(client, LOG_NOTICE, "403 found, but no proxy auth challenge"); redsocks_drop_client(client); dropped = 1; } else { free(auth->last_auth_query); char *ptr = auth_request; ptr += strlen(auth_request_header); while (isspace(*ptr)) ptr++; auth->last_auth_query = calloc(strlen(ptr) + 1, 1); strcpy(auth->last_auth_query, ptr); auth->last_auth_count = 0; free(auth_request); httpr_buffer_fini(&httpr->relay_buffer); if (bufferevent_disable(client->relay, EV_WRITE)) { redsocks_log_errno(client, LOG_ERR, "bufferevent_disable"); return; } /* close relay tunnel */ redsocks_close(EVENT_FD(&client->relay->ev_write)); bufferevent_free(client->relay); /* set to initial state*/ client->state = httpr_recv_request_headers; /* and reconnect */ redsocks_connect_relay(client); return; } } } else if (100 <= code && code <= 999) { client->state = httpr_reply_came; } else { redsocks_log_error(client, LOG_NOTICE, "%s", line); redsocks_drop_client(client); dropped = 1; } } free(line); } else if (len >= HTTP_HEAD_WM_HIGH) { redsocks_drop_client(client); dropped = 1; } } if (dropped) return; while (client->state == httpr_reply_came) { char *line = redsocks_evbuffer_readline(buffev->input); if (line) { httpr_buffer_append(&httpr->relay_buffer, line, strlen(line)); httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2); if (strlen(line) == 0) { client->state = httpr_headers_skipped; } free(line); } else { break; } } if (client->state == httpr_headers_skipped) { if (bufferevent_write(client->client, httpr->relay_buffer.buff, httpr->relay_buffer.len) != 0) { redsocks_log_error(client, LOG_NOTICE, "bufferevent_write"); redsocks_drop_client(client); return; } redsocks_start_relay(client); } }
static struct evbuffer *httpc_mkconnect(redsocks_client *client) { struct evbuffer *buff = NULL, *retval = NULL; int len; buff = evbuffer_new(); if (!buff) { redsocks_log_errno(client, LOG_ERR, "evbuffer_new"); goto fail; } http_auth *auth = (void*)(client->instance + 1); ++auth->last_auth_count; const char *auth_scheme = NULL; char *auth_string = NULL; if (auth->last_auth_query != NULL) { /* find previous auth challange */ if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) { auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password); auth_scheme = "Basic"; } else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) { /* calculate uri */ char uri[128]; snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port)); /* prepare an random string for cnounce */ char cnounce[17]; srand(time(0)); for (int i = 0; i < 16; i += 4) sprintf(cnounce + i, "%02x", rand() & 65535); auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line client->instance->config.login, client->instance->config.password, //user, pass "CONNECT", uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce auth_scheme = "Digest"; } } if (auth_string == NULL) { len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n\r\n", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port) ); } else { len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n%s %s %s\r\n\r\n", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port), auth_response_header, auth_scheme, auth_string ); } free(auth_string); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); goto fail; } retval = buff; buff = NULL; fail: if (buff) evbuffer_free(buff); return retval; }
static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; struct bufferevent * from = client->client; redsocks_touch_client(client); if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { redsocks_shutdown(client, to, SHUT_WR); return; } else if (aclient->state == AUTOPROXY_CONNECTED ) { /* Not send or receive data. */ if (!aclient->data_sent && !aclient->data_recv) { /* Ensure we have data to send */ if (EVBUFFER_LENGTH(from->input)) { /* copy data from input to output of relay */ aclient->data_sent = copy_evbuffer (to, from, 0); redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); } } /* * Relay reaceived data before writing to relay. */ else if (!aclient->data_sent && aclient->data_recv) { redsocks_log_error(client, LOG_DEBUG, "not sent, got"); aclient->data_sent = copy_evbuffer(to, from, 0); } /* aclient->state = AUTOPROXY_CONFIRMED; */ /* We have writen data to relay, but got nothing until we are requested to * write to it again. */ else if (aclient->data_sent && !aclient->data_recv) { /* No response from relay and no CONNECTION RESET, Send more data. */ redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", evbuffer_get_length(from->input), evbuffer_get_length(to->output), to->wm_write.high, aclient->data_sent ); /* Write more data util input buffer is full */ if (EVBUFFER_LENGTH(from->input)- aclient->data_sent > 0) /* we have more data waiting to be sent */ { aclient->data_sent += copy_evbuffer(to, from, aclient->data_sent); } else if (EVBUFFER_LENGTH(to->output) < aclient->data_sent /* data is sent out, more or less */ && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* do not confirm unless read buffer is full */ && EVBUFFER_LENGTH(from->input) == aclient->data_sent /* all data in read buffer is sent */ ) { evbuffer_drain(from->input, aclient->data_sent); aclient->data_sent = 0; aclient->state = AUTOPROXY_CONFIRMED; on_connection_confirmed(client); } } /* We sent data to and got data from relay. */ else if (aclient->data_sent && aclient->data_recv) { /* No CONNECTION RESET error occur after sending data, good. */ aclient->state = AUTOPROXY_CONFIRMED; on_connection_confirmed(client); redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv); if (evbuffer_get_length(from->input)) { evbuffer_drain(from->input, aclient->data_sent); aclient->data_sent = 0; } } } if (aclient->state == AUTOPROXY_CONFIRMED) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } }
static struct evbuffer *httpc_mkconnect(redsocks_client *client) { struct evbuffer *buff = NULL, *retval = NULL; char *auth_string = NULL; int len; buff = evbuffer_new(); if (!buff) { redsocks_log_errno(client, LOG_ERR, "evbuffer_new"); goto fail; } http_auth *auth = red_http_auth(client->instance); ++auth->last_auth_count; const char *auth_scheme = NULL; if (auth->last_auth_query != NULL) { /* find previous auth challange */ if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) { auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password); auth_scheme = "Basic"; } else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) { /* calculate uri */ char uri[128]; snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port)); /* prepare an random string for cnounce */ char cnounce[17]; snprintf(cnounce, sizeof(cnounce), "%08x%08x", red_randui32(), red_randui32()); auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line client->instance->config.login, client->instance->config.password, //user, pass "CONNECT", uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce auth_scheme = "Digest"; } } // TODO: do accurate evbuffer_expand() while cleaning up http-auth len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port)); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); goto fail; } if (auth_string) { len = evbuffer_add_printf(buff, "%s %s %s\r\n", auth_response_header, auth_scheme, auth_string); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); goto fail; } free(auth_string); auth_string = NULL; } const enum disclose_src_e disclose_src = client->instance->config.disclose_src; if (disclose_src != DISCLOSE_NONE) { char clientip[INET_ADDRSTRLEN]; const char *ip = inet_ntop(client->clientaddr.sin_family, &client->clientaddr.sin_addr, clientip, sizeof(clientip)); if (!ip) { redsocks_log_errno(client, LOG_ERR, "inet_ntop"); goto fail; } if (disclose_src == DISCLOSE_X_FORWARDED_FOR) { len = evbuffer_add_printf(buff, "X-Forwarded-For: %s\r\n", ip); } else if (disclose_src == DISCLOSE_FORWARDED_IP) { len = evbuffer_add_printf(buff, "Forwarded: for=%s\r\n", ip); } else if (disclose_src == DISCLOSE_FORWARDED_IPPORT) { len = evbuffer_add_printf(buff, "Forwarded: for=\"%s:%d\"\r\n", ip, ntohs(client->clientaddr.sin_port)); } if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); goto fail; } } len = evbuffer_add(buff, "\r\n", 2); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add"); goto fail; } retval = buff; buff = NULL; fail: if (auth_string) free(auth_string); if (buff) evbuffer_free(buff); return retval; }
static void direct_relay_relaywritecb(struct bufferevent *to, void *_client) { redsocks_client *client = _client; socks5_client *socks5 = (void*)(client + 1); struct bufferevent * from = client->client; redsocks_touch_client(client); if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) { redsocks_shutdown(client, to, SHUT_WR); return; } else if (client->state == socks5_direct ) { /* Not send or receive data. */ if (!socks5->data_sent && !socks5->got_data) { /* Ensure we have data to send */ if (EVBUFFER_LENGTH(from->input)) { /* copy data from input to output of relay */ socks5->data_sent = copy_evbuffer (to, from); redsocks_log_error(client, LOG_DEBUG, "not sent, not got %d", EVBUFFER_LENGTH(from->input)); } } /* * Relay reaceived data before writing to relay. */ else if (!socks5->data_sent && socks5->got_data) { redsocks_log_error(client, LOG_DEBUG, "not sent, got"); socks5->data_sent = copy_evbuffer(to, from); } /* client->state = socks5_direct_confirmed; */ /* We have writen data to relay, but got nothing until we are requested to * write to it again. */ else if (socks5->data_sent && !socks5->got_data) { /* No response from relay and no CONNECTION RESET, Send more data. */ redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d", evbuffer_get_length(from->input), evbuffer_get_length(to->output), to->wm_write.high, socks5->data_sent ); /* TODO: Write more data? */ if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /* data is sent out, more or less */ && EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */ && EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */ ) { evbuffer_drain(from->input, socks5->data_sent); socks5->data_sent = 0; client->state = socks5_direct_confirmed; } } /* We sent data to and got data from relay. */ else if (socks5->data_sent && socks5->got_data) { /* No CONNECTION RESET error occur after sending data, good. */ client->state = socks5_direct_confirmed; redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", socks5->got_data); if (evbuffer_get_length(from->input)) { evbuffer_drain(from->input, socks5->data_sent); socks5->data_sent = 0; } } } if (client->state == socks5_direct_confirmed) { if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) { if (bufferevent_write_buffer(to, from->input) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer"); if (bufferevent_enable(from, EV_READ) == -1) redsocks_log_errno(client, LOG_ERR, "bufferevent_enable"); } } }