I have a video encoder which can start/stop recording by sending a URL.
Start recording: http://192.168.1.5/blah-blah/record/record.cgi
Stop recording: http://192.168.1.5/blah-blah/record/stop.cgi
I don't have to worry about getting a response. I just have to send it.
I was exploring the GET method and also trying to use HappyHTTP library.
Is there a simple way to do it without using any library?
I am using visual studios on Windows.
You can use TCP connection to the server, after connected, you can send the plain HTTP request.
The following program shows this, connecting to google with TCP protocol, sending HTTP get request, and getting the first 2K of the response.
Note: See #Chase great comments below, the program is not complete: usage of gethostbyname should be avoided, as this method is deprecated, error handling should be changed to use WSAGetLastError and not errno, the program should call WSACleanup before it ends.
I don't have time now to fix the program, and I leave it as it, as someone may benefit from it as it is now.
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <winsock.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define SERVER_PORT ((u_short )80)
#define SERVER_HOST "www.google.com"
int main(int argc, char* argv[])
{
int ret;
SOCKET sd;
struct hostent* he = { 0 };
struct sockaddr_in addr_info = { 0 };
WSADATA wsaData = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
he = gethostbyname(SERVER_HOST);
if (he == NULL)
{
printf("failed to get host information for '%s': %s\n", SERVER_HOST, strerror(errno));
exit(-1);
}
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd == INVALID_SOCKET)
{
printf("failed to create socket: %s\n", strerror(errno));
exit(-1);
}
addr_info.sin_family = AF_INET;
addr_info.sin_port = htons(SERVER_PORT);
addr_info.sin_addr = *((struct in_addr*)he->h_addr);
memset(&(addr_info.sin_zero), 0, sizeof(addr_info.sin_zero));
printf("connecting to %s ....\n", SERVER_HOST );
ret = connect(sd, (struct sockaddr*)&addr_info, sizeof(struct sockaddr));
if ( ret == -1)
{
printf("failed to connect: %s\n", strerror(errno));
exit(-1);
}
printf("sending GET /index.html request ....\n");
char send_buffer[] = "GET /index.html HTTP/1.1\r\nHost : " SERVER_HOST "\r\n\r\n";
ret = send(sd, send_buffer, sizeof(send_buffer), 0);
if ( ret == -1)
{
printf("failed to send request: %s\n", strerror(errno));
exit(-1);
}
printf("getting response (first 2K)\n");
char response_buffer[2048] = "";
//using sizeof(response_buffer)-1 to have the buffer terminate with zero, even if it is fully consumed by the recv call. this is because I want to print it later on.
ret = recv(sd, response_buffer, sizeof(response_buffer)-1, 0);
if (ret == -1)
{
printf("failed to get response: %s\n", strerror(errno));
exit(-1);
}
printf("received (first 2KB): %s\n", response_buffer);
closesocket(sd);
return 0;
}
program output is:
connecting to www.google.com ....
sending GET /index.html request ....
getting response (first 2K)
received: HTTP/1.1 200 OK
Date: Tue, 29 Dec 2020 08:56:34 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2020-12-29-08; expires=Thu, 28-Jan-2021 08:56:34 GMT; path=/; domain=.google.com; Secure
Set-Cookie: NID=205=a7W2c7M39ojimAWRRgn7nwedmdouUrZfo8uBa1wJeEwo8DUk7ibclM-xwp5ozhKO2BYmcRnQ1l4wwjb_DYfOVsDoi-UdtqBmgySL_KlcG6zMjembghO8OL81e2iHee0_cnlDZvSCCGvPnaC0LHNzFtqeWaYSELF7-1t5Khuv4Yc; expires=Wed, 30-Jun-2021 08:56:34 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
...
The barebones way of sending a request to server is """pretty simple""".
Firstly, I'll be assuming you know the exact host and port of the url. I assume this since I cannot access the link you've provided. In most cases, you know the host being the domain you type in the url and the port being 80 for http, or 443 for https - I cannot confirm this for the given link however.
In any case, first things first - the headers you'd require on windows for socket programming is-
#include <WinSock2.h>
#include <WS2tcpip.h>
You'll also need to link the winsock2 library, you can do this by putting #pragma comment(lib, "Ws2_32.lib") after your headers. Or you can put Ws2_32.lib in Project Configuration -> Linker -> Input -> Additional Dependencies in Visual Studio.
Now, winsock2 requires you to initiate the library (and also to close it). You can do this in your main function ideally, since the startup is required for any socket connection to be made-
/* Initialize wsock2 2.2 */
WSADATA wsadat;
if (WSAStartup(MAKEWORD(2, 2), &wsadat) != 0)
{
printf("WSAStartup failed with error %d\n", WSAGetLastError());
return 1;
}
And after everything is done-
/* Cleanup wsock2 */
if (WSACleanup() == SOCKET_ERROR)
{
printf("WSACleanup failed with error %d\n", WSAGetLastError());
return 1;
}
Now you need to obtain information about the server you want to connect to, we do that using getaddrinfo. (Relevant posix docs)
static struct addrinfo const hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM };
struct addrinfo* addrs;
int gai_retcode = getaddrinfo("192.168.1.5", "http", &hints, &addrs);
if (gai_retcode != 0)
{
fprintf(stderr, "Error encountered during getaddrinfo: %s\n", gai_strerrorA(gai_retcode));
return 1;
}
Let's walk through this a bit, getaddrinfo takes a node parameter - which is either a hostname string or an ip address string.
The second parameter is service - which is a string specifying the port to connect to, or the service being used (http, https, telnet etc - you can find a list of services in the IANA port list or in /etc/services of your linux box).
The third parameter is the address of an addrinfo struct containing "hints" to aid in selecting the right address. For connecting to a server, you usually need to set only the ai_family and ai_socktype members, everything else should be 0 initialized (which is what the compound initializer does automatically). AF_UNSPEC on ai_family means "find me either ipv4 or ipv6 addresses - no particular requirement", SOCK_STREAM on ai_socktype means "find me stream servers addresses" (aka TCP servers, not Datagram servers). HTTP is a stream based protocol.
The final parameter is the address of a pointer to an addrinfo struct. This is where the results of the lookup are stored in.
getaddrinfo returns 0 on success and fills up the pointer to addrs pointer. addrs is now a linked list of addresses returned from the lookup - the first one in the list is directly accessible, the next one is accessible through .ai_next and so on until you reach NULL. The regular shenanigans.
For websites having multiple servers around the world, getaddrinfo will return many addresses. They are arranged in the most relevant way determined by your machine. That is, the first one is the address most likely to work well. But you should ideally try the next ones in the linked list in case the first one fails.
For non zero returns, you can use gai_strerror to print the error message. This is named gai_strerrorA on windows for whatever reason. There's also a gai_strerror on windows but it returns a WCHAR* as opposed to a char*. On linux, you'll be using gai_strerror, not gai_strerrorA. Relevant linux docs on gai_strerror
Now that you have a list of addresses, you can create a socket and connect to one of them-
int sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (sfd == INVALID_SOCKET)
{
fprintf(stderr, "Error encountered in socket call: %d\n", WSAGetLastError());
return 1;
}
if ((connect(sfd, addrs->ai_addr, addrs->ai_addrlen)) == -1)
{
fprintf(stderr, "Error encountered in socket call: %d\n", WSAGetLastError());
closesocket(sfd);
return 1;
}
NOTE: This is only trying the first address in the linked list, if the first one fails - it just gives up. This isn't ideal for a real world usecase. But for your specific usecase, it probably won't matter.
Pretty self explanatory, socket takes the ai_family, ai_socktype and ai_protocol values from your addrinfo structure and creates a socket. It returns INVALID_SOCKET on failure (this is just -1 on linux). Relevant linux docs on socket.
connect takes the socket descriptor you just got from socket, and the ai_addr and ai_addrlen values from your addrinfo structure to connect to the server. It returns SOCKET_ERROR on failure (again, just -1 on linux). Relevant linux docs on socket.
Now all the low level socket shenanigans is done. Next comes HTTP shenanigans. To make an HTTP request, you need to know the HTTP spec, you can read the entire thing here.
Just kidding, here's a more digestible version - courtesy of MDN.
But basically, the format of a message is sort of like this-
<HTTP-VERB> <REQUEST-PATH> HTTP/<VERSION>\r\n[HEADERS-TERMINATED-WITH-CRLF]\r\n[BODY]
So a GET request to /blah-blah/record/record.cgi using HTTP 1.0 and no headers (no body either, since this is a GET request) looks like-
GET /blah-blah/record/record.cgi HTTP/1.0\r\n\r\n
Before moving on, I need to mention why I'm demonstrating HTTP/1.0 instead of the more sensible for the times - HTTP/1.1. Since not all servers actually support 1.0 fully. The real difference between the 2 is that 1.1 requires a Host: header, and it allows keeping the connection alive - which is more efficient for multiple requests to the same server.
About the Host: header, if you know what hostname the server is expecting in the request header, great! Change it to HTTP/1.1 and put the Host: hostname in. Where hostname is...well, the hostname. For all I know, your server might just be accepting 192.168.1.5 as the hostname, but it also may not.
The second part is the keep-alive connection part. A keep-alive connection gets a bit complicated with blocking sockets. If you decide to use a keep-alive connection and try to recv the response from the server, when there is nothing more to read from the stream, recv will keep blocking until there's something - since the connection is still alive. There are ways to solve this ofcourse, the behaviors of which vary from linux to windows. So I'd recommend just setting the Connection header to close if using HTTP/1.1. I assume this would be "good enough" for your usecase. The HTTP request would then look like-
GET /blah-blah/record/record.cgi HTTP/1.1\r\nHost: hostname\r\nConnection: close\r\n\r\n
Now, to send the request-
char const reqmsg[] = "GET /blah-blah/record/record.cgi HTTP/1.0\r\n\r\n";
if (send(sfd, reqmsg, sizeof reqmsg, 0) == SOCKET_ERROR)
{
fprintf(stderr, "Error encountered in send call: %d\n", WSAGetLastError());
closesocket(sfd);
return 1;
}
send takes the socket descriptor, the message, and the message length and returns the number of bytes sent or SOCKET_ERROR on failure (-1 on linux). Relevant docs for linux on send
There's some peculiarity here that should be noted. send may send less bytes than the length of the full message. This is normal for stream sockets. When this happens, you have to send the rest by calling send again and starting from where send left off (add the return value to the message string pointer to obtain the continuation point). This is omitted for brevity but it is highly unlikely to actually happen for such a short message. If you have any machine from the last decade - send will send that tiny message in full.
Please don't rely on that in an actual project though.
Congrats! you've successfully sent a request to the server. Now you can receive its response. The response format is like this-
HTTP/<VERSION> <STATUS-CODE> <STATUS-MSG>\r\n[HEADERS-TERMINATED-WITH-CRLF]\r\n[BODY]
You don't have to receive if you don't want to, the data will just be sitting there in the stream buffer, which will "vanish" when you close the socket. But here's a small example on parsing out just the status code-
int status;
char stats[13];
if (recv(sfd, stats, 13, 0) != 13)
{
fprintf(stderr, "Error encountered in recv call: %d\n", WSAGetLastError());
closesocket(sfd);
return 1;
}
sscanf(stats, "HTTP/1.0 %d ", &status);
recv, works much like send, just the other way around. It fills the buffer you pass as its second parameter and the maximum number of bytes it'll read can be mentioned in its third parameter. The return value of recv is the number of bytes read. Relevant docs for linux on recv
The peculiarity mentioned before is present here too. recv may read less than the bytes you mention in the third param. But with any half decent internet connection, 13 bytes will definitely be read in one go.
Why 13 bytes? That's the length of the HTTP/1.0 + a space + 3 digit status code + a space. We're only interested in the status code for this. sscanf will then parse out the status code (assuming the server responded correctly) and put it into status.
If you want to read any marginally large size (and/or execute multiple recv calls), be sure to do it in a loop and use the return value of the number of bytes read to appropriately use the buffer.
After all of this, be sure to call freeaddrinfo on addrs and closesocket on sfd.
Further reading: MS docs on Winsock2
On linux, everything is very similar - but with much less headaches :) - I recommend checking out beej's guide to network programming if you're looking for implementing this on linux.
(In fact, it's a great read for windows programmers as well! Up to date with modern functions that support ipv4 and ipv6 seamlessly, no manual and painful struct filling etc)
Everything will be super similar, you just need to change the headers - get rid of the stupid windows specific startups and replace WSAGetLastError inside fprintf to just a regular perror.
Edit: Replacement for compound literal (static struct addrinfo const hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM };)-
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
Suffice to say, if you have access to a compiler that supports C99 and above - use compound literals instead - it's plain better :)
Related
I wrote a simple HTTP Client that can request data from a host.
I am using getaddrinfo(3). With "GET / HTTP/1.1" request I am able to download HTML page of a given http Host.
Here is a part of my code:
struct addrinfo hints, *ai;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; // Internet Protocol (IP) socket
hints.ai_socktype = SOCK_STREAM; // TCP
int res = getaddrinfo("example.com", "http", &hints, &ai);
if (res != 0)
ERROR_EXIT("getaddrinfo: %s\n", gai_strerror(res));
int sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
ERROR_EXIT("socket: %s\n", strerror(errno));
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
ERROR_EXIT("connect: %s\n", strerror(errno));
FILE *sockfile = fdopen(sockfd, "r+");
if (sockfile == NULL)
ERROR_EXIT("fdopen: %s\n", strerror(errno));
// send a GET request to the server:
if (fputs("GET / HTTP/1.1\r\n\r\n", sockfile) == EOF)
ERROR_EXIT("fputs: %s\n", strerror(errno));
if (fflush(sockfile) == EOF)
ERROR_EXIT("fflush: %s\n", strerror(errno));
char buf[1024];
// print the reply:
while (fgets(buf, sizeof(buf), sockfile) != NULL)
fputs(buf, stdout);
fclose(sockfile);
return 0;
Downloading a HTML page works ok, but downloading PNG image for example "GET /image.png HTTP/1.1\r\n\r\n" gives something like this:
???????ݹh??DHDZ?yW]%?9a??J?6F?Ѧ?E???ݐTd?US?:)??I??M,?-????=??U??&???Nr? ???б???
b??]??8?6+?;??i䂢d?G?WA?rԺ?H[??]?Z5????g?{8??i\?qAC?#c??v.?rb??'<?T?????O?z?
q,yĜ?ŷZI???X??fM?l?Z??l:;M???ۦ?????c?\\?W6+???o?}_???紈A??GvG?p??6{??{%?????0?{?
%??ژ??l?$r<?????ft*'W?N?m߂Ҿ4??E?:^?#?&?%%
????Dw??Z?$??1?4?l%&2?f-5!?? ?E? 8...
I understand that this is a byte transfer and that I have to do byte ordering, but don't know where to begin.
I know that I need to use ntohl(3) and Big-endian order for PNG images. Can Someone give me directions what to look up and how to approach this?
Do I save this output to .png file and then do byte order or do I do before I create a .png file?
The problem is more complex than just "byte order".
The Good News is that byte order is probably irrelevant. The code you've got now should work just fine sending and receiving text strings with the server.
The Bad News is that you need to connect differently if you want to read binary data from the server. Several considerations:
It sounds like you probably don't need to worry about "POST", "PUT" ... or anything but "GET".
It also sounds like you don't need to worry about "MIME Types" or "uuencoding/decoding" - you'll just be reading the data as-is.
You should definitely read the Content-Type and Content-Length headers from the server's HTTP response.
Perhaps the simplest approach is just call fgetc() in a loop if you're reading binary data. Use fgets() for strings, and fgetc() for Content-Length bytes to read the image.
You'll probably want to do an fopen() to write the image bytes to a file.
But calling fgets() eliminates the need to worry about "byte order".
Look here, here and here for examples.
'Hope that helps...
It wouldn't make a whole lot of sense to write out the image's bytes before deciding which order they should be in. What would be your next line be, fopen()?
If you're doing something architecture dependent like this, it's best to guard the case-specific sections with #if/#elif, and use #else to emit #error with a diagnostic message.
If everything is supposed to still work, it can just be #warning... but do put the warning in. For later, when it stops working.
Configuration macros you can test should include #ifdef __BIG_ENDIAN__ and #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; you can see what else is available by 'compiling' with -E -dD. [there will be a lot; grep is your friend]
Oh, and "big endian" means if you're reading bytes in address order (a[0], a[1], a[2], ...), then the byte at this end is the most significant/highest 'digit'. Hence "big-endian".
edit: this may also be of concern...
FILE *sockfile = fdopen(sockfd, "r+"); // "r+"
if (sockfile == NULL)
ERROR_EXIT("fdopen: %s\n", strerror(errno));
if (fputs("GET / HTTP/1.1\r\n\r\n", sockfile) == EOF) // fputs...
This question already has an answer here:
Differ between header and content of http server response (sockets)
(1 answer)
Closed 4 years ago.
I'm writing a wrapper for Berkley sockets on Windows and Linux. The test program got problem here:
char buf[BUFSIZE];
int res = 0;
while((res = NetRecv(sock, buf, BUFSIZE, 0)) > 0) // 'NetRecv' is pointing to 'recv'
{
buf[res-1] = '\0';
printf("%s", buf);
}
The response is to a HTTP-Get request of a web-page content. The socket is streaming.
The 'NetRecv' is initialized correctly - that is, no type mismatch of the functions' pointers there is, I've checked it.
So, Windows version works flawlessly, the Linux one is stuck after reading all page. Namely, the previous to the last 'NetRecv' call accepts last chunk of the response, outputs it, and the next (last) call just blocks. Closing the terminal causes 'SIGHUP' signal.
Looks like the Linux version just doesn't realize, that it received the last chunk of data and waits for more.
Is it as it should be? Don't understand then, for what reason there is blocking call possibility.
Now, I surely could make non-blocking call and use 'select', but do I really have to?
Thanks in advance)
EDIT: Minimal working example (all checks are omitted and net functions are the standard ones, which also were tested):
int sock = socket(AF_INET, SOCK_STREAM, 0);
// Here getting good IP address of google.com - no problem here
char serv_ip[IPADDR_BUFSIZE];
GetHostAddrByName(AF_INET, "www.google.com", serv_ip, IPADDR_BUFSIZE);
// ip ver site out buf out buf size
// The routine above is made with 'getaddrinfo', to be precise
printf("Current IP of '%s' is '%s'.\n", SERV_URL, serv_ip);
// Copying IP string to address struct
struct sockaddr_in addr;
NetIpFromStr(AF_INET, serv_ip, &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = NetHtons(80);
connect(sock, (const struct sockaddr*)&addr, sizeof(addr));
const char* msg = "GET / HTTP/1.1\r\n\r\n";
send(sock, msg, strlen(msg), 0);
char buf[BUFSIZE];
int res = 0;
while((res = recv(sock, buf, BUFSIZE-1, 0)) > 0)
{
buf[res] = '\0';
printf("%s", buf);
}
EDIT 2: Important notice: the Windows version also blocks the call, when all the data is read. Closing the terminal just doesn't crash the program, like it happens in Linux. Therefore, the whole question is such: How to realize that all data is read?
The problem is that you are blindly reading from the socket in a loop until an error occurs. Once you have received the entire response, you go back to the socket and keep reading, which then blocks because there is nothing left to read. The only error that can occur at this point is when the connection is closed (or lost), which the server is likely not doing since you are sending an HTTP 1.1 request, where keep-alive is the default behavior for 1.1 (see RFC 2616 Section 8.1 Persistent Connections)
The correct solution is to parse the HTTP response and stop reading from the socket when you reach the end of the response, NOT simply relying on the server to close the socket. Read RFC 2616 Section 4.4 Message Length for how to detect when you have reached the end of the response. DO NOT read more than the response indicates! Once you stop reading, then you can decide whether to close your end of the socket, or reuse it for a new request.
Have a look at this pseudo code for the type of parsing and reading logic you need to use.
Also, your HTTP request is malformed, as you are not sending a required Host header, so no matter what, you will always receive a 400 Bad Request response from any HTTP 1.1 compliant server:
const char* msg = "GET / HTTP/1.1\r\n"
"Host: www.google.com\r\n" // <-- add this!
"\r\n";
The solution was to shutdown the socket for reading, both in Windows and Linux:
// after sending a request:
shutdown(sock, SD_SEND); // or 'SHUT_WR' in Linux
// now read loop
Curiously, 'shutdown' was called in Winsock tutorials too, but I thought that was unnecessary.
I'm using a c program to respond to API calls. I want to reply using JSON.
I created a streaming socket listening on my port and create a GET request using a browser (firefox in my case). I then reply using the "send" method based on the request received.
The problem is when my reply is bigger than 29200 bytes. Then the send method returns 29200 and only sends the first 29200 bytes, then it just stops. I cannot find why it would stop at this number.
I tried google and found:
C++ socket programming Max size of TCP/IP socket Buffer?
My socket is blocking, so the send() function should block until all data is sent.
I also tried to find if linux blocks anything, but when I checked (not sure how I checked, cannot find the stackoverflow issue describing this) it was set to something way bigger than 29200.
I would like to know why my socket stops at 29200 and, if possible, how I can change the socket to make it send more data?
Edit:
Did some testing with the following results:
Created a test program to just send back 29999 bytes of data: https://pastebin.ca/4010317
I'm using curl to receive the data using
curl -X GET -i 'http://:12345'
When running on my computer the response is:
received: -1 bytes
received:
sent 29999 bytes
I can see that on my computer (x64) the receive does not work, but the send does (Curl does receive the data)
but when running on the ARM device the response is:
received: 83 bytes
received: GET / HTTP/1.1
Host: 192.168.1.118:12345
User-Agent: curl/7.47.0
Accept: */*
J
sent 29200 bytes
Here, curl receives 29200 bytes of data.
When trying to loop the send (https://pastebin.ca/4010318), the result is:
received: -1 bytes
received:
sent 29199 bytes
Here, curl receives 29200 bytes, the second send returns -1. So looping is not possible.
I will keep trying, but the help is appreciated.
Your code work.
Well, to be more exact, it work despite many little misusing function.
First, I asked you an mvce : you give a code with some useless line of code that add just complexity : if you don't need the information on the client connexion, the you can pass NULL to accept.
accept4(socketId, NULL, NULL, SOCK_NONBLOCK);
This way, we do not have 2 useless variables.
Second : Checking for error is cool, but displaying something (or logging) is better, because you can have hint why this doesn't work.
For example, the "received: -1 bytes" that you interpret as non-working is in fact working : The error (errno) is EAGAIN, meaning that since your socket is non-blocking, the data is not currently available so you have to loop your recv to read the incomming data. Looping recv will "solve" your false problem.
And finally : No, you do not loop your send either : you just merely detect if you haven't send all the data and try again one more time : do a LOOP !
Edit :
You can see how I do your "initServerSocket" function for the "check error and logging" part :
int InitServerSocket(int portNum, int nbClientMax)
{
int socketId = -1;
struct sockaddr_in addressInfo;
int returnFunction = -1;
if ((socketId = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
// Log !
goto END_FUNCTION;
}
addressInfo.sin_family = AF_INET;
addressInfo.sin_addr.s_addr = htonl(INADDR_ANY);
addressInfo.sin_port = htons(portNum);
if (bind(socketId, (struct sockaddr *)&addressInfo, sizeof(addressInfo)) == -1) {
// Log !
goto END_FUNCTION;
}
const int flags = fcntl(socketId, F_GETFL, 0);
fcntl(socketId, F_SETFL, flags ^ O_NONBLOCK);
// Error detection & log ?
if (listen(socketId, nbClientMax) == -1) {
// Log !
goto END_FUNCTION;
}
returnFunction = socketId;
socketId = -1;
/* GOTO */END_FUNCTION:
if (socketId != -1) {
close(socketId);
}
return (returnFunction);
}
I finally found the issue, the problem was that the
accept4(socket, (struct sockaddr *) &addrInfoFromClient, &sizeAddrInfo, SOCK_NONBLOCK);
was setting the socket to non blocking, while in the init the socket was set to blocking. On the ARM device, this caused the "write" function to stop writing after 29200 bytes (not sure why).
When i changed the accept4 to:
accept(socket, (struct sockaddr *) &addrInfoFromClient, &sizeAddrInfo);
It worked.
I am building a http proxy in c.
The proxy is supposed to filter some keywords in the URL and in the html content.
The first problem I have is with the send() function. When I am loading the page for the first time all is fine and dandy. And if I let the page finnish loading, the next request is also fine. But if I open www.google.com and start to type the "instant-feature" is making a new request before the last one is complete and i get the following error:
Program received signal SIGPIPE, Broken pipe.
0x00007ffff7b2efc2 in send () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) up
#1 0x0000000000401f1a in main () at net-ninny2.c:232
232 bytes_sent += send(i, buffer+bytes_sent, buffer_size-bytes_sent, 0);
The code-block that generates the error looks like this:
while(bytes_sent < buffer_size) {
bytes_sent += send(i, buffer+bytes_sent, buffer_size-bytes_sent, 0);
printf("* Bytes sent to Client: %d/%d\n", bytes_sent, buffer_size);
}
If you think it's relevant i'll be happy to provide more code.
My second problem is related to Http headers. Since I want to filter keywords in the html content, I don't want the content to be encoded. Google doesn't seem to agree with that and no matter what I put in the Accept-Encoding -header, I always get the content back encoded in gzip. Any ideas how to get rid of that?
EDIT:
I am also trying to use fork() to create child processes for the new connections, but that just throws a nasty error:
select: Interrupted system call
I have put it where I create a new file descriptor from a incoming connection:
if (i == listener) {
// New connection
remote_addr_len = sizeof remote_addr;
newfd = accept(listener, (struct sockaddr *)&remote_addr, &remote_addr_len);
if (newfd == -1) {
perror("accept");
}
else {
FD_SET(newfd, &master); // Add new connection to master set
if (newfd > fdmax) {
fdmax = newfd;
}
printf("* New connection from %s on "
"socket %d\n",
inet_ntop(remote_addr.ss_family,
get_in_addr((struct sockaddr*)&remote_addr),
remoteIP, INET6_ADDRSTRLEN), newfd);
if(!fork()) {
fprintf(stderr, "!fork()\n");
close(newfd);
exit(5);
}
}
}
But I'm guessing I am doing it all wrong.
Cheers!
For your first question, you will want to ignore the SIGPIPE signal:
signal(SIGPIPE, SIG_IGN);
See How to prevent SIGPIPEs (or handle them properly) for more detail. If you ignore the signal and the socket connection is reset, you will also want to handle the -1 error return value from send() appropriately.
For your second question, you may not be able to force Google to send data uncompressed, since Google may assume that all browsers can handle compressed data. You will probably need to embed a gzip decompressor in your proxy. It's certainly not fair to increase the bandwidth requirements of both ends just because you want to filter some keywords.
What is the right (portable, stable) way to get the ToS byte of a received packet? I'm doing UDP with recvmsg() and on linux I can get the ToS if I setsockopt() IP_RECVTOS/IPV6_RECVTCLASS, but IP_RECVTOS doesn't seem to be available on my BSD systems. What is the right way to do this?
I primarily want this to work on the BSDs and Solaris.
Edit:
To clarify:
I currently use recvmsg() where I get the TTL and TOS in the msg_control field on Linux, but in order to get TTL and TOS I need to setsockopt()-enable IP_RECVTTL and IP_RECVTOS. And since Solaris and BSD (working with FreeBSD at the moment) don't have IP_RECVTOS from what I can see I don't get TOS when looping over the CMSG data.
I tried enabling IP_RECVOPTS and IP_RECVRETOPTS, but I still don't get any IP_TOS type CMSG.
Edit 2:
I want ToS to be able to verify (as much as possible) that it wasn't overwritten in transit. If for example a VoIP app all of a sudden notices that it's not getting EF tagged packets, then something is wrong and there should be an alarm. (and no, I'm not expecting EF to be respected or preserved over the public internet)
I want TTL basically just because I can. Hypothetically this could be used to trigger "something changed in the network between me and the other side" alerts, which can be useful to know if somethings stops working at the same time.
I was thinking if you can create two sockets.
One socket of type DGRAM used exclusively for sending
One Raw socket used exclusively for receiving.
Since you are using UDP, you can call a bind + recvFrom on the Raw Sock Fd and then manually unpack the IP header to determine the TOS or TTL.
When you want to send, use the DGRAM sockFd so you dont have to bother to actually create the UDP & IP packet yourself.
There may be issues like the kernel may pass the received buffer to both sockets or to the UDP socket instead of Raw socket or just to the Raw socket. If that is the case (or if it is implementation dependent) then we are back to square one. However, you can try calling bind on the Raw socket and see if it helps. I am aware this maybe a hack but searching on the net for a setsockopt for BSD returned nothing.
EDIT: I wrote a sample program
It kind of achieves the objective.
The code below creates two sockets (one raw & one udp). The udp socket is bound on the actual port I am expecting to receive data whereas the raw socket is bound on Port 0. I tested this on Linux and like I expected any data for port 2905 is received by both the sockets. I am however able to retrieve the TTL & TOS values. Dont downvote for the quality of the code. I am just experimenting whether it will work.
Further EDIT: Disabled the receive by UDP socket.
I have further enhanced the code to disable the receive by the UDP packet. Using setsockopt, I set the UDP's socket receive buffer to 0. This ensures the kernel does not pass the packet to the UDP socket. IMHO,You can now use the UDP socket exclusively for sending and the raw socket for reading. This should work for you in BSD and Solaris also.
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<string.h>
#include "protHeaders.x"
#include "gen.h"
int main(void)
{
S32 rawSockFd;
S32 udpSockFd;
struct sockaddr_in rsin;
struct sockaddr_in usin;
S32 one = 1;
const S32* val = &one;
struct timeval tv;
fd_set rfds;
S32 maxFd;
S16 ret;
S8 rawBuffer[2048];
S8 udpBuffer[2048];
struct sockaddr udpFrom,rawFrom;
socklen_t rLen,uLen;
memset(rawBuffer,0,sizeof(rawBuffer));
memset(udpBuffer,0,sizeof(udpBuffer));
memset(udpFrom,0,sizeof(udpFrom));
memset(rawFrom,0,sizeof(rawFrom));
if ((rawSockFd = socket(PF_INET,SOCK_RAW,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
/* doing the IP_HDRINCL call */
if (setsockopt(rawSockFd,IPPROTO_IP,IP_HDRINCL,val,sizeof(one)) < 0)
{
perror("Server:setsockopt");
RETVALUE(RFAILED);
}
rsin.sin_family = AF_INET;
rsin.sin_addr.s_addr = htonl(INADDR_ANY);
rsin.sin_port = htons(0);
usin.sin_family = AF_INET;
usin.sin_addr.s_addr = htons(INADDR_ANY);
usin.sin_port = htons(2905);
if(bind(rawSockFd,(struct sockaddr *)&rsin, sizeof(rsin)) < 0 )
{
perror("Server: bind failed");
RETVALUE(RFAILED);
}
if ((udpSockFd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
if(bind(udpSockFd,(struct sockaddr *)&usin, sizeof(usin)) < 0 )
{
perror("Server: bind failed on udpsocket");
RETVALUE(RFAILED);
}
/*set upd socket receive buffer to 0 */
one = 0;
if (setsockopt(udpSockFd,SOL_SOCKET,SO_RCVBUF,(char *)&one,sizeof(one)) < 0)
{
perror("Server:setsockopt on udpsocket failed");
RETVALUE(RFAILED);
}
tv.tv_sec = 0;
tv.tv_usec = 0;
maxFd = (rawSockFd > udpSockFd)? rawSockFd:udpSockFd;
while(1)
{
FD_ZERO(&rfds);
FD_SET(rawSockFd,&rfds);
FD_SET(udpSockFd,&rfds);
ret = select(maxFd+1,&rfds,0,0,&tv);
if ( ret == -1)
{
perror("Select Failed");
RETVALUE(RFAILED);
}
if(FD_ISSET(rawSockFd,&rfds))
{
printf("Raw Socked Received Message\n");
if(recvfrom(rawSockFd,rawBuffer,sizeof(rawBuffer),0,&rawFrom,&rLen) == -1)
{
perror("Raw socket recvfrom failed");
RETVALUE(RFAILED);
}
/*print the tos */
printf("TOS:%x\n",*(rawBuffer+1));
printf("TTL:%x\n",*(rawBuffer+8));
}
if(FD_ISSET(udpSockFd,&rfds))
{
printf("UDP Socked Received Message\n");
if(recvfrom(udpSockFd,udpBuffer,sizeof(udpBuffer),0,&udpFrom,&uLen) == -1)
{
perror("Udp socket recvfrom failed");
RETVALUE(RFAILED);
}
printf("%s\n",udpBuffer);
}
}
RETVALUE(ROK);
}
The "proper" and standard solution is probably to use cmsg(3). You'll find a complete description in Stevens' "Unix network programming" book, a must-read.
Google Code Search found me this example of use.
My understanding is that firstly BSD does not support IP_RECVTOS like functionality and secondly BSD raw sockets do not support the reception of UDP nor TCP packets. However there are two other ways of doing this, firstly by using the /dev/bpf interface - either directly or via libpcap. Or secondly by using DIVERT sockets which allow for diversion of specified traffic flows to userland.
Has anyone actually tested the code above on a BSD box? (it may work on Solaris...)
On Linux this approach will work but as mentioned it is also possible (and more convenient) to use setsockopt() with IP_TOS on the outgoing socket to set the outgoing TOS byte and setsockopt() with IP_RECVTOS on the incoming socket and use recvmsg() to retrieve the TOS byte.
Unfortuneatly this sort of thing usually varies across different *ixs. On Solaris you want to use getsockopt with IP_TOS; I don't know about BSD.
See man 7 ip for details.