Example primitive web server in C ++ using OpenSSL

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

This article will focus on the creation of a primitive web server running the https protocol. We are not writing 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 your tasks.

First, we need certificate files. The process of generating a certificate is simple.
$ openssl req -new -x509 -days 30 -keyout server.key -out server.pem

To the question “Enter PEM pass phrase:” we answer with a password, confirm and remember.
To the question “Common Name (eg, YOUR name) []:” we answer 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 one - 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 website, but only I tried to simplify it. Yes, and just copying and compiling the example for some reason did not work for me.

Connection of libraries should not cause difficulties.
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
extern BIO *bio_err;
BIO *bio_err=0;
#define PEM_FILE "server.pem"
#define KEY_FILE "server.key"
#define PORT	4333

The socket creation function is also simple. Moreover, it does not yet 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);
}

Ssl initialization function. 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 when creating individual connections. This eliminates the need to download 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 here.
And the last stage, we connect 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 which it would work. But despite this, as I expected, the code turned out to be more than text. But I really hope that this article will help someone.
I wrote everything under Ubuntu, maybe when compiling in windows you will have to make minor edits.

In the appendage, I will add a link to OpenSSL examples

Also popular now: