/* return 1 for drop, 0 for retry. */ static int auto_retry_or_drop(redsocks_client * client) { time_t now = redsocks_time(NULL); autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len; if (aclient->state == AUTOPROXY_NEW) { if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { on_connection_blocked(client); auto_retry(client, 0); return 0; } } else if ( aclient->state == AUTOPROXY_CONNECTED) { // if (now - aclient->time_connect_relay <= CIRCUIT_RESET_SECONDS) { on_connection_blocked(client); auto_retry(client, 0); return 0; } } /* drop */ return 1; }
/* return 1 for drop, 0 for retry. */ static int auto_retry_or_drop(redsocks_client * client) { time_t now = redsocks_time(NULL); socks5_client *socks5 = (void*)(client + 1); if (client->state == socks5_pre_detect) { if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) { auto_retry(client, 1); return 0; } } if ( client->state == socks5_direct) { // if (now - socks5->time_connect_relay <= CIRCUIT_RESET_SECONDS) { auto_retry(client, 0); return 0; } } /* drop */ return 1; }
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 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); } }