OpenSSL Server Example
2003-05-24 12:24:54
Category: cpp:internet
Description: A basic threaded OpenSSL server with non-blocking connection handling. When a connection is made the thread waits for data to be recieved, then sends an html response so it can be tested with a browser. The thread closes the connection and terminates without waiting for more data.
Author: detour
Viewed: 10708
Rating: (49 votes)


 
This example has multiple files, click here to download.
/* osslserv.cpp by detour@metalshell.com
 *
 * A basic threaded OpenSSL server with non-blocking
 * connection handling.  When a connection is made the thread
 * waits for data to be recieved, then sends an html response
 * so it can be tested with a browser.  The thread closes the
 * connection and terminates without waiting for more data.  The
 * default keys are 'cert' and 'pkey' included.
 *
 * Creating New Keys:
 *   - Private Key
 *     openssl genrsa -out pkey 2048
 *   - Certificate
 *     openssl req -new -x509 -key pkey -out cert -days 365
 *
 * Compile:
 *   A Makefile is included to compile this example.
 *
 * Testing:
 *   https://www.yourhostorip.com:1420/
 *
 * http://www.metalshell.com/
 *
 */
 
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "sslserver.h"
 
#define REPLY "<html><body>Metalshell.com OpenSSL Server</body></html>"
#define MAX_PACKET_SIZE 1024
 
// Called when a new connection is made.
void *conn_thread(void *ssl) {
  int fd = SSL_get_fd((SSL *)ssl);
 
  if(SSL_accept((SSL *)ssl) == -1) {
    ERR_print_errors_fp(stderr);
  } else {
    char cipdesc[128];
    SSL_CIPHER *sslciph = SSL_get_current_cipher((SSL *)ssl);
 
    cout << "Encryption Description:\n";
    cout << SSL_CIPHER_description(sslciph, cipdesc, sizeof(cipdesc)) << endl;
 
    char buff[MAX_PACKET_SIZE];
    // Wait for data to be sent.
    int bytes = SSL_read((SSL *)ssl, buff, sizeof(buff));
    buff[bytes] = '\0';
 
    // Show the browser request.
    cout << "Recieved: \n" << buff << endl;
 
    // Send the html reply.
    SSL_write((SSL *)ssl, REPLY, strlen(REPLY));
  }
 
  // Tell the client we are closing the connection.
  SSL_shutdown((SSL *)ssl);
 
  // We do not wait for a reply, just clear everything.
  SSL_free((SSL *)ssl);
  close(fd);
 
  cout << "Connection Closed\n";
  cout << "---------------------------------------------\n";
 
  pthread_exit(NULL);
}
 
int main() {
  SSLServer server("cert", "pkey", 1420);
 
  // Set the thread function.
  server.SetPthread_F(conn_thread);
 
  while(1) {
    /* Wait for 10 seconds, and if no one trys
     * to connect return back.  This allows us to do
     * other things while waiting.
     */
    server.CheckClients(10);
  }
 
  return 0;
}
#include "sslserver.h"
 
SSLServer::SSLServer(char *cFile, char *kFile, int port) {
 
  PORT = port;
  CreateCTX();
  LoadCerts(cFile, kFile);
 
  // Get the server started.
  BindPort();
}
 
void SSLServer::CheckClients(int wait_t) {
  struct timeval tv;
 
  // Set how long to block.
  tv.tv_sec = wait_t;
  tv.tv_usec = 0;
 
  FD_ZERO(&fdset);
  FD_SET(master, &fdset);
 
  select(master+1, &fdset, NULL, NULL, (struct timeval *)&tv);
 
  // If master is set then someone is trying to connect
  if(FD_ISSET(master, &fdset)) {
    SSL *ssl;
 
    // Open up new connection
    struct sockaddr_in addr;
    int len = sizeof(addr);
    int client = accept(master, (struct sockaddr *)&addr, (socklen_t *)&len);
 
#ifdef DEBUG
    struct in_addr ip_address;
    memcpy(&ip_address, &addr.sin_addr.s_addr, 4);
 
    cout << "\n\n---------------------------------------------\n";
    cout << "Connection from: " << inet_ntoa(ip_address) << "  ("
         << ntohs(addr.sin_port) << ")\n";
#endif /* DEBUG */
 
    if(client == -1)
      perror("accept");
 
    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, client);
 
#ifdef DEBUG
    cout << "Creating Thread\n";
#endif /* DEBUG */
 
    pthread_t thread;
    if(pthread_create(&thread, NULL, pthr_f, (void *)ssl) != 0) {
      fprintf(stderr, "pthread_create() has failed.\n");
      exit(1);
    }
  }
}
 
void SSLServer::BindPort(void) {
  struct sockaddr_in addr;
 
  master = socket(PF_INET, SOCK_STREAM, 0);
  memset(&addr, 0, sizeof(addr));
 
  addr.sin_family = AF_INET;
  addr.sin_port = htons(PORT);
  addr.sin_addr.s_addr = INADDR_ANY;
 
  // Open the socket
  if(bind(master, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
    perror("bind");
    _exit(1);
  }
 
  // Set a limit on connection queue.
  if(listen(master, 5) != 0) {
    perror("listen");
    _exit(1);
  }
 
}
 
void SSLServer::CreateCTX(void) {
  // The method describes which SSL protocol we will be using.
  SSL_METHOD *method;
 
  // Load algorithms and error strings.
  OpenSSL_add_all_algorithms();
  SSL_load_error_strings();
 
  // Compatible with SSLv2, SSLv3 and TLSv1
  method = SSLv23_server_method();
 
  // Create new context from method.
  ctx = SSL_CTX_new(method);
  if(ctx == NULL) {
    ERR_print_errors_fp(stderr);
    _exit(1);
  }
}
 
/* Load the certification files, ie the public and private keys. */
void SSLServer::LoadCerts(char *cFile, char *kFile) {
  if ( SSL_CTX_use_certificate_chain_file(ctx, cFile) <= 0) {
    ERR_print_errors_fp(stderr);
    _exit(1);
  }
  if ( SSL_CTX_use_PrivateKey_file(ctx, kFile, SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    _exit(1);
  }
 
  // Verify that the two keys goto together.
  if ( !SSL_CTX_check_private_key(ctx) ) {
    fprintf(stderr, "Private key is invalid.\n");
    _exit(1);
  }
}
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
 
#ifndef __SSL_SERVER_H__
#define __SSL_SERVER_H__
 
using namespace std;
 
typedef void *(*P_ARG)(void *);
 
class SSLServer {
  // Private data
  private:
    SSL_CTX *ctx;
    int PORT;
    fd_set fdset;
    // FD for server
    int master;
    // Thread function
    P_ARG pthr_f;
 
  // Private Functions
  private:
    // Load algorithms and create context.
    void CreateCTX(void);
    // Load certification files.
    void LoadCerts(char *cFile, char *kFile);
    // Create and attach socket.
    void BindPort(void);
 
  public:
    // Set function for pthread to call.
    void SetPthread_F(P_ARG fp) { pthr_f = fp; }
 
    // Constructors
    SSLServer(char *cFile, char *kFile, int port);
 
    /* Check for new connections, block for wait_t amount of seconds,
       if no connection is made within wait_t function returns.  This must
       be called regularly from within main */
    void CheckClients(int wait_t = 0);
};
 
 
#endif
OBJS = sslserver.o main.o
EXECUTABLE = sslserver
 
CC=g++
CFLAGS=-Wall -ggdb -DDEBUG
COMPILE=$(CC) $(CFLAGS) -c
 
all: sslserver
 
sslserver: $(OBJS)
        $(CC) -lpthread -lssl -o $(EXECUTABLE) $(OBJS)
 
main.o: main.cpp
        $(COMPILE) -o main.o main.cpp
 
sslserver.o: sslserver.cpp
        $(COMPILE) -o sslserver.o sslserver.cpp
 
clean:
        -rm $(OBJS) $(EXECUTABLE)
-----BEGIN CERTIFICATE-----
MIIDpTCCAo2gAwIBAgIBADANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJVUzET
MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMB4XDTAzMDUyNDE0NTI0MloXDTA2MDUyMzE0NTI0MlowRTELMAkGA1UE
BhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp
ZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr7
vaKNMG9FbtEya2+GIX7NGfzEUU/b7Pmt0SaC5bZn/Fo1JNlEPnAtFPqZyW6zcqM5
SrCNCVS0B4H3n2GygssPppa88IkM2FolKhlWjWi2k4b+MGuYWwRGu/UGhEK//ciC
S+fNTHS20+6U4XXLuPbEIv7TQk/GNF2rSNxTBtL/cp8rpIk+fIY59FymZx6/Gxrp
+5vlrbYZFZMPUUd2JgaAUxOy1schetQhoRzrjlDmX9+fmwqKJYQ+ZLb1aCGZZJuR
W/i29CyxQODm1fUBpb1WwEFjXUZb8j2iicLoS9s9nJaUFvd2fImWic/11iYi+ufF
cMRjakVKMYXF24+GZd8CAwEAAaOBnzCBnDAdBgNVHQ4EFgQU2Ejnhbxwj0yLEdaW
BfSbx+qAj2wwbQYDVR0jBGYwZIAU2Ejnhbxwj0yLEdaWBfSbx+qAj2yhSaRHMEUx
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGSCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
AQQFAAOCAQEAoYBkFbBwSO+Vn56ilNG+mOl5ryHAwtjCgqdl156N52ysUFq+qar2
WNbmD3ieDm+UrNqX4FcLV/CkZQAXWj3YHLyGbMCekLbpctjbfmCVTD3EQ+a7Rw1s
uQVyrzbLHggzhBuzQrjKflr42MnfNClQAiiM7kOyuo1pwVgWZ4GXIBOfazizG3Yh
If0p+MUKJk7maKgXoiu+NrZIu7jyB4lyqTVGh1fKr1rWrciK5xnC7mxkndGuzcGo
XM/ea0cPl79g8PRHAtgqLoqECP184HC4gR+AVLoonva4Yz3mI6BiRI0lrcjQLWEF
GmLEzRjFsm/tdyMSzH2eM7zp1ibogbSBiA==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAuvu9oo0wb0Vu0TJrb4Yhfs0Z/MRRT9vs+a3RJoLltmf8WjUk
2UQ+cC0U+pnJbrNyozlKsI0JVLQHgfefYbKCyw+mlrzwiQzYWiUqGVaNaLaThv4w
a5hbBEa79QaEQr/9yIJL581MdLbT7pThdcu49sQi/tNCT8Y0XatI3FMG0v9ynyuk
iT58hjn0XKZnHr8bGun7m+WtthkVkw9RR3YmBoBTE7LWxyF61CGhHOuOUOZf35+b
CoolhD5ktvVoIZlkm5Fb+Lb0LLFA4ObV9QGlvVbAQWNdRlvyPaKJwuhL2z2clpQW
93Z8iZaJz/XWJiL658VwxGNqRUoxhcXbj4Zl3wIDAQABAoIBAAWTL+pCz2jh5xXx
rOZcV29Sai3xJIN/CSfAmPXO/U5c91cxMnIP6NSrY269WxYj340iTinJarfNzlN/
sI7XJbMsOklQRNOxQFoftYuf2wN+PhPOTF9I4Z3VBhGeKh9bXhO2XtEAfAEW2mbI
pZg/hLpGysxSPC3ouPL6AmgfSZrM2e6Jqgo1OxZnkxsMYG1iLUdyA01P4IwbnIF1
zTEi+cpqBiaX9QR3Q5bkXIhyscmaLCeqvjiAhLZXqy2fRy+VW48T08JWoPjOh/vW
EzFOADYXf3bOEeKw62+iSGvF97J87iiqGn7+aMG3FT4YqZ5luu4D1o++xfPj8om/
Rjte4GECgYEA4Z2GzYmaldfcxV1vfoUQDlSmdpcJ3UM/gMew4fSH22obpM81krih
H84KMzCIBT1dTJWygSwsNbivhTclRseV/TJ2vN2OQOCk4jwgXjuKyqMVEv7UFVp0
2Z3KTI9+AOTplSRUUymka0dwLd0B7rhuqIfPWT6cC7pgpJCWR4JR3gkCgYEA1CpP
mwLhTsDhkjkwsEvIYTJPvKg1ch77Ck3RYQ01tAaJ0IMZfBMuVYAzzbgVrsTuxgMP
tAoHTw9fgk+KU0ksIWR3yxrYnFALN9DJvT4nyYjesF1Y83MAwhVE+REJ77efJV7T
IkB4OZgdljn5caPEZMAGQxLWFRaV6HHvYnGtnqcCgYEA3OcIHiclHKIGn5gkmpRe
bCml82dfWS2G9+iN4C809jimaHAZ3Fa6LBHpGsXh6H904o+P/7nob5EtCho8fVje
GtNWPwYPSqapynlkl99kvZOABuFLdrzivFAqy1uT2/xGWKkBh4u2WPPRepZyVfJv
JsQS2SbcUv9hsL+A5PNMhUECgYEAwoCbhB9K0HjxEq1NXoHLDJgkE28duCaAvHyE
s/V5QzYvR7G4PlATTRz/4NufPR6bS3po/gOnmaodRAiJZjsRsvc4/0D4Tazv69aD
6/K8ZP0OMh8RufW3PzZiifc95b6vroHVC3SRAzPaA+vYK38YP8jutLTjAGg5O+Sf
sd9HbMcCgYEAliEIS+BRsf7Uj6jZj+/n3LIiREbZ0Euhqbfh8ZWnFA6Qs64jMvRs
/XJgfK/4jYvVWyMsnPOBRnTvI83PiC12UV4yJP2SbyJFkynqcmQjU9tXJoxfTg8m
CayJ0aG8qGd2SWmJob8w8WIC+2k7Pt4Otcgfm0KxgJ3jNQ5bwHQ8Udo=
-----END RSA PRIVATE KEY-----