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 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; }