📜 ⬆️ ⬇️

An example of a primitive C ++ web server using OpenSSL

I have long wanted to write a useful article and finally found a suitable informational occasion.

This article will discuss the creation of a primitive web server running over https protocol. We write to the server part, and any of the browsers will act as the client part.
As a result, we get the most simplified example of a primitive web server that can be improved and sharpened for its tasks.

First we need the certificate files. The certificate generation process is simple.
$ openssl req -new -x509 -days 30 -keyout server.key -out server.pem 

We answer the question “Enter PEM pass phrase: with a password, confirm and remember.
The question “Common Name (eg, YOUR name) []:” is answered with the name of the site for which we are creating a certificate.
All other answers are not particularly important.

After the answers, two new files will appear in the current - server.key and server.pem (key and certificate, respectively).
')
For convenience, remove the password from the key:
 $ cp server.key server.key.orig $ openssl rsa -in server.key.orig -out server.key $ rm server.key.orig 

Now the code. The code is based on examples from the official OpenSSL site, but only I tried to ask for it. Yes, and simple copying and compiling the example for some of the reasons did not work for me.

Connecting libraries should not cause difficulties.
 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <netdb.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <string.h> #include <openssl/ssl.h> #include <openssl/err.h> extern BIO *bio_err; BIO *bio_err=0; #define PEM_FILE "server.pem" #define KEY_FILE "server.key" #define PORT 4333 

The function of creating a socket is also simple. Especially since it does not contain anything from OpenSSL.
 /** *     */ int tcp_listen() { int sock; struct sockaddr_in sin; int val=1; if((sock=socket(AF_INET,SOCK_STREAM,0))<0) printf("Couldn't make socket"); memset(&sin,0,sizeof(sin)); sin.sin_addr.s_addr=INADDR_ANY; sin.sin_family=AF_INET; sin.sin_port=htons(PORT); setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)); if(bind(sock,(struct sockaddr *)&sin, sizeof(sin))<0) printf("Couldn't bind"); listen(sock,5); return(sock); } 

Initialization function ssl. I will carefully comment.
  /*     SIGPIPE           */ static void sigpipe_handle(int x){} SSL_CTX *initialize_ctx(const char *key_file,const char *pem_file) { if(!bio_err) { /*    OpenSSL,         OpenSSL */ SSL_library_init(); SSL_load_error_strings(); /* An error write context */ bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); } /* Set up a SIGPIPE handler */ /*    SIGPIPE           */ signal(SIGPIPE,sigpipe_handle); /* Create our context*/ SSL_CTX* ctx=SSL_CTX_new(SSLv23_method()); /* Load our keys and certificates*/ if(!(SSL_CTX_use_certificate_file(ctx,pem_file,SSL_FILETYPE_PEM))) { printf("    \n"); } if(!(SSL_CTX_use_PrivateKey_file(ctx, key_file,SSL_FILETYPE_PEM))) { printf("    \n"); } return ctx; } 

The initialize_ctx function once at the beginning loads our files and creates an SSL_CTX context. It will be used to create individual connections. This allows you to not download the key files for each new connection.

Now consider the function of working with the connection.
  #define BUFSIZZ 4048 static int http_serve(SSL *ssl,int s) { char buf[BUFSIZZ]; int r = 0; int e; bzero(buf,BUFSIZZ-1); //   r=SSL_read(ssl,buf,BUFSIZZ-1); //   if(r<0) //  r < 0    { e = SSL_get_error(ssl,r); } printf("[   %d, Error:%d]%s\n", r, e, buf); /*   */ r = SSL_write(ssl,"HTTP/1.0 200 OK\r\nServer: EKRServer\r\n\r\nServer test page\r\n",strlen("HTTP/1.0 200 OK\r\nServer: EKRServer\r\n\r\nServer test page\r\n")); if(r<=0) //  r < 0    { printf("Write error %d\n",r); } else { printf("Write ok %d\n",r); } /*   */ shutdown(s,1); SSL_shutdown(ssl); SSL_free(ssl); close(s); return(0); } 

I think everything is obvious.
And the last stage, we put everything together.
 int main(int argc, char *argv[]) { int sock,s; SSL_CTX *ctx; SSL *ssl; int r; // Build our SSL context ctx=initialize_ctx(KEY_FILE,PEM_FILE); sock=tcp_listen(); printf("\n"); while(1) { if((s=accept(sock,0,0))<0) { printf("Problem accepting\n"); } else { printf("Accepting %d\n",s); } ssl=SSL_new(ctx); SSL_set_fd(ssl, s); r=SSL_accept(ssl); if( r < 0 ) { printf("SSL accept error %d\n",r); printf("SSL accept error code %d\n",SSL_get_error(ssl,r) ); exit(0); } else { printf("SSL accept %d\n",r); } http_serve(ssl,s); printf("\n"); } SSL_CTX_free(ctx); } 

From the code I tried to remove everything without what will work. But despite this, as I expected, the code turned out more than the text. But I really hope that this article will help someone.
I wrote everything under Ubuntu, perhaps when compiling into windows you have to make minor edits.

In the appendix, add a link to the examples on OpenSSL

Source: https://habr.com/ru/post/167107/


All Articles