packages feed

uniform-io-0.2.0.0: src/System/IO/Uniform/ds.c

#include <malloc.h>
#include <unistd.h>
#include <string.h>
//#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <pthread.h>

#include "ds.h"

int openSslLoaded = 0;
char *availableCiphers = "EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:"
  "+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:"
  "!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA";

void *clear(void *ptr){
  int e = errno;
  if(ptr){
    free(ptr);
  }
  errno = e;
  return NULL;
}

pthread_mutex_t loadLock;

void loadOpenSSL(const char *dh){
  if(openSslLoaded)
    return;
  pthread_mutex_lock(&loadLock);
  if(!openSslLoaded){
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    ERR_load_crypto_strings();
    SSL_library_init();
    OpenSSL_add_all_algorithms();    
    openSslLoaded = 1;    
  }
  pthread_mutex_unlock(&loadLock);
}

void copy6addr(unsigned char d[16], const unsigned char s[16]){
  int i;
  for(i = 0; i < 16; i++)
    d[i] = s[i];
}

void zero6addr(unsigned char d[16]){
  int i;
  for(i = 0; i < 16; i++)
    d[i] = 0;
}

nethandler getNethandler(const int ipv6, const int port){
  nethandler h = (nethandler)malloc(sizeof(s_nethandler));
  h->ipv6 = ipv6;
  if(ipv6){
    h->fd = socket(AF_INET6, SOCK_STREAM, 0);
  }else{
    h->fd = socket(AF_INET, SOCK_STREAM, 0);
  }
  int optval = 1;
  setsockopt(h->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
  int e, en;
  if(ipv6){
    struct sockaddr_in6 add;
    add.sin6_family = AF_INET6;
    zero6addr(add.sin6_addr.s6_addr);
    add.sin6_port = htons(port);
    e = bind(h->fd, (struct sockaddr*) &add, sizeof(add));
  }else{
    struct sockaddr_in add;
    add.sin_family = AF_INET;
    add.sin_addr.s_addr = INADDR_ANY;
    add.sin_port = htons(port);
    e = bind(h->fd, (struct sockaddr*) &add, sizeof(add));
  }
  if(e)
    return clear(h);
  e = listen(h->fd, DEFAULT_LISTENNING_QUEUE);
  if(e)
    return clear(h);
  return h;
}

nethandler getIPv4Port(const int port){
  return getNethandler(0, port);
}

nethandler getPort(const int port){
  return getNethandler(1, port);
}

ds createFromFile(int f){
  ds d = (ds)malloc(sizeof(s_ds));
  d->tp = file;
  d->fd = f;
  return d;
}

ds createFromFileName(const char *f){
  int fd = open(f, O_CREAT | O_RDWR, 0666);
  if(fd == -1){
    return NULL;
  }
  return createFromFile(fd);
}

ds createFromHandler(nethandler h){
  ds d = (ds)malloc(sizeof(s_ds));
  d->tp = sock;
  unsigned int s = sizeof(d->peer);
  d->fd = accept(h->fd, (struct sockaddr*)&(d->peer), &s);
  if(d->fd <= 0)
    return clear(d);
  d->ipv6 = d->peer.ss_family == AF_INET6;
  d->server = 1;
  return d;
}

ds createToHost(struct sockaddr *add, const int add_size, const int ipv6){
  ds d = (ds)malloc(sizeof(s_ds));
  d->tp = sock;
  if(ipv6){
    d->fd = socket(AF_INET6, SOCK_STREAM, 0);
  }else{
    d->fd = socket(AF_INET, SOCK_STREAM, 0);
  }
  if(connect(d->fd, add, add_size) < 0){
    int e = errno;
    free(d);
    errno = e;
    return NULL;
  }
  d->server = 0;
  return d;
}

ds createToIPv4Host(const unsigned long host, const int port){
  struct sockaddr_in add;
  add.sin_family = AF_INET;
  add.sin_port = htons(port);
  add.sin_addr.s_addr = host;
  return createToHost((struct sockaddr*) &add, sizeof(add), 0);
}

ds createToIPv6Host(const unsigned char host[16], const int port){
  struct sockaddr_in6 add;
  add.sin6_family = AF_INET6;
  add.sin6_port = htons(port);
  add.sin6_flowinfo = 0;
  copy6addr(add.sin6_addr.s6_addr, host);
  add.sin6_scope_id = 0;
  return createToHost((struct sockaddr*) &add, sizeof(add), 1);
}

int getPeer(ds d, unsigned long *ipv4peer, unsigned char ipv6peer[16], int *ipv6){
  int port = 0;
  struct sockaddr_storage peer;
  int peer_size = sizeof(peer);
  if(getpeername(d->fd, (struct sockaddr*)&peer, &peer_size)){
    return 0;
  }
  if(peer.ss_family == AF_INET){
    struct sockaddr_in *a = (struct sockaddr_in*)&(peer);
    zero6addr(ipv6peer);
    *ipv6 = -1;
    *ipv4peer = a->sin_addr.s_addr;
    port = a->sin_port;
  }else{
    struct sockaddr_in6 *a = (struct sockaddr_in6*)&(peer);
    *ipv4peer = 0;
    *ipv6 = 1;
    copy6addr(ipv6peer, a->sin6_addr.s6_addr);
    port = a->sin6_port;
  }
  return port;
}

int sendDs(ds d, const char *b, const int s){
  return write(d->fd, b, s);
}
int tlsDsSend(tlsDs d, const char *b, const int s){
  return SSL_write(d->s, b, s);
}
int stdDsSend(const char *b, const int s){
  return write(1, b, s);
}

int recvDs(ds d, char *b, const int s){
  return read(d->fd, b, s);
}
int tlsDsRecv(tlsDs d, char *b, const int s){
  return SSL_read(d->s, b, s);
}
int stdDsRecv(char *b, const int s){
  return read(0, b, s);
}


int prepareToClose(ds d){
  int fd = d->fd;
  free(d);
  return fd;
}

ds closeTls(tlsDs d){
  ds original = d->original;
  SSL_shutdown(d->s);
  //No bidirectional shutdown supported
  //SSL_shutdown(d->s);
  SSL_free(d->s);
  free(d);
  return original;
}

void closeHandler(nethandler h){
  close(h->fd);
  free(h);
}

tlsDs startSockTls(ds d, const char *cert, const char *key, const char *dh){
  loadOpenSSL(dh);
  SSL_CTX * ctx = NULL;
  if(d->server)
    ctx = SSL_CTX_new(TLSv1_1_server_method());
  else
    ctx = SSL_CTX_new(TLSv1_1_client_method());
  if(!ctx)
    return NULL;
  if(d->server){
    FILE *dhfile = fopen(dh, "r");
    DH *dhdt = PEM_read_DHparams(dhfile, NULL, NULL, NULL);
    fclose(dhfile);
    if(SSL_CTX_set_tmp_dh(ctx, dhdt) <= 0){
      int f = prepareToClose(d);
      closeFd(f);
      clear(dhdt);
      return clear(ctx);    
    }
  }
  SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
  if(cert)
    if(SSL_CTX_use_certificate_chain_file(ctx, cert) != 1){
      int f = prepareToClose(d);
      closeFd(f);
      return clear(ctx);
    }
  if(key)
    if(SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != 1){
      int f = prepareToClose(d);
      closeFd(f);
      return clear(ctx);
    }
  if(SSL_CTX_set_cipher_list(ctx, availableCiphers) <= 0){
      int f = prepareToClose(d);
      closeFd(f);
      return clear(ctx);
  }
  tlsDs t = (tlsDs)malloc(sizeof(s_tlsDs));
  t->original = d;
  if(!(t->s = SSL_new(ctx))){
    int f = prepareToClose(d);
    closeFd(f);
    clear(ctx);
    return clear(t);
  }
  if(!SSL_set_fd(t->s, d->fd)){
    closeTls(t);
    return NULL;
  }
  int retry = 1;
  int e;
  while(retry){
    retry = 0;
    if(d->server){
      SSL_set_accept_state(t->s);
      e = SSL_accept(t->s);
    }else{
      SSL_set_connect_state(t->s);
      e = SSL_connect(t->s);
    }
    if(e <= 0){
      unsigned long erval = SSL_get_error(t->s, e);
      //char ertxt[300];
      //ERR_error_string(erval, ertxt);
      //fprintf(stderr, "SSL Error: %s\n", ertxt);
      if((erval == SSL_ERROR_WANT_READ) || (erval == SSL_ERROR_WANT_WRITE)){
	//Here goes support to non-blocking IO, once it's supported
	//retry = 1;
      }else{
	closeTls(t);
	return NULL;
      }
    }
  }
  return t;
}

int getFd(ds d){
  return d->fd;
}

int getTlsFd(tlsDs t){
  ds d = t->original;
  return d->fd;
}

void closeFd(int fd){
  close(fd);
}