HTTP proxy implementation: content encoding error - c

I am implementing an proxy. I could receive response from server, but failed to send the response to client.
To be more detail, I could only rend response header content, but failed to send message body. And webpage shows'content encoding error'
//I could sending request to server successfully.
send(connfd_to_server, request, strlen(request), 0);
//receive response from server
char res_buf[1024];
while(1){
bzero(res_buf, 1024);
if(recv(connfd_to_server, res_buf, sizeof(res_buf),0) <=0){
break; //if recv failed, then message body is finished.
} //receive response using recv
send(connfd_to_client, res_buf, strlen(res_buf));
}
I also tried:
char* response = (char*)malloc(strlen(res_buf));
char* res_line;
res_line = strtok(res_buf, "\r\n");
for(int i = 0; i<=11; i++){
strcat(response, res_line);
strcat(response, "\r\n");
res_line = strtok(NULL, "\r\n");
} //copy header content using strcat
while(res_line!= NULL){
memcpy(response, res_line, sizeof(res_line));
res_line = strtok(NULL, "\r\n");
} //copy message body as bytes using memcpy
then send response to client using send function.
However, no matter which function I use, message body are not send successfully.
like shown in a weird symbol above
Any hints?
Many thanks in advance

The function recv returns the number of bytes read which you could use when calling function send.
You used strlen which is based on detecting a null character to find the end of the buffer, which in this case is not valid.

Content encoding says gzip so the data coming from server will have binary data in its http response body which may also contain null character so you may end up sending partial data, as you have used strlen() in send(), that your client will not be able to decode

Related

no response from server in wireshark, but response received, and decompression failed error

This is my first proxy implementation, which send request from client to server, and send response from server to client.
I actually have two questions.
Q1:
As shown in second line, request is sent to server.
But I expected to receive response from server address'143.248.36.166'
However, there is no response from server address to source address.
But, as shown in 3rd line and below, response are sent from proxy to client.
Then where does proxy get response?
(I cleared history, so there is no cache I believe)
my code:
while(1){
char response[1024];
bzero(response, 1024);
int n = read(connfd_to_server, res_buf, 1024);
if (n <=0){
printf("will break");
break;
}
else{ //if first packet, modify header
if(packet == 0){
res_line = strtok(res_buf, "\r\n");
for(int i = 0; i<11; i++){ //copy first 11 lines as it is
strcat(response, res_line);
strcat(response, "\r\n");
res_line = strtok(NULL, "\r\n");
}
strcat(response, "Via: 1.1 ubuntu (ee323_proxy/1.0.0)");
strcat(response, "\r\n"); //add this new line
memcpy(response+strlen(response),res_line,n-strlen(response)); //memcpy rest header, and some message body
if (send(connfd_to_client, response, n, 0)<0){
perror("send failed");
}
}
else{ //if not first packet, there is no header, just send
if (send(connfd_to_client, res_buf, n, 0)<0){
perror("send failed");
}
}
}
packet++;
}
Q2:
I want to send modified content to client.
But there is an error.
As shown, new line is added.
However, the line after new line doesn't end with '\r\n' as other lines.
I don't know why the ending changed.
Also there is an error saying: decompression failed
I want my last res_line buffer get (rest header+message body), however, it seems it is wrong.
How can I fixed these two problem?
Many many thanks in advance
I have been stuck for a while...

C - sockets, separating data and http header?

I am trying to simulate client/server communication to send and receive files using sockets and http requests.
The client sends an http request to the server saying that they want to download a simple .txt file and the server sends the file data back to the client with an http response header.
However, the data written into the received text file also contains the http header information which it shouldn't.
How do I effectively separate the http header from the actual data?
Here's what the code looks like:
Server:
//receives an http header request from client saying they want to download a .txt file
bzero(buffer, 256);
sprintf(buffer, "HTTP/1.1 200 OK\r\n Date:%s\r\n Content-Type:%s\r\n Content-Length:%s\r\n\r\n", time, content, length);
data = write(incoming_socket, buffer, strlen(buffer)); // first sends an http response header
bzero(buffer, 256);
int data;
while((data = fread(buffer, sizeof(char), 256, fs)) > 0){
if(send(cli_socket, buffer, strlen(buffer), 0) < 0){ // sends the actual file data to client in this while cycle
perror("ERROR: Could not send file.");
break;
exit(EXIT_FAILURE);
}
if (block_size < 256){
if (ferror(fs)){
perror("ERROR: Failed while sending data.");
exit(EXIT_FAILURE);
}
bzero(buffer, 256);
break;
}
bzero(buffer, 256);
}
Client:
bzero(buffer, 256);
data = read(client_socket, buffer, 256); // first receive the http header response
bzero(buffer, 256);
while (1){
data = recv(client_socket, buffer, 256, 0); // receive data
fwrite(buffer, sizeof(char), data, fr); // write data into the file
bzero(buffer, 256);
if (data == 0 || data < 256){
fclose(fr);
break;
}
if(data< 0){
printf("failed while copyng file!\n");
exit(1);
}
}
However, this fills the .txt file with the http response as well, not just the data.
Now, I know that I should probably look for /r/n/r/n in the header response in order to separate the header and write just the actual data but, if someone could show me the best way to do this in my example with my particular buffer, I'd be very grateful!
Unfortunately, you've chosen a very compelex protocol to implement. Your only choice is to read the documentation for HTTP version 1.1 and follow the specification precisely. The documentation will tell you how to identify the end of the header. Note that you must support chunked encoding. It is required for HTTP 1.1 compliance.
But you've really chosen an H-bomb to kill an ant here. HTTP 1.1 is a complex, high-performance protocol and a terrible choice for just messing around transferring a file.
A few more mistakes:
if(send(cli_socket, buffer, strlen(buffer), 0) < 0){ // sends the actual file data to client in this while cycle
The strlen function is only for C-style strings, not arbitrary data. The recv function returns the number of bytes read for a reason.
data = recv(client_socket, buffer, 256, 0); // receive data
fwrite(buffer, sizeof(char), file_block_size, fr); // write data into the file
Surely the number of bytes you write should be the number of bytes you read (which you helpfully stored in a variable, data, but didn't use), not some other number.
If you really want to make HTTP 1.1 client and server code, you really should start by thoroughly understanding both the HTTP 1.1 specification and example client and server code. But that's a terrible way to learn how to send and receive files because HTTP 1.1 is so complicated.

Receive http response messages using OpenSSL in C

I have some problems when trying to receive http response message of a website.
This is my function:
void Reveive_response(char *resp, SSL *ssl) {
const int BUFFER_SIZE = 1024;
char response[1048576];
char *buffer = NULL; // to read from ssl
char *check = (char *) malloc(BUFFER_SIZE*sizeof(char));
int bytes; // number of bytes actually read
int received = 0; // number of bytes received
buffer = (char *) malloc(BUFFER_SIZE*sizeof(char)); // malloc
memset(response, '\0', sizeof(response)); // response
assign = '\0'
do{
memset(buffer, '\0', BUFFER_SIZE); // empty buffer
bytes = SSL_read(ssl, buffer, BUFFER_SIZE);
if (bytes < 0) {
printf("Error: Receive response\n");
exit(0);
}
if (bytes == 0) break;
received += bytes;
printf("Received...%d bytes\n", received);
strncat(response, buffer, bytes); // concat buffer to response
} while (SSL_pending(ssl)); // while pending
response[received] = '\0';
printf("Receive DONE\n");
printf("Response: \n%s\n", response);
free(buffer);
strcpy(resp, response); // return via resp
}
When I call the function, it seems like the response message is not complete. Like this:
Received...1014 bytes
Received...1071 bytes
Receive DONE
Response:
HTTP/1.1 200 OK
<... something else....>
Vary: Accept-Encoding
Content-Type: text/html
Conne
Then if i call the function again, it returns:
Received...39 bytes
Receive DONE
Response:
ction: keep-alive
Content-Length: 0
The field Connection was split. Why my function didn't receive all the response message? I used do while loop inside. Please tell me where did i go wrong? Thank you.
There is nothing wrong. This is simply how TCP works. It is a streaming transport, it has no concept of message boundaries. There is no 1-to-1 relationship between the number of bytes sent and the number of bytes read. Your reading receives arbitrary bytes, which you are then responsible for processing as needed. Keep reading, buffering and parsing the HTTP data as you go, until you discover the end of the response (see RFC 2616 Section 4.4 Message Length for details). Looping on SSL_pending() is not sufficient (or correct).
In this case, you have to read CRLF-delimited lines one at a time until you reach a CRLF/CRLF pair indicating the end of the response headers, then you need to analyze the headers you have received to know whether a response body is present and how to read it, as it may be in one of several different encoded formats. If present, you can then read the body (decoding it as you go along) until you reach the end of the body as specified by the headers.
See the pseudo-code I posted in my answer to the following question:
Receiving Chunked HTTP Data With Winsock
That said, you really should not be implementing HTTP (let alone HTTPS) manually to begin with. HTTP is not trivial to implement from scratch, and neither is SSL/TLS for that matter. You have dived head-first into a deep well without understand some important basics of network programming and OpenSSL programming. You should use an existing HTTP/S library instead, such as libcurl, and let it handle the details for you so you can focus on your code's business logic and not its communications logic.

Two way socket communication

I'm trying to write a simple two way socket communication using C. So far, the main while loop for my client.c file looks like this:
while ( gets(str) != NULL ) {
sSize = send(s, str, strlen(str), 0);
if ( len != strlen(str) ) {
exit(1);
}
else {
rSize = recv(s, buffer, 64, 0);
buf[rSize] = '\0';
printf("%s\n", buffer);
}
}
while loop in sever.c looks like this:
while ( 1 ) {
gets(str);
send(p, str, strlen(str), 0);
rSize = recv(p, buffer, 32, 0);
if ( rSize < 0 ) {
exit(1);
}
buf[len] = '\0';
else{
printf("%s\n", buffer);
}
}
The program compiles normally and I can establish connection between both machines, but when I send message either from client or server, I get an anomaly:
Sending message 'hi' from client
client -------------------------- server
hi
If I go to server to send 'you' message, I get:
client -------------------------- server
hi
you
you hi
Not sure exactly how this is, but what I'm trying to achieve is that, whenever message is sent from either client or server, it should display immediately on the other side.
Please note that gets() is a blocking function. Initially both client and server are blocked in gets() waiting for input. When you type 'hi' on client, it sends this to the server which is still blocked on gets.
After sending hi, the client blocks on recv() call, waiting for message from server. On the other side, server hasn't still received the 'hi' message send by the client.
When you type 'you' on the server, it comes out of gets() and sends 'you' to client. After that the server calls recv() and reads the 'hi' sent by the client. Since the client is already waiting in recv(), it reads 'you' sent by the server.
Thus the program is working absolutely the way it has been implemented. Please mention your object, not sure what do you want to achieve.

C Sockets recv: connection reset by peer

I'm trying to use sockets to get a small JSON test file, which is hosted on my website (http://a-cstudios.com/text.json). When I do this
long numbytes;
char *request = malloc(sizeof(char) * 300);
sprintf(request, "GET %s \r\nHOST:%s \r\n\r\n", restOfURL, baseServer);
// restOfURL = "/text.json" baseServer = "www.a-cstudios.com"
send(sockfd, request, strlen(request) + 1, 0);
char buf[1024];
if ((numbytes = recv(sockfd, buf, 1024-1, 0)) == -1) {
perror("recv");
}
I get recv: connection reset by peer. But if I use the same code, where restOfURL is /index.html and baseServer is www.google.com, this works fine, and buf will contain the text of index.html. Why won't this work for the file on my website?
Since you didn't post full code, I am going to take a stab at it and make an assumption:
You populate the format string of "GET %s \r\nHOST:%s \r\n\r\n" with restOfURL and baseServer
However, during the time of the sprintf call restOfURL isn't initilized so you're pushing garbage data into the first %s
Either post more of your code or make sure you initialize resOfURL
As #Kninnug pointed out, you need the HTTP version field (e.g., HTTP/1.1) at the end of the first line of the request. I just want to point out that you should not include the null terminator when you send the request. That is, change the send statement to
send(sockfd, request, strlen(request), 0);
Also, it is a good practice to always use snprintf instead of sprintf to prevent buffer overflow, although to be really safe you still need to check for truncation.

Resources