TCP socket hangs when reading response from custom HTTP request - C - c

I have the following code that I have written which is suppose to send a simple http request over a TCP socket, I get a response but as soon as I try to read in the loop it hangs, in the 2nd read operation (tried it manually)
if anyone has an idea of why this might fail I will appreciate it a lot
attached below is the entire code
I am running the program like this: ./http_client yahoo.com
I get this response text at first:
HTTP/1.1 301 Moved Permanently
Date: Sat, 06 Aug 2022 08:07:11 GMT
Connection: keep-alive
Server: ATS
Cache-Control: no-store, no-cache
Content-Type: text/html
Content-Language: en
X-Frame-Options: SAMEORIGIN
Location: https://www.yahoo.com/
Content-Length: 8
redirect
and then it hangs and closes the socket, it shouldn't hang at all, it should run and exit without a delay or anything
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(int argc, char *argv[]) {
int sockfd;
struct sockaddr_in cli_name;
struct sockaddr_in *saddr;
char *hostname;
struct addrinfo *res;
int port = 80;
if (argc != 2) {
perror("Usage: establish tcp connection to: <hostname>\n");
exit(1);
}
hostname = argv[1];
printf("Client is alive and establishing socket connection %s.\n", hostname);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Error opening channel");
close(sockfd);
exit(1);
}
if (0 != getaddrinfo(hostname, NULL, NULL, &res)) {
fprintf(stderr, "Error in resolving hostname %s\n", hostname);
exit(1);
}
bzero(&cli_name, sizeof(cli_name));
cli_name.sin_family = AF_INET;
saddr = (struct sockaddr_in *) res->ai_addr;
cli_name.sin_addr.s_addr = inet_addr(inet_ntoa(saddr->sin_addr));
cli_name.sin_port = htons(port);
fflush(stdout);
if (connect(sockfd, (struct sockaddr *) &cli_name, sizeof(cli_name)) < 0) {
perror("Error establishing communications");
close(sockfd);
exit(1);
}
char header[100];
int cx;
char buf[2056];
size_t byte_count = 0;
size_t sent_byte_count = 0;
cx = snprintf(header, 100, "GET / HTTP/1.1\r\nHost: %s:%d\r\n\r\n", hostname, port);
size_t total = strlen(header);
size_t sent = 0;
do {
sent_byte_count = write(sockfd, header + sent, total - sent);
if (sent_byte_count < 0)
printf("ERROR writing message to socket");
if (sent_byte_count == 0)
break;
sent += sent_byte_count;
} while (sent < total);
memset(buf,0,sizeof(buf));
while ((byte_count = read(sockfd, buf, 2054)) > 0) {
buf[byte_count] = '\0';
printf("%s", buf); // <-- give printf() the actual data size
fflush(stdout);
}
printf("Exiting now.\n");
close(sockfd);
exit(0);
}

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 programming client sometimes don't receive server mesage

I made a simple client program with C that sends HTTP request to a host. The last part of the code, where the client receives HTTP response from server looks like this:
int main(int argc, char *argv[])
{
// ...
char buf[BUFSIZ];
int content_length;
content_length = recv(clientfd, buf, sizeof(buf) - 1, 0);
buf[content_length] = 0;
printf("%s", buf);
fflush(stdout);
exit(EXIT_SUCCESS);
}
With this code, I often get the expected result like the following:
GET /foo HTTP/1.0
Host: 127.0.0.1:8080
HTTP/1.0 200 OK
Content-Length: 12
Connection: close
abadakedavra
But sometimes, the content of the request doesn't show up.
GET /foo HTTP/1.0
Host: 127.0.0.1:8080
HTTP/1.0 200 OK
Content-Length: 12
Connection: close
What could be the reason for this behavior?
cf. My whole client code looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MAX_REQUEST 10000
#define MAX_URL 2048
void error(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
void parse_url(char *src, char *hostname, char *port, char *url)
{
size_t i, j = 0;
for (i = 7; src[i] != ':'; i++)
hostname[j++] = src[i];
hostname[j] = 0;
j = 0;
for (i = i + 1; src[i] != '/'; i++)
port[j++] = src[i];
port[j] = 0;
j = 0;
for (i = i + 1; src[i]; i++)
url[j++] = src[i];
url[j] = 0;
}
int main(int argc, char *argv[])
{
/*
Expect
argv[0] : ./client (Executable name)
argv[1] : -G (GET) or -P (POST)
argv[2] : http://hostname:port/url
*/
int clientfd;
char hostname[MAX_URL], port[6], url[MAX_URL];
char msg[MAX_REQUEST];
struct addrinfo hints, *listp, *p;
if (argc < 3 || (strcmp(argv[1], "-G") != 0 && strcmp(argv[1], "-P") != 0))
{
printf("Usage:\n %s -P <URL> HTTP 1.0 POST from stdin\n"
" %s -G <URL> HTTP 1.0 GET to stdin\n",
argv[0], argv[0]);
exit(EXIT_FAILURE);
}
parse_url(argv[2], hostname, port, url);
if(strcmp(argv[1], "-P") == 0) fgets(msg, MAX_REQUEST, stdin);
/* Client socket creation */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; // Use TCP
hints.ai_flags = AI_NUMERICSERV; // Use numeric port arg
// Generate a list of addrinfo in listp
getaddrinfo(hostname, port, &hints, &listp);
for (p = listp; p; p = p->ai_next)
{
// Create a socket based on addrinfo struct
if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue;
if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)
break;
close(clientfd); // Bind fail, loop to try again
}
freeaddrinfo(listp); // Not needed anymore
if (!p) // Entire loop failed
{
error("Failed in socket binding");
}
/* Send HTTP Request */
char httpRequest[MAX_REQUEST];
if(strcmp(argv[1], "-G") == 0) sprintf(httpRequest, "GET /%s HTTP/1.0\r\nHost: %s:%s\r\n\r\n", url, hostname, port);
else if(strcmp(argv[1]), "-P" == 0) sprintf(httpRequest, "POST /%s HTTP/1.0\r\nHost: %s:%s\r\nContent-Type: plain/text\r\nContent-Length: %lu\r\n\r\n%s\n", url, hostname, port, strlen(msg), msg);
else error("Invalid request");
printf("%s", httpRequest);
send(clientfd, httpRequest, strlen(httpRequest), 0);
/* Recieve HTTP response */
char buf[BUFSIZ];
int content_length;
content_length = recv(clientfd, buf, sizeof(buf) - 1, 0);
buf[content_length] = 0;
printf("%s", buf);
fflush(stdout);
exit(EXIT_SUCCESS);
}
You never implemented the HTTP protocol. If you want to receive an HTTP protocol response, you need to write code that receives an HTTP protocol response in accord with the HTTP protocol. Your code just receives a bunch of bytes, so that's all you get out.

Unable to show all UPnP devices within the local network C++

I am new to UPnP development and trying to discover all UPnP device within the local network, and I followed an example from the online resource, but my code will only keep looping at the first response. How could I get another response other than the first one, could I get some hints for this?
Example :
First Response from 192.168.xxx.123, and it will keeps printing the following result:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1790
DATE: Thu, 01 Jan 2015 10:43:15 GMT
ST: uuid:4d696xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
USN: uuid:4d696xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
EXT:
SERVER: Linux 2.6 DLNADOC/1.50 UPnP/1.0 ReadyDLNA/1.0.26
LOCATION: http://192.168.xxx.123:xxxx/rootDesc.xml
Content-Length: 0
I checked in Wireshark, and I can see the other device [IP: 192.168.xxx.99] has given me a response, but I am not able to receive it in my code.
I also read a question on SO and used select in my code, but still cannot get it working.
Receiving response(s) from N number of clients in reply to a broadcast request over UDP
The code:
#include <QCoreApplication>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#define RESPONSE_BUFFER_LEN 1024
#define SSDP_MULTICAST "239.255.255.250"
#define SSDP_PORT 1900
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int sock;
size_t ret;
unsigned int socklen;
struct sockaddr_in sockname;
struct sockaddr clientsock;
struct hostent *hostname;
char data[] =
"M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"ST: ssdp:all\r\n"
"MX: 120\r\n"
"\r\n";
char buffer[RESPONSE_BUFFER_LEN];
unsigned int len = RESPONSE_BUFFER_LEN;
fd_set fds;
struct timeval timeout;
hostname = gethostbyname(SSDP_MULTICAST);
hostname->h_addrtype = AF_INET;
if((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
{
printf("err: socket() failed");
return -1;
}
memset((char *)&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_port = htons(SSDP_PORT);
sockname.sin_addr.s_addr = *((unsigned long *)(hostname->h_addr_list[0]));
ret = sendto(sock, data, strlen(data), 0, (struct sockaddr *)&sockname,
sizeof(struct sockaddr_in));
if(ret != strlen(data))
{
printf("err:sendto");
return -1;
}
/* Get response */
FD_ZERO(&fds);
FD_SET(sock, &fds);
timeout.tv_sec = 5;
timeout.tv_usec = 5;
while(select(sock + 1, &fds, NULL, NULL, &timeout) > 0)
{
if(FD_ISSET(sock, &fds))
{
socklen = sizeof(clientsock);
if((len = recvfrom(sock, buffer, len, MSG_PEEK, &clientsock, &socklen)) == (size_t)-1)
{
printf("err: recvfrom");
return -1;
}
buffer[len] = '\0';
/* Check the HTTP response code */
if(strncmp(buffer, "HTTP/1.1 200 OK", 12) != 0)
{
printf("err: ssdp parsing ");
return -1;
}
printf(buffer);
}
else
{
printf("err: no ssdp answer");
}
}
//close(sock);
return a.exec();
}
You are using MSG_PEEK, which means to read the first message in the socket's receive buffer, but not remove it from the buffer.
Therefore every time you call recvfrom you get the first received message.
Change MSG_PEEK to 0 and then each call will read the first message that hasn't been read yet. (So the second call will read the second message, and so on)

Simple C Sockets HTTP Program Can't Find Resource: 404

I have spring-boot server that I am running in AWS. The server works fine. I can access it using chrome, postman, and curl with no issues. However, I have an embedded device that is running C and I am using sockets to try to connect to my server. The embedded device is running Linux so I can use curl to talk to the server with no issues. However, my C sockets code cannot seem to find the resource on the server. I keep getting 404's.
Here is my embedded client code,
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <errno.h>
int main() {
// Define some parameters
int sockfd, bytes_read;
struct sockaddr_in dest;
char buffer[4000];
char hdr[1000];
// Create Server Client Strings
bzero(hdr, sizeof(hdr));
strcpy(hdr, "GET /hello HTTP/1.1\r\n");
strcat(hdr, "Host: 52.200.39.81\r\n\r\n");
// Clean things up a bit before sarting
printf("\n\n");
// Create Socket
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
printf("Socket not created\n");
return 0;
}
// Initialize server address/port struct
bzero(&dest, sizeof(dest));
// *** Added this line to fix the code ***
dest.sin_addr.s_addr = inet_addr("52.200.39.81", &dest.sin_addr.s_addr);
dest.sin_family = AF_INET;
dest.sin_port = htons(8080);
if ( inet_addr("52.200.39.81", &dest.sin_addr.s_addr) == 0 ) {
printf("Incorrect Address Expression\n");
return 0;
}
// Connect Socket
if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) {
printf("Socket Connection Failed\n");
close(sockfd);
return 0;
}
// Send data
if (send(sockfd, hdr, strlen(hdr), 0) < 0) {
printf("Send Data Failed\n");
return 0;
}
printf("Socket successfully sent\n");
printf("\nSend Message - TxBufferSize = %d\n\n",strlen(hdr));
printf("%s", hdr);
bzero(buffer, sizeof(buffer));
bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);
if (bytes_read < 0) {
printf("Read Data Failed\n");
}
if (bytes_read > 0) {
// Print out receive buffer
printf("\n\nReceived Message -- RxSize = %d \n\n", strlen(buffer));
printf("%s", buffer);
}
if (bytes_read == 0) {
printf("No Read Bytes Received\n");
}
/*---Clean up---*/
close(sockfd);
return 0;
}
Here is what I get back,
debian#beaglebone:~$ ./helloworld
Socket successfully sent
***** Send Message -- TxBufferSize = 80 *****
GET http://52.200.39.81:8080/hello HTTP/1.1
Host: 52.200.39.81
accept: */*
***** Received Message -- RxBufferSize = 467 *****
HTTP/1.1 404 Not Found
Date: Sat, 02 Apr 2016 17:36:37 GMT
Server: Apache/2.2.22 (Debian)
Vary: Accept-Encoding
Content-Length: 283
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /hello was not found on this server.</p>
<hr>
<address>Apache/2.2.22 (Debian) Server at 52.200.39.81 Port 8080</address>
</body></html>
debian#beaglebone:~$
If I use the curl command on the embedded device,
debian#beaglebone:~$ curl 52.200.39.81:8080/hello
Greetings WebServiceTest1 -- ServiceCount = 11
debian#beaglebone:~$
I get the correct response from the server. So I am confident that the embedded device is talking to my server. Just can't seem to get the sockets code to work. Any help would be appreciated.
Ok, I found out what the problem was. I did not set the socket address. A very simple omission but that what the problem was. I have updated the code and it is now working. I added gethostbyname to make the code more standardized. The new lines of code are in the comment section "// Initialize server address/port struct". I would like to thank Lilas for providing me the reference code that led me to the omission of the dest.sin_addr.s_addr line. Below is the corrected code.
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
int main() {
// Define some parameters
int sockfd, bytes_read;
struct sockaddr_in dest;
char *hostname = "skmdev1.net";
struct hostent *hostent;
char buffer[4000];
char hdr[1000];
// Create Server Client Strings
bzero(hdr, sizeof(hdr));
strcpy(hdr, "GET /hello HTTP/1.1\r\n");
strcat(hdr, "Host: skmdev1.net\r\n\r\n");
// Clean things up a bit before sarting
printf("\n\n");
// Build the address
hostent = gethostbyname(hostname);
if (hostent == NULL) {
printf("error: gethostbyname(\"%s\")\n", hostname);
return 0;
}
// Create Socket
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
printf("Socket not created\n");
return 0;
}
// Initialize server address/port struct
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(8080);
dest.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)hostent->h_addr));
if ( dest.sin_addr.s_addr == INADDR_NONE ) {
printf("Incorrect Address Expression\n");
return 0;
}
// Connect Socket
if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) {
printf("Socket Connection Failed\n");
close(sockfd);
return 0;
}
// Send data
if (send(sockfd, hdr, strlen(hdr), 0) < 0) {
printf("Send Data Failed\n");
return 0;
}
printf("Socket successfully sent\n");
printf("\nSend Message - TxBufferSize = %d\n\n",strlen(hdr));
printf("%s", hdr);
bzero(buffer, sizeof(buffer));
bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);
if (bytes_read < 0) {
printf("Read Data Failed\n");
}
if (bytes_read > 0) {
// Print out receive buffer
printf("\n\nReceived Message -- RxSize = %d \n\n", strlen(buffer));
printf("%s\n\n", buffer);
}
if (bytes_read == 0) {
printf("No Read Bytes Received\n");
}
/*---Clean up---*/
close(sockfd);
return 0;
}

Simple c++ web server sending wrong headers

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);

Resources