Simple c++ web server sending wrong headers - c

I have very simple web server:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Connection: Keep-Alive\r\n"
"Server: michal\r\n"
"Vary: Accept-Encoding\r\n"
"Keep-Alive: timeout=5, max=100\r\n\r\n"
"<html><body><h1>It works!</h1>"
"<p>This is the default web page for this server.</p>"
"<p>The web server software is running but no content has been added, yet.</p>"
"</body></html>\r\n";
int main()
{
int one = 1, client_fd;
struct sockaddr_in svr_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
err(1, "can't open socket");
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
int port = 8080;
svr_addr.sin_family = AF_INET;
svr_addr.sin_addr.s_addr = INADDR_ANY;
svr_addr.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
close(sock);
err(1, "Can't bind");
}
listen(sock, 5);
while (1) {
client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
printf("got connection\n");
if (client_fd == -1) {
perror("Can't accept");
continue;
}
write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
close(client_fd);
}
}
This works in browser, the page is rendering correctly but when I am doing ab test I have error:
Benchmarking localhost (be patient)...apr_poll: The timeout specified has expired (70007)
Total of 2 requests completed
ab works fine when I am benchmarking localhost (Apache)
When I tried to download page with php file_get_contents I have following error:
PHP Notice: file_get_contents(): send of 2 bytes failed with errno=32 Broken pipe in /home/mitch/Dokumenty/projects/cpp/webserver/webserver/bench.php on line 7
What is wrong ?

My guess is that you are terminating connection before client has a chance to send all the request headers it wants.
So adding delay before close, or actually reading data before empty line is received would help.

Here is similar problem explained: https://stackoverflow.com/a/6269273/250944 (p.2)
Headers is ok, but you should read something from client first and only then write response, here is diff for your code snippet with added code for reading data:
## -8,6 +8,8 ##
#include <arpa/inet.h>
#include <err.h>
+#define MAXMSG 16384
+
char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Connection: Keep-Alive\r\n"
## -24,6 +26,8 ## int main()
int one = 1, client_fd;
struct sockaddr_in svr_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
+ char read_buffer[MAXMSG];
+ int nbytes;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
## -50,6 +54,13 ## int main()
perror("Can't accept");
continue;
}
+ nbytes = read (client_fd, read_buffer, MAXMSG);
+
+ if (nbytes < 0) {
+ perror("Can't read");
+ close(client_fd);
+ continue;
+ }
write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
close(client_fd);

Related

Using a simple C program how to load HTTPS web page

I am using a C web page loading program. When I ask for a page with an"http://" prefix it loads successfully. However, when I ask for "https://" it crashes. If I remove the "s" then I receive:
Host: a.b.c
nr 90 (90)
Full Buffer header HTTP/1.1 301 Moved Permanently
Date: Mon, 17 Oct 2022 10:02:05 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: max-age=3600
Expires: Mon, 17 Oct 2022 11:02:05 GMT
Location: https://a.b.c/ViewDocument.aspx?fileid=12345678
Server: cloudflare
This "moved" location is the original URL that I am requesting.
When this address is used in a browser it will automatically download a PDF file - which is the ultimate aim of this code.
Some relevant coding:
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include <unistd.h>
#include "stdint.h"
#ifdef __linux
#include "netinet/in.h"
#include "sys/socket.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#endif
...
void receive(char *ip, char *domain, char *page){
int sockfd, portno;
struct sockaddr_in server_addr;
char header[512];
portno = 80;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1){
printf("error opening socket\n");
return;
}else{
printf("socket opened\n");
}
memset((char *) &server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portno);
#ifdef __WIN32
server_addr.sin_addr.s_addr = inet_addr(ip);
#else
inet_pton(AF_INET, ip, &(server_addr.sin_addr) );
#endif
int err = 0;
if( (err = connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) ) < 0){
printf("failed to connect %d\n", err);
return;
}else{
printf("connection successful\n");
}
snprintf(header, 512, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", page, domain);
printf("Header %s\n", header);
int nr = send(sockfd, header, tec_string_length(header), 0);
if(nr){
printf("nr %d (%u)\n", nr, tec_string_length(header));
}
char *buf;
buf = (char *) malloc(BUF_SIZE_INC * sizeof(char));
memset(buf, 0, BUF_SIZE_INC);
ssize_t bytes_received = recv(sockfd, buf, BUF_SIZE, 0);
int size = 0;
char *new_buffer = process_header(buf, &size);
memmove(buf, new_buffer, BUF_SIZE_INC - (new_buffer - buf) );
load_body(sockfd, buf, size);
puts("*****");
#ifdef __WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif
}//receive*/
The code is from https://gitlab.com/greggink/youtube_episode_loading_webpage (using the main_final.c file).
Ubuntu 20.04
Thank you.

C Socket does not connect and returns exit value of 255

I have a piece of C code that should connect to www.google.com and make a HTTP GET request, but when I run it, it stays on "Connecting.." for about 30 seconds before returning "Connection Failed" and an exit return value of 255. What am I doing wrong?
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define PORT 8000
struct hostent *hostinfo;
int main(void) {
int sock = 0, valread;
struct sockaddr_in serv_addr;
char *hostname = "www.google.com";
char *request = "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n";
hostinfo = gethostbyname(hostname);
char *ip = inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[0]);
char buffer[1024] = {0};
printf("Creating socket...\n");
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
printf("Checking address...\n");
if(inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0){
printf("\n Invalid IP/Address not supported \n");
return -1;
}
printf("Connecting to host %s...\n", ip);
if(connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
printf("\n Connection Failed \n");
return -1;
}
send(sock, request, strlen(request), 0);
printf("Message sent\n");
valread = read(sock, buffer, 1024);
printf("%s\n", buffer);
return 0;
}
I see two major problems.
You use the wrong port. Use port 80 for http.
Your read and printf is a dangerous combination that could easily cause access out of bounds (and undefined behavior). What you read from the socket will not be null terminated. You could instead do something like this:
...
printf("Message sent\n");
while((valread = read(sock, buffer, sizeof(buffer))) > 0) {
fwrite(buffer, valread, 1, stdout);
}
This will however block when everything has been read. See non-blocking I/O or consider using select, epoll or poll to wait for available data on sockets.
If you are only interested in getting the response and then disconnect, you could however use Connection: close to close the connection after the server has sent the response. Full code below:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define PORT 80
int main(void) {
int sock = 0, valread;
struct hostent *hostinfo;
struct sockaddr_in serv_addr;
const char *hostname = "www.google.com";
const char *request = "GET / HTTP/1.1\r\n"
"Host: www.google.com\r\n"
"Connection: close\r\n\r\n"; // <- added
hostinfo = gethostbyname(hostname);
char *ip = inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[0]);
char buffer[1024] = {0};
printf("Creating socket...\n");
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
printf("Checking address...\n");
if(inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0){
printf("\n Invalid IP/Address not supported \n");
return -1;
}
printf("Connecting to host %s...\n", ip);
if(connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
perror("connect()");
return -1;
}
send(sock, request, strlen(request), 0);
printf("Message sent\n");
while((valread = read(sock, buffer, sizeof(buffer))) > 0) {
fwrite(buffer, valread, 1, stdout);
}
}

503 - Service temporarily unavailable

I need simple HTTP client on Raspberry PI zero with Raspbian. I used few example codes, but when i send about 7 requests then i can download just page with this error:
503 Service temporarily unavailable
There is no available fastcgi process to fullfill your request.
One of used codes:
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <unistd.h>
#define SA struct sockaddr
#define MAXLINE 4096
#define MAXSUB 200
ssize_t process_http(int sockfd, char *host, char *page)
{
ssize_t n;
snprintf(sendline, MAXSUB,
"GET %s\r\n"
"Host: %s\r\n"
"Connection: close\n"
"\n", page, host);
write(sockfd, sendline, strlen(sendline));
while ((n = read(sockfd, recvline, MAXLINE)) > 0)
{
recvline[n] = '\0';
}
printf("%s", recvline);
return n;
}
and in main is this:
int sockfd;
struct sockaddr_in servaddr;
char **pptr;
char *hname = "plankter.cz";
char *page = "http://plankter.cz/iot/list.json";
char str[50];
struct hostent *hptr;
if ((hptr = gethostbyname(hname)) == NULL) {
fprintf(stderr, " gethostbyname error for host: %s: %s",
hname, hstrerror(h_errno));
exit(1);
}
printf("hostname: %s\n", hptr->h_name);
if (hptr->h_addrtype == AF_INET
&& (pptr = hptr->h_addr_list) != NULL) {
printf("address: %s\n",
inet_ntop(hptr->h_addrtype, *pptr, str,
sizeof(str)));
} else {
fprintf(stderr, "Error call inet_ntop \n");
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(80);
inet_pton(AF_INET, str, &servaddr.sin_addr);
connect(sockfd, (SA *) & servaddr, sizeof(servaddr));
process_http(sockfd, hname, page);
close(sockfd);
In this example i download json, but when i read php or txt, i have same problem. I tried some another example codes using sockets, example with happyhttp library and all give me same result after 7 requests. I think it doesn't close connection. I need to send http request and recieve data, and i need to do it few times in minute.
Thanks for all ideas.
You provide a full URI to the GET field:
char *page = "http://plankter.cz/iot/list.json";
...
snprintf(sendline, MAXSUB,
"GET %s\r\n"
"Host: %s\r\n"
"Connection: close\n"
"\n", page, host);
You should not include protocol and host name.
Try this instead:
char *page = "/iot/list.json";

C Web Server - How to get URI?

A Simple C Web Server: How to get the URI ?
Also:
Perhaps there is a way to simply see all of the incoming raw data ?
such as the whole GET request.. along with the URL .. etc.. ?
I searched the web but could not find any info.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"test\r\n";
int main()
{
int one = 1, client_fd;
struct sockaddr_in svr_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
err(1, "can't open socket");
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
int port = 82;
svr_addr.sin_family = AF_INET;
svr_addr.sin_addr.s_addr = INADDR_ANY;
svr_addr.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
close(sock);
err(1, "Can't bind");
}
listen(sock, 5);
while (1) {
client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
printf("got connection\n");
if (client_fd == -1) {
perror("Can't accept");
continue;
}
write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
close(client_fd);
}
}
Well, according to RFC 7230, the GET query should looks like:
GET /index.html HTTP/1.1
Host: www.example.org
So, when client connects, you must read query from client, and parse it:
/* data to store client query, warning, should not be big enough to handle all cases */
char query[1024] = "";
char page[128] = "";
char host[128] = "";
/* read query */
if (read(client_fd, query, sizeof query-1) > 0)
{
char *tok;
char sep[] * "\r\n";
char tmp[128];
/* cut query in lines */
tok = strtok(query, sep);
/* process each line */
while (tok)
{
/* See if line contains 'GET' */
if (1 == sscanf(tok, "GET %s HTTP/1.1", tmp))
{
strcpy(page, tmp);
}
/* See if line contains 'Host:' */
else if (1 == sscanf(tok, "Host: %s", tmp))
{
strcpy(host, tmp);
}
/* get next line */
tok = strtok(query, sep);
}
/* print got data */
printf("wanted page is: %s%s\n", host, page);
}
else
{
/* handle the error (-1) or no data to read (0) */
}

C Server - Print Received Massage?

I send this C Server a message w/ netcat.
echo <message> | nc <ip> <port>
it prints:
Client IP : <ip>
I want it to also print:
Client Message : <message>
C SERVER
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
char response[] = "hi";
int main()
{
int one = 1, client_fd;
struct sockaddr_in svr_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
err(1, "can't open socket");
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
int port = 85;
svr_addr.sin_family = AF_INET;
svr_addr.sin_addr.s_addr = INADDR_ANY;
svr_addr.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
close(sock);
err(1, "Can't bind");
}
listen(sock, 5);
while (1) {
client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
printf("Client IP : [%s]\n", inet_ntoa(cli_addr.sin_addr));
if (client_fd == -1) {
perror("Can't accept");
continue;
}
write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
close(client_fd);
}
}
You just have to read data from the socket (after the accept call, once you've checked that client_fd is usable):
if (client_fd == -1) {
perror("Can't accept");
continue;
}
printf("Client Message : <");
/* buffer to store result */
char buffer[64] = "";
/* while we can read from socket */
while (read(client_fd, buffer, sizeof buffer-1) > 0)
{
/* write what have been read */
printf("%s", buffer);
}
puts(">");
write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
close(client_fd);
You could also use recv function.

Resources