This is originally from git@github.com:YanziNetworks/tinyDTLS.git commit d7ca5120e7e853f76e9743db2a0236a26ba71363 Change-Id: I6e6c2da76e11b473ead1b0ddac929f8bd670d7e3 Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
863 lines
22 KiB
C
863 lines
22 KiB
C
/* 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;
|
|
}
|