summary history branches tags files
examples/server.c
// server.c
//
// Example of using Ossuary as a server in C
//
// This is the server half of a C client/server example for Ossuary.
// Run this server first, then run the client to send encrypted messages
// one way from client to server.
//
// The purpose of this example is to show the Ossuary FFI API, and demonstrate
// how it sits 'in between' the read and write calls of a TCP connection.
//
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "ossuary.h"

int net_connect();

uint8_t secret_key[] = {
  0x10, 0x86, 0x6e, 0xc4, 0x8a, 0x11, 0xf3, 0xc5,
  0x6d, 0x77, 0xa6, 0x4b, 0x2f, 0x54, 0xaa, 0x06,
  0x6c, 0x0c, 0xb4, 0x75, 0xd8, 0xc8, 0x7d, 0x35,
  0xb4, 0x91, 0xee, 0xd6, 0xac, 0x0b, 0xde, 0xbc
};

uint8_t public_key[32] = {
  0xbe, 0x1c, 0xa0, 0x74, 0xf4, 0xa5, 0x8b, 0xbb,
  0xd2, 0x62, 0xa7, 0xf9, 0x52, 0x3b, 0x6f, 0xb0,
  0xbb, 0x9e, 0x86, 0x62, 0x28, 0x7c, 0x33, 0x89,
  0xa2, 0xe1, 0x63, 0xdc, 0x55, 0xde, 0x28, 0x1f
};

int main(int argc, char **argv) {
  uint8_t read_buf[1024];  // data received from network socket
  uint8_t write_buf[1024]; // data to send on network socket
  uint8_t text_buf[1024];  // data received and decrypted by ossuary
  OssuaryConnection *server_conn = NULL;
  int read_len;
  int client;
  int handshake;
  int ossuary_res;
  uint16_t read_buf_len = 0;
  uint16_t write_buf_len = 0;
  uint16_t text_buf_len = 0;

  // Wait for a TCP connection from a client
  client = net_connect();

  // Create an Ossuary server
  if ((server_conn = ossuary_create_connection(OSSUARY_CONN_TYPE_UNAUTHENTICATED_SERVER, secret_key)) == NULL) {
    fprintf(stderr, "ERROR: could not create Ossuary connection\n");
    exit(1);
  }

  while (1) {
    // Read from network socket
    if ((read_len = read(client, read_buf + read_buf_len, sizeof(read_buf) - read_buf_len)) <= 0) {
      if (errno != EAGAIN) {
        fprintf(stderr, "ERROR: read failed\n");
        exit(1);
      }
      read_len = 0;
    }
    read_buf_len += read_len;

    // Ossuary handshake
    if ((handshake = ossuary_handshake_done(server_conn)) == 0) {
      write_buf_len = sizeof(write_buf);
      // Check if we have any handshake packets to send
      if ((ossuary_res = ossuary_send_handshake(server_conn, write_buf, &write_buf_len)) < 0) {
        if (ossuary_res != OSSUARY_ERR_WOULDBLOCK) {
          fprintf(stderr, "ERROR: handshake send failed\n");
          exit(1);
        }
      }
      // Check if we have any received handshake packets to parse
      if (read_buf_len > 0) {
        read_len = read_buf_len;
        if ((ossuary_res = ossuary_recv_handshake(server_conn, read_buf, (uint16_t*)&read_len)) < 0) {
          if (ossuary_res != OSSUARY_ERR_WOULDBLOCK) {
            fprintf(stderr, "ERROR: handshake recv failed\n");
            exit(1);
          }
        }
        // Consume bytes from read_buf
        memmove(read_buf, read_buf + read_len, read_buf_len - read_len);
        read_buf_len -= read_len;
      }
    }
    // Ossuary handshake failed
    else if (handshake < 0) {
      fprintf(stderr, "ERROR: handshake failed: %d\n", handshake);
      exit(1);
    }
    // Ossuary data exchange over established connection
    else {
      // If data has been received, decrypt it
      if (read_buf_len > 0) {
        text_buf_len = sizeof(text_buf);
        read_len = read_buf_len;
        if ((ossuary_res = ossuary_recv_data(server_conn, read_buf,
                                             (uint16_t*)&read_len, text_buf, &text_buf_len)) < 0) {
          if (ossuary_res != OSSUARY_ERR_WOULDBLOCK) {
            fprintf(stderr, "ERROR: recv failed\n");
            exit(1);
          }
        }
        // Data can be consumed even if ossuary_recv_data() returned no data
        if (read_len > 0) {
          memmove(read_buf, read_buf + read_len, read_buf_len - read_len);
          read_buf_len -= read_len;
        }
        // Print message if one was decrypted
        if (text_buf_len > 0) {
          printf("MSG: %s\n", text_buf);
        }
      }
    }

    // Write any encrypted messages generated by Ossuary
    if (write_buf_len) {
      if (write(client, write_buf, write_buf_len) != write_buf_len) {
        fprintf(stderr, "ERROR: write failed\n");
        exit(1);
      }
      write_buf_len = 0;
    }
  }

  close(client);
}

int net_connect() {
  struct sockaddr_in addr;
  struct in_addr inaddr;
  socklen_t addr_len;
  int sock;
  int conn;
  int flags;
  int client;

  printf("Starting on localhost port 9981...\n");
  if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    fprintf(stderr, "ERROR: could not create IPv4 socket\n");
    exit(1);
  }
  memset(&addr, 0, sizeof(struct sockaddr_in));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(9981);
  inaddr.s_addr = 0;
  addr.sin_addr = inaddr;

  if ((conn = bind(sock, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in))) < 0) {
    fprintf(stderr, "ERROR: could not bind to 127.0.0.1:9981\n");
    exit(1);
  }
  if (listen(sock, 3) < 0) {
    fprintf(stderr, "ERROR: could not listen on 127.0.0.1:9981\n");
    exit(1);
  }
  addr_len = sizeof(struct sockaddr_in);
  printf("Waiting for client...\n");
  if ((client = accept(sock, (struct sockaddr*)&addr, &addr_len)) < 0) {
    fprintf(stderr, "ERROR: could not listen on 127.0.0.1:9981\n");
    exit(1);
  }

  flags = fcntl(client, F_GETFL, 0);
  if (fcntl(client, F_SETFL, flags | O_NONBLOCK) < 0) {
    fprintf(stderr, "ERROR: could not set non-blocking\n");
    exit(1);
  }

  close(sock);
  printf("Client connected!\n");
  return client;
}