Transfer HTTP connection to HTTPS in Pure C - c

I am having a serious problem transferring my HTTP connection socket program over to HTTPS connection socket code, how do I make only an HTTPS connection in pure C?
I am working on a package manager and am rewriting the connection.c file, the only thing this file contains is the code used to make the initial connection to the server containg packages, it does nothing else. I had this working 100% with an HTTP connection, however I need to move to an HTTPS connection and need to use LibreSSL; at the moment I am trying to use OpenSSL as I can't find anything on LibreSSL. The HTTP code I had is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include "repos.h"
#include "resolv.h"
short connection()
{
short socket_desc;
socket_desc = socket(AF_INET, SOCK_STREAM, 0); /* create socket with IPv4 and TCP protocol */
char host[17];
if (socket_desc == -1)
printf("could not create socket\n");
struct sockaddr_in *serv_addr = calloc(1, sizeof(struct sockaddr_in));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(80);
resolv(DEFAULT_HOST, host); /* set repository to use */
if (inet_pton(AF_INET, host, &serv_addr->sin_addr) <= 0) {
printf("error");
free(serv_addr);
return -1;
}
if (connect(socket_desc, (struct sockaddr *)serv_addr, sizeof(*serv_addr)) < 0) {
printf("connection failed\n");
return 1;
}
else {
printf("connection initialized\n");
return 0;
}
/* close the connection */
free(serv_addr);
close(socket_desc);
return 0;
}
This works 100% and I want to just port this over to HTTPS. After looking at the horribly formatted OpenSSL client.c example (see here: https://wiki.openssl.org/index.php/SSL/TLS_Client) I got that code working on my system (had to make some changes to it), and then went off to port over my HTTP code to HTTPS. I worked on it for a bit and thought I got it working, I have been debugging it but can't figure out why it keeps failing. The code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "repos.h"
#include "resolv.h"
SSL *cSSL;
void initssl()
{
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
}
void destroyssl()
{
ERR_free_strings();
EVP_cleanup();
}
void shutdownssl()
{
SSL_shutdown(cSSL);
SSL_free(cSSL);
}
int main()
{
short socket_desc;
short socket_ssl;
char host[17];
socklen_t sock_size;
SSL_CTX *sslctx;
initssl();
socket_desc = socket(AF_INET, SOCK_STREAM, 0); /* create socket with IPv4 and TCP protocol */
if (socket_desc == -1)
printf("could not create socket\n");
struct sockaddr_in *serv_addr = calloc(1, sizeof(struct sockaddr_in));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(443);
resolv(DEFAULT_HOST, host); /* resolve DEFAULT_HOST and store the ip in host */
if (inet_pton(AF_INET, host, &serv_addr->sin_addr) <= 0) {
printf("error");
free(serv_addr);
return -1;
}
bind(socket_desc, (struct sockaddr *)serv_addr, sizeof(struct sockaddr_in));
listen(socket_desc, 5);
sock_size = sizeof(struct sockaddr_in);
socket_ssl = accept(socket_desc, (struct sockaddr *)serv_addr, &sock_size); /* this is where hang occurs, however I am usnure why. I am reading docs and such and if I figure this out I will post the fix; however I would love some advice/help if anyone sees my error */
sslctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_options(sslctx, SSL_OP_SINGLE_DH_USE);
short use_cert = SSL_CTX_use_certificate_file(sslctx, "/serverCertificate.pem" , SSL_FILETYPE_PEM);
short use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "/serverCertificate.pem", SSL_FILETYPE_PEM);
cSSL = SSL_new(sslctx);
SSL_set_fd(cSSL, socket_ssl);
char ssl_err = SSL_accept(cSSL);
if(ssl_err <= 0) {
printf("connection failed\n");
shutdownssl();
}
else
printf("connected\n");
return 0;
}
Now I know it is missing some obvious things such as writing my own initssl (I am unsure why that isn't already in the lib, but I am starting to see why OpenBSD decided to fork). I left those out as I am more interested in this working with LibreSSL and don't believe you need them with LibreSSL. I tried using print statements to debug but they never get printed even when given at the top of main(). I am unsure why this isn't working and need some help getting this ported. The other files I wrote, repos.h and resolv.c can be seen below:
/* repos.h */
char DEFAULT_HOST[11] = "gitlab.com";
char DEFAULT_PAGE[24] = "Puffles_the_Dragon/core";
/* resolv.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
short resolv(char *host, char *ip)
{
struct hostent *hp = calloc(1, sizeof(struct hostent));
hp = gethostbyname(host);
if (hp == NULL) {
fprintf(stderr, "gethostbyname() failed\n");
exit(1);
}
else {
short i = 0;
while (hp->h_addr_list[i] != NULL) {
inet_ntoa(*(struct in_addr *)(hp->h_addr_list[i]));
i++;
}
strlcpy(ip, inet_ntoa(*(struct in_addr *)(hp->h_addr_list[0])), 16);
}
return 0;
}
I know some of these calls are outdated due to IPv6, but I am going to add for IPv6 after I get this all working and port from BSD libc to musl libc.
I expected the HTTPS code to run and connect to the server thus printing connected, but it just runs and doesn't fail or print anything.

Related

C client socket does not output/connect to tcp server

Why does this header file go into a freeze state or it does not output anything even if the server is accepting connection form other scripts/tools
tcpclient.h
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#pragma once
int socket_desc;
struct sockaddr_in server_addr;
int init_client()
{
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc < 0 )
{
printf("Failed to create socket\n");
return -1;
}
return 0;
}
int connection(char *host,int port )
{
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(host);
if (connect(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr))<0)
{
printf("Failed to connect # %s:%d\n",host,port);
return -1;
}
printf("Connected to [%s:%d]\n",host,port);
return 0;
}
using a main.c like so
int main()
{
int soc = init_client();
int con = connection("192.168.0.12",6666);
return 0;
}
the only warning I get is that the variable soc is not used
Your program works correctly in my computer.... It's not clear what do you claim on.
Anyway, after tweaking it a bit, I have some points to comment on it. I changed the ip address to 127.0.0.1 and port to 80 and started an apache server locally to check that the server is, indeed ready to accept connections.
First I checked that the connection opened locally with a telnet(1) command (you will probably have to install it in your machine as it is not installed now by default)
$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
^]
telnet> close
Connection closed.
$ _
then I changed several things in your code (they should not affect the run, but they will make you trouble in the future if you don't take this path)
I first created a socket.h file to put prototypes for both functions, so you don't run in trouble if you later change the prototypes.
socket.h
#ifndef _SOCKET_H
#define _SOCKET_H
int init_client(void);
int connection(const char *host, int port);
#endif /* _SOCKET_H */
As you see, I changed the parameter `host` into a `const char *` type, so the compiler knows that the string is not changed inside the function and can optimize calls, based on that.
Second, I #included the file in both modules, so if you change the interface, both modules (provider and consumer) know about the interface change.
socket.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
I used the last two includes to use strerror(errno), that gives you the reason for the connection error (mainly being a connection refused error)
#include "socket.h"
int socket_desc = -1;
struct sockaddr_in server_addr;
this is not good practice. A better solution would be to return the socket and pass it as parameter to the connection call below. Using global variables will run you into trouble later if you want to use these functions in different threads, as the socket_desc global variable is common to all the threads you will have.
int init_client(void)
{
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc < 0 )
{
printf("Failed to create socket, %s\n",
strerror(errno));
return -1;
}
return 0;
}
int connection(const char *host, int port)
{
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(host);
printf("trying connect to %s:%d\n", host, port);
if (connect(socket_desc, (struct sockaddr*) &server_addr, sizeof(server_addr))<0)
{
printf("Failed to connect # %s:%d, %s\n",
host, port, strerror(errno));
return -1;
printing here the reason of the non connection is a good idea to know what is happening.
}
printf("Connected to [%s:%d]\n",host,port);
return 0;
}
A better solution would be the following:
socket.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include "socket.h"
int init_client(void)
{
int socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc < 0 )
{
printf("Failed to create socket: %s\n",
strerror(errno));
}
return socket_desc;
}
int connection(int sd, const char *host, int port)
{
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(host);
printf("trying connect to %s:%d\n", host, port);
if (connect(sd, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0) {
printf("Failed to connect # %s:%d, %s\n",
host, port, strerror(errno));
return -1;
}
printf("Connected to [%s:%d]\n", host, port);
return 0;
}
where socket.h becomes:
socket.h
#ifndef _SOCKET_H
#define _SOCKET_H
int init_client(void);
int connection(int socket_descriptor, const char *host, int port);
#endif /* _SOCKET_H */
this modifications, made that calling the program to localhost:80 resulted in:
$ a.out
trying connect to 127.0.0.1:80
Connected to [127.0.0.1:80]
$ _
while using 79 as the port resulted in:
$ a.out
trying connect to 127.0.0.1:79
Failed to connect # 127.0.0.1:79, Connection refused
Failed connection(), Connection refused
$ _

No route to host error in socket programming

I am writing a simple client program which connects to a the ip address "172.31.1.34" and sends a message. Everything works fine but I am not able to recieve any message from the server. The error says "no route to host".
My code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
struct sockaddr_in server,client;
int s1,s2,len;
int n;
char buffer[500];
strcpy(buffer,"GET http://172.31.1.34/ HTTP/1.0\n\n");
bzero((char *)&client,sizeof(client));
client.sin_port = htons(80);
client.sin_addr.s_addr = inet_addr("172.31.1.34");
client.sin_family = AF_INET;
s2 = socket(AF_INET,SOCK_DGRAM,0);
if(connect(s2,(struct sockaddr *)&client,sizeof(client)) == -1) {
perror("can't connect\n");
exit(1);
}
n = send(s2,buffer,strlen(buffer),0);
if(n < 0) {
perror("message not sent");
exit(1);
}
while(1) {
memset(buffer,0,sizeof(buffer));
n = recv(s2,buffer,500,0);
if(n < 0) {
perror("coudnot read");
exit(1);
}
buffer[n] = '\0';
printf("%s",buffer);
}
close(s2);
return 0;
}
Why are you using SOCK_DGRAM? That is for UDP packets. HTML uses TCP. You should use SOCK_STREAM
I have only briefly looked at your code, but at first glance it seems OK. However I would start with the obvious - maybe there is no route to the host....
Assuming you are on Linux or other Unix platform (including OSX), I would do the following:
ping 172.31.1.34. Note this does not guarantee the host is not available as ping may be blocked.
telnet 172.31.1.34. This should connect and you can enter your HTTP query directly
tcptraceroute 172.31.1.34 80
If all of these fail, the problem is the network, not your code.
On OSX, you can install tcptraceroute from "homebrew". On Linux use your normal package manager (or ask your system managers).
Try the code below instead:
client.sin_addr.s_addr = inet_addr("172.31.1.34");
inet_pton(AF_INET, "172.31.1.34", &client.sin_addr);

C / Linux - How to connect and read the answer of a mail server like gmail or outlook with sockets?

I would like to connect to a mail server to read the mail. For example, I have an outlook address toto#outlook.com, and I want to get all the mail of this mailbox and be able to read it. So I believe I only have to do a client, but : How do I contact and connect to this type of servers, and how do I listen the answer?
Here is my current client code :
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
int main()
{
struct protoent *pe;
int fd_client;
struct sockaddr_in s_in;
int port;
char *ip;
char buff[4096];
ip = strdup("157.56.242.251"); // I get it while calling "ping outlook.com"
port = 210;
pe = getprotobyname("TCP");
fd_client = socket(AF_INET, SOCK_STREAM, pe->p_proto);
s_in.sin_family = AF_INET;
s_in.sin_port = htons(port);
s_in.sin_addr.s_addr = inet_addr(ip);
if (connect(fd_client, (struct sockaddr *)&s_in, sizeof(s_in)) == -1)
{
if (close(fd_client) == -1)
return (-1);
fprintf(stderr, "Failed to connect\n");
return (-1);
}
//From here I don't know what to do, so I tried this
recv(fd_client, buff, 4096, 0);
printf("%s\n",buff);
write(fd_client, "Hello !\n", strlen("Hello !\n"));
//To Here
if (close(fd_client) == -1)
return (-1);
return (0);
}
Thanks in advance.

Check port reachable in C

I have a C function to check a host and its port, when I use FQDN host name, the function return error like: connect() failed: connect time out, but if I use IP address instead, it seems ok, how to fix this?
Thanks.
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
int is_network_up(char *chkhost, unsigned short chkport) {
int sock;
struct sockaddr_in chksock;
struct hostent *host = NULL;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
syslog(LOG_ERR, "socket() creation error: %s", strerror(errno));
return 0;
}
memset(&chksock, 0, sizeof(chksock));
chksock.sin_family = AF_INET;
chksock.sin_port = htons(chkport);
/* get the server address */
if (inet_pton(AF_INET, chkhost, &(chksock.sin_addr.s_addr)) <= 0) {
if ((host = gethostbyname(chkhost)) == NULL) {
syslog(LOG_ERR, "%s", hstrerror(h_errno));
return 0;
}
memcpy(&(chksock.sin_addr.s_addr), &(host->h_addr_list[0]),
sizeof(struct in_addr));
}
/* try to connect */
if (connect(sock, (struct sockaddr *) &chksock, sizeof(chksock)) < 0) {
syslog(LOG_ERR, "connect() failed: %s", strerror(errno));
return 0;
}
close(sock);
return 1;
}
inet_pton() is the wrong task for that. It only accepts numerical addresses.
In former times, people used to use gethostbyname() for name resolution.
But as we have 2012 meanwhile, this method is outdated for several years now, as it is still restricted to AF_INET.
With the program below, you should achieve about the same and stay future compatible.
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
int is_network_up(char *chkhost, unsigned short chkport) {
int sock = -1;
struct addrinfo * res, *rp;
int ret = 0;
char sport[10];
snprintf(sport, sizeof sport, "%d", chkport);
struct addrinfo hints = { .ai_socktype=SOCK_STREAM };
if (getaddrinfo(chkhost, sport, &hints, &res)) {
perror("gai");
return 0;
}
for (rp = res; rp && !ret; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sock == -1) continue;
if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) {
char node[200], service[100];
getnameinfo(res->ai_addr, res->ai_addrlen, node, sizeof node, service, sizeof
service, NI_NUMERICHOST);
printf("Success on %s, %s\n", node, service);
ret = 1; /* Success */
}
close(sock);
}
freeaddrinfo(res);
return ret;
}
int main(int argc, char** argv) {
if (argc > 1) {
printf("%s: %d\n", argv[1], is_network_up(argv[1], 22));
}
}
Make sure name resolution is working. See if you can ping the machine by name from the exact same environment in which your code runs.
If ping works, try telnet <machinename> <portnumber> -- If both of those work it is likely a problem with your code (which I did not look at in depth, too sleepy:).
Make sure you're converting anything returned by the OS as an ip address from network order to host order. IIRC, gethostbyname returns binary ip addresses in network order.
ntohl can be used on chksock.sin_addr.s_addr after the memcpy to achieve this.

SIGPIPE error in a TCP based Concurrent Echo Cleint-Sever

I am new to network programming, and have been learning this by writing small programs that make use of the Socket API. Currently, I am writing a simple echo server, that uses fork to create a copy of it, as soon as it gets a connect request, this adds up as in improvement over the previous Iterative server (here). However, after I start the server and fire up the client, and type a message on its console, it quits unexpectedly. Running the program under Gdb shows that SIGPIPE was delivered. But as far as I know as the socket is still valid, a SIGPIPE shouldn't have occured. Any kind of help involved is appreciated.
Here is the client code
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#define MAXCOUNT 1024
int main(int argc, char* argv[])
{
int sfd,i;
struct sockaddr_in saddr;
char buff[MAXCOUNT];
char mesg[MAXCOUNT];
sfd = socket(AF_INET,SOCK_STREAM,0);
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr);
saddr.sin_port = htons(5008);
connect(sfd,(struct sockaddr*) &saddr,sizeof(saddr));
fgets(buff,MAXCOUNT,stdin);
send(sfd,buff,strlen(buff),0);
if (recv(sfd,mesg,MAXCOUNT,0) == -1) {
perror("Nothing to read\n");
exit(1);
}
printf("%s\n",mesg);
exit(0);
}
Here is the server code
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#define MAXCOUNT 1024
int main(int argc, char* argv[])
{
int sfd,nsfd,cn;
pid_t c;
char buf[MAXCOUNT];
socklen_t clen;
struct sockaddr_in caddr,saddr;
sfd = socket(AF_INET,SOCK_STREAM,0);
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
saddr.sin_port = htons(5008);
bind(sfd,(struct sockaddr*) &saddr,0);
listen(sfd,2);
for (; ;) {
clen = sizeof(caddr);
nsfd = accept(sfd,(struct sockaddr*) &caddr, &clen);
if( (c = fork()) == 0) {
close(sfd);
memset(buf,0,sizeof(buf));
cn = recv(nsfd,buf,sizeof(buf),0);
if ( cn == 0) {
perror("Reading from the client socket failed\n PROGRAM CRASH :\n");
exit(1);
}
buf[cn] = '\0';
send(nsfd,buf,strlen(buf),0);
close(nsfd);
exit(0);
}
}
return 0;
}
send(sfd,buff,strlen(buff),0);
if (recv(sfd,mesg,MAXCOUNT,0) == -1) {
perror("Nothing to read\n");
exit(1);
}
printf("%s\n",mesg);
The %s format specifier is for C-style strings, not arbitrary data. And since you throw away the return value from recv, you have no way to know how many bytes you got.
Your client also doesn't shut down the socket gracefully or make sure it has received all the data the server may send. So it's possible that you're triggering an abnormal termination. The server closes the connection when it's done sending, so the client should keep trying to receive until it detects that the connection has closed.

Resources