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);
}