blob: 6ba525875b9c41333d693dee13d1906e95754bd6 [file] [log] [blame]
/* secure-server -- A (broken) DTLS server example
*
* Copyright (C) 2011 Olaf Bergmann <bergmann@tzi.org>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#ifdef WITH_DTLS
#define SERVER_CERT_PEM "./server-cert.pem"
#define SERVER_KEY_PEM "./server-key.pem"
#define CA_CERT_PEM "./ca-cert.pem"
#endif
#ifdef HAVE_ASSERT_H
# include <assert.h>
#else
# define assert(x)
#endif /* HAVE_ASSERT_H */
static int quit=0;
/* SIGINT handler: set quit to 1 for graceful termination */
void
handle_sigint(int signum) {
quit = 1;
}
int
check_connect(int sockfd, char *buf, int buflen,
struct sockaddr *src, int *ifindex) {
/* for some reason, the definition in netinet/in.h is not exported */
#ifndef IN6_PKTINFO
struct in6_pktinfo
{
struct in6_addr ipi6_addr; /* src/dst IPv6 address */
unsigned int ipi6_ifindex; /* send/recv interface index */
};
#endif
size_t bytes;
struct iovec iov[1] = { {buf, buflen} };
char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
struct in6_pktinfo *p = NULL;
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
msg.msg_name = src;
msg.msg_namelen = sizeof(struct sockaddr_in6);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
bytes = recvmsg(sockfd, &msg, MSG_DONTWAIT | MSG_PEEK);
if (bytes < 0) {
perror("recvmsg");
return bytes;
}
/* TODO: handle msg.msg_flags & MSG_TRUNC */
if (msg.msg_flags & MSG_CTRUNC) {
fprintf(stderr, "control was truncated!\n");
return -1;
}
if (ifindex) {
/* Here we try to retrieve the interface index where the packet was received */
*ifindex = 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
p = (struct in6_pktinfo *)(CMSG_DATA(cmsg));
*ifindex = p->ipi6_ifindex;
break;
}
}
}
return bytes;
}
typedef enum { UNKNOWN=0, DTLS=1 } protocol_t;
protocol_t
demux_protocol(const char *buf, int len) {
return DTLS;
}
#ifdef WITH_DTLS
typedef enum {
PEER_ST_ESTABLISHED, PEER_ST_PENDING, PEER_ST_CLOSED
} peer_state_t;
typedef struct {
peer_state_t state;
unsigned long h;
SSL *ssl;
} ssl_peer_t;
#define MAX_SSL_PENDING 2 /* must be less than MAX_SSL_PEERS */
#define MAX_SSL_PEERS 10 /* MAX_SSL_PENDING of these might be pending */
ssl_peer_t *ssl_peer_storage[MAX_SSL_PEERS];
static int pending = 0;
void
check_peers() {
typedef struct bio_dgram_data_st
{
union {
struct sockaddr sa;
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
} peer;
unsigned int connected;
unsigned int _errno;
unsigned int mtu;
struct timeval next_timeout;
struct timeval socket_timeout;
} bio_dgram_data;
struct sockaddr_in6 peer;
int i;
BIO *bio;
for (i = 0; i < MAX_SSL_PEERS; i++) {
if (ssl_peer_storage[i]) {
if (!ssl_peer_storage[i]->ssl)
fprintf(stderr, "invalid SSL object for peer %d!\n",i);
else {
bio = SSL_get_rbio(ssl_peer_storage[i]->ssl);
if (bio) {
(void) BIO_dgram_get_peer(bio, (struct sockaddr *)&peer);
if (peer.sin6_port && ssl_peer_storage[i]->h != ntohs(peer.sin6_port)) {
fprintf(stderr, " bio %p: port differs from hash: %d != %d! (%sconnected)\n", bio,
ssl_peer_storage[i]->h,
ntohs(((struct sockaddr_in6 *)&peer)->sin6_port),
((bio_dgram_data *)bio->ptr)->connected ? "" : "not ");
}
}
}
}
}
}
/** Creates a hash value from the first num bytes of s, taking init as
* initialization value. */
static inline unsigned long
_hash(unsigned long init, const char *s, int num) {
int c;
while (num--)
while ( (c = *s++) ) {
init = ((init << 7) + init) + c;
}
return init;
}
static inline unsigned long
hash_peer(const struct sockaddr *peer, int ifindex) {
unsigned long h;
/* initialize hash value to interface index */
h = _hash(0, (char *)&ifindex, sizeof(int));
#define CAST(TYPE,VAR) ((TYPE)VAR)
assert(peer);
switch (peer->sa_family) {
case AF_INET:
return ntohs(CAST(const struct sockaddr_in *, peer)->sin_port);
h = _hash(h, (char *) &CAST(const struct sockaddr_in *, peer)->sin_addr,
sizeof(struct in_addr));
h = _hash(h, (char *) &CAST(const struct sockaddr_in *, peer)->sin_port,
sizeof(in_port_t));
break;
case AF_INET6:
return ntohs(CAST(const struct sockaddr_in6 *, peer)->sin6_port);
h = _hash(h,
(char *) &CAST(const struct sockaddr_in6 *, peer)->sin6_addr,
sizeof(struct in6_addr));
h = _hash(h,
(char *) &CAST(const struct sockaddr_in6 *, peer)->sin6_port,
sizeof(in_port_t));
break;
default:
/* last resort */
h = _hash(h, (char *)peer, sizeof(struct sockaddr));
}
return 42;
return h;
}
/* Returns index of peer object for specified address/ifindex pair. */
int
get_index_of_peer(const struct sockaddr *peer, int ifindex) {
unsigned long h;
int idx;
#ifndef NDEBUG
char addr[INET6_ADDRSTRLEN];
char port[6];
#endif
if (!peer)
return -1;
h = hash_peer(peer,ifindex);
for (idx = 0; idx < MAX_SSL_PEERS; idx++) {
if (ssl_peer_storage[idx] && ssl_peer_storage[idx]->h == h) {
#ifndef NDEBUG
getnameinfo((struct sockaddr *)peer, sizeof(struct sockaddr_in6),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
fprintf(stderr, "get_index_of_peer: [%s]:%s => %lu\n",
addr, port, h);
#endif
return idx;
}
}
return -1;
}
SSL *
get_ssl(SSL_CTX *ctx, int sockfd, struct sockaddr *src, int ifindex) {
int idx;
BIO *bio;
SSL *ssl;
#ifndef NDEBUG
struct sockaddr_storage peer;
char addr[INET6_ADDRSTRLEN];
char port[6];
int i;
#endif
idx = get_index_of_peer(src,ifindex);
if (idx >= 0) {
fprintf(stderr,"found peer %d ",idx);
switch (ssl_peer_storage[idx]->state) {
case PEER_ST_ESTABLISHED: fprintf(stderr,"established\n"); break;
case PEER_ST_PENDING: fprintf(stderr,"pending\n"); break;
case PEER_ST_CLOSED: fprintf(stderr,"closed\n"); break;
default:
OPENSSL_assert(0);
}
#ifndef NDEBUG
memset(&peer, 0, sizeof(peer));
(void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[idx]->ssl), &peer);
getnameinfo((struct sockaddr *)&peer, sizeof(peer),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
fprintf(stderr," [%s]:%s \n", addr, port);
#endif
return ssl_peer_storage[idx]->ssl;
}
/* none found, create new if sufficient space available */
if (pending < MAX_SSL_PENDING) {
for (idx = 0; idx < MAX_SSL_PEERS; idx++) {
if (ssl_peer_storage[idx] == NULL) { /* found space */
ssl = SSL_new(ctx);
if (ssl) {
bio = BIO_new_dgram(sockfd, BIO_NOCLOSE);
if (!bio) {
SSL_free(ssl);
return NULL;
}
SSL_set_bio(ssl, bio, bio);
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
SSL_set_accept_state(ssl);
ssl_peer_storage[idx] = (ssl_peer_t *) malloc(sizeof(ssl_peer_t));
if (!ssl_peer_storage[idx]) {
SSL_free(ssl);
return NULL;
}
ssl_peer_storage[idx]->state = PEER_ST_PENDING;
ssl_peer_storage[idx]->h = hash_peer(src,ifindex);
ssl_peer_storage[idx]->ssl = ssl;
pending++;
fprintf(stderr,
"created new SSL peer %d for ssl object %p (storage: %p)\n",
idx, ssl, ssl_peer_storage[idx]);
#ifndef NDEBUG
if (getnameinfo((struct sockaddr *)&src, sizeof(src),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
perror("getnameinfo");
fprintf(stderr, "port was %u\n", ntohs(((struct sockaddr_in6 *)src)->sin6_port));
} else {
fprintf(stderr," [%s]:%s \n", addr, port);
}
#endif
OPENSSL_assert(ssl_peer_storage[idx]->ssl == ssl);
fprintf(stderr,"%d objects pending\n", pending);
check_peers();
return ssl;
}
}
}
} else {
fprintf(stderr, "too many pending SSL objects\n");
return NULL;
}
fprintf(stderr, "too many peers\n");
return NULL;
}
/** Deletes peer stored at index idx and frees allocated memory. */
static inline void
delete_peer(int idx) {
if (idx < 0 || !ssl_peer_storage[idx])
return;
if (ssl_peer_storage[idx]->state == PEER_ST_PENDING)
pending--;
OPENSSL_assert(ssl_peer_storage[idx]->ssl);
SSL_free(ssl_peer_storage[idx]->ssl);
free(ssl_peer_storage[idx]);
ssl_peer_storage[idx] = NULL;
printf("deleted peer %d\n",idx);
}
/** Deletes all closed objects from ssl_peer_storage. */
void
remove_closed() {
int idx;
for (idx = 0; idx < MAX_SSL_PEERS; idx++)
if (ssl_peer_storage[idx]
&& ssl_peer_storage[idx]->state == PEER_ST_CLOSED)
delete_peer(idx);
}
#define min(a,b) ((a) < (b) ? (a) : (b))
unsigned int
psk_server_callback(SSL *ssl, const char *identity,
unsigned char *psk, unsigned int max_psk_len) {
static char keybuf[] = "secretPSK";
printf("psk_server_callback: check identity of client %s\n", identity);
memcpy(psk, keybuf, min(strlen(keybuf), max_psk_len));
return min(strlen(keybuf), max_psk_len);
}
#endif
#ifdef WITH_DTLS
/**
* This function tracks the status changes from libssl to manage local
* object state.
*/
void
info_callback(const SSL *ssl, int where, int ret) {
int idx, i;
struct sockaddr_storage peer;
struct sockaddr_storage peer2;
char addr[INET6_ADDRSTRLEN];
char port[6];
if (where & SSL_CB_LOOP) /* do not care for intermediary states */
return;
memset(&peer, 0, sizeof(peer));
(void) BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);
/* lookup SSL object */ /* FIXME: need to get the ifindex */
idx = get_index_of_peer((struct sockaddr *)&peer, 0);
if (idx >= 0)
fprintf(stderr, "info_callback: assert: %d < 0 || %p == %p (storage: %p)\n",
idx, ssl, ssl_peer_storage[idx]->ssl, ssl_peer_storage[idx]);
if (idx >= 0 && ssl != ssl_peer_storage[idx]->ssl) {
getnameinfo((struct sockaddr *)&peer, sizeof(peer),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
fprintf(stderr," ssl: [%s]:%s ", addr, port);
(void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[idx]->ssl), &peer2);
getnameinfo((struct sockaddr *)&peer2, sizeof(peer2),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
fprintf(stderr," ssl_peer_storage[idx]->ssl: [%s]:%s\n", addr, port);
fprintf(stderr, " hash:%lu h: %lu\n",
hash_peer((const struct sockaddr *)&peer, 0),
ssl_peer_storage[idx]->h);
for (i = 0; i < MAX_SSL_PEERS; i++) {
if (ssl_peer_storage[i]) {
fprintf(stderr, "%02d: %p ssl: %p ",
i, ssl_peer_storage[i] ,ssl_peer_storage[i]->ssl);
(void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[i]->ssl), &peer2);
getnameinfo((struct sockaddr *)&peer2, sizeof(peer2),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
fprintf(stderr," peer: [%s]:%s h: %lu\n", addr, port, ssl_peer_storage[i]->h);
}
}
fprintf(stderr, "***** ASSERT FAILED ******\n");
memset(&peer, 0, sizeof(peer));
(void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &peer);
idx = get_index_of_peer((struct sockaddr *)&peer, 0);
fprintf(stderr, " get_index_of_peer for wbio returns %d, type is %04x\n",
idx, where);
}
#if 1
check_peers();
OPENSSL_assert((idx < 0) || (ssl == ssl_peer_storage[idx]->ssl));
#endif
if (where & SSL_CB_ALERT) {
#ifndef NDEBUG
if (ret != 0)
fprintf(stderr,"%s:%s:%s\n", SSL_alert_type_string(ret),
SSL_alert_desc_string(ret), SSL_alert_desc_string_long(ret));
#endif
/* examine alert type */
switch (*SSL_alert_type_string(ret)) {
case 'F':
/* move SSL object from pending to close */
if (idx >= 0) {
ssl_peer_storage[idx]->state = PEER_ST_CLOSED;
pending--;
}
break;
case 'W':
if ((ret & 0xff) == SSL_AD_CLOSE_NOTIFY) {
if (where == SSL_CB_WRITE_ALERT)
fprintf(stderr,"sent CLOSE_NOTIFY\n");
else /* received CN */
fprintf(stderr,"received CLOSE_NOTIFY\n");
}
break;
default: /* handle unknown alert types */
#ifndef NDEBUG
printf("not handled!\n");
#endif
}
}
if (where & SSL_CB_HANDSHAKE_DONE) {
/* move SSL object from pending to established */
printf("HANDSHAKE_DONE ");
if (idx >= 0) {
if (ssl_peer_storage[idx]->state == PEER_ST_PENDING) {
ssl_peer_storage[idx]->state = PEER_ST_ESTABLISHED;
pending--;
printf("moved SSL object %d to ESTABLISHED\n", idx);
printf("%d objects pending\n", pending);
} else {
#ifndef NDEBUG
printf("huh, object %d was not pending? (%d)\n", idx,
ssl_peer_storage[idx]->state);
#endif
}
return;
}
return;
}
return;
}
#endif
#ifdef WITH_DTLS
/* checks if ssl object was closed and can be removed */
int
check_close(SSL *ssl) {
int res, err, idx;
struct sockaddr_storage peer;
memset(&peer, 0, sizeof(peer));
(void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);
res = 0;
if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) {
printf("SSL_RECEIVED_SHUTDOWN\n");
res = SSL_shutdown(ssl);
if (res == 0) {
printf("must call SSL_shutdown again\n");
res = SSL_shutdown(ssl);
}
if (res < 0) {
err = SSL_get_error(ssl,res);
fprintf(stderr, "shutdown: SSL error %d: %s\n", err,
ERR_error_string(err, NULL));
}
/* we can close the SSL object anyway */
/* FIXME: need to get ifindex from somewhere */
idx = get_index_of_peer((struct sockaddr *)&peer, 0);
OPENSSL_assert(idx < 0 || ssl == ssl_peer_storage[idx]->ssl);
if (idx >= 0) {
ssl_peer_storage[idx]->state = PEER_ST_CLOSED;
printf("moved SSL object %d to CLOSED\n",idx);
}
}
return res;
}
int
check_timeout() {
int i, result, err;
for (i = 0; i < MAX_SSL_PEERS; i++) {
if (ssl_peer_storage[i]) {
OPENSSL_assert(ssl_peer_storage[i]->ssl);
result = DTLSv1_handle_timeout(ssl_peer_storage[i]->ssl);
if (result < 0) {
err = SSL_get_error(ssl_peer_storage[i]->ssl,result);
fprintf(stderr, "dtls1_handle_timeout (%d): %s\n",
err, ERR_error_string(err, NULL));
}
}
}
/* remove outdated obbjects? */
return 0;
}
#endif /* WITH_DTLS */
int
_read(SSL_CTX *ctx, int sockfd) {
char buf[2000];
struct sockaddr_in6 src;
int len, ifindex, i;
char addr[INET6_ADDRSTRLEN];
char port[6];
socklen_t sz = sizeof(struct sockaddr_in6);
#ifdef WITH_DTLS
SSL *ssl;
int err;
#endif
/* Retrieve remote address and interface index as well as the first
few bytes of the message to demultiplex protocols. */
memset(&src, 0, sizeof(struct sockaddr_in6));
len = check_connect(sockfd, buf, 4, (struct sockaddr *)&src, &ifindex);
if (len < 0) /* error */
return len;
#ifndef NDEBUG
fprintf(stderr,"received packet");
if (getnameinfo((struct sockaddr *)&src, sizeof(src),
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV) == 0)
fprintf(stderr," from [%s]:%s", addr, port);
fprintf(stderr," on interface %d\n", ifindex);
#endif
switch (demux_protocol(buf, len)) {
#ifdef WITH_DTLS
case DTLS :
ssl = get_ssl(ctx, sockfd, (struct sockaddr *)&src, ifindex);
if (!ssl) {
fprintf(stderr, "cannot create new SSL object\n");
/* return recv(sockfd, buf, sizeof(buf), MSG_DONTWAIT);*/
len = recvfrom(sockfd, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr *)&src, &sz);
getnameinfo((struct sockaddr *)&src, sz,
addr, sizeof(addr), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
printf("discarded %d bytes from [%s]:%s\n", len, addr, port);
return len;
}
len = SSL_read(ssl, buf, sizeof(buf));
break;
#endif
case UNKNOWN:
default :
len = recv(sockfd, buf, sizeof(buf), MSG_DONTWAIT);
}
if (len > 0) {
printf("here is the data:\n");
for (i=0; i<len; i++)
printf("%c",buf[i]);
} if (len == 0) { /* session closed? */
#ifdef WITH_DTLS
if (check_close(ssl) <= 0) {
fprintf(stderr, "not closed\n");
}
#endif
} else {
#ifdef WITH_DTLS
err = SSL_get_error(ssl,len);
switch (err) {
case SSL_ERROR_WANT_READ:
fprintf(stderr, "SSL_ERROR_WANT_READ\n");
return 0;
case SSL_ERROR_WANT_WRITE:
fprintf(stderr, "SSL_ERROR_WANT_WRITE\n");
return 0;
default:
fprintf(stderr, "read: SSL error %d: %s\n", err,
ERR_error_string(err, NULL));
return 0;
}
#else
perror("recv");
#endif
}
return len;
}
int
_write(SSL_CTX *ctx, int sockfd) {
int res = 0;
#ifdef WITH_DTLS
SSL *ssl;
int err;
ssl = get_ssl(ctx, sockfd, NULL, 1);
if (!ssl) {
fprintf(stderr, "no SSL object for writing");
return 0;
}
res = SSL_write(ssl, NULL, 0);
if (res < 0) {
/*
if (SSL_want_write(ssl))
return 0;
*/
/* FIXME: check SSL_want_read(ssl) */
err = SSL_get_error(ssl,res);
fprintf(stderr,"SSL_write returned %d (%s)\n", err, ERR_error_string(err, NULL));
} else {
printf("SSL_write successful\n");
}
#else
#endif
return res;
}
int
generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) {
/* FIXME: generate secure client-specific cookie */
#define DUMMYSTR "ABCDEFGHIJKLMNOP"
*cookie_len = strlen(DUMMYSTR);
memcpy(cookie, DUMMYSTR, *cookie_len);
return 1;
}
int
verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) {
/* FIXME */
return 1;
}
enum { READ, WRITE };
int
main(int argc, char **argv) {
int sockfd = 0;
int on = 1;
struct sockaddr_in6 listen_addr = { AF_INET6, htons(20220), 0, IN6ADDR_ANY_INIT, 0 };
size_t addr_size = sizeof(struct sockaddr_in6);
fd_set fds[2];
int result, flags;
int idx, res = 0;
struct timeval timeout;
struct sigaction act, oact;
#ifdef WITH_DTLS
SSL_CTX *ctx;
memset(ssl_peer_storage, 0, sizeof(ssl_peer_storage));
SSL_load_error_strings();
SSL_library_init();
ctx = SSL_CTX_new(DTLSv1_server_method());
SSL_CTX_set_cipher_list(ctx, "ALL");
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
res = SSL_CTX_use_certificate_file(ctx, SERVER_CERT_PEM, SSL_FILETYPE_PEM);
if (res != 1) {
fprintf(stderr, "cannot read server certificate from file '%s' (%s)\n",
SERVER_CERT_PEM, ERR_error_string(res,NULL));
goto end;
}
res = SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_PEM, SSL_FILETYPE_PEM);
if (res != 1) {
fprintf(stderr, "cannot read server key from file '%s' (%s)\n",
SERVER_KEY_PEM, ERR_error_string(res,NULL));
goto end;
}
res = SSL_CTX_check_private_key (ctx);
if (res != 1) {
fprintf(stderr, "invalid private key\n");
goto end;
}
res = SSL_CTX_load_verify_locations(ctx, CA_CERT_PEM, NULL);
if (res != 1) {
fprintf(stderr, "cannot read ca file '%s'\n", CA_CERT_PEM);
goto end;
}
/* Client has to authenticate */
/* Client has to authenticate */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
SSL_CTX_set_read_ahead(ctx, 1); /* disable read-ahead */
SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie);
SSL_CTX_use_psk_identity_hint(ctx, "Enter password for CoAP-Gateway");
SSL_CTX_set_psk_server_callback(ctx, psk_server_callback);
SSL_CTX_set_info_callback(ctx, info_callback);
#endif
sockfd = socket(listen_addr.sin6_family, SOCK_DGRAM, 0);
if ( sockfd < 0 ) {
perror("socket");
return -1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0)
perror("setsockopt SO_REUSEADDR");
flags = fcntl(sockfd, F_GETFL, 0);
if (flags < 0 || fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl");
return -1;
}
on = 1;
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on) ) < 0) {
perror("setsockopt IPV6_PKTINFO");
}
if (bind (sockfd, (const struct sockaddr *)&listen_addr, addr_size) < 0) {
perror("bind");
res = -2;
goto end;
}
act.sa_handler = handle_sigint;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, &oact);
while (!quit) {
FD_ZERO(&fds[READ]);
FD_ZERO(&fds[WRITE]);
FD_SET(sockfd, &fds[READ]);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
result = select( FD_SETSIZE, &fds[READ], &fds[WRITE], 0, &timeout);
if (result < 0) { /* error */
if (errno != EINTR)
perror("select");
} else if (result > 0) { /* read from socket */
if ( FD_ISSET( sockfd, &fds[READ]) ) {
_read(ctx, sockfd); /* read received data */
} else if ( FD_ISSET( sockfd, &fds[WRITE]) ) { /* write to socket */
_write(ctx, sockfd); /* write data */
}
} else { /* timeout */
check_timeout();
}
remove_closed();
}
end:
#ifdef WITH_DTLS
for (idx = 0; idx < MAX_SSL_PEERS; idx++) {
if (ssl_peer_storage[idx] && ssl_peer_storage[idx]->ssl) {
if (ssl_peer_storage[idx]->state == PEER_ST_ESTABLISHED)
SSL_shutdown(ssl_peer_storage[idx]->ssl);
SSL_free(ssl_peer_storage[idx]->ssl);
}
}
SSL_CTX_free(ctx);
#endif
close(sockfd); /* don't care if we close stdin at this point */
return res;
}