redirect to a website in c - sockets

I am using the following code to redirect client request. But when doing the following the clients are not redirected. Its showing "Unable to connect" in the browser. I redirect the clients to port 8080 using iptables. And running the following executable to redirect. How to redirect the clients. Please provide solution....
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
char *reply = "HTTP/1.1 301 Moved Permanently\nServer: Apache/2.2.3\nLocation:
http://www.google.com\nContent-Length: 1000\nConnection: close\nContent-Type:
text/html; charset=UTF-8";
char sendBuff[1025];
time_t ticks;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(8080);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
while(1)
{
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
printf("client connected\n");
send(connfd, reply, strlen(reply), 0);
close(connfd);
sleep(1);
}
}

Please refer to the example in this page or this page to construct a valid http response on the server side. Then, add your html body below it.
Minimum you would need is
HTTP/1.1 200 OK
Content-Length: XXXXX <- put size of the your html body
Connection: close
Content-Type: text/html; charset=UTF-8

I am unable to reproduce the error that you see. You should provide more details (e.g., what kind of client, exact text of iptables rule). For my test, I did not set any iptables rule, and instead pointed the Firefox 12.0 browser directly to localhost:8080.
Splitting up your reply so that it is easier to read shows:
char *reply =
"HTTP/1.1 301 Moved Permanently\n"
"Server: Apache/2.2.3\n"
"Location: http://www.google.com\n"
"Content-Length: 1000\n"
"Connection: close\n"
"Content-Type: text/html; charset=UTF-8"
;
Although the RFC specifies \r\n for line terminators, most clients will accept \n (you don't say which client you are using). But, three other glaring issues are that the last line is not terminated, the response itself is not terminated by a blank line, and you have a Content-Length header of 1000, but no content. Any of these issues could be cause for a client to treat the response as invalid and ignore it.
char *reply =
"HTTP/1.1 301 Moved Permanently\r\n"
"Server: Apache/2.2.3\r\n"
"Location: http://www.google.com\r\n"
"Content-Length: 0\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"\r\n"
;
Reading farther into your code, you close the connection immediately after sending your reply without first reading the request. This might lead to an (albeit unlikely) race where you close the connection before the request is fully delivered to the server. Then, when the request does arrive, it will trigger a reset to the client, and the response could be dropped. So, you should add code to make the delivery of your reply more robust:
printf("client connected\n");
send(connfd, reply, strlen(reply), 0);
shutdown(connfd, SHUT_WR);
while (recv(connfd, sendBuff, sizeof(sendBuff), 0) > 0) {}
close(connfd);
Given that I cannot reproduce your issue with the response as it is, though, it is also possible that you did not set your iptable redirect rule properly.

Related

How to DEFLATE with a command line tool to extract a page from Microsoft Server

How can I view html page from Server: Microsoft-IIS/10.0 with header: "Accept-Encoding: deflate"
I can't decompress such html page in Linux(Centos).
I can decompress with "Accept-Encoding: gzip"(gunzip).
I can decompress with "Accept-Encoding: br"(brotli).
I want to uncompress page from Server: Microsoft-IIS/10.0 with header:
"Accept-Encoding: deflate".
X-Frame-Options: SAMEORIGIN
X-UA-Compatible: IE=Edge
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
I'm looking for a command line wrapper for the DEFLATE algorithm.
Unfortunately:
zlib-flate -uncompress < deflate.dat > page.html flate: inflate: data: incorrect header check
unpigz -c deflate.dat
unpigz: skipping: deflate.dat is not compressed
openssl zlib -d < deflate.dat > page.html
140264494790544:error:29065064:lib(41):BIO_ZLIB_READ:zlib inflate error:c_zlib.c:548:zlib error:data error
Microsoft and their IIS server is what ruined the deflate HTTP content encoding for everyone. The HTTP standard in RFC 2616 clearly states:
deflate
The "zlib" format defined in RFC 1950 [31] in combination with
the "deflate" compression mechanism described in RFC 1951 [29].
However the authors at Microsoft of IIS did not read the standard, and instead decided on their own that deflate meant raw deflate, just RFC 1951, without the zlib wrapper. Then browsers either didn't work with deflate encoding, or they had to try decoding it both ways, with and without the zlib wrapper. Now the recommendation is to simply not use the deflate encoding, and use gzip instead.
I am not aware of a stock command line tool to decompress raw deflate data. You can compile this with zlib to create such a tool:
// Decompress raw deflate data from stdin to stdout. Return with an exit code
// of 1 if there is an error.
#include <stdio.h>
#include "zlib.h"
#ifdef _WIN32
# include <fcntl.h>
# include <io.h>
# define BINARY() (_setmode(0, _O_BINARY), _setmode(1, _O_BINARY))
#else
# define BINARY()
#endif
int main(void) {
BINARY();
z_stream strm = {0};
int ret = inflateInit2(&strm, -15);
if (ret != Z_OK)
return 1;
unsigned char in[32768], out[32768];
do {
if (strm.avail_in == 0) {
strm.avail_in = fread(in, 1, sizeof(in), stdin);
strm.next_in = in;
}
strm.avail_out = sizeof(out);
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
fwrite(out, 1, sizeof(out) - strm.avail_out, stdout);
} while (ret == Z_OK);
inflateEnd(&strm);
return ret != Z_STREAM_END;
}

Is it possible to add content to the body of a webpage with a second http request from the server

I would like to create a webserver which sends the content he fetched from a socket server to a browser client. For this I have to fetch a char from the socket server and send it directly to the client (Webbrowser). I am sending the html body using the following headers:
html = "hello world\r\n";
headers << "HTTP/1.1 200 OK\r\n"
<< "Content-Type: text/html\r\n"
<< "Content-Length: " << html.length() << "\r\n"
<< "Connection: close\r\n"
<< "Server: CPi\r\n\r\n"
<< html; // the body of the request
server.sendMessage(client, headers.str());
but I would like to keep the connection alive and add something to the body with a second request like this:
html1 = "hello \r\n"
headers << "HTTP/1.1 200 OK\r\n"
<< "Content-Type: text/html\r\n"
<< "Content-Length: " << html1.length() << "\r\n" // "hello " has 6 chars
//<< "Connection: close\r\n" keep the connection alive
<< "Server: CPi\r\n\r\n"
<< html1; // !! send only the first part
server.sendMessage(client, headers.str());
headers.str(""); // clear header data
html2 = "world\r\n";
headers << "HTTP/1.1 200 OK\r\n"
<< "Content-Type: text/html\r\n"
<< "Content-Length: " << html2.length() << "\r\n"
<< "Connection: close\r\n" // close the connection because the body is complete
<< "Server: CPi\r\n\r\n"
<< html2; // !! append the second part to the body
server.sendMessage(client, headers.str());
But it seems not to work. Is there a way to do it?
But it seems not to work.
HTTP is a protocol where one request has one response. You are trying to send two responses for a single request and the client will simply not expect the second response. This means it will be either ignored or it erroneously will be taken as the response to another request the client is sending on the same TCP connection.
Is there a way to do it?
It is not fully clear to me what you are trying to achieve with this at the end.
But if your problem is that you don't have the full response at once and that you want to send it to the client in pieces you can use for example chunked transfer encoding of the body.

Docker API returns 200 OK then 400 BAD REQUEST

I am writing an API client for Docker. I understood from the documentation that the API is Restful/HTTP, yet if you connect to the local daemon you have to do it over the exposed unix socket.
It all seems to work, I open a socket, send an HTTP request (which respects the specification), I receive the expected response, but also a 400 BAD REQUEST response follows immediately.
Here is the request:
GET /info HTTP/1.1
Host: localhost
Accept: application/json
And here is what I get:
HTTP/1.1 200 OK
Api-Version: 1.30
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/17.06.1-ce (linux)
Date: Thu, 01 Feb 2018 18:53:18 GMT
Transfer-Encoding: chunked
892
{"ID":"6MGE:35TO:BI..." ...}
0
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad Request
First, I figured that there is a bug on my side and I am somehow sending 2 requests, but I enabled debugging and followed the logs with sudo journalctl -fu docker.service and there is exactly one request received... at least one is logged, the GET /info. I've also debugged the code and 1 single request is sent.
Any hint is greatly appreciated!
Edit: here is the client's code:
final StringBuilder hdrs = new StringBuilder();
for(final Map.Entry<String, String> header : headers) {
hdrs.append(header.getKey() + ": " + header.getValue())
.append("\r\n");
}
final String request = String.format(
this.template(), method, home, hdrs, this.readContent(content)
);
final UnixSocketChannel channel = UnixSocketChannel.open(
new UnixSocketAddress(this.path)
);
final PrintWriter writer = new PrintWriter(
Channels.newOutputStream(channel)
);
writer.print(request);
writer.flush();
final InputStreamReader reader = new InputStreamReader(
Channels.newInputStream(channel)
);
CharBuffer result = CharBuffer.allocate(1024);
reader.read(result);
result.flip();
System.out.println("read from server: " + result.toString());
It seems like you have an extra CRLF between headers and body.
private String template() {
final StringBuilder message = new StringBuilder();
message
.append("%s %s HTTP/1.1\r\n")
.append("Host: localhost").append("\r\n")
.append("%s")
.append("\r\n").append("\r\n") //one of these is superfluous, as each header line ends with "\r\n" itself
.append("%s");
return message.toString();
}
Remove one append("\r\n") after headers and see what happens.
Fixed. Initially, I thought the problem was with the line endings (that they should have been \n instead of \r\n). Turns out, the 400 BAD REQUEST occured because the Connection: close header was missing, while the Request made was being closed right after receiving the response.
More details here.

logging in to server using Socket Programming

I am trying to write a code that connect our local server, log in to a webpage on that and retrieve some data. I could connect the server using the server IP and connect function. Now I need to log in on a webpage that accept the following format:
addUPI?function=login&user=user-name&passwd=user-password&host-id=xxxx&mode=t/z
I wrote something like this:
int ret= send(sock,"addUPI?funcion...&mode=t",strlen("addUPI?funcion...&mode=t"),0);
but it does not work. Can anybody help me please?
This isn't really the right way to do HTTP. For one thing, the typical HTTP lifecycle looks something like this (very abbreviated):
...Connect
>>> GET / HTTP/1.0
>>> Host: localhost
>>> Referrer: http://www.google.com
>>>
<<< HTTP/1.0 200 OK
<<< Date: Wed, 08 Apr 2015 05:21:32 GMT
<<< Content-Type: text/html
<<< Content-Length: 20
<<< Set-Cookie: ...
<<<
<<< <html><h1>Hello World</h1></html>
And that's assuming there are no redirects, SSL or other mystical protocol happenings. So, just writing the string you specified above is going to result in a closed connection due to not following the protocol.
Really, you probably want to use a fully-baked HTTP library like cURL, which manages all the protocol requirements.
I shamelessly adapted this example from the curl website:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/addUPI?function=login&user=user-name&passwd=user-password&host-id=xxxx&mode=t");
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}

using curllib to send data to server

I am very new to all this http thing and I am trying to make communication with a server.
I need to write the client that post to server - I know what server returns but I am not sure how to send it the data:
I (the client) Post XML (binary format) to server at url: 192.168.2.111/senddata (i.e post /senddata) using HTTP.
Server response is:
TCP: [ACK] Seq=1274 Ack=504 Win=525088 Len=0
I.E here is the problem - the server does not reply with HTTP reply.
The protocol server uses after I send it the XML is TCP and this makes curl_easy_perform
to not return.
After server TCP reply in step 2, I-the client should send binary data in a loop to server using HTTP Continuation or non-HTTP traffic.
Server asynchronicity answers with
TCP: [ACK] Seq=1274 Ack=3424 Win=522176 Len=0
for each binary data sending .
So to send the XML in step 1, I use
curl_easy_setopt(mCurlHndl, CURLOPT_URL, "192.168.2.111");
curl_easy_setopt(mCurlHndl, CURLOPT_CUSTOMREQUEST, "POST /senddata");
curl_easy_setopt(mCurlHndl, CURLOPT_POST, 1L);
curl_easy_setopt(mCurlHndl, CURLOPT_POSTFIELDS, sXML.c_str());
curl_easy_setopt(mCurlHndl, CURLOPT_POSTFIELDSIZE, sXML.length());
headers = curl_slist_append(headers, "Content-Type: application/octet-stream");
headers = curl_slist_append(headers, "Accept:");
headers = curl_slist_append(headers, (string("Content-Length: ") + string(to_string(sXML.length()))).c_str());
curl_easy_setopt(mCurlHndl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(mCurlHndl);
Server does send in reply the:
TCP: [ACK] Seq=1274 Ack=504 Win=525088 Len=0
(just with other seq/ack/win numbers of course).
OK, here starts the problems.
a. curl_easy_perform does not return since (I think) server reply was not HTTP, but TCP.
b. OK, lets say that i can bypass it giving Timeout
curl_easy_setopt(mCurlHndl, CURLOPT_TIMEOUT_MS, 1000);
But i just know that i am doing something wrong.
c. Lets say I gave the timeout, now in step 3 I need to send in a loop stream of data using HTTP Continuation or non-HTTP traffic.
I do not know how to do that since it should not be regular post again like in step 1.
It should be HTTP Continuation or non-HTTP traffic.
Sorry for my bad explanation but again it is from my very little experience in HTTP.
Seems that after step 1, server breaks that normal http connection rules and what I need to send it is the binary data in a loop but again, i do not know how to do step 2 and 3. I.E how to get the TCP ACK from server using curllib that I will know to move to step 3 and how to (step 3) send to server over the same connection (I think) the binary data in loop.
I want to add that servers returns what is supposed to return. in step 2 server should return the TCP: [ACK] Seq=1274 Ack=504 Win=525088 Len=0 since this is what server should do - this is that server protocol. It is not that i do not send it right (in step 1) and that is why it send TCP reply and not http.
Here is Wireshark log:
192.168.2.7 is the client (that works).
192.168.2.111 is the server.
in the first line (seq 9) the clinet post the binary xml (POST /senddata) and then the loop of binary data stream sending starts as you can see:
9 0.173574000 192.168.2.7 192.168.2.111 HTTP 380 POST /senddata HTTP/1.1 (application/octet-stream)
10 0.176611000 192.168.2.111 192.168.2.7 TCP 60 4089 > 53419 [ACK] Seq=1274 Ack=504 Win=525088 Len=0
11 0.182581000 192.168.2.111 192.168.2.7 UDP 90 Source port: 4020 Destination port: 4005
12 0.553367000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
13 0.553403000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
14 0.555539000 192.168.2.111 192.168.2.7 TCP 60 4089 > 53419 [ACK] Seq=1274 Ack=3424 Win=522176 Len=0
15 0.555698000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
16 0.555724000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
17 0.555724000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
18 0.555724000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
19 0.558870000 192.168.2.111 192.168.2.7 TCP 60 4089 > 53419 [ACK] Seq=1274 Ack=6344 Win=519248 Len=0
20 0.559033000 192.168.2.7 192.168.2.111 HTTP 1354 Continuation or non-HTTP traffic
..... and many other sends like seq 12 - seq 20 ........
How do i do something like this with curllib - this is my question.
Thanks again
You are not using curl correctly for sending an HTTP POST request. Try this instead:
curl_easy_setopt(mCurlHndl, CURLOPT_URL, "http://192.168.2.111/senddata");
curl_easy_setopt(mCurlHndl, CURLOPT_POST, 1L);
curl_easy_setopt(mCurlHndl, CURLOPT_POSTFIELDS, sXML.c_str());
curl_easy_setopt(mCurlHndl, CURLOPT_POSTFIELDSIZE, sXML.length());
struct curl_slist *headers = curl_slist_append(NULL, "Content-Type: application/octet-stream");
headers = curl_slist_append(headers, "Accept:");
curl_easy_setopt(mCurlHndl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(mCurlHndl);
curl_slist_free_all(headers);
// process reply using curl_easy_getinfo() and curl_easy_recv() as needed...
With that said, are you sure that you should be sending the Content-Type as application/octet-stream? Typically XML is posted using text/xml, application/xml, or other XML-related Content-Type so the server knows it is dealing with XML data. application/octet-stream refers to arbitrary binary data when its actual type is not known.
Also, setting the Accept header to a blank value basically means you do not accept any data in reply. Unless you want to accept only specific Content-Type reply types, or want to accept any data the server wants to send, then you should omit the Accept header.
Read RFC 2616 for the formal definition of the HTTTP protocol and how it works.
Update: since you are obviously dealing with a custom protocol and not standard HTTP, you cannot use curl_easy_perform() to send the HTTP request. You will have to enable the CURLOPT_CONNECT_ONLY option so curl_easy_perform() will only open the socket connection, and then use curl_easy_send() to send the actual request and subsequent binary data, eg:
CURLcode curl_send(CURL *curl, const void * buffer, size_t buflen)
{
size_t sent;
uint8_t *pbuf = (uint8_t*) buffer;
while (buflen > 0)
{
do
{
res = curl_easy_send(curl, pbuf, buflen, &sent);
}
while (res == CURLE_AGAIN);
if (res != CURLE_OK)
return res;
pbuf += sent;
buflen -= sent;
}
while (buflen > 0);
return CURLE_OK;
}
mCurlHndl = curl_easy_init();
...
curl_easy_setopt(mCurlHndl, CURLOPT_URL, "http://192.168.2.111");
curl_easy_setopt(mCurlHndl, CURLOPT_CONNECT_ONLY, 1);
res = curl_easy_perform(mCurlHndl);
if (res == CURLE_OK)
{
string req =
"POST /senddata HTTP/1.1\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Length: " + to_string(sXML.length()) + "\r\n"
"\r\n"
+ sXML;
res = curl_send(mCurlHndl, req.c_str(), req.length());
if (res !- CURLE_OK) ...
res = curl_send(mCurlHndl, ...binary data...);
if (res !- CURLE_OK) ...
res = curl_send(mCurlHndl, ...binary data...);
if (res !- CURLE_OK) ...
res = curl_send(mCurlHndl, ...binary data...);
if (res !- CURLE_OK) ...
...
}
...
curl_easy_cleanup(mCurlHndl);