diff --git a/src/lib/proxy.c b/src/lib/proxy.c index fadf6ba..cbb7df3 100644 --- a/src/lib/proxy.c +++ b/src/lib/proxy.c @@ -300,25 +300,8 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf) { return 0; } -int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg) { - char buffer[CHUNK_SIZE], err_buf[256]; - const char *connection, *upgrade, *ws_version; - long ret; - int tries = 0, retry = 0; - - *proxy_ptr = proxy_get_by_conf(conf); - proxy_ctx_t *proxy = *proxy_ptr; - proxy->client = NULL; - - if (proxy->initialized && sock_has_pending(&proxy->proxy) == 0) - goto proxy; - - retry: - if (proxy->initialized) - proxy_close(proxy); - - retry = 0; - tries++; +static int proxy_connect(proxy_ctx_t *proxy, host_config_t *conf, http_res *res, http_status_ctx *ctx, char *err_msg) { + char err_buf[256], addr_buf[1024]; int fd; if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) { @@ -329,8 +312,13 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu } sock_init(&proxy->proxy, fd, 0); - if (sock_set_socket_timeout(&proxy->proxy, 1) != 0 || sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT_INIT) != 0) - goto proxy_timeout_err; + if (sock_set_socket_timeout(&proxy->proxy, 1) != 0 || sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT_INIT) != 0) { + res->status = http_get_status(500); + ctx->origin = INTERNAL; + error("Unable to set timeout for reverse proxy socket"); + sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf))); + return -1; + } struct hostent *host_ent = gethostbyname2(conf->proxy.hostname, AF_INET6); if (host_ent == NULL) { @@ -340,7 +328,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu ctx->origin = SERVER_REQ; error("Unable to connect to server: Name or service not known"); sprintf(err_msg, "Unable to connect to server: Name or service not known."); - goto proxy_err; + return -1; } } @@ -353,9 +341,9 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu memcpy(&address.sin6_addr, addr, 16); } - inet_ntop(address.sin6_family, (void *) &address.sin6_addr, buffer, sizeof(buffer)); + inet_ntop(address.sin6_family, (void *) &address.sin6_addr, addr_buf, sizeof(addr_buf)); - info(BLUE_STR "Connecting to " BLD_STR "[%s]:%i" CLR_STR BLUE_STR "...", buffer, conf->proxy.port); + info(BLUE_STR "Connecting to " BLD_STR "[%s]:%i" CLR_STR BLUE_STR "...", addr_buf, conf->proxy.port); if (connect(proxy->proxy.socket, (struct sockaddr *) &address, sizeof(address)) < 0) { if (errno == ETIMEDOUT || errno == EINPROGRESS) { res->status = http_get_status(504); @@ -367,18 +355,17 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu res->status = http_get_status(500); ctx->origin = INTERNAL; } - error("Unable to connect to [%s]:%i", buffer, conf->proxy.port); + error("Unable to connect to [%s]:%i", addr_buf, conf->proxy.port); sprintf(err_msg, "Unable to connect to server: %s.", error_str(errno, err_buf, sizeof(err_buf))); - goto proxy_err; + return -1; } if (sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT) != 0) { - proxy_timeout_err: res->status = http_get_status(500); ctx->origin = INTERNAL; error("Unable to set timeout for reverse proxy socket"); sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf))); - goto proxy_err; + return -1; } if (conf->proxy.enc) { @@ -386,6 +373,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu SSL_set_fd(proxy->proxy.ssl, proxy->proxy.socket); SSL_set_connect_state(proxy->proxy.ssl); + int ret; if ((ret = SSL_do_handshake(proxy->proxy.ssl)) != 1) { sock_error(&proxy->proxy, (int) ret); SSL_free(proxy->proxy.ssl); @@ -393,7 +381,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu ctx->origin = SERVER_REQ; error("Unable to perform handshake"); sprintf(err_msg, "Unable to perform handshake: %s.", error_str(errno, err_buf, sizeof(err_buf))); - goto proxy_err; + return -1; } proxy->proxy.enc = 1; } @@ -401,173 +389,196 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu proxy->initialized = 1; proxy->cnx_s = clock_micros(); proxy->host = conf->name; - info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", buffer, conf->proxy.port); + info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", addr_buf, conf->proxy.port); - proxy: - connection = http_get_header_field(&req->hdr, "Connection"); - if (strcontains(connection, "upgrade") || strcontains(connection, "Upgrade")) { - upgrade = http_get_header_field(&req->hdr, "Upgrade"); - ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version"); - if (streq(upgrade, "websocket") && streq(ws_version, "13")) { - ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key"); + return 0; +} + +int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg) { + char buffer[CHUNK_SIZE], err_buf[256]; + long ret; + int tries = 0, retry = 1; + + *proxy_ptr = proxy_get_by_conf(conf); + proxy_ctx_t *proxy = *proxy_ptr; + proxy->client = NULL; + + while (retry) { + errno = 0; + + if (!proxy->initialized || sock_has_pending(&proxy->proxy) != 0) { + if (proxy->initialized) + proxy_close(proxy); + + retry = 0; + tries++; + + if (proxy_connect(proxy, conf, res, ctx, err_msg) != 0) + continue; + } + + const char *connection = http_get_header_field(&req->hdr, "Connection"); + if (strcontains(connection, "upgrade") || strcontains(connection, "Upgrade")) { + const char *upgrade = http_get_header_field(&req->hdr, "Upgrade"); + const char *ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version"); + if (streq(upgrade, "websocket") && streq(ws_version, "13")) { + ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key"); + } else { + res->status = http_get_status(501); + ctx->origin = INTERNAL; + return -1; + } } else { - res->status = http_get_status(501); + http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL); + http_add_header_field(&req->hdr, "Connection", "keep-alive"); + } + + ret = proxy_request_header(req, client); + if (ret != 0) { + res->status = http_get_status(500); ctx->origin = INTERNAL; return -1; } - } else { - http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL); - http_add_header_field(&req->hdr, "Connection", "keep-alive"); - } - ret = proxy_request_header(req, client); - if (ret != 0) { - res->status = http_get_status(500); - ctx->origin = INTERNAL; - return -1; - } - - ret = http_send_request(&proxy->proxy, req); - if (ret < 0) { - res->status = http_get_status(502); - ctx->origin = SERVER_REQ; - error("Unable to send request to server (1)"); - sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf))); - retry = tries < 4; - goto proxy_err; - } - - const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); - unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0; - const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding"); - - ret = 0; - if (content_len > 0) { - ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len); - } else if (strcontains(transfer_encoding, "chunked")) { - ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED); - } - - if (ret < 0 || (content_len != 0 && ret != content_len)) { - if (ret == -1 && errno != EPROTO) { + ret = http_send_request(&proxy->proxy, req); + if (ret < 0) { res->status = http_get_status(502); ctx->origin = SERVER_REQ; - error("Unable to send request to server (2)"); + error("Unable to send request to server (1)"); sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf))); retry = tries < 4; - goto proxy_err; - } else if (ret == -1) { - res->status = http_get_status(400); - ctx->origin = CLIENT_REQ; - error("Unable to receive request from client"); - sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf))); + continue; + } + + const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); + unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0; + const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding"); + + ret = 0; + if (content_len > 0) { + ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len); + } else if (strcontains(transfer_encoding, "chunked")) { + ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED); + } + + if (ret < 0 || (content_len != 0 && ret != content_len)) { + if (ret == -1 && errno != EPROTO) { + res->status = http_get_status(502); + ctx->origin = SERVER_REQ; + error("Unable to send request to server (2)"); + sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf))); + retry = tries < 4; + continue; + } else if (ret == -1) { + res->status = http_get_status(400); + ctx->origin = CLIENT_REQ; + error("Unable to receive request from client"); + sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf))); + return -1; + } + res->status = http_get_status(500); + ctx->origin = INTERNAL; + error("Unknown Error"); return -1; } - res->status = http_get_status(500); - ctx->origin = INTERNAL; - error("Unknown Error"); - return -1; - } - ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK); - if (ret <= 0) { - int e_sys = error_get_sys(), e_ssl = error_get_ssl(); - if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) { - res->status = http_get_status(504); - ctx->origin = SERVER_RES; - } else { + ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK); + if (ret <= 0) { + int e_sys = error_get_sys(), e_ssl = error_get_ssl(); + if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) { + res->status = http_get_status(504); + ctx->origin = SERVER_RES; + } else { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + } + error("Unable to receive response from server"); + sprintf(err_msg, "Unable to receive response from server: %s.", error_str(errno, err_buf, sizeof(err_buf))); + retry = tries < 4; + continue; + } + buffer[ret] = 0; + + char *buf = buffer; + unsigned short header_len = (unsigned short) (strstr(buffer, "\r\n\r\n") - buffer + 4); + + if (header_len <= 0) { res->status = http_get_status(502); ctx->origin = SERVER_RES; + error("Unable to parse header: End of header not found"); + sprintf(err_msg, "Unable to parser header: End of header not found."); + continue; } - error("Unable to receive response from server"); - sprintf(err_msg, "Unable to receive response from server: %s.", error_str(errno, err_buf, sizeof(err_buf))); - retry = tries < 4; - goto proxy_err; - } - buffer[ret] = 0; - char *buf = buffer; - unsigned short header_len = (unsigned short) (strstr(buffer, "\r\n\r\n") - buffer + 4); - - if (header_len <= 0) { - res->status = http_get_status(502); - ctx->origin = SERVER_RES; - error("Unable to parse header: End of header not found"); - sprintf(err_msg, "Unable to parser header: End of header not found."); - goto proxy_err; - } - - for (int i = 0; i < header_len; i++) { - if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) { - res->status = http_get_status(502); - ctx->origin = SERVER_RES; - error("Unable to parse header: Header contains illegal characters"); - sprintf(err_msg, "Unable to parse header: Header contains illegal characters."); - goto proxy_err; + for (int i = 0; i < header_len; i++) { + if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + error("Unable to parse header: Header contains illegal characters"); + sprintf(err_msg, "Unable to parse header: Header contains illegal characters."); + continue; + } } - } - char *ptr = buf; - while (header_len != (ptr - buf)) { - char *pos0 = strstr(ptr, "\r\n"); - if (pos0 == NULL) { - res->status = http_get_status(502); - ctx->origin = SERVER_RES; - error("Unable to parse header: Invalid header format"); - sprintf(err_msg, "Unable to parse header: Invalid header format."); - goto proxy_err; - } - if (ptr == buf) { - if (!strstarts(ptr, "HTTP/")) { + char *ptr = buf; + while (header_len != (ptr - buf)) { + char *pos0 = strstr(ptr, "\r\n"); + if (pos0 == NULL) { res->status = http_get_status(502); ctx->origin = SERVER_RES; error("Unable to parse header: Invalid header format"); sprintf(err_msg, "Unable to parse header: Invalid header format."); - goto proxy_err; + continue; } - int status_code = (int) strtol(ptr + 9, NULL, 10); - res->status = http_get_status(status_code); - if (res->status == NULL && status_code >= 100 && status_code <= 999) { - custom_status->code = status_code; - custom_status->type = 0; - snprintf(custom_status->msg, sizeof(custom_status->msg), "%.*s", - (int) (strchr(ptr, '\r') - ptr - 13), ptr + 13); - res->status = custom_status; - } else if (res->status == NULL) { - res->status = http_get_status(502); - ctx->origin = SERVER_RES; - error("Unable to parse header: Invalid or unknown status code"); - sprintf(err_msg, "Unable to parse header: Invalid or unknown status code."); - goto proxy_err; + if (ptr == buf) { + if (!strstarts(ptr, "HTTP/")) { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + error("Unable to parse header: Invalid header format"); + sprintf(err_msg, "Unable to parse header: Invalid header format."); + continue; + } + int status_code = (int) strtol(ptr + 9, NULL, 10); + res->status = http_get_status(status_code); + if (res->status == NULL && status_code >= 100 && status_code <= 999) { + custom_status->code = status_code; + custom_status->type = 0; + snprintf(custom_status->msg, sizeof(custom_status->msg), "%.*s", + (int) (strchr(ptr, '\r') - ptr - 13), ptr + 13); + res->status = custom_status; + } else if (res->status == NULL) { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + error("Unable to parse header: Invalid or unknown status code"); + sprintf(err_msg, "Unable to parse header: Invalid or unknown status code."); + continue; + } + } else { + if (http_parse_header_field(&res->hdr, ptr, pos0, 0) != 0) { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + error("Unable to parse header"); + sprintf(err_msg, "Unable to parse header."); + continue; + } } - } else { - if (http_parse_header_field(&res->hdr, ptr, pos0, 0) != 0) { - res->status = http_get_status(502); - ctx->origin = SERVER_RES; - error("Unable to parse header"); - sprintf(err_msg, "Unable to parse header."); - goto proxy_err; + if (pos0[2] == '\r' && pos0[3] == '\n') { + break; } + ptr = pos0 + 2; } - if (pos0[2] == '\r' && pos0[3] == '\n') { - break; - } - ptr = pos0 + 2; - } - sock_recv_x(&proxy->proxy, buffer, header_len, 0); + sock_recv_x(&proxy->proxy, buffer, header_len, 0); - ret = proxy_response_header(req, res, conf); - if (ret != 0) { - res->status = http_get_status(500); - ctx->origin = INTERNAL; - return -1; + ret = proxy_response_header(req, res, conf); + if (ret != 0) { + res->status = http_get_status(500); + ctx->origin = INTERNAL; + return -1; + } + + return 0; } - return 0; - - proxy_err: - errno = 0; - if (retry) goto retry; return -1; }