Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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);
	}
}
Пример #4
0
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;
}
Пример #5
0
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;
}