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.
Related
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);
}
I am new to learning C sockets, and I was able to successfully send an html <h1>hello world!</h1> as text/html content from a C socket server to the browser(client). However, even though the h1 tag displays correctly, I'm not sure why the page is stuck with a loading indicator. I tried adding a Content-Length property to indicate the length of my response, which works, but I was told that this shouldn't be necessary.
I think I am reading and writing properly to the socket, so I'm not sure what's hanging. Code:
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void servConn(int port)
{
int sd, new_sd;
struct sockaddr_in name, cli_name;
int sock_opt_val = 1;
int cli_len;
char data[256]; /* Our receive data buffer. */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("(servConn): socket() error");
exit(-1);
}
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt_val, sizeof(sock_opt_val)) < 0)
{
perror("(servConn): Failed to set SO_REUSEADDR on INET socket");
exit(-1);
}
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sd, (struct sockaddr *)&name, sizeof(name)) < 0)
{
perror("(servConn): bind() error");
exit(-1);
}
listen(sd, 5);
for (;;)
{
cli_len = sizeof(cli_name);
new_sd = accept(sd, (struct sockaddr *)&cli_name, &cli_len);
printf("Assigning new socket descriptor: %d\n", new_sd);
if (new_sd < 0)
{
perror("(servConn): accept() error");
exit(-1);
}
if (fork() == 0)
{ /* Child process. */
close(sd);
char reply[] = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
char *requestType;
char *filename;
int status = 200;
char *strPter;
int index = 0;
char c;
while (1)
{
read(new_sd, &c, 1);
if (index > 254)
{
data[index] = '\0';
break;
}
if (c == '\n')
{
data[index] = '\0';
break;
}
else
{
data[index++] = c;
}
}
printf("read: %d bytes: %s\n", index, data);
requestType = strtok_r(data, " ", &strPter);
if (strcmp(requestType, "GET") != 0)
status = 501;
else
printf("request was GET\n");
filename = strtok_r(NULL, " ", &strPter);
printf("filename: %s\n", filename);
strtok_r(NULL, " ", &strPter);
char *response = "<h1>hello world!</h1>";
strcat(reply, response);
printf("\nresponse is (%d): \n%s\n\n", strlen(reply), reply);
send(new_sd, reply, strlen(reply), 0);
close(new_sd);
printf("closed connection!\n");
exit(0);
}
}
}
int main()
{
servConn(5050); /* Server port. */
return 0;
}
Here is my output, which seems to be in the correct HTTP format:
Here is the browser output, which is stuck in loading even though the content is displayed:
How do I correctly close the socket and stop the page from loading after sending hello world?
Before calling
close(new_sd);
you need
shutdown(new_sd, SHUT_RDWR);
It is this call that sends proper connection termination sequence. close doesn't, it just destroys the socket.
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)
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) */
}
I'm trying to write a simple client in C that will interface with the Google Search API and return search results. I am able to send a search request and get a response back with a 200 OK code and some header text, but there are no search results. Am I doing something wrong?
Here is my code:
#include sys/socket.h
#include sys/types.h
#include netinet/in.h
#include netdb.h
#include stdio.h
#include string.h
#include stdlib.h
#include unistd.h
#include errno.h
#include openssl/rand.h
#include openssl/ssl.h
#include openssl/err.h
// Simple structure to keep track of the handle, and
// of what needs to be freed later.
typedef struct {
int socket;
SSL *sslHandle;
SSL_CTX *sslContext;
} connection;
// For this example, we'll be testing on openssl.org
#define KEY "/customsearch/v1?key=AIzaSyAOdB5MgAEmvzglw05rR1OPYEYgFuZrT9o&cx=003397780648636422832:u25rx3s92ro&q="
#define SERVER "www.googleapis.com"
#define PORT 443
// Establish a regular tcp connection
int tcpConnect ()
{
int error, handle;
struct hostent *host;
struct sockaddr_in server;
host = gethostbyname (SERVER);
handle = socket (AF_INET, SOCK_STREAM, 0);
if (handle == -1)
{
perror ("Socket");
handle = 0;
}
else
{
server.sin_family = AF_INET;
server.sin_port = htons (PORT);
server.sin_addr = *((struct in_addr *) host->h_addr);
bzero (&(server.sin_zero), 8);
error = connect (handle, (struct sockaddr *) &server,
sizeof (struct sockaddr));
if (error == -1)
{
perror ("Connect");
handle = 0;
}
}
return handle;
}
// Establish a connection using an SSL layer
connection *sslConnect (void)
{
connection *c;
c = malloc (sizeof (connection));
c->sslHandle = NULL;
c->sslContext = NULL;
c->socket = tcpConnect ();
if (c->socket)
{
// Register the error strings for libcrypto & libssl
SSL_load_error_strings ();
// Register the available ciphers and digests
SSL_library_init ();
// New context saying we are a client, and using SSL 2 or 3
c->sslContext = SSL_CTX_new (SSLv23_client_method ());
if (c->sslContext == NULL)
ERR_print_errors_fp (stderr);
// Create an SSL struct for the connection
c->sslHandle = SSL_new (c->sslContext);
if (c->sslHandle == NULL)
ERR_print_errors_fp (stderr);
// Connect the SSL struct to our connection
if (!SSL_set_fd (c->sslHandle, c->socket))
ERR_print_errors_fp (stderr);
// Initiate SSL handshake
if (SSL_connect (c->sslHandle) != 1)
ERR_print_errors_fp (stderr);
}
else
{
perror ("Connect failed");
}
return c;
}
// Disconnect & free connection struct
void sslDisconnect (connection *c)
{
if (c->socket)
close (c->socket);
if (c->sslHandle)
{
SSL_shutdown (c->sslHandle);
SSL_free (c->sslHandle);
}
if (c->sslContext)
SSL_CTX_free (c->sslContext);
free (c);
}
// Read all available text from the connection
char *sslRead (connection *c)
{
const int readSize = 2048;
char *rc = NULL;
int received, count = 0;
char buffer[2048];
if (c)
{
while (1)
{
if (!rc)
rc = malloc (readSize * sizeof (char) + 1);
else
rc = realloc (rc, (count + 1) *
readSize * sizeof (char) + 1);
received = SSL_read (c->sslHandle, buffer, readSize);
buffer[received] = '\0';
if (received > 0)
strcat (rc, buffer);
if (received < readSize)
break;
count++;
}
}
return rc;
}
// Write text to the connection
void sslWrite (connection *c, char *text)
{
if (c)
SSL_write (c->sslHandle, text, strlen (text)
}
// Very basic main: we send GET / and print the response.
int main (int argc, char **argv)
{
connection *c;
char *response;
char request[512]="";
c = sslConnect ();
sprintf(request, "GET https://%s%s%s\r\n\r\n", SERVER, KEY, argv[1]);
printf("%s", request);
sslWrite (c, request);
response = sslRead (c);
printf ("%s\n", response);
sslDisconnect (c);
free (response);
return 0;
}
Here are my results (running "app_name stove"):
¸h ÆÁ*HTTP/1.0 200 OK
Expires: Wed, 23 May 2012 05:49:58 GMT
Date: Wed, 23 May 2012 05:49:58 GMT
Cache-Control: private, max-age=0, must-revalidate, no-transform
ETag: "ewDGMApuuzSJ2mUepyXm8PLTiIU/uPd2cbC0DjaL0y0Y6HiAvzSqSts"
Content-Type: application/json; charset=UTF-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
There must certainly more information than nothing out there about stoves, right?
The two problems in the above code are #1) the start of rc is not initialized to zero after the malloc and before using strcat -- which results in the garbage characters, and #2) the line if (received < readSize) should be if (received == 0), since servers can potentially send the header and content as separate chunks.