Exemple #1
0
BOOL rdg_in_channel_recv(rdpRdg* rdg)
{
	BOOL status = TRUE;
	HttpResponse* response = NULL;

	switch (rdg->state)
	{
		case RDG_CLIENT_STATE_IN_CHANNEL_REQUEST:
			response = http_response_recv(rdg->tlsIn);

			if (!response)
				return FALSE;

			status = rdg_process_in_channel_response(rdg, response);
			http_response_free(response);
			break;

		case RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZE:
			response = http_response_recv(rdg->tlsIn);

			if (!response)
				return FALSE;

			status = rdg_process_in_channel_authorization(rdg, response);
			http_response_free(response);
			break;
	}

	return status;
}
Exemple #2
0
BOOL rts_connect(rdpRpc* rpc)
{
	int status;
	RTS_PDU rts_pdu;
	HttpResponse* http_response;

	if (!rpc_ntlm_http_out_connect(rpc))
	{
		printf("rpc_out_connect_http error!\n");
		return FALSE;
	}

	if (!rts_send_CONN_A1_pdu(rpc))
	{
		printf("rpc_send_CONN_A1_pdu error!\n");
		return FALSE;
	}

	if (!rpc_ntlm_http_in_connect(rpc))
	{
		printf("rpc_in_connect_http error!\n");
		return FALSE;
	}

	if (!rts_send_CONN_B1_pdu(rpc))
	{
		printf("rpc_send_CONN_B1_pdu error!\n");
		return FALSE;
	}

	/* Receive OUT Channel Response */
	http_response = http_response_recv(rpc->tls_out);

	if (http_response->StatusCode != 200)
	{
		printf("rts_connect error!\n");
		http_response_print(http_response);
		http_response_free(http_response) ;
		return FALSE;
	}

	http_response_print(http_response);

	http_response_free(http_response);

	/* Receive CONN_A3 RTS PDU */
	status = rts_recv_pdu(rpc, &rts_pdu);

	/* Receive CONN_C2 RTS PDU */
	status = rts_recv_pdu(rpc, &rts_pdu);

	return TRUE;
}
Exemple #3
0
int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc)
{
	char* token64;
	int ntlm_token_length = 0;
	BYTE* ntlm_token_data = NULL;
	HttpResponse* http_response;
	rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;

	http_response = http_response_recv(rpc->TlsIn);

	if (!http_response)
		return -1;

	if (ListDictionary_Contains(http_response->Authenticates, "NTLM"))
	{
		token64 = ListDictionary_GetItemValue(http_response->Authenticates, "NTLM");

		if (!token64)
			goto out;

		crypto_base64_decode(token64, strlen(token64), &ntlm_token_data, &ntlm_token_length);
	}

out:
	ntlm->inputBuffer[0].pvBuffer = ntlm_token_data;
	ntlm->inputBuffer[0].cbBuffer = ntlm_token_length;
	http_response_free(http_response);

	return 0;
}
Exemple #4
0
/* This is a convenience wrapper around http_parse_status_line that only returns
   the status code. Returns the status code on success or -1 on failure. */
int http_parse_status_line_code(const char *line)
{
    struct http_response resp;
    int code;

    if (http_parse_status_line(line, &resp) != 0)
        return -1;
    code = resp.code;
    http_response_free(&resp);

    return code;
}
Exemple #5
0
BOOL rdg_out_channel_recv(rdpRdg* rdg)
{
	wStream* s;
	BOOL status = TRUE;
	HttpResponse* response = NULL;

	switch (rdg->state)
	{
		case RDG_CLIENT_STATE_OUT_CHANNEL_REQUEST:
			response = http_response_recv(rdg->tlsOut);
			if (!response)
			{
				return FALSE;
			}
			status = rdg_process_out_channel_response(rdg, response);
			http_response_free(response);
			break;

		case RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZE:
			response = http_response_recv(rdg->tlsOut);
			if (!response)
			{
				return FALSE;
			}
			status = rdg_process_out_channel_authorization(rdg, response);
			http_response_free(response);
			break;

		default:
			s = rdg_receive_packet(rdg);
			if (s)
			{
				status = rdg_process_packet(rdg, s);
				Stream_Free(s, TRUE);
			}
	}

	return status;
}
static void push_http_response(struct http_thread_env *hte, HttpResponse *resp)
{
	gchar *msg;

	add_std_headers(resp, hte->json ? "json" : "xml");

	msg = http_response_to_string(resp);
	http_response_free(resp);

	write_all(hte->te->sock, msg, strlen(msg));

	mbb_log_lvl(MBB_LOG_HTTP, "send: %s", msg);
	g_free(msg);
}
Exemple #7
0
boolean rpc_ntlm_http_in_connect(rdpRpc* rpc)
{
	STREAM* s;
	int ntlm_token_length;
	uint8* ntlm_token_data;
	HttpResponse* http_response;
	rdpNtlm* ntlm = rpc->ntlm_http_in->ntlm;

	ntlm_client_init(ntlm, true, rpc->settings->username,
			rpc->settings->domain, rpc->settings->password);

	ntlm_authenticate(ntlm);

	s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0, TSG_CHANNEL_IN);

	/* Send IN Channel Request */

	DEBUG_RPC("\n%s", s->data);
	tls_write_all(rpc->tls_in, s->data, s->size);
	stream_free(s);

	/* Receive IN Channel Response */

	http_response = http_response_recv(rpc->tls_in);

	ntlm_token_data = NULL;
	crypto_base64_decode((uint8*) http_response->AuthParam, strlen(http_response->AuthParam),
			&ntlm_token_data, &ntlm_token_length);

	ntlm->inputBuffer.pvBuffer = ntlm_token_data;
	ntlm->inputBuffer.cbBuffer = ntlm_token_length;

	ntlm_authenticate(ntlm);

	http_response_free(http_response);

	s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0x40000000, TSG_CHANNEL_IN);

	/* Send IN Channel Request */

	DEBUG_RPC("\n%s", s->data);
	tls_write_all(rpc->tls_in, s->data, s->size);
	stream_free(s);

	ntlm_client_uninit(ntlm);
	ntlm_free(ntlm);

	return true;
}
Exemple #8
0
/* Receives an HTTP response from socket SOCKFD; returns a decoded KVMessage.
 * Returns NULL if there is an error. */
kvresponse_t *kvresponse_recieve(int sockfd) {
  kvresponse_t *kvres = calloc(1, sizeof(kvresponse_t));

  struct http_response *res = http_response_parse(sockfd);
  if (!res) goto error;
  kvres->type = kvresponse_get_status_code(res->status);
  if (kvres->type == EMPTY) goto error;
  kvres->body = res->body;

  free(res);
  return kvres;

error:
  http_response_free(res);
  kvresponse_free(kvres);
  return NULL;
}
void test_post() {
    char*  p;
    http_response * response = http_post("123.125.44.242", 80, "/Plogin.do",
                                         "[email protected]&password=88888");  // xiaonei.com/home
    printf("%d\n", response -> status_code);

    while(( p = evbuffer_readline(response->headers))) {
        printf("%s\n", p);
        free(p);
    }

    while(( p = evbuffer_readline(response->body))) {
        printf("%s\n", p);
        free(p);
    }
    http_response_free(response);
}
void test_get() {
    char*  p;
    //http://www.baidu.com/search/error.html
    http_response * response = http_get("220.181.111.85", 80, "/");  // xiaonei.com/home
    printf("%d\n", response -> status_code);

    while(( p = evbuffer_readline(response->headers))) {
        printf("%s\n", p);
        free(p);
    }

    while(( p = evbuffer_readline(response->body))) {
        printf("%s\n", p);
        free(p);
    }
    http_response_free(response);
}
Exemple #11
0
int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc)
{
	int ntlm_token_length;
	BYTE* ntlm_token_data;
	HttpResponse* http_response;
	rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;

	http_response = http_response_recv(rpc->TlsIn);

	ntlm_token_data = NULL;
	crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
			&ntlm_token_data, &ntlm_token_length);

	ntlm->inputBuffer.pvBuffer = ntlm_token_data;
	ntlm->inputBuffer.cbBuffer = ntlm_token_length;

	http_response_free(http_response);

	return 0;
}
void webserver_send_response(ClientSocket*    client,
                             enum EHttpStatus status,
                             const char*      body,
                             const char*      content_type)
{
  // Build the Content-Length string
  char content_length[20] = {0};
  snprintf(content_length, 20, "%zu", (body ? strlen(body) : 0));
  if (!content_type) { content_type = "text/plain"; }

  // Build the response object
  HttpResponse* res = http_response_new();
  http_response_set_status(res, HTTP_VERSION_1_0, status);
  http_response_add_header(res, "Server", "webserver");
  http_response_add_header(res, "Content-Length", content_length);
  if (body) { http_response_add_header(res, "Content-Type", content_type); }
  if (body) { http_response_set_body(res, body); }

  // Send the response and clean up
  client_socket_send(client, http_response_string(res), http_response_length(res));
  http_response_free(res);
}
Exemple #13
0
int rpc_ncacn_http_recv_out_channel_response(rdpRpc* rpc)
{
	int ntlm_token_length = 0;
	BYTE* ntlm_token_data;
	HttpResponse* http_response;
	rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm;

	http_response = http_response_recv(rpc->TlsOut);

	ntlm_token_data = NULL;
	if (http_response && http_response->AuthParam)
	{
		crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
				&ntlm_token_data, &ntlm_token_length);
	}

	ntlm->inputBuffer[0].pvBuffer = ntlm_token_data;
	ntlm->inputBuffer[0].cbBuffer = ntlm_token_length;

	http_response_free(http_response);

	return 0;
}
Exemple #14
0
HttpResponse* http_response_new(void)
{
	HttpResponse* response = (HttpResponse*) calloc(1, sizeof(HttpResponse));

	if (!response)
		return NULL;

	response->Authenticates = ListDictionary_New(FALSE);

	if (!response->Authenticates)
		goto fail;

	response->data = Stream_New(NULL, 2048);

	if (!response->data)
		goto fail;

	ListDictionary_KeyObject(response->Authenticates)->fnObjectEquals = strings_equals_nocase;
	ListDictionary_ValueObject(response->Authenticates)->fnObjectEquals = strings_equals_nocase;
	return response;
fail:
	http_response_free(response);
	return NULL;
}
Exemple #15
0
HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
{
	size_t size;
	size_t position;
	size_t bodyLength = 0;
	size_t payloadOffset;
	HttpResponse* response;
	size = 2048;
	payloadOffset = 0;
	response = http_response_new();

	if (!response)
		return NULL;

	response->ContentLength = 0;

	while (payloadOffset == 0)
	{
		size_t s;
		char* end;
		/* Read until we encounter \r\n\r\n */
		int status = BIO_read(tls->bio, Stream_Pointer(response->data), 1);

		if (status <= 0)
		{
			if (!BIO_should_retry(tls->bio))
			{
				WLog_ERR(TAG, "%s: Retries exceeded", __FUNCTION__);
				ERR_print_errors_cb(print_bio_error, NULL);
				goto out_error;
			}

			USleep(100);
			continue;
		}

#ifdef HAVE_VALGRIND_MEMCHECK_H
		VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(response->data), status);
#endif
		Stream_Seek(response->data, (size_t)status);

		if (!Stream_EnsureRemainingCapacity(response->data, 1024))
			goto out_error;

		position = Stream_GetPosition(response->data);

		if (position < 4)
			continue;
		else if (position > RESPONSE_SIZE_LIMIT)
		{
			WLog_ERR(TAG, "Request header too large! (%"PRIdz" bytes) Aborting!", bodyLength);
			goto out_error;
		}

		/* Always check at most the lase 8 bytes for occurance of the desired
		 * sequence of \r\n\r\n */
		s = (position > 8) ? 8 : position;
		end = (char*)Stream_Pointer(response->data) - s;

		if (string_strnstr(end, "\r\n\r\n", s) != NULL)
			payloadOffset = Stream_GetPosition(response->data);
	}

	if (payloadOffset)
	{
		size_t count = 0;
		char* buffer = (char*)Stream_Buffer(response->data);
		char* line = (char*) Stream_Buffer(response->data);

		while ((line = string_strnstr(line, "\r\n", payloadOffset - (line - buffer) - 2UL)))
		{
			line += 2;
			count++;
		}

		response->count = count;

		if (count)
		{
			response->lines = (char**) calloc(response->count, sizeof(char*));

			if (!response->lines)
				goto out_error;
		}

		buffer[payloadOffset - 1] = '\0';
		buffer[payloadOffset - 2] = '\0';
		count = 0;
		line = strtok(buffer, "\r\n");

		while (line && (response->count > count))
		{
			response->lines[count] = line;
			line = strtok(NULL, "\r\n");
			count++;
		}

		if (!http_response_parse_header(response))
			goto out_error;

		response->BodyLength = Stream_GetPosition(response->data) - payloadOffset;
		bodyLength = response->BodyLength; /* expected body length */

		if (readContentLength)
		{
			const char* cur = response->ContentType;

			while (cur != NULL)
			{
				if (http_use_content_length(cur))
				{
					if (response->ContentLength < RESPONSE_SIZE_LIMIT)
						bodyLength = response->ContentLength;

					break;
				}

				cur = strchr(cur, ';');
			}
		}

		if (bodyLength > RESPONSE_SIZE_LIMIT)
		{
			WLog_ERR(TAG, "Expected request body too large! (%"PRIdz" bytes) Aborting!", bodyLength);
			goto out_error;
		}

		/* Fetch remaining body! */
		while (response->BodyLength < bodyLength)
		{
			int status;

			if (!Stream_EnsureRemainingCapacity(response->data, bodyLength - response->BodyLength))
				goto out_error;

			status = BIO_read(tls->bio, Stream_Pointer(response->data), bodyLength - response->BodyLength);

			if (status <= 0)
			{
				if (!BIO_should_retry(tls->bio))
				{
					WLog_ERR(TAG, "%s: Retries exceeded", __FUNCTION__);
					ERR_print_errors_cb(print_bio_error, NULL);
					goto out_error;
				}

				USleep(100);
				continue;
			}

			Stream_Seek(response->data, (size_t)status);
			response->BodyLength += (unsigned long)status;

			if (response->BodyLength > RESPONSE_SIZE_LIMIT)
			{
				WLog_ERR(TAG, "Request body too large! (%"PRIdz" bytes) Aborting!", response->BodyLength);
				goto out_error;
			}
		}

		if (response->BodyLength > 0)
			response->BodyContent = &(Stream_Buffer(response->data))[payloadOffset];

		if (bodyLength != response->BodyLength)
		{
			WLog_WARN(TAG, "%s: %s unexpected body length: actual: %d, expected: %d",
			          __FUNCTION__, response->ContentType, response->BodyLength, bodyLength);

			if (bodyLength > 0)
				response->BodyLength = MIN(bodyLength, response->BodyLength);
		}
	}

	return response;
out_error:
	http_response_free(response);
	return NULL;
}
Exemple #16
0
int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    log_init("client.log", 1);

    char buffer[2049];
    int buflen;
    char url[256], host[256], res[256];
    printf("URL to access: ");
    gets(url);
    if(strncmp(url, "http://", 7)) {
        printf("error: url should started with http://\n");
        return -1;
    }

    int i, len = strlen(url), hostlen = 0;
    for(i = 7; i < len; i++, hostlen++) {
        if(url[i] == ':') {
            strncpy(host, url + 7, hostlen);
            portno = atoi(url + i + 1);
            while(i < len && url[i] != '/') i++;
            if(i >= len) strcpy(res, "/");
            else strcpy(res, url + i);
            break;
        } else if(url[i] == '/') {
            strncpy(host, url + 7, hostlen);
            portno = 80;
            strcpy(res, url + i);
            break;
        }
    }
    if(i >= len) {
        printf("error: malformed url");
        return 0;
    }

    printf("connecting to host %s at port %d with url %s\n", host, portno, res);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        perror("error while opening socket: ");
    server = gethostbyname(host);
    if (server == NULL) {
        perror("host not found: ");
        return 0;
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
        perror("error while connecting: ");
        return 0;
    }

    http_request_t req;
    http_response_t resp;
    http_request_init(&req);
    http_response_init(&resp);
    req.type = HTTP_GET;
    strcpy(req.url, res);
    req.version = HTTP_VERSION_1_1;
    sprintf(host, "%s:%d", host, portno);
    dict_put(req.fields, "Host", host);
    if(0 > http_assemble_request(&req, sockfd)) {
        perror("error while writing: ");
        return 0;
    }

    buflen = 0;
    do {
        n = read(sockfd, buffer + buflen, 2048 - buflen);
        if(n > 0)
            buflen += n;
    } while(n > 0 && buflen < 2048);

    puts("===raw resp===");
    for(i = 0; i < buflen; i++) {
        putchar(buffer[i]);
    }
    puts("\n===end===\n");

    if(http_parse_response(&resp, buffer, buflen)) {
        printf("error while parsing response\n");
        return 0;
    }
    printf("%s\n", resp.document);
    http_request_free(&req);
    http_response_free(&resp);
    close(sockfd);
    log_close();
    return 0;
}
Exemple #17
0
HttpResponse* http_response_recv(rdpTls* tls)
{
	BYTE* p;
	int nbytes;
	int length;
	int status;
	BYTE* buffer;
	char* content;
	char* header_end;
	HttpResponse* http_response;

	nbytes = 0;
	length = 10000;
	content = NULL;
	buffer = malloc(length);
	if (!buffer)
		return NULL;

	http_response = http_response_new();
	if (!http_response)
		goto out_free;

	p = buffer;
	http_response->ContentLength = 0;

	while (TRUE)
	{
		while (nbytes < 5)
		{
			status = tls_read(tls, p, length - nbytes);
            
			if (status < 0)
				goto out_error;

			if (!status)
				continue;

			nbytes += status;
			p = (BYTE*) &buffer[nbytes];
		}

		header_end = strstr((char*) buffer, "\r\n\r\n");
        
		if (!header_end)
		{
			fprintf(stderr, "http_response_recv: invalid response:\n");
			winpr_HexDump(buffer, status);
			goto out_error;
		}

		header_end += 2;

		if (header_end != NULL)
		{
			int count;
			char* line;

			header_end[0] = '\0';
			header_end[1] = '\0';
			content = &header_end[2];

			count = 0;
			line = (char*) buffer;

			while ((line = strstr(line, "\r\n")) != NULL)
			{
				line++;
				count++;
			}

			http_response->count = count;
			if (count)
			{
				http_response->lines = (char **)calloc(http_response->count, sizeof(char *));
				if (!http_response->lines)
					goto out_error;
			}

			count = 0;
			line = strtok((char*) buffer, "\r\n");

			while (line != NULL)
			{
				http_response->lines[count] = _strdup(line);
				if (!http_response->lines[count])
					goto out_error;

				line = strtok(NULL, "\r\n");
				count++;
			}

			if (!http_response_parse_header(http_response))
				goto out_error;

			if (http_response->ContentLength > 0)
			{
				http_response->Content = _strdup(content);
				if (!http_response->Content)
					goto out_error;
			}

			break;
		}

		if ((length - nbytes) <= 0)
		{
			length *= 2;
			buffer = realloc(buffer, length);
			p = (BYTE*) &buffer[nbytes];
		}
	}

	free(buffer);

	return http_response;

out_error:
	http_response_free(http_response);
out_free:
	free(buffer);
	return NULL;
}
Exemple #18
0
static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls,
        const char* method, const char* peerAddress, int timeout, BOOL* rpcFallback)
{
	HttpResponse* response = NULL;
	int statusCode;
	int bodyLength;

	if (!rdg_tls_connect(rdg, tls, peerAddress, timeout))
		return FALSE;

	if (rdg->extAuth == HTTP_EXTENDED_AUTH_NONE)
	{
		if (!rdg_ntlm_init(rdg, tls))
			return FALSE;

		if (!rdg_send_http_request(rdg, tls, method, NULL))
			return FALSE;

		response = http_response_recv(tls);

		if (!response)
			return FALSE;

		if (response->StatusCode == HTTP_STATUS_NOT_FOUND)
		{
			WLog_INFO(TAG, "RD Gateway does not support HTTP transport.");

			if (rpcFallback) *rpcFallback = TRUE;

			http_response_free(response);
			return FALSE;
		}

		if (!rdg_handle_ntlm_challenge(rdg->ntlm, response))
		{
			http_response_free(response);
			return FALSE;
		}

		http_response_free(response);
	}

	if (!rdg_send_http_request(rdg, tls, method, NULL))
		return FALSE;

	ntlm_free(rdg->ntlm);
	rdg->ntlm = NULL;
	response = http_response_recv(tls);

	if (!response)
		return FALSE;

	statusCode = response->StatusCode;
	bodyLength = response->BodyLength;
	http_response_free(response);
	WLog_DBG(TAG, "%s authorization result: %d", method, statusCode);

	if (statusCode != HTTP_STATUS_OK)
		return FALSE;

	if (strcmp(method, "RDG_OUT_DATA") == 0)
	{
		if (!rdg_skip_seed_payload(tls, bodyLength))
			return FALSE;
	}
	else
	{
		if (!rdg_send_http_request(rdg, tls, method, "chunked"))
			return FALSE;
	}

	return TRUE;
}
Exemple #19
0
void *handle_connection(void *arg) {
  st_netfd_t client_nfd = (st_netfd_t)arg;
  struct http_stream *s = http_stream_create(HTTP_SERVER, SEC2USEC(5));
  char buf[4*1024];
  int error = 0;
  struct http_stream *cs = NULL;
  uri_t *u = uri_new();
  int should_close = 1;
  for (;;) {
    should_close = 1;
    if (s->status != HTTP_STREAM_OK) break;
    cs = NULL;
    error = 0;
    s->timeout = SEC2USEC(5);
    int status = http_stream_request_read(s, client_nfd);
    s->timeout = SEC2USEC(30); // longer timeout for the rest
    if (status != HTTP_STREAM_OK) {
      if (s->status == HTTP_STREAM_CLOSED || s->status == HTTP_STREAM_TIMEOUT) {
        error = 1;
      } else {
        error = 400;
      }
      goto release;
    }
    cs = http_stream_create(HTTP_CLIENT, SEC2USEC(30));
    //http_request_debug_print(s->req);

    fprintf(stderr, "request uri: %s\n", s->req->uri);
    const char *error_at = NULL;
    uri_clear(u);
    if (uri_parse(u, s->req->uri, strlen(s->req->uri), &error_at) == 0) {
      fprintf(stderr, "uri_parse error: %s\n", error_at);
      error = 400;
      goto release;
    }
    uri_normalize(u);
    if (http_stream_connect(cs, u->host, u->port) != HTTP_STREAM_OK) { error = 504; goto release; }
    http_request_header_remove(s->req, "Accept-Encoding");
    http_request_header_remove(s->req, "Proxy-Connection");
    /* TODO: need to expose a copy api for http message */
    http_request_t *tmp_req = cs->req;
    cs->req = s->req;
    char *request_uri = uri_compose_partial(u);
    char *tmp_uri = s->req->uri;
    cs->req->uri = request_uri;
    if (http_stream_request_send(cs) != HTTP_STREAM_OK) { error = 504; goto release; }
    cs->req = tmp_req;
    s->req->uri = tmp_uri;
    free(request_uri);

    /* TODO: fix this. post might not contain data. probably move this logic into stream */
    size_t total = 0;
    if (g_strcmp0("POST", s->req->method) == 0) {
      for (;;) {
        ssize_t nr = sizeof(buf);
        status = http_stream_read(s, buf, &nr);
        fprintf(stderr, "server http_stream_read nr: %zd\n", nr);
        if (nr < 0 || status != HTTP_STREAM_OK) { error = 1; goto release; }
        if (nr == 0) break;
        /*fwrite(buf, sizeof(char), nr, stdout);*/
        ssize_t nw = st_write(cs->nfd, buf, nr, s->timeout);
        if (nw != nr) { error=1; goto release; }
        fprintf(stderr, "st_write nw: %zd\n", nr);
        total += nr;
      }
      fprintf(stderr, "http_stream_read total: %zu\n", total);
    }

    if (http_stream_response_read(cs) != HTTP_STREAM_OK) { error = 502; goto release; }

    /* TODO: properly create a new response and copy headers */
    http_response_t *tmp_resp = s->resp;
    s->resp = cs->resp;
    s->resp->http_version = "HTTP/1.1";
    http_response_header_remove(s->resp, "Content-Length");
    http_response_header_remove(s->resp, "Transfer-Encoding");
    if (s->resp->status_code != 204)
        http_response_header_append(s->resp, "Transfer-Encoding", "chunked");
    ssize_t nw = http_stream_response_send(s, 0);
    s->resp = tmp_resp;

    fprintf(stderr, "http_stream_response_send: %zd\n", nw);
    if (s->resp->status_code != 204 &&
           (cs->content_size > 0 || cs->transfer_encoding == TE_CHUNKED)) {
      total = 0;
      fprintf(stderr, "content size: %zd\n", cs->content_size);
      for (;;) {
        ssize_t nr = sizeof(buf);
        status = http_stream_read(cs, buf, &nr);
        fprintf(stderr, "client http_stream_read nr: %zd\n", nr);
        if (nr <= 0 || status != HTTP_STREAM_OK) break;
        /*fwrite(buf, sizeof(char), nr, stdout);*/
        total += nr;
        if (http_stream_send_chunk(s, buf, nr) != HTTP_STREAM_OK) break;
      }
      fprintf(stderr, "written to client: %zu\n", total);
      if (total > 0 && s->status == HTTP_STREAM_OK) {
        http_stream_send_chunk_end(s);
      } else {
        fprintf(stderr, "for request: %s status: %d\n", s->req->uri, s->status);
      }
    }
release:
    if (!error) {
        if ((g_strcmp0("HTTP/1.1", s->req->http_version) == 0) &&
          (g_strcmp0(http_request_header_getstr(s->req, "Connection"), "close") != 0)) {
          // if HTTP/1.1 client and no Connection: close, then don't close
          should_close = 0;
        } else if (g_strcmp0(http_request_header_getstr(s->req, "Connection"), "keepalive") == 0) {
          should_close = 0;
        }
    }
    http_request_clear(s->req);
    uri_clear(u);
    if (cs) http_stream_close(cs);
    /* TODO: break loop if HTTP/1.0 and not keep-alive */
    if (error) {
      fprintf(stderr, "ERROR: %d STATUS: %d, exiting\n", error, s->status);
      /* TODO: use reason string */
      if (error >= 400 && s->status != HTTP_STREAM_CLOSED) {
        http_response_free(s->resp);
        s->resp = http_response_new(error, "Error");
        http_response_header_append(s->resp, "Content-Length", "0");
        s->status = HTTP_STREAM_OK; /* TODO: might want to move this logic into http_stream */
        http_stream_response_send(s, 0);
      }
      break;
    }
    if (should_close) break;
  }
  fprintf(stderr, "exiting handle_connection (should_close: %u)\n", should_close);
  uri_free(u);
  http_stream_close(s);
  return NULL;
}
Exemple #20
0
BOOL rts_connect(rdpRpc* rpc)
{
	RPC_PDU* pdu;
	rpcconn_rts_hdr_t* rts;
	HttpResponse* http_response;
	freerdp* instance = (freerdp*) rpc->settings->instance;
	rdpContext* context = instance->context;

	/**
	 * Connection Opening
	 *
	 * When opening a virtual connection to the server, an implementation of this protocol MUST perform
	 * the following sequence of steps:
	 *
	 * 1. Send an IN channel request as specified in section 2.1.2.1.1, containing the connection timeout,
	 *    ResourceType UUID, and Session UUID values, if any, supplied by the higher-layer protocol or application.
	 *
	 * 2. Send an OUT channel request as specified in section 2.1.2.1.2.
	 *
	 * 3. Send a CONN/A1 RTS PDU as specified in section 2.2.4.2
	 *
	 * 4. Send a CONN/B1 RTS PDU as specified in section 2.2.4.5
	 *
	 * 5. Wait for the connection establishment protocol sequence as specified in 3.2.1.5.3.1 to complete
	 *
	 * An implementation MAY execute steps 1 and 2 in parallel. An implementation SHOULD execute steps
	 * 3 and 4 in parallel. An implementation MUST execute step 3 after completion of step 1 and execute
	 * step 4 after completion of step 2.
	 *
	 */

	rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
	WLog_DBG(TAG, "VIRTUAL_CONNECTION_STATE_INITIAL");

	rpc->client->SynchronousSend = TRUE;
	rpc->client->SynchronousReceive = TRUE;

	if (!rpc_ntlm_http_out_connect(rpc))
	{
		WLog_ERR(TAG, "rpc_out_connect_http error!");
		return FALSE;
	}

	if (rts_send_CONN_A1_pdu(rpc) != 0)
	{
		WLog_ERR(TAG, "rpc_send_CONN_A1_pdu error!");
		return FALSE;
	}

	if (!rpc_ntlm_http_in_connect(rpc))
	{
		WLog_ERR(TAG, "rpc_in_connect_http error!");
		return FALSE;
	}

	if (rts_send_CONN_B1_pdu(rpc) < 0)
	{
		WLog_ERR(TAG, "rpc_send_CONN_B1_pdu error!");
		return FALSE;
	}

	rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT;
	WLog_DBG(TAG, "VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT");

	/**
	 * Receive OUT Channel Response
	 *
	 * A client implementation MUST NOT accept the OUT channel HTTP response in any state other than
	 * Out Channel Wait. If received in any other state, this HTTP response is a protocol error. Therefore,
	 * the client MUST consider the virtual connection opening a failure and indicate this to higher layers
	 * in an implementation-specific way. The Microsoft Windows® implementation returns
	 * RPC_S_PROTOCOL_ERROR, as specified in [MS-ERREF], to higher-layer protocols.
	 *
	 * If this HTTP response is received in Out Channel Wait state, the client MUST process the fields of
	 * this response as defined in this section.
	 *
	 * First, the client MUST determine whether the response indicates a success or a failure. If the status
	 * code is set to 200, the client MUST interpret this as a success, and it MUST do the following:
	 *
	 * 1. Ignore the values of all other header fields.
	 *
	 * 2. Transition to Wait_A3W state.
	 *
	 * 3. Wait for network events.
	 *
	 * 4. Skip the rest of the processing in this section.
	 *
	 * If the status code is not set to 200, the client MUST interpret this as a failure and follow the same
	 * processing rules as specified in section 3.2.2.5.6.
	 *
	 */

	http_response = http_response_recv(rpc->TlsOut);
	if (!http_response)
	{
		WLog_ERR(TAG, "unable to retrieve OUT Channel Response!");
		return FALSE;
	}

	if (http_response->StatusCode != HTTP_STATUS_OK)
	{
		WLog_ERR(TAG, "error! Status Code: %d", http_response->StatusCode);
		http_response_print(http_response);
		http_response_free(http_response);

		if (http_response->StatusCode == HTTP_STATUS_DENIED)
		{
			if (!connectErrorCode)
			{
				connectErrorCode = AUTHENTICATIONERROR;
			}

			if (!freerdp_get_last_error(context))
			{
				freerdp_set_last_error(context, FREERDP_ERROR_AUTHENTICATION_FAILED);
			}
		}

		return FALSE;
	}

	if (http_response->bodyLen)
	{
		/* inject bytes we have read in the body as a received packet for the RPC client */
		rpc->client->RecvFrag = rpc_client_fragment_pool_take(rpc);
		Stream_EnsureCapacity(rpc->client->RecvFrag, http_response->bodyLen);
		CopyMemory(rpc->client->RecvFrag, http_response->BodyContent,  http_response->bodyLen);
	}

	//http_response_print(http_response);
	http_response_free(http_response);

	rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_WAIT_A3W;
	WLog_DBG(TAG, "VIRTUAL_CONNECTION_STATE_WAIT_A3W");

	/**
	 * Receive CONN_A3 RTS PDU
	 *
	 * A client implementation MUST NOT accept the CONN/A3 RTS PDU in any state other than
	 * Wait_A3W. If received in any other state, this PDU is a protocol error and the client
	 * MUST consider the virtual connection opening a failure and indicate this to higher
	 * layers in an implementation-specific way.
	 *
	 * Set the ConnectionTimeout in the Ping Originator of the Client's IN Channel to the
	 * ConnectionTimeout in the CONN/A3 PDU.
	 *
	 * If this RTS PDU is received in Wait_A3W state, the client MUST transition the state
	 * machine to Wait_C2 state and wait for network events.
	 *
	 */

	rpc_client_start(rpc);

	pdu = rpc_recv_dequeue_pdu(rpc);

	if (!pdu)
		return FALSE;

	rts = (rpcconn_rts_hdr_t*) Stream_Buffer(pdu->s);

	if (!rts_match_pdu_signature(rpc, &RTS_PDU_CONN_A3_SIGNATURE, rts))
	{
		WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/A3");
		return FALSE;
	}

	rts_recv_CONN_A3_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));

	rpc_client_receive_pool_return(rpc, pdu);

	rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_WAIT_C2;
	WLog_DBG(TAG, "VIRTUAL_CONNECTION_STATE_WAIT_C2");

	/**
	 * Receive CONN_C2 RTS PDU
	 *
	 * A client implementation MUST NOT accept the CONN/C2 RTS PDU in any state other than Wait_C2.
	 * If received in any other state, this PDU is a protocol error and the client MUST consider the virtual
	 * connection opening a failure and indicate this to higher layers in an implementation-specific way.
	 *
	 * If this RTS PDU is received in Wait_C2 state, the client implementation MUST do the following:
	 *
	 * 1. Transition the state machine to opened state.
	 *
	 * 2. Set the connection time-out protocol variable to the value of the ConnectionTimeout field from
	 *    the CONN/C2 RTS PDU.
	 *
	 * 3. Set the PeerReceiveWindow value in the SendingChannel of the Client IN Channel to the
	 *    ReceiveWindowSize value in the CONN/C2 PDU.
	 *
	 * 4. Indicate to higher-layer protocols that the virtual connection opening is a success.
	 *
	 */

	pdu = rpc_recv_dequeue_pdu(rpc);
	if (!pdu)
		return FALSE;

	rts = (rpcconn_rts_hdr_t*) Stream_Buffer(pdu->s);

	if (!rts_match_pdu_signature(rpc, &RTS_PDU_CONN_C2_SIGNATURE, rts))
	{
		WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/C2");
		return FALSE;
	}

	rts_recv_CONN_C2_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));

	rpc_client_receive_pool_return(rpc, pdu);

	rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_OPENED;
	WLog_DBG(TAG, "VIRTUAL_CONNECTION_STATE_OPENED");

	rpc->client->SynchronousSend = TRUE;
	rpc->client->SynchronousReceive = TRUE;

	return TRUE;
}
Exemple #21
0
HttpResponse* http_response_recv(rdpTls* tls)
{
	wStream* s;
	int size;
	int count;
	int status;
	int position;
	char* line;
	char* buffer;
	char* header;
	char* payload;
	int bodyLength;
	int payloadOffset;
	HttpResponse* response;

	size = 1024;
	payload = NULL;
	payloadOffset = 0;

	s = Stream_New(NULL, size);

	if (!s)
		goto out_free;

	buffer = (char*) Stream_Buffer(s);

	response = http_response_new();

	if (!response)
		goto out_free;

	response->ContentLength = 0;

	while (TRUE)
	{
		while (!payloadOffset)
		{
			status = BIO_read(tls->bio, Stream_Pointer(s), Stream_Capacity(s) - Stream_GetPosition(s));

			if (status <= 0)
			{
				if (!BIO_should_retry(tls->bio))
					goto out_error;

				USleep(100);
				continue;
			}

#ifdef HAVE_VALGRIND_MEMCHECK_H
			VALGRIND_MAKE_MEM_DEFINED(Stream_Pointer(s), status);
#endif

			Stream_Seek(s, status);

			if (Stream_GetRemainingLength(s) < 1024)
			{
				Stream_EnsureRemainingCapacity(s, 1024);
				buffer = (char*) Stream_Buffer(s);
				payload = &buffer[payloadOffset];
			}

			position = Stream_GetPosition(s);

			if (position >= 4)
			{
				line = string_strnstr(buffer, "\r\n\r\n", position);

				if (line)
				{
					payloadOffset = (line - buffer) + 4;
					payload = &buffer[payloadOffset];
				}
			}
		}

		if (payloadOffset)
		{
			count = 0;
			line = buffer;

			position = Stream_GetPosition(s);

			while ((line = string_strnstr(line, "\r\n", payloadOffset - (line - buffer) - 2)))
			{
				line += 2;
				count++;
			}

			response->count = count;

			if (count)
			{
				response->lines = (char**) calloc(response->count, sizeof(char*));

				if (!response->lines)
					goto out_error;
			}

			header = (char*) malloc(payloadOffset);

			if (!header)
				goto out_error;

			CopyMemory(header, buffer, payloadOffset);
			header[payloadOffset - 1] = '\0';
			header[payloadOffset - 2] = '\0';

			count = 0;
			line = strtok(header, "\r\n");

			while (line && response->lines)
			{
				response->lines[count] = _strdup(line);

				if (!response->lines[count])
					goto out_error;

				line = strtok(NULL, "\r\n");
				count++;
			}

			free(header);

			if (!http_response_parse_header(response))
				goto out_error;

			response->BodyLength = Stream_GetPosition(s) - payloadOffset;

			if (response->BodyLength > 0)
			{
				response->BodyContent = (BYTE*) malloc(response->BodyLength);

				if (!response->BodyContent)
					goto out_error;

				CopyMemory(response->BodyContent, payload, response->BodyLength);
			}

			bodyLength = 0; /* expected body length */

			if (response->ContentType)
			{
				if (_stricmp(response->ContentType, "text/plain") == 0)
				{
					bodyLength = response->ContentLength;
				}
			}

			if (bodyLength != response->BodyLength)
			{
				WLog_WARN(TAG, "http_response_recv: %s unexpected body length: actual: %d, expected: %d",
						response->ContentType, response->ContentLength, response->BodyLength);
			}

			break;
		}

		if (Stream_GetRemainingLength(s) < 1024)
		{
			Stream_EnsureRemainingCapacity(s, 1024);
			buffer = (char*) Stream_Buffer(s);
			payload = &buffer[payloadOffset];
		}
	}

	Stream_Free(s, TRUE);
	return response;
out_error:
	http_response_free(response);
out_free:
	Stream_Free(s, TRUE);
	return NULL;
}
Exemple #22
0
HttpResponse* http_response_recv(rdpTls* tls)
{
    BYTE* p;
    int nbytes;
    int length;
    int status;
    BYTE* buffer;
    char* content;
    char* header_end;
    HttpResponse* http_response;
    nbytes = 0;
    length = 10000;
    content = NULL;

    buffer = calloc(length, 1);

    if (!buffer)
        return NULL;

    http_response = http_response_new();

    if (!http_response)
        goto out_free;

    p = buffer;
    http_response->ContentLength = 0;

    while (TRUE)
    {
        while (nbytes < 5)
        {
            status = BIO_read(tls->bio, p, length - nbytes);

            if (status <= 0)
            {
                if (!BIO_should_retry(tls->bio))
                    goto out_error;

                USleep(100);
                continue;
            }

#ifdef HAVE_VALGRIND_MEMCHECK_H
            VALGRIND_MAKE_MEM_DEFINED(p, status);
#endif
            nbytes += status;
            p = (BYTE*) &buffer[nbytes];
        }

        header_end = strstr((char*) buffer, "\r\n\r\n");

        if (!header_end)
        {
            WLog_ERR(TAG, "invalid response:");
            winpr_HexDump(TAG, WLOG_ERROR, buffer, status);
            goto out_error;
        }

        header_end += 2;

        if (header_end != NULL)
        {
            int count;
            char* line;
            header_end[0] = '\0';
            header_end[1] = '\0';
            content = header_end + 2;
            count = 0;
            line = (char*) buffer;

            while ((line = strstr(line, "\r\n")) != NULL)
            {
                line++;
                count++;
            }

            http_response->count = count;

            if (count)
            {
                http_response->lines = (char**) calloc(http_response->count, sizeof(char*));

                if (!http_response->lines)
                    goto out_error;
            }

            count = 0;
            line = strtok((char*) buffer, "\r\n");

            while ((line != NULL) && http_response->lines)
            {
                http_response->lines[count] = _strdup(line);

                if (!http_response->lines[count])
                    goto out_error;

                line = strtok(NULL, "\r\n");
                count++;
            }

            if (!http_response_parse_header(http_response))
                goto out_error;

            http_response->bodyLen = nbytes - (content - (char*)buffer);

            if (http_response->bodyLen > 0)
            {
                http_response->BodyContent = (BYTE*) malloc(http_response->bodyLen);

                if (!http_response->BodyContent)
                    goto out_error;

                CopyMemory(http_response->BodyContent, content, http_response->bodyLen);
            }

            break;
        }

        if ((length - nbytes) <= 0)
        {
            length *= 2;
            buffer = realloc(buffer, length);
            p = (BYTE*) &buffer[nbytes];
        }
    }

    free(buffer);
    return http_response;
out_error:
    http_response_free(http_response);
out_free:
    free(buffer);
    return NULL;
}
Exemple #23
0
BOOL rpc_ntlm_http_in_connect(rdpRpc* rpc)
{
    STREAM* s;
    rdpSettings* settings;
    int ntlm_token_length;
    BYTE* ntlm_token_data;
    HttpResponse* http_response;
    rdpNtlm* ntlm = rpc->ntlm_http_in->ntlm;

    settings = rpc->settings;

    if (settings->tsg_same_credentials)
    {
        ntlm_client_init(ntlm, TRUE, settings->username,
                         settings->domain, settings->password);
        ntlm_client_make_spn(ntlm, _T("HTTP"), settings->tsg_hostname);
    }
    else
    {
        ntlm_client_init(ntlm, TRUE, settings->tsg_username,
                         settings->tsg_domain, settings->tsg_password);
        ntlm_client_make_spn(ntlm, _T("HTTP"), settings->tsg_hostname);
    }

    ntlm_authenticate(ntlm);

    s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0, TSG_CHANNEL_IN);

    /* Send IN Channel Request */

    DEBUG_RPC("\n%s", s->data);
    tls_write_all(rpc->tls_in, s->data, s->size);
    stream_free(s);

    /* Receive IN Channel Response */

    http_response = http_response_recv(rpc->tls_in);

    ntlm_token_data = NULL;
    crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
                         &ntlm_token_data, &ntlm_token_length);

    ntlm->inputBuffer.pvBuffer = ntlm_token_data;
    ntlm->inputBuffer.cbBuffer = ntlm_token_length;

    ntlm_authenticate(ntlm);

    http_response_free(http_response);

    s = rpc_ntlm_http_request(rpc, &ntlm->outputBuffer, 0x40000000, TSG_CHANNEL_IN);

    /* Send IN Channel Request */

    DEBUG_RPC("\n%s", s->data);
    tls_write_all(rpc->tls_in, s->data, s->size);
    stream_free(s);

    ntlm_client_uninit(ntlm);
    ntlm_free(ntlm);

    return TRUE;
}
/* Do a GET, HEAD, or POST transaction. */
static int do_transaction(struct http_request *request,
    struct socket_buffer *client_sock, struct socket_buffer *server_sock)
{
    char buf[BUFSIZ];
    struct http_response response;
    char *line;
    char *request_str, *response_str;
    size_t len;
    int code, n;

    /* We don't handle the chunked transfer encoding, which in the absence of a
       Content-Length is the only way we know the end of a request body. RFC
       2616, section 4.4 says, "If a request contains a message-body and a
       Content-Length is not given, the server SHOULD respond with 400 (bad
       request) if it cannot determine the length of the message, or with 411
       (length required) if it wishes to insist on receiving a valid
       Content-Length." */
    if (strcmp(request->method, "POST") == 0 && request->content_length == 0) {
        if (o.debug)
            logdebug("POST request with no Content-Length.\n");
        return 400;
    }

    /* The version we use to talk to the server. */
    request->version = HTTP_10;

    /* Remove headers that only apply to our connection with the client. */
    code = http_header_remove_hop_by_hop(&request->header);
    if (code != 0) {
        if (o.verbose)
            logdebug("Error removing hop-by-hop headers.\n");
        return code;
    }

    /* Build the Host header. */
    if (request->uri.port == -1 || request->uri.port == 80)
        n = Snprintf(buf, sizeof(buf), "%s", request->uri.host);
    else
        n = Snprintf(buf, sizeof(buf), "%s:%hu", request->uri.host, request->uri.port);
    if (n < 0 || n >= sizeof(buf)) {
        /* Request Entity Too Large. */
        return 501;
    }
    request->header = http_header_set(request->header, "Host", buf);

    request->header = http_header_set(request->header, "Connection", "close");

    /* Send the request to the server. */
    request_str = http_request_to_string(request, &len);
    n = send(server_sock->fdn.fd, request_str, len, 0);
    free(request_str);
    if (n < 0)
        return 504;
    /* Send the request body, if any. Count up to Content-Length. */
    while (request->bytes_transferred < request->content_length) {
        n = socket_buffer_read(client_sock, buf, MIN(sizeof(buf), request->content_length - request->bytes_transferred));
        if (n < 0)
            return 504;
        if (n == 0)
            break;
        request->bytes_transferred += n;
        n = send(server_sock->fdn.fd, buf, n, 0);
        if (n < 0)
            return 504;
    }
    if (o.debug && request->bytes_transferred < request->content_length)
        logdebug("Received only %lu request body bytes (Content-Length was %lu).\n", request->bytes_transferred, request->content_length);


    /* Read the response. */
    code = http_read_status_line(server_sock, &line);
    if (o.debug > 1)
        logdebug("Status-Line: %s", line);
    if (code != 0) {
        if (o.verbose)
            logdebug("Error reading Status-Line.\n");
        return 0;
    }
    code = http_parse_status_line(line, &response);
    free(line);
    if (code != 0) {
        if (o.verbose)
            logdebug("Error parsing Status-Line.\n");
        return 0;
    }

    code = http_read_header(server_sock, &line);
    if (code != 0) {
        if (o.verbose)
            logdebug("Error reading header.\n");
        return 0;
    }
    if (o.debug > 1)
        logdebug("Response header:\n%s", line);

    code = http_response_parse_header(&response, line);
    free(line);
    if (code != 0) {
        if (o.verbose)
            logdebug("Error parsing response header.\n");
        return 0;
    }


    /* The version we use to talk to the client. */
    response.version = HTTP_10;

    /* Remove headers that only apply to our connection with the server. */
    code = http_header_remove_hop_by_hop(&response.header);
    if (code != 0) {
        if (o.verbose)
            logdebug("Error removing hop-by-hop headers.\n");
        return code;
    }

    response.header = http_header_set(response.header, "Connection", "close");

    /* Send the response to the client. */
    response_str = http_response_to_string(&response, &len);
    n = fdinfo_send(&client_sock->fdn, response_str, len);
    free(response_str);
    if (n < 0) {
        http_response_free(&response);
        return 504;
    }
    /* If the Content-Length is 0, read until the connection is closed.
       Otherwise read until the Content-Length. At this point it's too late to
       return our own error code so return 0 in case of any error. */
    while (response.content_length == 0
        || response.bytes_transferred < response.content_length) {
        size_t remaining = response.content_length - response.bytes_transferred;
        size_t count;

        count = sizeof(buf);
        if (response.content_length > 0 && remaining < count)
            count = remaining;
        n = socket_buffer_read(server_sock, buf, count);
        if (n <= 0)
            break;
        response.bytes_transferred += n;
        n = fdinfo_send(&client_sock->fdn, buf, n);
        if (n < 0)
            break;
    }

    http_response_free(&response);

    return 0;
}