1286 lines
36 KiB
C
1286 lines
36 KiB
C
/**
|
|
* @file dtlsServer.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* Sample DTLS server.
|
|
* Supports multiple simultaneous clients and non-blocking sockets
|
|
*/
|
|
/*
|
|
* Copyright (c) 2014-2016 INSIDE Secure Corporation
|
|
* Copyright (c) PeerSec Networks, 2002-2011
|
|
* All Rights Reserved
|
|
*
|
|
* The latest version of this code is available at http://www.matrixssl.org
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This General Public License does NOT permit incorporating this software
|
|
* into proprietary programs. If you are unable to comply with the GPL, a
|
|
* commercial license for this software may be purchased from INSIDE at
|
|
* http://www.insidesecure.com/
|
|
*
|
|
* This program is distributed in WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
#include <signal.h> /* Defines SIGTERM, etc. */
|
|
#ifdef OSX
|
|
#include <sys/select.h> /* Defines fd_set, etc. */
|
|
#define MSG_NOSIGNAL 0
|
|
#endif
|
|
|
|
#include "dtlsCommon.h"
|
|
|
|
#ifdef USE_DTLS
|
|
|
|
#include "../../crypto/cryptoApi.h"
|
|
|
|
/* #define USE_CERT_VALIDATOR */
|
|
|
|
#define DTLS_PORT 4433
|
|
static int packet_loss_prob = 0; /* Reciprocal of packet loss probability
|
|
(i.e. P(packet loss) = 1/x).
|
|
Default value is 0 (no packet loss). */
|
|
/*
|
|
Client management
|
|
*/
|
|
#define MAX_CLIENTS 16
|
|
|
|
typedef struct {
|
|
psTime_t lastRecvTime;
|
|
uint32 timeout; /* in seconds */
|
|
uint32 connStatus;
|
|
SOCKET fd;
|
|
struct sockaddr_in addr;
|
|
ssl_t *ssl;
|
|
} serverDtls_t;
|
|
|
|
static serverDtls_t *clientTable;
|
|
static uint16 tableSize;
|
|
|
|
#define RESUMED_HANDSHAKE_COMPLETE 1
|
|
|
|
/* Static Prototypes */
|
|
static int32 initClientList(uint16 maxPeers);
|
|
static int32 handleResends(SOCKET sock);
|
|
static serverDtls_t *findClient(struct sockaddr_in addr);
|
|
static serverDtls_t *registerClient(struct sockaddr_in addr, SOCKET sock,
|
|
ssl_t *ssl);
|
|
static void clearClient(serverDtls_t *dtls);
|
|
static void closeClientList();
|
|
static SOCKET newUdpSocket(char *ip, short port, int *err);
|
|
static int sigsetup(void);
|
|
static void sigsegv_handler(int);
|
|
static void sigintterm_handler(int);
|
|
static void usage(void);
|
|
static int32 process_cmd_options(int32 argc, char **argv);
|
|
|
|
#ifdef USE_DTLS_DEBUG_TRACE
|
|
static unsigned char * getaddrstring(struct sockaddr *addr, int withport);
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#pragma message("DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS.")
|
|
#else
|
|
#warning "DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS."
|
|
#endif
|
|
|
|
/* Pick ONE cipher suite type you want to use to auto select a certificate
|
|
and private key identity that support those algorithms.
|
|
|
|
In general, it works the other way: a server would obtain an identity
|
|
certificate and private key and then only enable cipher suites that the
|
|
material supports.
|
|
*/
|
|
//#define ID_RSA /* Standard RSA suites. RSA Key Exchange and Authentication */
|
|
//#define ID_DHE_RSA /* Diffie-Hellman key exchange. RSA Authentication */
|
|
//#define ID_DH_ANON /* Diffie-Hellman key exchange. No auth */
|
|
//#define ID_DHE_PSK /* Diffie-Hellman key exchange. Pre-Shared Key Auth */
|
|
//#define ID_PSK /* Basic Pre-Shared Key suites */
|
|
//#define ID_ECDH_ECDSA /* Elliptic Curve Key Exchange and Authentication */
|
|
//#define ID_ECDHE_ECDSA /* Same as above with ephemeral Key Exchange */
|
|
//#define ID_ECDHE_RSA /* Ephemeral EC Key Exchange, RSA Authentication */
|
|
//#define ID_ECDH_RSA /* EC Key Exchange, RSA Authentication */
|
|
|
|
#ifdef ID_RSA
|
|
#ifndef USE_RSA
|
|
#error USE_RSA shall be defined for ID_RSA
|
|
#endif /* USE_RSA */
|
|
#endif /* ID_RSA */
|
|
|
|
#ifdef ID_DHE_RSA
|
|
#if !defined(USE_DH) || !defined(USE_RSA)
|
|
#error USE_DH and USE_RSA shall be defined for ID_DHE_RSA
|
|
#endif /* USE_DH || USE_RSA */
|
|
#endif /* ID_DHE_RSA */
|
|
|
|
#if defined(ID_DH_ANON) || defined(ID_DHE_PSK)
|
|
#ifndef USE_DH
|
|
#error USE_DH shall be defined for ID_DH_ANON and ID_DHE_PSK
|
|
#endif /* USE_DH */
|
|
#endif /* ID_DH_ANON */
|
|
|
|
#if defined(ID_ECDH_ECDSA) || defined(ID_ECDHE_ECDSA)
|
|
#ifndef USE_ECC
|
|
#error USE_ECC shall be defined for ID_ECDH_ECDSA and ID_ECDHE_ECDSA
|
|
#endif /* USE_ECC */
|
|
#endif /* ID_ECDH_ECDSA || ID_ECDHE_ECDSA */
|
|
|
|
#if defined(ID_ECDHE_RSA) || defined(ID_ECDH_RSA)
|
|
#if !defined(USE_ECC) || !defined(USE_RSA)
|
|
#error USE_ECC and USE_RSA shall be defined for ID_ECDHE_RSA and ID_ECDH_RSA
|
|
#endif /* USE_ECC || USE_RSA */
|
|
#endif /* ID_ECDHE_RSA || ID_ECDH_RSA */
|
|
|
|
|
|
#define ALLOW_ANON_CONNECTIONS 1
|
|
#define USE_HEADER_KEYS
|
|
|
|
|
|
/* The keys that are loaded are compatible with the MatrixSSL sample client.
|
|
The CA files loaded assume the same authentication mechanism as the
|
|
cipher suite
|
|
*/
|
|
#ifdef USE_HEADER_KEYS
|
|
/* Identity Key and Cert */
|
|
#ifdef ID_RSA
|
|
#define EXAMPLE_RSA_KEYS
|
|
#include "testkeys/RSA/1024_RSA.h"
|
|
#include "testkeys/RSA/1024_RSA_KEY.h"
|
|
#include "testkeys/RSA/2048_RSA.h"
|
|
#include "testkeys/RSA/2048_RSA_KEY.h"
|
|
#include "testkeys/RSA/3072_RSA.h"
|
|
#include "testkeys/RSA/3072_RSA_KEY.h"
|
|
#include "testkeys/RSA/4096_RSA.h"
|
|
#include "testkeys/RSA/4096_RSA_KEY.h"
|
|
#endif
|
|
|
|
|
|
#if defined(ID_DHE_RSA) || defined(ID_ECDHE_RSA)
|
|
#define EXAMPLE_RSA_KEYS
|
|
#include "testkeys/RSA/1024_RSA.h"
|
|
#include "testkeys/RSA/1024_RSA_KEY.h"
|
|
#include "testkeys/RSA/2048_RSA.h"
|
|
#include "testkeys/RSA/2048_RSA_KEY.h"
|
|
#include "testkeys/RSA/3072_RSA.h"
|
|
#include "testkeys/RSA/3072_RSA_KEY.h"
|
|
#include "testkeys/RSA/4096_RSA.h"
|
|
#include "testkeys/RSA/4096_RSA_KEY.h"
|
|
#endif
|
|
|
|
#if defined(ID_ECDHE_ECDSA) || defined(ID_ECDH_ECDSA)
|
|
#define EXAMPLE_EC_KEYS
|
|
#include "testkeys/EC/192_EC.h"
|
|
#include "testkeys/EC/192_EC_KEY.h"
|
|
#include "testkeys/EC/224_EC.h"
|
|
#include "testkeys/EC/224_EC_KEY.h"
|
|
#include "testkeys/EC/256_EC.h"
|
|
#include "testkeys/EC/256_EC_KEY.h"
|
|
#include "testkeys/EC/384_EC.h"
|
|
#include "testkeys/EC/384_EC_KEY.h"
|
|
#include "testkeys/EC/521_EC.h"
|
|
#include "testkeys/EC/521_EC_KEY.h"
|
|
#endif
|
|
|
|
#ifdef ID_ECDH_RSA
|
|
#define EXAMPLE_ECDH_RSA_KEYS
|
|
#include "testkeys/ECDH_RSA/256_ECDH-RSA.h"
|
|
#include "testkeys/ECDH_RSA/256_ECDH-RSA_KEY.h"
|
|
#include "testkeys/ECDH_RSA/521_ECDH-RSA.h"
|
|
#include "testkeys/ECDH_RSA/521_ECDH-RSA_KEY.h"
|
|
#endif
|
|
|
|
#ifdef REQUIRE_DH_PARAMS
|
|
#include "testkeys/DH/2048_DH_PARAMS.h"
|
|
#endif
|
|
|
|
|
|
/* CA files for client auth are selected more generously. If the algorithm
|
|
type is supported, we'll load it */
|
|
#ifdef USE_RSA
|
|
#include "testkeys/RSA/ALL_RSA_CAS.h"
|
|
#ifdef USE_ECC
|
|
#include "testkeys/ECDH_RSA/ALL_ECDH-RSA_CAS.h"
|
|
#endif /* USE_ECC */
|
|
#endif /* USE_RSA */
|
|
#ifdef USE_ECC
|
|
|
|
#if defined(USE_SECP192R1) && defined(USE_SECP224R1) && defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS.h"
|
|
#endif /* USE_SECP192R1 && USE_SECP224R1 && USE_SECP521R1 */
|
|
|
|
#if !defined(USE_SECP192R1) && defined(USE_SECP224R1) && defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P192.h"
|
|
#endif /* !USE_SECP192R1 && USE_SECP224R1 && USE_SECP521R1 */
|
|
|
|
#if defined(USE_SECP192R1) && !defined(USE_SECP224R1) && defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P224.h"
|
|
#endif /* USE_SECP192R1 && !USE_SECP224R1 && USE_SECP521R1 */
|
|
|
|
#if defined(USE_SECP192R1) && defined(USE_SECP224R1) && !defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P521.h"
|
|
#endif /* USE_SECP192R1 && USE_SECP224R1 && !USE_SECP521R1 */
|
|
|
|
#if !defined(USE_SECP192R1) && !defined(USE_SECP224R1) && defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P192_AND_P224.h"
|
|
#endif /* !USE_SECP192R1 && !USE_SECP224R1 && USE_SECP521R1 */
|
|
|
|
#if !defined(USE_SECP192R1) && defined(USE_SECP224R1) && !defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P192_AND_P521.h"
|
|
#endif /* !USE_SECP192R1 && USE_SECP224R1 && !USE_SECP521R1 */
|
|
|
|
#if defined(USE_SECP192R1) && !defined(USE_SECP224R1) && !defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P224_AND_P521.h"
|
|
#endif /* USE_SECP192R1 && USE_SECP224R1 && !USE_SECP521R1 */
|
|
|
|
#if !defined(USE_SECP192R1) && !defined(USE_SECP224R1) && !defined(USE_SECP521R1)
|
|
#include "testkeys/EC/ALL_EC_CAS_EXCEPT_P192_P224_AND_P521.h"
|
|
#endif /* !USE_SECP192R1 && USE_SECP224R1 && !USE_SECP521R1 */
|
|
|
|
#endif /* USE_ECC */
|
|
|
|
|
|
/* File-based keys */
|
|
#else
|
|
#ifdef ID_RSA
|
|
#define EXAMPLE_RSA_KEYS
|
|
static char rsaCertFile[] = "testkeys/RSA/2048_RSA.pem";
|
|
static char rsaPrivkeyFile[] = "testkeys/RSA/2048_RSA_KEY.pem";
|
|
#endif
|
|
|
|
|
|
#if defined(ID_DHE_RSA) || defined(ID_ECDHE_RSA)
|
|
#define EXAMPLE_RSA_KEYS
|
|
static char rsaCertFile[] = "testkeys/RSA/2048_RSA.pem";
|
|
static char rsaPrivkeyFile[] = "testkeys/RSA/2048_RSA_KEY.pem";
|
|
#endif
|
|
|
|
#if defined(ID_ECDHE_ECDSA) || defined(ID_ECDH_ECDSA)
|
|
#define EXAMPLE_EC_KEYS
|
|
static char ecCertFile[] = "testkeys/EC/256_EC.pem";
|
|
static char ecPrivkeyFile[] = "testkeys/EC/256_EC_KEY.pem";
|
|
#endif
|
|
|
|
#ifdef ID_ECDH_RSA
|
|
#define EXAMPLE_ECDH_RSA_KEYS
|
|
static char ecdhRsaCertFile[] = "testkeys/ECDH_RSA/256_ECDH-RSA.pem";
|
|
static char ecdhRsaPrivkeyFile[] = "testkeys/ECDH_RSA/256_ECDH-RSA_KEY.pem";
|
|
#endif
|
|
|
|
#ifdef REQUIRE_DH_PARAMS
|
|
static char dhParamFile[] = "testkeys/DH/2048_DH_PARAMS.pem";
|
|
#endif
|
|
|
|
|
|
/* CA files for client auth are selected more generously. If the algorithm
|
|
type is supported, we'll load it */
|
|
#ifdef USE_RSA
|
|
static char rsaCAFile[] = "testkeys/RSA/ALL_RSA_CAS.pem";
|
|
static char rsaCA3072File[] = "testkeys/RSA/3072_RSA_CA.pem";
|
|
#ifdef USE_ECC
|
|
static char ecdhRsaCAFile[] = "testkeys/ECDH_RSA/ALL_ECDH-RSA_CAS.pem";
|
|
#endif /* USE_ECC */
|
|
#endif /* USE_RSA */
|
|
#ifdef USE_ECC
|
|
|
|
#if defined(USE_SECP192R1) && defined(USE_SECP521R1)
|
|
static char ecCAFile[] = "testkeys/EC/ALL_EC_CAS.pem";
|
|
#endif /* USE_SECP192R1 && USE_SECP521R1 */
|
|
|
|
#if !defined(USE_SECP192R1) && defined(USE_SECP521R1)
|
|
static char ecCAFile[] = "testkeys/EC/ALL_EC_CAS_EXCEPT_P192.pem";
|
|
#endif /* USE_SECP192R1 && USE_SECP521R1 */
|
|
|
|
#if defined(USE_SECP192R1) && !defined(USE_SECP521R1)
|
|
static char ecCAFile[] = "testkeys/EC/ALL_EC_CAS_EXCEPT_P521.pem";
|
|
#endif /* USE_SECP192R1 && USE_SECP521R1 */
|
|
|
|
#if !defined(USE_SECP192R1) && !defined(USE_SECP521R1)
|
|
static char ecCAFile[] = "testkeys/EC/ALL_EC_CAS_EXCEPT_P192_AND_P521.pem";
|
|
#endif /* USE_SECP192R1 && USE_SECP521R1 */
|
|
|
|
#endif /* USE_ECC */
|
|
|
|
#endif /* USE_FILE_KEYS */
|
|
|
|
|
|
#ifdef USE_PSK_CIPHER_SUITE
|
|
#include "testkeys/PSK/psk.h"
|
|
#endif /* PSK */
|
|
|
|
|
|
|
|
static int exitFlag;
|
|
|
|
static uint32_t g_rsaKeySize;
|
|
static uint32_t g_eccKeySize;
|
|
static uint32_t g_ecdhKeySize;
|
|
|
|
#ifdef USE_CERT_VALIDATOR
|
|
/******************************************************************************/
|
|
/*
|
|
Stub for a user-level certificate validator. Just using
|
|
the default validation value here.
|
|
*/
|
|
static int32 certValidator(ssl_t *ssl, psX509Cert_t *cert, int32 alert)
|
|
{
|
|
/* Example to allow anonymous connections based on a define */
|
|
if (alert > 0) {
|
|
if (ALLOW_ANON_CONNECTIONS) {
|
|
/* Could be NULL if SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG */
|
|
if (cert) {
|
|
_psTraceStr("Allowing anonymous connection for: %s.\n",
|
|
cert->subject.commonName);
|
|
} else {
|
|
_psTrace("Client didn't send certificate. Reverting to standard handshake due to SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG\n");
|
|
}
|
|
return SSL_ALLOW_ANON_CONNECTION;
|
|
}
|
|
_psTrace("Certificate callback returning fatal alert\n");
|
|
return alert;
|
|
}
|
|
_psTraceStr("Validated cert for: %s.\n", cert->subject.commonName);
|
|
return 0;
|
|
}
|
|
#else
|
|
/* No certificate validator is needed */
|
|
#define certValidator NULL
|
|
#endif /* USE_CERT_VALIDATOR */
|
|
|
|
static void usage(void)
|
|
{
|
|
printf("\nusage: dltsServer { option }\n"
|
|
"\n"
|
|
"where option can be one of the following:\n"
|
|
"\n"
|
|
"-h - Print usage and exit\n"
|
|
"-r <keyLen> - RSA keyLen (1024|2048|3072|4096)\n"
|
|
"-e <keyLen> - ECC keyLen (192|224|256|384|521)"
|
|
"-d <keyLen> - ECDH_RSA keyLen (256|521)\n"
|
|
#ifdef DTLS_PACKET_LOSS_TEST
|
|
"-l <value> - Reciprocal of packet loss probability\n"
|
|
" (for packet loss simulation tests)\n"
|
|
#endif /* DTLS_PACKET_LOSS_TEST */
|
|
);
|
|
}
|
|
|
|
/* Return 0 on good set of cmd options, return -1 if a bad cmd option is
|
|
encountered OR a request for help is seen (i.e. '-h' option). */
|
|
static int32 process_cmd_options(int32 argc, char **argv)
|
|
{
|
|
int32 optionChar;
|
|
|
|
// Set some default options:
|
|
g_rsaKeySize = 2048;
|
|
g_eccKeySize = g_ecdhKeySize = 256;
|
|
|
|
opterr = 0;
|
|
while ((optionChar = getopt(argc, argv, "hr:e:d:l:")) != -1)
|
|
{
|
|
switch (optionChar) {
|
|
case '?':
|
|
return -1;
|
|
|
|
case 'h':
|
|
break;
|
|
|
|
case 'r':
|
|
g_rsaKeySize = atoi(optarg);
|
|
if ((g_rsaKeySize != 1024) && (g_rsaKeySize != 2048)
|
|
&& (g_rsaKeySize != 3072) && (g_rsaKeySize != 4096)) {
|
|
printf("invalid -r option\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case 'e':
|
|
g_eccKeySize = atoi(optarg);
|
|
if ((g_eccKeySize != 192) && (g_eccKeySize != 224)
|
|
&& (g_eccKeySize != 256) && (g_eccKeySize != 384)
|
|
&& (g_eccKeySize != 521)) {
|
|
printf("invalid -e option\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
g_ecdhKeySize = atoi(optarg);
|
|
if ((g_ecdhKeySize != 256) && (g_ecdhKeySize != 521)) {
|
|
printf("invalid -d option\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
#ifdef DTLS_PACKET_LOSS_TEST
|
|
case 'l':
|
|
packet_loss_prob = atoi(optarg);
|
|
if (packet_loss_prob < 0) {
|
|
printf("invalid -l option\n");
|
|
return -1;
|
|
}
|
|
if (packet_loss_prob > 0) {
|
|
printf("Server simulating packet loss with probability 1/%d\n",
|
|
packet_loss_prob);
|
|
}
|
|
break;
|
|
#endif /* DTLS_PACKET_LOSS_TEST */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Main
|
|
*/
|
|
int main(int argc, char ** argv)
|
|
{
|
|
struct sockaddr_in inaddr;
|
|
socklen_t inaddrlen;
|
|
struct timeval timeout;
|
|
ssl_t *ssl;
|
|
serverDtls_t *dtlsCtx;
|
|
SOCKET sock;
|
|
fd_set readfd;
|
|
unsigned char *sslBuf, *recvfromBuf, *CAstream;
|
|
#ifdef USE_DTLS_DEBUG_TRACE
|
|
unsigned char *addrstr;
|
|
#endif
|
|
#if !defined(ID_PSK) && !defined(ID_DHE_PSK)
|
|
unsigned char *keyValue, *certValue;
|
|
int32 keyLen, certLen;
|
|
#endif
|
|
sslKeys_t *keys;
|
|
int32 freeBufLen, rc, val, recvLen, err, CAstreamLen;
|
|
int32 sslBufLen, rcr, rcs, sendLen, recvfromBufLen;
|
|
sslSessOpts_t options;
|
|
|
|
#ifdef WIN32
|
|
WSADATA wsaData;
|
|
WSAStartup(MAKEWORD(1, 1), &wsaData);
|
|
#endif
|
|
|
|
rc = 0;
|
|
ssl = NULL;
|
|
dtlsCtx = NULL;
|
|
CAstream = NULL;
|
|
sock = INVALID_SOCKET;
|
|
|
|
if (0 != process_cmd_options(argc, argv)) {
|
|
usage();
|
|
return 0;
|
|
}
|
|
if (sigsetup() < 0) {
|
|
_psTrace("Init error creating signal handlers\n");
|
|
return DTLS_FATAL;
|
|
}
|
|
if (matrixSslOpen() < 0) {
|
|
_psTrace("Init error opening MatrixDTLS library\n");
|
|
return DTLS_FATAL;
|
|
}
|
|
if (matrixSslNewKeys(&keys, NULL) < 0) {
|
|
_psTrace("Init error allocating key structure\n");
|
|
matrixSslClose();
|
|
return DTLS_FATAL;
|
|
}
|
|
if ((rc = initClientList(MAX_CLIENTS)) < 0) {
|
|
_psTrace("Init error opening client list\n");
|
|
goto MATRIX_EXIT;
|
|
}
|
|
|
|
#ifdef USE_HEADER_KEYS
|
|
/*
|
|
In-memory based keys
|
|
Build the CA list first for potential client auth usage
|
|
*/
|
|
CAstreamLen = 0;
|
|
#ifdef USE_RSA
|
|
CAstreamLen += sizeof(RSACAS);
|
|
#ifdef USE_ECC
|
|
CAstreamLen += sizeof(ECDHRSACAS);
|
|
#endif
|
|
#endif
|
|
#ifdef USE_ECC
|
|
CAstreamLen += sizeof(ECCAS);
|
|
#endif
|
|
CAstream = psMalloc(NULL, CAstreamLen);
|
|
|
|
CAstreamLen = 0;
|
|
#ifdef USE_RSA
|
|
memcpy(CAstream, RSACAS, sizeof(RSACAS));
|
|
CAstreamLen += sizeof(RSACAS);
|
|
#ifdef USE_ECC
|
|
memcpy(CAstream + CAstreamLen, ECDHRSACAS, sizeof(ECDHRSACAS));
|
|
CAstreamLen += sizeof(ECDHRSACAS);
|
|
#endif
|
|
#endif
|
|
#ifdef USE_ECC
|
|
memcpy(CAstream + CAstreamLen, ECCAS, sizeof(ECCAS));
|
|
CAstreamLen += sizeof(ECCAS);
|
|
#endif
|
|
|
|
#ifdef EXAMPLE_RSA_KEYS
|
|
switch (g_rsaKeySize) {
|
|
case 1024:
|
|
certValue = (unsigned char *)RSA1024;
|
|
certLen = sizeof(RSA1024);
|
|
keyValue = (unsigned char *)RSA1024KEY;
|
|
keyLen = sizeof(RSA1024KEY);
|
|
break;
|
|
case 2048:
|
|
certValue = (unsigned char *)RSA2048;
|
|
certLen = sizeof(RSA2048);
|
|
keyValue = (unsigned char *)RSA2048KEY;
|
|
keyLen = sizeof(RSA2048KEY);
|
|
break;
|
|
case 3072:
|
|
certValue = (unsigned char *)RSA3072;
|
|
certLen = sizeof(RSA3072);
|
|
keyValue = (unsigned char *)RSA3072KEY;
|
|
keyLen = sizeof(RSA3072KEY);
|
|
break;
|
|
case 4096:
|
|
certValue = (unsigned char *)RSA4096;
|
|
certLen = sizeof(RSA4096);
|
|
keyValue = (unsigned char *)RSA4096KEY;
|
|
keyLen = sizeof(RSA4096KEY);
|
|
break;
|
|
default:
|
|
_psTraceInt("Invalid RSA key length (%d)\n", g_rsaKeySize);
|
|
goto CLIENT_EXIT;
|
|
}
|
|
|
|
if ((rc = matrixSslLoadRsaKeysMem(keys, (const unsigned char *)certValue,
|
|
certLen, (const unsigned char *)keyValue, keyLen, CAstream,
|
|
CAstreamLen)) < 0) {
|
|
_psTrace("No certificate material loaded. Exiting\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef EXAMPLE_ECDH_RSA_KEYS
|
|
switch (g_ecdhKeySize) {
|
|
case 256:
|
|
certValue = (unsigned char *)ECDHRSA256;
|
|
certLen = sizeof(ECDHRSA256);
|
|
keyValue = (unsigned char *)ECDHRSA256KEY;
|
|
keyLen = sizeof(ECDHRSA256KEY);
|
|
break;
|
|
case 521:
|
|
certValue = (unsigned char *)ECDHRSA521;
|
|
certLen = sizeof(ECDHRSA521);
|
|
keyValue = (unsigned char *)ECDHRSA521KEY;
|
|
keyLen = sizeof(ECDHRSA521KEY);
|
|
break;
|
|
default:
|
|
_psTraceInt("Invalid ECDH_RSA key length (%d)\n", g_ecdhKeySize);
|
|
goto CLIENT_EXIT;
|
|
}
|
|
|
|
if ((rc = matrixSslLoadEcKeysMem(keys, (const unsigned char *)certValue,
|
|
certLen, (const unsigned char *)keyValue, keyLen, CAstream,
|
|
CAstreamLen)) < 0) {
|
|
_psTrace("No certificate material loaded. Exiting\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
#endif
|
|
|
|
#ifdef EXAMPLE_EC_KEYS
|
|
switch (g_eccKeySize) {
|
|
case 192:
|
|
certValue = (unsigned char *)EC192;
|
|
certLen = sizeof(EC192);
|
|
keyValue = (unsigned char *)EC192KEY;
|
|
keyLen = sizeof(EC192KEY);
|
|
break;
|
|
case 224:
|
|
certValue = (unsigned char *)EC224;
|
|
certLen = sizeof(EC224);
|
|
keyValue = (unsigned char *)EC224KEY;
|
|
keyLen = sizeof(EC224KEY);
|
|
break;
|
|
case 256:
|
|
certValue = (unsigned char *)EC256;
|
|
certLen = sizeof(EC256);
|
|
keyValue = (unsigned char *)EC256KEY;
|
|
keyLen = sizeof(EC256KEY);
|
|
break;
|
|
case 384:
|
|
certValue = (unsigned char *)EC384;
|
|
certLen = sizeof(EC384);
|
|
keyValue = (unsigned char *)EC384KEY;
|
|
keyLen = sizeof(EC384KEY);
|
|
break;
|
|
case 521:
|
|
certValue = (unsigned char *)EC521;
|
|
certLen = sizeof(EC521);
|
|
keyValue = (unsigned char *)EC521KEY;
|
|
keyLen = sizeof(EC521KEY);
|
|
break;
|
|
default:
|
|
_psTraceInt("Invalid ECC key length (%d)\n", g_eccKeySize);
|
|
goto CLIENT_EXIT;
|
|
}
|
|
|
|
if ((rc = matrixSslLoadEcKeysMem(keys, certValue, certLen,
|
|
keyValue, keyLen, CAstream, CAstreamLen)) < 0) {
|
|
_psTrace("No certificate material loaded. Exiting\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
#endif
|
|
|
|
#ifdef REQUIRE_DH_PARAMS
|
|
if (matrixSslLoadDhParamsMem(keys, DHPARAM2048, DHPARAM2048_SIZE) < 0) {
|
|
_psTrace("Unable to load DH parameters\n");
|
|
}
|
|
#endif /* DH_PARAMS */
|
|
|
|
#else /* USE_HEADER_KEYS */
|
|
/*
|
|
File based keys
|
|
Build the CA list first for potential client auth usage
|
|
*/
|
|
CAstreamLen = 0;
|
|
#ifdef USE_RSA
|
|
if (g_rsaKeySize == 3072) {
|
|
CAstreamLen += (int32)strlen(rsaCA3072File) + 1;
|
|
} else {
|
|
CAstreamLen += (int32)strlen(rsaCAFile) + 1;
|
|
}
|
|
#ifdef USE_ECC
|
|
CAstreamLen += (int32)strlen(ecdhRsaCAFile) + 1;
|
|
#endif
|
|
#endif
|
|
#ifdef USE_ECC
|
|
CAstreamLen += (int32)strlen(ecCAFile) + 1;
|
|
#endif
|
|
CAstream = psMalloc(NULL, CAstreamLen);
|
|
memset(CAstream, 0x0, CAstreamLen);
|
|
|
|
CAstreamLen = 0;
|
|
#ifdef USE_RSA
|
|
if (g_rsaKeySize == 3072) {
|
|
memcpy(CAstream, rsaCA3072File, strlen(rsaCA3072File));
|
|
CAstreamLen += strlen(rsaCA3072File);
|
|
} else {
|
|
memcpy(CAstream, rsaCAFile, strlen(rsaCAFile));
|
|
CAstreamLen += strlen(rsaCAFile);
|
|
}
|
|
#ifdef USE_ECC
|
|
memcpy(CAstream + CAstreamLen, ";", 1); CAstreamLen++;
|
|
memcpy(CAstream + CAstreamLen, ecdhRsaCAFile, strlen(ecdhRsaCAFile));
|
|
CAstreamLen += strlen(ecdhRsaCAFile);
|
|
#endif
|
|
#endif
|
|
#ifdef USE_ECC
|
|
if (CAstreamLen > 0) {
|
|
memcpy(CAstream + CAstreamLen, ";", 1); CAstreamLen++;
|
|
}
|
|
memcpy(CAstream + CAstreamLen, ecCAFile, strlen(ecCAFile));
|
|
#endif
|
|
|
|
/* Load Identiy */
|
|
#ifdef EXAMPLE_RSA_KEYS
|
|
if ((rc = matrixSslLoadRsaKeys(keys, rsaCertFile, rsaPrivkeyFile, NULL,
|
|
(char*)CAstream)) < 0) {
|
|
_psTrace("No certificate material loaded. Exiting\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
#endif
|
|
|
|
#ifdef EXAMPLE_ECDH_RSA_KEYS
|
|
if ((rc = matrixSslLoadEcKeys(keys, ecdhRsaCertFile, ecdhRsaPrivkeyFile,
|
|
NULL, (char*)CAstream)) < 0) {
|
|
_psTrace("No certificate material loaded. Exiting\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
#endif
|
|
|
|
#ifdef EXAMPLE_EC_KEYS
|
|
if ((rc = matrixSslLoadEcKeys(keys, ecCertFile, ecPrivkeyFile, NULL,
|
|
(char*)CAstream)) < 0) {
|
|
_psTrace("No certificate material loaded. Exiting\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
#endif
|
|
|
|
#ifdef REQUIRE_DH_PARAMS
|
|
if (matrixSslLoadDhParams(keys, dhParamFile) < 0) {
|
|
_psTrace("Unable to load DH parameters\n");
|
|
}
|
|
#endif
|
|
|
|
#endif /* USE_HEADER_KEYS */
|
|
|
|
psFree(CAstream, NULL);
|
|
CAstream = NULL;
|
|
|
|
#ifdef USE_PSK_CIPHER_SUITE
|
|
/* The first ID is considered as null-terminiated string for
|
|
compatibility with OpenSSL's s_client default client identity
|
|
"Client_identity" */
|
|
|
|
matrixSslLoadPsk(keys,
|
|
PSK_HEADER_TABLE[0].key,
|
|
sizeof(PSK_HEADER_TABLE[0].key),
|
|
PSK_HEADER_TABLE[0].id,
|
|
strlen((const char *)PSK_HEADER_TABLE[0].id));
|
|
|
|
for (rc = 1; rc < PSK_HEADER_TABLE_COUNT; rc++) {
|
|
matrixSslLoadPsk(keys,
|
|
PSK_HEADER_TABLE[rc].key,
|
|
sizeof(PSK_HEADER_TABLE[rc].key),
|
|
PSK_HEADER_TABLE[rc].id,
|
|
sizeof(PSK_HEADER_TABLE[rc].id));
|
|
}
|
|
#endif /* PSK */
|
|
|
|
recvfromBufLen = matrixDtlsGetPmtu();
|
|
if ((recvfromBuf = psMalloc(MATRIX_NO_POOL, recvfromBufLen)) == NULL) {
|
|
rc = PS_MEM_FAIL;
|
|
_psTrace("Init error allocating receive buffer\n");
|
|
goto CLIENT_EXIT;
|
|
}
|
|
|
|
if ((sock = newUdpSocket(NULL, DTLS_PORT, &err)) == INVALID_SOCKET) {
|
|
_psTrace("Error creating UDP socket\n");
|
|
goto DTLS_EXIT;
|
|
}
|
|
_psTraceInt("DTLS server running on port %d\n", DTLS_PORT);
|
|
|
|
/* Server loop */
|
|
for (exitFlag = 0; exitFlag == 0;) {
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
FD_ZERO(&readfd);
|
|
FD_SET(sock, &readfd);
|
|
/*
|
|
Always just wait a second for any incoming data. The primary loop
|
|
mechanism reads data from one source and replies with handshake data
|
|
if needed (that reply may be a resend if reading a repeat message).
|
|
Individual client timeouts are then handled
|
|
*/
|
|
val = select(sock+1, &readfd, NULL, NULL, &timeout);
|
|
|
|
if (val > 0 && FD_ISSET(sock, &readfd)) {
|
|
psTraceIntDtls("Select woke %d\n", val);
|
|
/* recvfrom data must always go into generic buffer becuase we
|
|
don't yet know who it is from */
|
|
inaddrlen = sizeof(struct sockaddr_in);
|
|
if ((recvLen = (int32)recvfrom(sock, recvfromBuf, recvfromBufLen, 0,
|
|
(struct sockaddr *)&inaddr, &inaddrlen)) < 0) {
|
|
#ifdef WIN32
|
|
if (SOCKET_ERRNO != EWOULDBLOCK &&
|
|
SOCKET_ERRNO != WSAECONNRESET) {
|
|
#else
|
|
if (SOCKET_ERRNO != EWOULDBLOCK) {
|
|
#endif
|
|
_psTraceInt("recvfrom error %d. Exiting\n", SOCKET_ERRNO);
|
|
goto DTLS_EXIT;
|
|
}
|
|
continue;
|
|
}
|
|
#ifdef USE_DTLS_DEBUG_TRACE
|
|
/* nice for debugging */
|
|
{
|
|
const char *addrstr;
|
|
addrstr = getaddrstring((struct sockaddr *)&inaddr, 1);
|
|
psTraceIntDtls("Read %d bytes ", recvLen);
|
|
psTraceStrDtls("from %s\n", (char*)addrstr);
|
|
psFree(addrstr, NULL);
|
|
}
|
|
#endif
|
|
|
|
/* Locate the SSL context of this receive and create a new session
|
|
if not found */
|
|
if ((dtlsCtx = findClient(inaddr)) == NULL) {
|
|
memset(&options, 0x0, sizeof(sslSessOpts_t));
|
|
options.versionFlag = SSL_FLAGS_DTLS;
|
|
options.truncHmac = -1;
|
|
|
|
if (matrixSslNewServerSession(&ssl, keys,
|
|
certValidator, &options) < 0) {
|
|
rc = DTLS_FATAL; goto DTLS_EXIT;
|
|
}
|
|
if ((dtlsCtx = registerClient(inaddr, sock, ssl)) == NULL) {
|
|
/* Client list is full. Just have to ignore */
|
|
matrixSslDeleteSession(ssl);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ssl = dtlsCtx->ssl;
|
|
/* Move socket data into internal buffer */
|
|
freeBufLen = matrixSslGetReadbuf(ssl, &sslBuf);
|
|
psAssert(freeBufLen >= recvLen);
|
|
psAssert(freeBufLen == matrixDtlsGetPmtu());
|
|
memcpy(sslBuf, recvfromBuf, recvLen);
|
|
|
|
/* Notify SSL state machine that we've received more data into the
|
|
ssl buffer retreived with matrixSslGetReadbuf. */
|
|
if ((rcr = matrixSslReceivedData(ssl, recvLen, &sslBuf,
|
|
(uint32*)&freeBufLen)) < 0) {
|
|
clearClient(dtlsCtx);
|
|
continue; /* Next connection */
|
|
}
|
|
/* Update last activity time and reset timeout*/
|
|
psGetTime(&dtlsCtx->lastRecvTime, NULL);
|
|
dtlsCtx->timeout = MIN_WAIT_SECS;
|
|
|
|
PROCESS_MORE_FROM_BUFFER:
|
|
/* Process any incoming plaintext application data */
|
|
switch (rcr) {
|
|
case MATRIXSSL_HANDSHAKE_COMPLETE:
|
|
/* This is a resumed handshake case which means we are
|
|
the last to receive handshake flights and we know the
|
|
handshake is complete. However, the internal workings
|
|
will not flag us officially complete until we receive
|
|
application data from the peer so we need a local flag
|
|
to handle this case so we are not resending our final
|
|
flight */
|
|
dtlsCtx->connStatus = RESUMED_HANDSHAKE_COMPLETE;
|
|
psTraceDtls("Got HANDSHAKE_COMPLETE out of ReceivedData\n");
|
|
break;
|
|
case MATRIXSSL_APP_DATA:
|
|
/* Now safe to clear the connStatus flag that was keeping
|
|
track of the state between receiving the final flight of
|
|
a resumed handshake and receiving application data. The
|
|
reciept of app data has now internally disabled flight
|
|
resends */
|
|
dtlsCtx->connStatus = 0;
|
|
_psTrace("Client connected. Received...\n");
|
|
_psTraceStr("%s\n", (char*)sslBuf);
|
|
break;
|
|
case MATRIXSSL_REQUEST_SEND:
|
|
/* Still handshaking with this particular client */
|
|
while ((sslBufLen = matrixDtlsGetOutdata(ssl,
|
|
&sslBuf)) > 0) {
|
|
if ((sendLen = udpSend(dtlsCtx->fd, sslBuf, sslBufLen,
|
|
(struct sockaddr*)&inaddr,
|
|
sizeof(struct sockaddr_in),
|
|
dtlsCtx->timeout,
|
|
packet_loss_prob,
|
|
NULL)) < 0) {
|
|
psTraceDtls("udpSend error. Ignoring\n");
|
|
}
|
|
/* Always indicate the entire datagram was sent as
|
|
there is no way for DTLS to handle partial records.
|
|
Resends and timeouts will handle any problems */
|
|
rcs = matrixDtlsSentData(ssl, sslBufLen);
|
|
|
|
if (rcs == MATRIXSSL_REQUEST_CLOSE) {
|
|
psTraceDtls("Got REQUEST_CLOSE out of SentData\n");
|
|
clearClient(dtlsCtx);
|
|
break;
|
|
}
|
|
if (rcs == MATRIXSSL_HANDSHAKE_COMPLETE) {
|
|
/* This is the standard handshake case */
|
|
_psTrace("Got HANDSHAKE_COMPLETE from SentData\n");
|
|
break;
|
|
}
|
|
/* SSL_REQUEST_SEND is handled by loop logic */
|
|
}
|
|
break;
|
|
case MATRIXSSL_REQUEST_RECV:
|
|
psTraceDtls("Got REQUEST_RECV from ReceivedData\n");
|
|
break;
|
|
case MATRIXSSL_RECEIVED_ALERT:
|
|
/* The first byte of the buffer is the level */
|
|
/* The second byte is the description */
|
|
if (*sslBuf == SSL_ALERT_LEVEL_FATAL) {
|
|
psTraceIntDtls("Fatal alert: %d, closing connection.\n",
|
|
*(sslBuf + 1));
|
|
clearClient(dtlsCtx);
|
|
continue; /* Next connection */
|
|
}
|
|
/* Closure alert is normal (and best) way to close */
|
|
if (*(sslBuf + 1) == SSL_ALERT_CLOSE_NOTIFY) {
|
|
clearClient(dtlsCtx);
|
|
continue; /* Next connection */
|
|
}
|
|
psTraceIntDtls("Warning alert: %d\n", *(sslBuf + 1));
|
|
if ((rcr = matrixSslProcessedData(ssl, &sslBuf,
|
|
(uint32*)&freeBufLen)) == 0) {
|
|
continue;
|
|
}
|
|
goto PROCESS_MORE_FROM_BUFFER;
|
|
|
|
default:
|
|
continue; /* Next connection */
|
|
}
|
|
} else if (val < 0) {
|
|
if (SOCKET_ERRNO != EINTR) {
|
|
psTraceIntDtls("unhandled error %d from select", SOCKET_ERRNO);
|
|
}
|
|
}
|
|
/*
|
|
Have either timed out waiting for a read or have processed a single
|
|
recv. Now check to see if any timeout resends are required
|
|
*/
|
|
rc = handleResends(sock);
|
|
} /* Main Select Loop */
|
|
|
|
|
|
DTLS_EXIT:
|
|
psFree(recvfromBuf, NULL);
|
|
CLIENT_EXIT:
|
|
if (CAstream) {
|
|
psFree(CAstream, NULL);
|
|
}
|
|
closeClientList();
|
|
MATRIX_EXIT:
|
|
matrixSslDeleteKeys(keys);
|
|
matrixSslClose();
|
|
if (sock != INVALID_SOCKET) close(sock);
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Work through client list and resend handshake flight if haven't heard
|
|
from them in a while
|
|
*/
|
|
static int32 handleResends(SOCKET sock)
|
|
{
|
|
serverDtls_t *dtlsCtx;
|
|
ssl_t *ssl;
|
|
psTime_t now;
|
|
unsigned char *sslBuf;
|
|
int16_t i;
|
|
int32_t sendLen, sslBufLen, rc;
|
|
uint32_t timeout, clientCount;
|
|
|
|
clientCount = 0; /* return code is number of active clients or < 0 on error */
|
|
psGetTime(&now, NULL);
|
|
for (i = 0; i < tableSize; i++) {
|
|
dtlsCtx = &clientTable[i];
|
|
if (dtlsCtx->ssl != NULL) {
|
|
clientCount++;
|
|
timeout = psDiffMsecs(dtlsCtx->lastRecvTime, now, NULL) / 1000;
|
|
/* Haven't heard from this client in a while. Might need resend */
|
|
if (timeout > dtlsCtx->timeout) {
|
|
/* if timeout is too great. clear conn */
|
|
if (dtlsCtx->timeout >= MAX_WAIT_SECS) {
|
|
clearClient(dtlsCtx);
|
|
clientCount--;
|
|
break;
|
|
}
|
|
/* Increase the timeout for next pass */
|
|
dtlsCtx->timeout *= 2;
|
|
|
|
/* If we are in a RESUMED_HANDSHAKE_COMPLETE state that means
|
|
we are positive the handshake is complete so we don't want to
|
|
resend no matter what. This is an interim state before the
|
|
internal mechaism sees an application data record and flags
|
|
us as complete officially */
|
|
if (dtlsCtx->connStatus == RESUMED_HANDSHAKE_COMPLETE) {
|
|
psTraceDtls("Connected but awaiting data\n");
|
|
continue;
|
|
}
|
|
ssl = dtlsCtx->ssl;
|
|
while ((sslBufLen = matrixDtlsGetOutdata(ssl,
|
|
&sslBuf)) > 0) {
|
|
if ((sendLen = udpSend(dtlsCtx->fd, sslBuf, sslBufLen,
|
|
(struct sockaddr*)&dtlsCtx->addr,
|
|
sizeof(struct sockaddr_in),
|
|
dtlsCtx->timeout / 2,
|
|
packet_loss_prob,
|
|
NULL)) < 0) {
|
|
psTraceDtls("udpSend error. Ignoring\n");
|
|
}
|
|
/* Always indicate the entire datagram was sent as
|
|
there is no way for DTLS to handle partial records.
|
|
Resends and timeouts will handle any problems */
|
|
if ((rc = matrixDtlsSentData(ssl, sslBufLen)) < 0) {
|
|
psTraceDtls("internal error\n");
|
|
clearClient(dtlsCtx);
|
|
clientCount--;
|
|
break;
|
|
}
|
|
if (rc == MATRIXSSL_REQUEST_CLOSE) {
|
|
psTraceDtls("Got REQUEST_CLOSE out of SentData\n");
|
|
clearClient(dtlsCtx);
|
|
clientCount--;
|
|
break;
|
|
}
|
|
if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) {
|
|
/* This is the standard handshake case */
|
|
psTraceDtls("Got HANDSHAKE_COMPLETE out of SentData\n");
|
|
break;
|
|
}
|
|
/* SSL_REQUEST_SEND is handled by loop logic */
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return clientCount;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Make sure the socket is not inherited by exec'd processes
|
|
Set the REUSE flag to minimize the number of sockets in TIME_WAIT
|
|
Then we set REUSEADDR, NODELAY and NONBLOCK on the socket
|
|
*/
|
|
static int32_t setSocketOptions(SOCKET fd)
|
|
{
|
|
int32 rc;
|
|
|
|
#ifdef POSIX
|
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
|
|
return PS_PLATFORM_FAIL;
|
|
}
|
|
#endif
|
|
rc = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)) < 0) {
|
|
return PS_PLATFORM_FAIL;
|
|
}
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
|
|
static SOCKET newUdpSocket(char *ip, short port, int *err)
|
|
{
|
|
struct sockaddr_in addr = { 0 };
|
|
SOCKET fd;
|
|
|
|
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
|
_psTraceInt("Error creating socket %d\n", SOCKET_ERRNO);
|
|
*err = SOCKET_ERRNO;
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
if (setSocketOptions(fd) < 0) {
|
|
*err = SOCKET_ERRNO;
|
|
close(fd);
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
if (ip == NULL) {
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
_psTrace("Can't bind socket. Port in use or permission problem\n");
|
|
*err = SOCKET_ERRNO;
|
|
close(fd);
|
|
return INVALID_SOCKET;
|
|
}
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/* catch any segvs */
|
|
static void sigsegv_handler(int arg)
|
|
{
|
|
_psTrace("Aiee, segfault! You should probably report "
|
|
"this as a bug to the developer\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* catch ctrl-c or sigterm */
|
|
static void sigintterm_handler(int arg)
|
|
{
|
|
exitFlag = 1; /* Rudimentary exit flagging */
|
|
}
|
|
|
|
static int sigsetup(void)
|
|
{
|
|
/* set up cleanup handler */
|
|
if (signal(SIGINT, sigintterm_handler) == SIG_ERR ||
|
|
#ifndef DEBUG_VALGRIND
|
|
signal(SIGTERM, sigintterm_handler) == SIG_ERR ||
|
|
#endif
|
|
signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
|
return -1;
|
|
}
|
|
if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_DTLS_DEBUG_TRACE
|
|
/******************************************************************************/
|
|
/* Return a string representation of the socket address passed. The return
|
|
* value is allocated with malloc() */
|
|
static unsigned char * getaddrstring(struct sockaddr *addr,
|
|
int withport) {
|
|
|
|
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
|
char *retstring = NULL;
|
|
int ret;
|
|
unsigned int len;
|
|
|
|
len = sizeof(struct sockaddr_storage);
|
|
/* Some platforms such as Solaris 8 require that len is the length
|
|
* of the specific structure. Some older linux systems (glibc 2.1.3
|
|
* such as debian potato) have sockaddr_storage.__ss_family instead
|
|
* but we'll ignore them */
|
|
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
|
|
if (addr->ss_family == AF_INET) {
|
|
len = sizeof(struct sockaddr_in);
|
|
}
|
|
#ifdef AF_INET6
|
|
if (addr->ss_family == AF_INET6) {
|
|
len = sizeof(struct sockaddr_in6);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
|
sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
|
|
|
|
if (ret != 0) {
|
|
/* This is a fairly bad failure - it'll fallback to IP if it
|
|
* just can't resolve */
|
|
_psTrace("failed lookup for getaddrstring");
|
|
strcpy(hbuf, "UNKNOWN");
|
|
strcpy(sbuf, "?");
|
|
}
|
|
|
|
if (withport) {
|
|
len = strlen(hbuf) + 2 + strlen(sbuf);
|
|
retstring = (char*)psMalloc(MATRIX_NO_POOL, len);
|
|
snprintf(retstring, len, "%s:%s", hbuf, sbuf);
|
|
} else {
|
|
retstring = strdup(hbuf);
|
|
}
|
|
|
|
return (unsigned char *)retstring;
|
|
}
|
|
#endif /* USE_DTLS_DEBUG_TRACE */
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Client management
|
|
*/
|
|
/******************************************************************************/
|
|
static int32 initClientList(uint16 maxPeers)
|
|
{
|
|
clientTable = psCalloc(NULL, maxPeers, sizeof(serverDtls_t));
|
|
if (clientTable == NULL) {
|
|
return PS_MEM_FAIL;
|
|
}
|
|
tableSize = maxPeers;
|
|
|
|
udpInitProxy();
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Associates a new sockaddr with an existing ssl context and returns context
|
|
*/
|
|
serverDtls_t *registerClient(struct sockaddr_in addr, SOCKET sock,
|
|
ssl_t *ssl)
|
|
{
|
|
int16 i;
|
|
serverDtls_t *dtlsCtx;
|
|
|
|
for (i = 0; i < tableSize && clientTable[i].ssl != NULL &&
|
|
(clientTable[i].addr.sin_addr.s_addr != addr.sin_addr.s_addr ||
|
|
clientTable[i].addr.sin_port != addr.sin_port); i++);
|
|
if (i >= tableSize) {
|
|
/* no available slots */
|
|
return NULL;
|
|
}
|
|
|
|
dtlsCtx = &clientTable[i];
|
|
|
|
dtlsCtx->addr = addr;
|
|
dtlsCtx->fd = sock;
|
|
dtlsCtx->timeout = MIN_WAIT_SECS;
|
|
dtlsCtx->connStatus = 0;
|
|
psGetTime(&dtlsCtx->lastRecvTime, NULL);
|
|
dtlsCtx->ssl = ssl;
|
|
return dtlsCtx;
|
|
}
|
|
|
|
static void clearClient(serverDtls_t *dtls)
|
|
{
|
|
ssl_t *ssl;
|
|
unsigned char *buf;
|
|
int32 len;
|
|
|
|
ssl = dtls->ssl;
|
|
|
|
/* Quick attempt to send a closure alert, don't worry about failure */
|
|
if (matrixSslEncodeClosureAlert(ssl) >= 0) {
|
|
if ((len = matrixDtlsGetOutdata(ssl, &buf)) > 0) {
|
|
if (sendto(dtls->fd, buf, len, 0, (struct sockaddr*)&dtls->addr,
|
|
sizeof(struct sockaddr_in)) >= 0) {
|
|
matrixDtlsSentData(ssl, len);
|
|
}
|
|
}
|
|
}
|
|
matrixSslDeleteSession(ssl);
|
|
|
|
/*
|
|
Free up the entry in the client table
|
|
*/
|
|
dtls->ssl = NULL;
|
|
dtls->connStatus = 0;
|
|
dtls->fd = 0;
|
|
memset(&dtls->addr, 0x0, sizeof(struct sockaddr_in));
|
|
return;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Return the ssl_t given the sockaddr or NULL if doesn't exist
|
|
*/
|
|
serverDtls_t *findClient(struct sockaddr_in addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < tableSize; i++) {
|
|
if (clientTable[i].ssl != NULL) {
|
|
if (clientTable[i].addr.sin_addr.s_addr == addr.sin_addr.s_addr &&
|
|
clientTable[i].addr.sin_port == addr.sin_port) {
|
|
return &clientTable[i];
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void closeClientList()
|
|
{
|
|
int i;
|
|
/* Free any leftover clients */
|
|
for (i = 0; i < tableSize && clientTable[i].ssl != NULL; i++) {
|
|
matrixSslDeleteSession(clientTable[i].ssl);
|
|
}
|
|
psFree(clientTable, NULL);
|
|
tableSize = 0;
|
|
}
|
|
|
|
#else
|
|
/******************************************************************************/
|
|
/*
|
|
Stub main for compiling without dtls enabled
|
|
*/
|
|
int32 main(int32 argc, char **argv)
|
|
{
|
|
printf("USE_DTLS must be enabled in " \
|
|
"matrixsslConfig.h at build time to run this application\n");
|
|
return -1;
|
|
}
|
|
#endif /* USE_DTLS */
|
|
|
|
/******************************************************************************/
|