struct bufferevent* red_connect_relay_tfo(const char *ifname, struct sockaddr_in *addr, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, void *cbarg, const struct timeval *timeout_write, void *data, size_t *len) { struct bufferevent *retval = NULL; int relay_fd = -1; int error; retval = red_prepare_relay(ifname, readcb, writecb, errorcb, cbarg); if (retval) { relay_fd = bufferevent_getfd(retval); if (timeout_write) bufferevent_set_timeouts(retval, NULL, timeout_write); #ifdef MSG_FASTOPEN size_t s = sendto(relay_fd, data, * len, MSG_FASTOPEN, (struct sockaddr *)addr, sizeof(*addr) ); *len = 0; // Assume nothing sent, caller needs to write data again when connection is setup. if (s == -1) { if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) { // Remote server doesn't support tfo or it's the first connection to the server. // Connection will automatically fall back to conventional TCP. log_error(LOG_DEBUG, "TFO: no cookie"); return retval; } else if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT || errno == ENOPROTOOPT) { // Disable fast open as it's not supported log_error(LOG_DEBUG, "TFO: not support"); goto fallback; } else { log_errno(LOG_NOTICE, "sendto"); goto fail; } } else { log_error(LOG_DEBUG, "TFO: cookie found"); *len = s; // data is put into socket buffer return retval; } fallback: #endif error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; } // write data to evbuffer so that data can be sent when connection is set up if (bufferevent_write(retval, data, *len) != 0) { log_errno(LOG_NOTICE, "bufferevent_write"); *len = 0; // Nothing sent, caller needs to write data again when connection is setup. goto fail; } } return retval; fail: if (retval) { bufferevent_disable(retval, EV_READ|EV_WRITE); bufferevent_free(retval); } if (relay_fd != -1) redsocks_close(relay_fd); return NULL; }
struct bufferevent* red_connect_relay_ssl(const char *ifname, struct sockaddr_in *addr, SSL * ssl, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, void *cbarg, const struct timeval *timeout_write) { struct bufferevent *retval = NULL; struct bufferevent *underlying = NULL; int relay_fd = -1; int error; underlying = red_prepare_relay(ifname, NULL, NULL, NULL, NULL); if (!underlying) goto fail; relay_fd = bufferevent_getfd(underlying); if (timeout_write) bufferevent_set_timeouts(underlying, NULL, timeout_write); error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "connect"); goto fail; } retval = bufferevent_openssl_filter_new(bufferevent_get_base(underlying), underlying, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS); if (!retval) { log_errno(LOG_NOTICE, "bufferevent_openssl_filter_new"); goto fail; } if (timeout_write) bufferevent_set_timeouts(retval, NULL, timeout_write); bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); if (writecb) { error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... if (error) { log_errno(LOG_ERR, "bufferevent_enable"); goto fail; } } return retval; fail: if (retval) { bufferevent_disable(retval, EV_READ|EV_WRITE); bufferevent_free(retval); } if (underlying) { bufferevent_disable(underlying, EV_READ|EV_WRITE); bufferevent_free(underlying); } if (relay_fd != -1) redsocks_close(relay_fd); return NULL; }
void httpc_read_cb(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; int dropped = 0; assert(client->state >= httpc_request_sent); redsocks_touch_client(client); if (client->state == httpc_request_sent) { size_t len = EVBUFFER_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 = (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 { 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(line); 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_close(EVENT_FD(&client->relay->ev_write)); 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, "%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 == 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); } }
struct bufferevent* red_prepare_relay(const char *ifname, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb errorcb, void *cbarg) { struct bufferevent *retval = NULL; int relay_fd = -1; int error; relay_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (relay_fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; } if (ifname && strlen(ifname)) { #ifdef USE_PF // BSD error = setsockopt(relay_fd, SOL_SOCKET, IP_RECVIF, ifname, strlen(ifname)); #else // Linux error = setsockopt(relay_fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); #endif if (error) { log_errno(LOG_ERR, "setsockopt"); goto fail; } } error = evutil_make_socket_nonblocking(relay_fd); if (error) { log_errno(LOG_ERR, "evutil_make_socket_nonblocking"); goto fail; } retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); if (!retval) { log_errno(LOG_ERR, "bufferevent_socket_new"); goto fail; } bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); if (writecb) { error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... if (error) { log_errno(LOG_ERR, "bufferevent_enable"); goto fail; } } if (apply_tcp_keepalive(relay_fd)) goto fail; return retval; fail: if (retval){ bufferevent_disable(retval, EV_READ|EV_WRITE); bufferevent_free(retval); } if (relay_fd != -1) redsocks_close(relay_fd); return NULL; }
struct bufferevent* red_connect_relay2(struct sockaddr_in *addr, evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg, const struct timeval *timeout_write) { struct bufferevent *retval = NULL; int on = 1; int relay_fd = -1; int error; relay_fd = socket(AF_INET, SOCK_STREAM, 0); if (relay_fd == -1) { log_errno(LOG_ERR, "socket"); goto fail; } error = evutil_make_socket_nonblocking(relay_fd); if (error) { log_errno(LOG_ERR, "fcntl"); goto fail; } error = setsockopt(relay_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); if (error) { log_errno(LOG_WARNING, "setsockopt"); goto fail; } retval = bufferevent_socket_new(get_event_base(), relay_fd, 0); if (!retval) { log_errno(LOG_ERR, "bufferevent_socket_new"); goto fail; } bufferevent_setcb(retval, readcb, writecb, errorcb, cbarg); error = bufferevent_enable(retval, EV_WRITE); // we wait for connection... if (error) { log_errno(LOG_ERR, "bufferevent_enable"); goto fail; } bufferevent_set_timeouts(retval, NULL, timeout_write); // error = bufferevent_socket_connect(retval, (struct sockaddr*)addr, sizeof(*addr)); // if (error) { error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr)); if (error && errno != EINPROGRESS) { log_errno(LOG_NOTICE, "bufferevent_socket_connect"); goto fail; } return retval; fail: if (relay_fd != -1) redsocks_close(relay_fd); if (retval) bufferevent_free(retval); return NULL; }