static void revertToDefaultBufsize(ssl_t *ssl, uint16 inOrOut) { int32 defaultSize; unsigned char *p; if (inOrOut == SSL_INBUF) { #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { defaultSize = matrixDtlsGetPmtu(); } else { defaultSize = SSL_DEFAULT_IN_BUF_SIZE; } #else defaultSize = SSL_DEFAULT_IN_BUF_SIZE; #endif if (ssl->insize > defaultSize && ssl->inlen < defaultSize) { /* It's not fatal if we can't realloc it smaller */ if ((p = psRealloc(ssl->inbuf, defaultSize, ssl->bufferPool)) != NULL) { ssl->inbuf = p; ssl->insize = defaultSize; } } } else { #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { defaultSize = matrixDtlsGetPmtu(); } else { defaultSize = SSL_DEFAULT_OUT_BUF_SIZE; } #else defaultSize = SSL_DEFAULT_OUT_BUF_SIZE; #endif if (ssl->outsize > defaultSize && ssl->outlen < defaultSize) { /* It's not fatal if we can't realloc it smaller */ if ((p = psRealloc(ssl->outbuf, defaultSize, ssl->bufferPool)) != NULL) { ssl->outbuf = p; ssl->outsize = defaultSize; } } } }
/* This public API allows the user to encrypt the plaintext buffer of their choice into the internal outbuf that is retrieved when matrixSslGetOutdata is called. This is non-in-situ support and will leave the callers plaintext buffer intact ptBuf The plaintext buffer to be converted into an SSL application data record. len The length, in bytes, of the ptBuf plaintext data Returns < 0 on error, total #bytes in outgoing data buf on success */ int32 matrixSslEncodeToOutdata(ssl_t *ssl, unsigned char *ptBuf, uint32 len) { unsigned char *internalBuf; int32 rc, fragLen, recLen, index; if (!ssl || !ptBuf) { return PS_ARG_FAIL; } if (ssl->bFlags & BFLAG_CLOSE_AFTER_SENT) { return PS_PROTOCOL_FAIL; } #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { rc = matrixSslGetEncodedSize(ssl, len); if (rc > matrixDtlsGetPmtu()) { return PS_LIMIT_FAIL; } } #endif /* Fragmentation support */ index = 0; while (len > 0) { /* We just call matrixSslGetWritebuf to prepare the buffer */ if ((rc = matrixSslGetWritebuf(ssl, &internalBuf, len)) < 0) { psTraceIntInfo("matrixSslEncodeToOutbuf allocation error: %d\n", rc); return rc; } recLen = fragLen = min((uint32)rc, len); psAssert(ssl->outsize > 0 && ssl->outbuf != NULL); if (ssl->outbuf == NULL || (ssl->outsize - ssl->outlen) < (int32)fragLen) { return PS_FAILURE; } internalBuf = ssl->outbuf + ssl->outlen; rc = matrixSslEncode(ssl, internalBuf, (ssl->outsize - ssl->outlen), ptBuf + index, (uint32*)&fragLen); if (rc < 0) { psAssert(rc != SSL_FULL); /* should not happen */ return PS_FAILURE; } index += recLen; len -= recLen; #ifdef USE_MATRIXSSL_STATS matrixsslUpdateStat(ssl, APP_DATA_SENT_STAT, fragLen); #endif ssl->outlen += fragLen; } return ssl->outlen; }
/* Caller is asking for an allocated buffer to write plaintext into. That plaintext will then be encoded when the caller subsequently calls matrixSslEncodeWritebuf() This is also explicitly called by matrixSslEncodeToOutdata ssl SSL session context buf The data storage to write into will be populated here on success requestedLen The amount of buffer space the caller would like to use Return > 0, success returns # bytes available for plaintext at buf PS_MEM_FAIL if requiredLen too large for current memory <= 0 on error */ int32 matrixSslGetWritebuf(ssl_t *ssl, unsigned char **buf, uint32 requestedLen) { uint32 requiredLen, sz, overhead; #ifdef USE_DTLS int32 pmtu; #endif unsigned char *p; if (!ssl || !buf) { return PS_ARG_FAIL; } psAssert(ssl->outsize > 0 && ssl->outbuf != NULL); #ifdef USE_BEAST_WORKAROUND /* This is a client-only feature */ if (!(ssl->flags & SSL_FLAGS_SERVER)) { /* Not a problem at all beginning in TLS 1.1 (version 3.2) and never a problem on stream ciphers */ if ((ssl->majVer == SSL3_MAJ_VER) && (ssl->minVer <= TLS_MIN_VER) && (ssl->enBlockSize > 1) && (requestedLen > 1) && !(ssl->bFlags & BFLAG_STOP_BEAST)) { ssl->bFlags |= BFLAG_STOP_BEAST; } } #endif /* First thing is to ensure under the maximum allowed plaintext len according to the SSL specification (or the negotiated max). If not, set it to the max for the calculations and make sure that exact max is returned to the caller. The responsibilty for fragmenting the message is left to them */ if (requestedLen > (uint32)ssl->maxPtFrag) { requestedLen = ssl->maxPtFrag; } /* What is the total encoded size for a plaintext requestedLen. The overhead includes leading header as well as trailing MAC and pad We want to tweak the overhead an extra block to account for a padding miscalculation in matrixSslGetEncodedSize. If that call was made on an exact-sized message and the user decides to use a different record size than requested, we'll need to make sure there is enough available room for any potential padding length. */ requiredLen = matrixSslGetEncodedSize(ssl, requestedLen + ssl->enBlockSize); psAssert(requiredLen >= requestedLen); overhead = requiredLen - requestedLen; #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { pmtu = matrixDtlsGetPmtu(); if (requiredLen > (uint32)pmtu) { overhead = matrixSslGetEncodedSize(ssl, 0) + ssl->enBlockSize; requiredLen = matrixSslGetEncodedSize(ssl, pmtu - overhead); } } #endif /* Get current available space in outbuf */ if (ssl->outsize < ssl->outlen) { return PS_FAILURE; } sz = ssl->outsize - ssl->outlen; /* If not enough free space for requiredLen, grow the buffer */ if (sz < requiredLen) { if ((p = psRealloc(ssl->outbuf, ssl->outsize + (requiredLen - sz), ssl->bufferPool)) == NULL) { return PS_MEM_FAIL; } ssl->outbuf = p; ssl->outsize = ssl->outsize + (requiredLen - sz); /* Recalculate available free space */ if (ssl->outsize < ssl->outlen) { return PS_FAILURE; } sz = ssl->outsize - ssl->outlen; } /* Now that requiredLen has been confirmed/created, return number of available plaintext bytes */ if (requestedLen < (uint32)ssl->maxPtFrag) { requestedLen = sz - overhead; if (requestedLen > (uint32)ssl->maxPtFrag) { requestedLen = ssl->maxPtFrag; } } /* Now return the pointer that has skipped past the record header */ #ifdef USE_TLS_1_1 /* If a block cipher is being used TLS 1.1 requires the use of an explicit IV. This is an extra random block of data prepended to the plaintext before encryption. Account for that extra length here. */ if ((ssl->flags & SSL_FLAGS_WRITE_SECURE) && (ssl->flags & SSL_FLAGS_TLS_1_1) && (ssl->enBlockSize > 1)) { *buf = ssl->outbuf + ssl->outlen + ssl->recordHeadLen + ssl->enBlockSize; return requestedLen; /* may not be what was passed in */ } /* GCM mode will need to save room for the nonce */ if (ssl->flags & SSL_FLAGS_AEAD_W) { *buf = ssl->outbuf + ssl->outlen + ssl->recordHeadLen + AEAD_NONCE_LEN(ssl); return requestedLen; /* may not be what was passed in */ } #endif /* USE_TLS_1_1 */ #ifdef USE_BEAST_WORKAROUND if (ssl->bFlags & BFLAG_STOP_BEAST) { /* The reserved space accounts for a full encoding of a 1 byte record. The final -1 is so that when the second encrypt arrives it will land as an in-situ */ overhead = ((ssl->enMacSize + 1) % ssl->enBlockSize) ? ssl->enBlockSize : 0; *buf = ssl->outbuf + ssl->outlen + (2 * ssl->recordHeadLen) + overhead + (ssl->enBlockSize * ((ssl->enMacSize + 1)/ssl->enBlockSize)) - 1; } else { *buf = ssl->outbuf + ssl->outlen + ssl->recordHeadLen; } #else *buf = ssl->outbuf + ssl->outlen + ssl->recordHeadLen; #endif return requestedLen; /* may not be what was passed in */ }
/* 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; sock = INVALID_SOCKET; /* parse input arguments */ 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; } recvfromBufLen = matrixDtlsGetPmtu(); if ((recvfromBuf = psMalloc(MATRIX_NO_POOL, recvfromBufLen)) == NULL) { rc = PS_MEM_FAIL; _psTrace("Init error allocating receive buffer\n"); goto CLIENT_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); return -1; } if ((rc = matrixSslLoadRsaKeysMem(keys, (const unsigned char *)certValue, certLen, (const unsigned char *)keyValue, keyLen, CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #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); return -1; } if ((rc = matrixSslLoadEcKeysMem(keys, (const unsigned char *)certValue, certLen, (const unsigned char *)keyValue, keyLen, CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #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); return -1; } if ((rc = matrixSslLoadEcKeysMem(keys, certValue, certLen, keyValue, keyLen, CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef REQUIRE_DH_PARAMS if (matrixSslLoadDhParamsMem(keys, DHPARAM2048, DHPARAM2048_SIZE) < 0) { _psTrace("Unable to load DH parameters\n"); } #endif /* DH_PARAMS */ psFree(CAstream, NULL); #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"); psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_ECDH_RSA_KEYS if ((rc = matrixSslLoadEcKeys(keys, ecdhRsaCertFile, ecdhRsaPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_EC_KEYS if ((rc = matrixSslLoadEcKeys(keys, ecCertFile, ecPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef REQUIRE_DH_PARAMS if (matrixSslLoadDhParams(keys, dhParamFile) < 0) { _psTrace("Unable to load DH parameters\n"); } #endif psFree(CAstream); #endif /* USE_HEADER_KEYS */ #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 */ 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: 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 i; int32 sendLen, rc; uint32 timeout, sslBufLen, 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; }