Discord HTTPS POST with C - c

I'm trying to build a Discord API with C (libdiscord it's constantly crashing).
I have found this promosing code (found here):
#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#include <sys/socket.h> /* socket, connect */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#include <netdb.h> /* struct hostent, gethostbyname */
void error(const char *msg) { perror(msg); exit(0); }
int main(int argc,char *argv[])
{
/* first what are we going to send and where are we going to send it? */
int portno = 80;
char *host = "api.somesite.com";
char *message_fmt = "POST /apikey=%s&command=%s HTTP/1.0\r\n\r\n";
struct hostent *server;
struct sockaddr_in serv_addr;
int sockfd, bytes, sent, received, total;
char message[1024],response[4096];
if (argc < 3) { puts("Parameters: <apikey> <command>"); exit(0); }
/* fill in the parameters */
sprintf(message,message_fmt,argv[1],argv[2]);
printf("Request:\n%s\n",message);
/* create the socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
/* lookup the ip address */
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
/* fill in the structure */
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
/* connect the socket */
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
/* send the request */
total = strlen(message);
sent = 0;
do {
bytes = write(sockfd,message+sent,total-sent);
if (bytes < 0)
error("ERROR writing message to socket");
if (bytes == 0)
break;
sent+=bytes;
} while (sent < total);
/* receive the response */
memset(response,0,sizeof(response));
total = sizeof(response)-1;
received = 0;
do {
bytes = read(sockfd,response+received,total-received);
if (bytes < 0)
error("ERROR reading response from socket");
if (bytes == 0)
break;
received+=bytes;
} while (received < total);
if (received == total)
error("ERROR storing complete response from socket");
/* close the socket */
close(sockfd);
/* process response */
printf("Response:\n%s\n",response);
return 0;
}
But I need to send a header and data, according to this post (in Python):
import requests
import json
channelID = "your_id_goes_here" # enable dev mode on discord, right-click on the channel, copy ID
botToken = "your_token_here" # get from the bot page. must be a bot, not a discord app
baseURL = "https://discordapp.com/api/channels/{}/messages".format(channelID)
headers = { "Authorization":"Bot {}".format(botToken),
"User-Agent":"myBotThing (http://some.url, v0.1)",
"Content-Type":"application/json", }
message = "hello world"
POSTedJSON = json.dumps ( {"content":message} )
r = requests.post(baseURL, headers = headers, data = POSTedJSON)
I'm sorry, I don't know the protocols in detail.
I'd like to read messages too, but for now I'm OK just writing they.
UPDATE:
My new code (error 400, bad request).
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl = NULL;
CURLcode res;
const char *data = "{\"content\": \"data to send\"}";
const char *ID = "...";
const char *token = "...";
char tmp[1024];
curl = curl_easy_init();
if(curl != NULL) {
struct curl_slist *headers = NULL;
/* Remove a header curl would otherwise add by itself */
headers = curl_slist_append(headers, "Accept:");
/* Add a custom header */
sprintf(tmp, "Host: https://discordapp.com/api/channels/%s/messages", ID);
headers = curl_slist_append(headers, tmp);
sprintf(tmp, "Authorization: Bot %s", token);
headers = curl_slist_append(headers, tmp);
headers = curl_slist_append(headers, "User-Agent: myBotThing (http://some.url, v0.1)");
headers = curl_slist_append(headers, "Content-Type: application/json");
/* set our custom set of headers */
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
sprintf(tmp, "https://discordapp.com/api/channels/%s/messages", ID);
curl_easy_setopt(curl, CURLOPT_URL, tmp);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// POST
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK) printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
}
return 0;
}

Related

OpenSSL handshake error, no shared cipher

I'm trying to make a client/server program with the OpenSSL that can set up a secure connection and then pass encrypted messages back and forth. I'm using a self-signed certificate for the server and a private key for the client. When a connection is established, I use SSL_accept() to initiate the handshake. This is where the program breaks. On the client-side, the error message is:
140104940065408:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1543:SSL alert number 40
and on the server-side the error message is:
140062094398080:error:1417A0C1:SSL routines:tls_post_process_client_hello:no shared cipher:../ssl/statem/statem_srvr.c:2283:
I'm very new to OpenSSL and to cryptography in general so I'm quite lost as to how to proceed from here. Any help would be greatly appreciated. I'll post the full code for the client and server below.
Client Program:
#include <errno.h> /*USING THE ERROR LIBRARY FOR FINDING ERRORS*/
#include <stdio.h> /*standard i/o*/
#include <unistd.h> /*FOR USING FORK for at a time send and receive messages*/
#include <malloc.h> /*FOR MEMORY ALLOCATION */
#include <string.h> /*using fgets funtions for geting input from user*/
#include <sys/socket.h> /*for creating sockets*/
#include <resolv.h> /*server to find out the runner's IP address*/
#include <netdb.h> /*definitions for network database operations */
#include <openssl/ssl.h> /*using openssl function's and certificates and configuring them*/
#include <openssl/err.h> /* helps in finding out openssl errors*/
#include <unistd.h> /*FOR USING FORK for at a time send and receive messages*/
#define FAIL -1 /*for error output == -1 */
#define BUFFER 1024 /*buffer for reading messages*/
// CLIENT PROGRAM
//code from: https://github.com/ElectronSz/Encrypted-Chat-Server-Using-C-Programming-and-OpenSSL-AES-DES
int OpenConnection(const char *hostname, int port) {
int sd;
struct hostent *host;
struct sockaddr_in addr; /*creating the sockets*/
if ( (host = gethostbyname(hostname)) == NULL ) {
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0); /* setting the connection as tcp it creates endpoint for connection */
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) /*initiate a connection on a socket*/ {
close(sd);
perror(hostname);
abort();
}
return sd;
}
void LoadKey(SSL_CTX* ctx, char* KeyFile) /* to load a certificate into an SSL_CTX structure*/ {
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 ) {
ERR_print_errors_fp(stderr);
abort();
}
}
SSL_CTX* InitCTX(void) /*creating and setting up ssl context structure*/ {
const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = TLS_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL ) {
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl) /*show the ceritficates to server and match them but here we are not using any client certificate*/ {
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL ) {
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
} else {
printf("Info: No client certificates configured.\n");
}
}
int main(int count, char *strings[]) /* getting port and ip as an argument*/ {
SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
char input[BUFFER];
int bytes;
char *hostname, *portnum;
pid_t cpid; /* fork variable*/
if ( count != 3 ) {
printf("usage: %s \n", strings[0]);
exit(0);
}
SSL_library_init(); /*load encryption and hash algo's in ssl*/
hostname=strings[1];
portnum=strings[2];
ctx = InitCTX();
//SSL_CTX_set_ciphersuites(ctx, OSSL_default_ciphersuites());
LoadKey(ctx, "client.pem"); /* load certs */
server = OpenConnection(hostname, atoi(portnum)); /*converting ascii port to interger */
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
//SSL_set_ciphersuites(ssl, OSSL_default_ciphersuites());
/* Host common name */
// Not necessarily needed
//SSL_set_tlsext_host_name(ssl, "Conor");
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */ {
ERR_print_errors_fp(stderr);
} else {
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl);
/* get any certs */
cpid=fork();
/*Fork system call is used to create a new process*/
if(cpid==0) {
while(1) {
fflush(stdout);
printf("\nMESSAGE TO SERVER:");
fgets(input, BUFFER, stdin);
SSL_write(ssl, input, strlen(input)); /* encrypt & send message */
}
} else {
while(1) {
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if ( bytes > 0 ) {
buf[bytes] = 0;
printf("\nMESSAGE FROM SERVER: %s\n", buf);
printf("\nMESSAGE TO SERVER:");
fflush(stdout);
}
}
}
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
Server Program:
//#include <openssl/applink.c>
#include <unistd.h> /*FOR USING FORK for at a time send and receive messages*/
#include <errno.h> /*USING THE ERROR LIBRARY FOR FINDING ERRORS*/
#include <malloc.h> /*FOR MEMORY ALLOCATION */
#include <string.h> /*using fgets funtions for geting input from user*/
#include <arpa/inet.h> /*for using ascii to network bit*/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h> /* network to asii bit */
#include <resolv.h> /*server to find out the runner's IP address*/
#include <stdio.h> /*standard i/o*/
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <inttypes.h>
#define FAIL -1 /*for error output == -1 */
#define BUFFER 1024 /*buffer for reading messages*/
// SERVER PROGRAM
void InitializeSSL() {
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
}
void DestroySSL() {
ERR_free_strings();
EVP_cleanup();
}
void ShutdownSSL(SSL* cSSL) {
SSL_shutdown(cSSL);
SSL_free(cSSL);
}
int OpenListener(int port) {
int sd;
struct sockaddr_in addr; /*creating the sockets*/
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr)); /*free output the garbage space in memory*/
addr.sin_family = AF_INET; /*getting ip address form machine */
addr.sin_port = htons(port); /* converting host bit to n/w bit */
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) /* assiging the ip address and port*/ {
perror("can't bind port"); /* reporting error using errno.h library */
abort(); /*if error will be there then abort the process */
}
if ( listen(sd, 10) != 0 ) /*for listening to max of 10 clients in the queue*/ {
perror("Can't configure listening port"); /* reporting error using errno.h library */
abort(); /*if erroor will be there then abort the process */
}
return sd;
}
void Servlet(SSL* ssl) /* Serve the connection -- threadable */ {
char buf[1024];
int sd, bytes;
char input[BUFFER];
pid_t cpid;
//Here is the SSL Accept portion. Now all reads and writes must use SSL
int ret = SSL_accept(ssl);
printf("%d\n", ret);
if (ret == 0) {
/* Hard error */
exit(-1);
} else if ( ret == -1 ) /* do SSL-protocol accept */ {
printf("Handshake Error %d\n", SSL_get_error(ssl, ret));
ERR_print_errors_fp(stderr);
exit(-1);
}
/*Fork system call is used to create a new process*/
cpid=fork();
if(cpid==0) {
while(1) {
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request and read message from server*/
if ( bytes > 0 ) {
buf[bytes] = 0;
printf("\nMESSAGE FROM CLIENT: %s\n", buf);
printf("\nMESSAGE TO CLIENT:");
fflush(stdout);
} else {
ERR_print_errors_fp(stderr);
}
}
} else {
while(1) {
fflush(stdout);
printf("\nMESSAGE TO CLIENT:");
fgets(input, BUFFER, stdin); /* get request and reply to client*/
SSL_write(ssl, input, strlen(input));
}
}
sd = SSL_get_fd(ssl); /* get socket connection */
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
}
bool str_to_uint16(char *str, uint16_t *res) {
char *end;
errno = 0;
intmax_t val = strtoimax(str, &end, 10);
if (errno == ERANGE || val < 0 || val > UINT16_MAX || end == str || *end != '\0') {
return false;
}
*res = (uint16_t) val;
return true;
}
int main(int count, char *strings[]) /* getting port as a argument*/ {
int sockfd, newsockfd;
SSL_CTX *sslctx;
SSL *cSSL;
char buf[1024];
char input[BUFFER];
int bytes;
char *portnum;
pid_t cpid; /* fork variable*/
if ( count != 2 ) {
printf("Usage: %s <portnum> \n", strings[0]); /*send the usage guide if syntax of setting port is different*/
exit(0);
}
portnum = strings[1];
uint16_t temp;
uint16_t *aPortNumber = &temp;
if (str_to_uint16(portnum, aPortNumber) == false) {
printf("Port number must be between 0 and 65535.");
exit(1);
}
InitializeSSL();
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd< 0) {
//Log and Error
exit(1);
}
struct sockaddr_in saiServerAddress;
bzero((char *) &saiServerAddress, sizeof(saiServerAddress));
saiServerAddress.sin_family = AF_INET;
saiServerAddress.sin_addr.s_addr = INADDR_ANY;
saiServerAddress.sin_port = htons(*aPortNumber);
bind(sockfd, (struct sockaddr *) &saiServerAddress, sizeof(saiServerAddress));
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &len);
sslctx = SSL_CTX_new( TLS_server_method());
// check return values vvvv
int use_cert = SSL_CTX_use_certificate_file(sslctx, "server.pem" , SSL_FILETYPE_PEM);
if (use_cert == 0) {
ERR_print_errors_fp(stderr);
exit(-1);
}
int use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "private-key.pem", SSL_FILETYPE_PEM);
if (use_prv == 0) {
ERR_print_errors_fp(stderr);
exit(-1);
}
cSSL = SSL_new(sslctx);
SSL_set_fd(cSSL, newsockfd );
//SSL_CTX_set_ciphersuites(sslctx, cipher_list.c_str());
//SSL_set_ciphersuites(cSSL, cipher_list.c_str());
Servlet(cSSL);
ShutdownSSL(cSSL);
return 0;
}

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.

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) */
}

Socket Programming in C

I'm trying to write a web server but with the code I have I'm getting 'open failed'. The html document (ht.html) which is supposed to be opened in the browser.
The code I have is:
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10
int main(int argc, char *argv[])
{
int s, b, l, fd, sa, bytes, on = 1;
char buf[BUF_SIZE]; //buffer for outgoing file
struct hostent *h; //info about server
struct sockaddr_in channel; //holds IP address
//Build address structure to bind to socket
memset(&channel, 0, sizeof(channel)); //zero channel
channel.sin_family = AF_INET;
channel.sin_addr.s_addr = htonl(INADDR_ANY);
channel.sin_port = htons(SERVER_PORT);
//Passive open. Wait for connection
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
if (s < 0) fatal("socket failed");
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
if (b < 0) fatal("bind failed");
l = listen(s, QUEUE_SIZE); /* specify queue size */
if (l < 0) fatal("listen failed");
/* Socket is now set up and bound. Wait for connection and process it. */
while(1) {
sa = accept(s, 0, 0); /* block for connection request */
if (sa < 0) fatal("accept failed");
read(sa, buf, BUF_SIZE); /* read file name from socket */
/* Get and return the file. */
fd = open(buf, O_RDONLY); /* open the file to be sent back */
if (fd < 0) fatal("open failed");
while(1){
bytes = read(fd, buf, BUF_SIZE); /* read from file */
if (bytes <= 0) break; /* check for end of file */
write(sa, buf, bytes); /*write bytes to socket*/
}
close(fd); //close file
close(sa); //close connection
}
}
fatal(char*string)
{
printf("%s", string);
exit(1);
}
Where's my error? Or what has to be added?
Maybe you can start with outputting the data received from the socket, or at least run in a debugger, otherwise everything will be run in the dark, without you knowing what is going on. In the code below I have added a printf to print what we get from the web browser.
Also like others have pointed out, it is good to know what errno is trying to tell us. It is a bit awkward/annoying to use perror + exit, so in Linux and BSD you can use err(3) and warn(3). err will print errno message and then exit, while warn will just print errno message and not exit, I replaced your fatal function with these.
The web browser most likely will send GET /ht.html HTTP/1.1\r\n and this is what you are attempting to open. In order to open the file we need to extract the ht.html part. I have updated your code below and now strchr(3) and strstr(3) are used to extract ht.html.
We also need to send a HTTP response code and tell the web browser we want to send HTML, that is why the HTTP/1.1 200 OK is sent. Remember all HTTP headers need to be separated by \r\n (carriage return - newline). You will find more info about the HTTP protocol in RFC 2616.
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10
int main(int argc, char *argv[])
{
int s, b, l, fd, sa, bytes, on = 1;
char buf[BUF_SIZE]; /* buffer for outgoing file */
char *p, *endp, *cp;
struct sockaddr_in channel; /* holds IP address */
/* Build address structure to bind to socket */
memset(&channel, 0, sizeof(channel)); /* zero channel */
channel.sin_family = AF_INET; /* ipv4 */
channel.sin_addr.s_addr = htonl(INADDR_ANY); /* 0.0.0.0 */
channel.sin_port = htons(SERVER_PORT);
/* Passive open. Wait for connection */
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
if (s < 0) err(1, "socket failed");
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
if (b < 0) err(1, "bind failed");
l = listen(s, QUEUE_SIZE); /* specify queue size */
if (l < 0) err(1, "listen failed");
/* Socket is now set up and bound. Wait for connection and process it. */
while(1) {
sa = accept(s, NULL, NULL); /* block for connection request */
if(sa < 0) {
warn("accept failed");
continue;
}
bytes = 0;
endp = buf + sizeof(buf); /* pointer to end of buf */
cp = NULL;
buf[0] = '\0';
/* read first line from socket */
/* should be "GET /[file] HTTP/1.1" */
for(p = buf; (bytes = read(sa, p, endp - p)) > 0; p += bytes) {
p[bytes] = '\0'; /* read(2) doesn't NUL terminate buf */
if((cp = strchr(p, '\r')) != NULL) /* break at first carriage return */
break;
}
printf("incoming request %lu bytes:\n%s\n", strlen(buf), buf);
/* no carrige return or no "GET /" was found */
if(cp == NULL || strstr(buf, "GET /") != buf) {
warnx("incomplete request");
close(sa);
continue;
}
*cp = '\0'; /* replace '\r' with '\0' */
p = buf + sizeof("GET /") - 1; /* point to after "GET /" */
cp = strchr(p, ' '); /* find " HTTP/1.1" */
if(cp == NULL) {
warnx("HTTP version was not found");
close(sa);
continue;
}
*cp = '\0'; /* replace ' ' with '\0' */
/* Get and return the file. */
fd = open(p, O_RDONLY); /* open the file to be sent back */
if(fd < 0) {
warn("open failed: %s", p);
close(fd);
close(sa);
continue;
}
/* Send HTTP header */
/* Should probably also send Content-Length: <sizeof file>, */
/* this can be checked using fstat(2) */
write(sa, "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html;charset=UTF-8\r\n\r\n", 58);
while(1) {
bytes = read(fd, buf, sizeof(buf)); /* read from file */
if (bytes <= 0) break; /* check for end of file */
write(sa, buf, bytes); /*write bytes to socket*/
}
close(fd); /* close file */
close(sa); /* close connection */
}
return 0;
}
To connect from your web browser to your HTTP server go to: http://127.0.0.1:12345/ht.html.
Try to add some debug messages or run with a debugger.
I think that the problem relies in the buffer passed to open statement. It looks like buf is not initialized with zeroes and also not NULL terminated by "read".
n = read(sa, buf, BUF_SIZE);
buf[n] = '\0';
In general, when working with read, it should be called in a loop until 0 or -1 returned. It might fill only a fraction of the buffer.
Try to look ERRNO.
if (fd < 0) perror("open failed");
Try to look buf.
if (fd < 0){
printf("%s\n", buf);
perror("open failed");
}
Try to look buf this way:
if (fd < 0){
for(i=0;i<strlen(buf);i++)
printf("%d", buf[i]);
perror("open failed");
}
This will be enough to understand the error because your application simply does not open the file.
The stuff you are reading from the browser is a HTTP request.
You will need to decode this - so read the spec for HTTP.
Example of HTTP requests can be found here

Google Custom Search results in C

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.

Resources