/* Plaintext data has been processed as a response to MATRIXSSL_APP_DATA or MATRIXSSL_RECEIVED_ALERT return codes from matrixSslReceivedData() Return: < 0 on error 0 if there is no more incoming ssl data in the buffer Caller should take whatever action is appropriate to the specific protocol implementation, eg. read for more data, close, etc. > 0 error code is same meaning as from matrixSslReceivedData() In this case, ptbuf and ptlen will be modified and caller should handle return code identically as from matrixSslReceivedData() This is the case when more than one SSL record is in the buffer */ int32 matrixSslProcessedData(ssl_t *ssl, unsigned char **ptbuf, uint32 *ptlen) { uint32 ctlen; if (!ssl || !ptbuf || !ptlen) { return PS_ARG_FAIL; } *ptbuf = NULL; *ptlen = 0; psAssert(ssl->insize > 0 && ssl->inbuf != NULL); /* Move any remaining data to the beginning of the buffer */ if (ssl->inlen > 0) { ctlen = ssl->rec.len + ssl->recordHeadLen; #if defined(USE_TLS_1_2) && defined(USE_AES_GCM) if (ssl->flags & SSL_FLAGS_GMAC_R) { /* This overhead was removed from rec.len after the decryption to keep buffer logic working. */ ctlen += TLS_GCM_TAG_LEN + ssl->nonceCtrLen; } #endif memmove(ssl->inbuf, ssl->inbuf + ctlen, ssl->inlen); } /* Shrink inbuf to default size once inlen < default size */ revertToDefaultBufsize(ssl, SSL_INBUF); /* If there's more data, try to decode it here and return that code */ if (ssl->inlen > 0) { /* NOTE: ReceivedData cannot return 0 */ return matrixSslReceivedData(ssl, 0, ptbuf, ptlen); } return MATRIXSSL_SUCCESS; }
/* Plaintext data has been processed as a response to MATRIXSSL_APP_DATA or MATRIXSSL_RECEIVED_ALERT return codes from matrixSslReceivedData() Return: < 0 on error 0 if there is no more incoming ssl data in the buffer Caller should take whatever action is appropriate to the specific protocol implementation, eg. read for more data, close, etc. > 0 error code is same meaning as from matrixSslReceivedData() In this case, ptbuf and ptlen will be modified and caller should handle return code identically as from matrixSslReceivedData() This is the case when more than one SSL record is in the buffer */ int32 matrixSslProcessedData(ssl_t *ssl, unsigned char **ptbuf, uint32 *ptlen) { uint32 ctlen; if (!ssl || !ptbuf || !ptlen) { return PS_ARG_FAIL; } *ptbuf = NULL; *ptlen = 0; psAssert(ssl->insize > 0 && ssl->inbuf != NULL); /* Move any remaining data to the beginning of the buffer */ if (ssl->inlen > 0) { ctlen = ssl->rec.len + ssl->recordHeadLen; if (ssl->flags & SSL_FLAGS_AEAD_R) { /* This overhead was removed from rec.len after the decryption to keep buffer logic working. */ /* TODO: This is for checking async or not. If async, this length tweak never happens. Need a more generic way to look for blocking or not */ ctlen += AEAD_TAG_LEN(ssl) + AEAD_NONCE_LEN(ssl); } memmove(ssl->inbuf, ssl->inbuf + ctlen, ssl->inlen); } /* Shrink inbuf to default size once inlen < default size */ revertToDefaultBufsize(ssl, SSL_INBUF); /* If there's more data, try to decode it here and return that code */ if (ssl->inlen > 0) { /* NOTE: ReceivedData cannot return 0 */ return matrixSslReceivedData(ssl, 0, ptbuf, ptlen); } return MATRIXSSL_SUCCESS; }
CAMLprim value stub_received_data(value ssl, value str) { CAMLparam2(ssl,str); CAMLlocal1(ret); int str_len = caml_string_length(str); unsigned char *buf=NULL; unsigned int len, to_cpy; int rc; len=matrixSslGetReadbuf(ssl_t_val(ssl), &buf); to_cpy = (len<str_len) ? len : str_len; memcpy(buf,String_val(str),len); rc=matrixSslReceivedData(ssl_t_val(ssl), to_cpy, &buf, &len); if(rc<0) { caml_failwith("Received Data"); } CAMLreturn(get_return_code(rc,buf,len)); }
/* Non-blocking socket event handler Wait one time in select for events on any socket This will accept new connections, read and write to sockets that are connected, and close sockets as required. */ static int32 selectLoop(sslKeys_t *keys, SOCKET lfd) { httpConn_t *cp; psTime_t now; DLListEntry connsTmp; DLListEntry *pList; fd_set readfd, writefd; struct timeval timeout; SOCKET fd, maxfd; unsigned char *buf; int32 rc, len, transferred, val; unsigned char rSanity, wSanity, acceptSanity; DLListInit(&connsTmp); rc = PS_SUCCESS; maxfd = INVALID_SOCKET; timeout.tv_sec = SELECT_TIME / 1000; timeout.tv_usec = (SELECT_TIME % 1000) * 1000; FD_ZERO(&readfd); FD_ZERO(&writefd); /* Always set readfd for listening socket */ FD_SET(lfd, &readfd); if (lfd > maxfd) { maxfd = lfd; } /* Check timeouts and set readfd and writefd for connections as required. We use connsTemp so that removal on error from the active iteration list doesn't interfere with list traversal */ psGetTime(&now); while (!DLListIsEmpty(&g_conns)) { pList = DLListGetHead(&g_conns); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&connsTmp, &cp->List); /* If timeout != 0 msec ith no new data, close */ if (cp->timeout && (psDiffMsecs(cp->time, now) > (int32)cp->timeout)) { closeConn(cp, PS_TIMEOUT_FAIL); continue; /* Next connection */ } /* Always select for read */ FD_SET(cp->fd, &readfd); /* Select for write if there's pending write data or connection */ if (matrixSslGetOutdata(cp->ssl, NULL) > 0) { FD_SET(cp->fd, &writefd); } /* Housekeeping for maxsock in select call */ if (cp->fd > maxfd) { maxfd = cp->fd; } } /* Use select to check for events on the sockets */ if ((val = select(maxfd + 1, &readfd, &writefd, NULL, &timeout)) <= 0) { /* On error, restore global connections list */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); } /* Select timeout */ if (val == 0) { return PS_TIMEOUT_FAIL; } /* Woke due to interrupt */ if (SOCKET_ERRNO == EINTR) { return PS_TIMEOUT_FAIL; } /* Should attempt to handle more errnos, such as EBADF */ return PS_PLATFORM_FAIL; } /* Check listener for new incoming socket connections */ if (FD_ISSET(lfd, &readfd)) { for (acceptSanity = 0; acceptSanity < ACCEPT_QUEUE; acceptSanity++) { fd = accept(lfd, NULL, NULL); if (fd == INVALID_SOCKET) { break; /* Nothing more to accept; next listener */ } setSocketOptions(fd); cp = malloc(sizeof(httpConn_t)); if ((rc = matrixSslNewServerSession(&cp->ssl, keys, certCb)) < 0) { close(fd); fd = INVALID_SOCKET; continue; } cp->fd = fd; fd = INVALID_SOCKET; cp->timeout = SSL_TIMEOUT; psGetTime(&cp->time); cp->parsebuf = NULL; cp->parsebuflen = 0; DLListInsertTail(&connsTmp, &cp->List); /* Fake that there is read data available, no harm if there isn't */ FD_SET(cp->fd, &readfd); /* _psTraceInt("=== New Client %d ===\n", cp->fd); */ } } /* Check each connection for read/write activity */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); rSanity = wSanity = 0; /* See if there's pending data to send on this connection We could use FD_ISSET, but this is more reliable for the current state of data to send. */ WRITE_MORE: if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) { /* Could get a EWOULDBLOCK since we don't check FD_ISSET */ transferred = send(cp->fd, buf, len, MSG_DONTWAIT); if (transferred <= 0) { #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); continue; /* Next connection */ } } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if (rc == MATRIXSSL_REQUEST_CLOSE) { closeConn(cp, MATRIXSSL_REQUEST_CLOSE); continue; /* Next connection */ } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If the protocol is server initiated, send data here */ #ifdef ENABLE_FALSE_START /* OR this could be a Chrome browser using FALSE_START and the application data is already waiting in our inbuf for processing */ if ((rc = matrixSslReceivedData(cp->ssl, 0, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } if (rc > 0) { /* There was leftover data */ goto PROCESS_MORE; } #endif /* ENABLE_FALSE_START */ } /* Update activity time */ psGetTime(&cp->time); /* Try to send again if more data to send */ if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) { if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; } } } else if (len < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } /* Check the file descriptor returned from select to see if the connection has data to be read */ if (FD_ISSET(cp->fd, &readfd)) { READ_MORE: /* Get the ssl buffer and how much data it can accept */ /* Note 0 is a return failure, unlike with matrixSslGetOutdata */ if ((len = matrixSslGetReadbuf(cp->ssl, &buf)) <= 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if ((transferred = recv(cp->fd, buf, len, MSG_DONTWAIT)) < 0) { /* We could get EWOULDBLOCK despite the FD_ISSET on goto */ #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); } continue; /* Next connection */ } /* If EOF, remote socket closed. This is semi-normal closure. Officially, we should close on closure alert. */ if (transferred == 0) { /* psTraceIntInfo("Closing connection %d on EOF\n", cp->fd); */ closeConn(cp, 0); continue; /* Next connection */ } /* Notify SSL state machine that we've received more data into the ssl buffer retreived with matrixSslGetReadbuf. */ if ((rc = matrixSslReceivedData(cp->ssl, (int32)transferred, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } /* Update activity time */ psGetTime(&cp->time); PROCESS_MORE: /* Process any incoming plaintext application data */ switch (rc) { case MATRIXSSL_HANDSHAKE_COMPLETE: /* If the protocol is server initiated, send data here */ goto READ_MORE; case MATRIXSSL_APP_DATA: /* Remember, must handle if len == 0! */ if ((rc = httpBasicParse(cp, buf, len)) < 0) { _psTrace("Couldn't parse HTTP data. Closing conn.\n"); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } if (rc == HTTPS_COMPLETE) { if (httpWriteResponse(cp->ssl) < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* For HTTP, we assume no pipelined requests, so we close after parsing a single HTTP request */ /* Ignore return of closure alert, it's optional */ matrixSslEncodeClosureAlert(cp->ssl); rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len); if (rc > 0) { /* Additional data is available, but we ignore it */ _psTrace("HTTP data parsing not supported, ignoring.\n"); closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } else if (rc < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* rc == 0, write out our response and closure alert */ goto WRITE_MORE; } /* We processed a partial HTTP message */ if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { goto READ_MORE; } goto PROCESS_MORE; case MATRIXSSL_REQUEST_SEND: /* Prevent us from reading again after the write, although that wouldn't be the end of the world */ FD_CLR(cp->fd, &readfd); if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; break; case MATRIXSSL_REQUEST_RECV: if (rSanity++ < GOTO_SANITY) goto READ_MORE; break; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*buf == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Fatal alert: %d, closing connection.\n", *(buf + 1)); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } psTraceIntInfo("Warning alert: %d\n", *(buf + 1)); if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { /* No more data in buffer. Might as well read for more. */ goto READ_MORE; } goto PROCESS_MORE; default: /* If rc <= 0 we fall here */ closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Always try to read more if we processed some data */ if (rSanity++ < GOTO_SANITY) goto READ_MORE; } /* readfd handling */ } /* connection loop */ return PS_SUCCESS; } /******************************************************************************/ /* Create an HTTP response and encode it to the SSL buffer */ #define TEST_SIZE 16000 static int32 httpWriteResponse(ssl_t *cp) { unsigned char *buf; int32 available; if ((available = matrixSslGetWritebuf(cp, &buf, strlen((char *)g_httpResponseHdr) + 1)) < 0) { return PS_MEM_FAIL; } strncpy((char *)buf, (char *)g_httpResponseHdr, available); if (matrixSslEncodeWritebuf(cp, strlen((char *)buf)) < 0) { return PS_MEM_FAIL; } return MATRIXSSL_REQUEST_SEND; } /******************************************************************************/ /* Main non-blocking SSL server Initialize MatrixSSL and sockets layer, and loop on select */ int32 main(int32 argc, char **argv) { sslKeys_t *keys; SOCKET lfd; int32 err, rc; #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif keys = NULL; DLListInit(&g_conns); g_exitFlag = 0; lfd = INVALID_SOCKET; #ifdef POSIX if (sighandlers() < 0) { return PS_PLATFORM_FAIL; } #endif /* POSIX */ if ((rc = matrixSslOpen()) < 0) { _psTrace("MatrixSSL library init failure. Exiting\n"); return rc; } if (matrixSslNewKeys(&keys) < 0) { _psTrace("MatrixSSL library key init failure. Exiting\n"); return -1; } #ifdef USE_HEADER_KEYS /* In-memory based keys */ if ((rc = matrixSslLoadRsaKeysMem(keys, certSrvBuf, sizeof(certSrvBuf), privkeySrvBuf, sizeof(privkeySrvBuf), NULL, 0)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #else /* USE_HEADER_KEYS */ /* File based keys */ if ((rc = matrixSslLoadRsaKeys(keys, certSrvFile, privkeySrvFile, NULL, NULL)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif /* USE_HEADER_KEYS */ /* Create the listening socket that will accept incoming connections */ if ((lfd = socketListen(HTTPS_PORT, &err)) == INVALID_SOCKET) { _psTraceInt("Can't listen on port %d\n", HTTPS_PORT); goto L_EXIT; } /* Main select loop to handle sockets events */ while (!g_exitFlag) { selectLoop(keys, lfd); } L_EXIT: if (lfd != INVALID_SOCKET) close(lfd); if (keys) matrixSslDeleteKeys(keys); matrixSslClose(); return 0; }
/* Primary MatrixSSL read function that transparently handles SSL handshakes and the subsequent incoming application data records. A NULL inbuf parameter is an indication that this is a call to perform a new SSL handshake with an incoming client and no out data is expected Params: inbuf allocated storage for plaintext data to be copied to inlen length of inbuf Return codes: -1 EOF or internal failure. caller should free sess and close socket 0 success status. no data is being returned to the caller >0 success status. number of plaintext bytes written to inbuf Note that unlike a standard "socket read", a read of an SSL record can produce data that must be written, for example, a response to a handshake message that must be sent before any more data is read. Also, data can be read from the network such as an SSL alert, that produces no data to pass back to caller. Because webs doesn't have the concept of a read forcing a write, we do the write here, inline. If we are non-blocking, this presents an issue because we can't block indefinitely on a send, and we also can't indicate that the send be done later. The workaround below uses select() to implement a "timed send", which will fail and indicate the connection be closed if not complete within a time threshold. This situation is extremely unlikely in normal operation, since the only records that must be sent as a result of a recv are handshake messages, and TCP buffers would not typically be full enough at that point to result in an EWOULDBLOCK on send. Conceivably, it could occur on a client initiated SSl re-handshake that is sent by a slow-reading client to a server with full TCP buffers. A non-malicious client in this situation would read immediately after a re-handshake request and fail anyway because a buffered appdata record would be read. */ int sslRead(sslConn_t *cp, char *inbuf, int inlen) { unsigned char *buf; int rc, len, transferred; /* Always first look to see if any plaintext application data is waiting We have two levels of buffer here, one for decoded data and one for partial, still encoded SSL records. The partial SSL records are stored transparently in MatrixSSL, but the plaintext is stored in 'cp'. */ if (inbuf != NULL && inlen > 0 && cp->ptBytes > 0 && cp->pt) { if (cp->ptBytes < inlen) { inlen = cp->ptBytes; } memcpy(inbuf, cp->currPt, inlen); cp->currPt += inlen; cp->ptBytes -= inlen; /* Free buffer as we go if empty */ if (cp->ptBytes == 0) { bfree(B_L, cp->pt); cp->pt = cp->currPt = NULL; } return inlen; } /* If there is outgoing data buffered, just try to write it here before doing our read on the socket. Because the read could produce data to be written, this will ensure we have as much room as possible in that case for the written record. Note that there may be data here to send because of a previous sslWrite that got EWOULDBLOCK. */ WRITE_MORE: if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) { transferred = send(cp->fd, buf, len, MSG_NOSIGNAL); if (transferred <= 0) { if (socketGetError() != EWOULDBLOCK) { return -1; } if (waitForWriteEvent(cp->fd, MAX_WRITE_MSEC) == 0) { goto WRITE_MORE; } return -1; } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) { return -1; } if (rc == MATRIXSSL_REQUEST_CLOSE) { return -1; } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If called via sslAccept (NULL buf), then we can just leave */ return 0; } /* Try to send again if more data to send */ if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) { goto WRITE_MORE; } } } else if (len < 0) { return -1; } READ_MORE: /* Get the ssl buffer and how much data it can accept */ /* Note 0 is a return failure, unlike with matrixSslGetOutdata */ if ((len = matrixSslGetReadbuf(cp->ssl, &buf)) <= 0) { return -1; } if ((transferred = recv(cp->fd, buf, len, MSG_NOSIGNAL)) < 0) { /* Support non-blocking sockets if turned on */ if (socketGetError() == EWOULDBLOCK) { return 0; } trace(1, T("RECV error: %d\n"), socketGetError()); return -1; } if (transferred == 0) { /* If EOF, remote socket closed. This is semi-normal closure. */ trace(4, T("Closing connection %d on EOF\n"), cp->fd); return -1; } /* Notify SSL state machine that we've received more data into the ssl buffer retreived with matrixSslGetReadbuf. */ if ((rc = matrixSslReceivedData(cp->ssl, transferred, &buf, (uint32*)&len)) < 0) { return -1; } PROCESS_MORE: switch (rc) { case MATRIXSSL_REQUEST_SEND: /* There is a handshake response we must send */ goto WRITE_MORE; case MATRIXSSL_REQUEST_RECV: goto READ_MORE; case MATRIXSSL_HANDSHAKE_COMPLETE: /* Session resumption handshake */ goto READ_MORE; case MATRIXSSL_RECEIVED_ALERT: /* Any fatal alert will simply cause a read error and exit */ if (*buf == SSL_ALERT_LEVEL_FATAL) { trace(1, T("Fatal alert: %d, closing connection.\n"), *(buf + 1)); return -1; } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { return -1; } /* Eating warning alerts */ trace(4, T("Warning alert: %d\n"), *(buf + 1)); if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { /* Possible there was plaintext before the alert */ if (inbuf != NULL && inlen > 0 && cp->ptBytes > 0 && cp->pt) { if (cp->ptBytes < inlen) { inlen = cp->ptBytes; } memcpy(inbuf, cp->currPt, inlen); cp->currPt += inlen; cp->ptBytes -= inlen; /* Free buffer as we go if empty */ if (cp->ptBytes == 0) { bfree(B_L, cp->pt); cp->pt = cp->currPt = NULL; } return inlen; } else { return 0; } } goto PROCESS_MORE; case MATRIXSSL_APP_DATA: if (cp->ptBytes == 0) { /* Catching here means this is new app data just grabbed off the wire. */ cp->ptBytes = len; cp->pt = balloc(B_L, len); memcpy(cp->pt, buf, len); cp->currPt = cp->pt; } else { /* Multi-record. This case should only ever be possible if no data has already been read out of the 'pt' cache so it is fine to assume an unprocessed buffer. */ psAssert(cp->pt == cp->currPt); cp->pt = brealloc(B_L, cp->pt, cp->ptBytes + len); memcpy(cp->pt + cp->ptBytes, buf, len); cp->currPt = cp->pt; cp->ptBytes += len; } if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) < 0) { return -1; } /* Check for multi-record app data*/ if (rc > 0) { goto PROCESS_MORE; } /* Otherwise pass back how much the caller wants to read (if any) */ if (inbuf != 0 && inlen > 0) { if (cp->ptBytes < inlen) { inlen = cp->ptBytes; } memcpy(inbuf, cp->currPt, inlen); cp->currPt += inlen; cp->ptBytes -= inlen; return inlen; /* Just a breakpoint holder */ } return 0; /* Have it stored, but caller didn't want any data */ default: return -1; } return 0; /* really can never hit this */ }
/* return 0 on successful encryption/decryption communication return -1 on failed comm */ static int32 exchangeAppData(sslConn_t *sendingSide, sslConn_t *receivingSide) { int32 writeBufLen, inBufLen, dataSent, rc; uint32 ptLen, totalProcessed, requestedLen; unsigned char *writeBuf, *inBuf, *plaintextBuf; /* Client sends the data TODO: randomize the requested length */ requestedLen = 1234; writeBufLen = matrixSslGetWritebuf(sendingSide->ssl, &writeBuf, requestedLen); psAssert(writeBufLen >= (int32)requestedLen); memset(writeBuf, 4, (size_t)requestedLen); /* EncodeWriteBuf does not return the buffer. */ writeBufLen = matrixSslEncodeWritebuf(sendingSide->ssl, requestedLen); totalProcessed = 0; SEND_MORE: writeBufLen = matrixSslGetOutdata(sendingSide->ssl, &writeBuf); /* Receiving side must ask for storage space to receive data into. A good optimization of the buffer management can be seen here if a second pass was required: the inBufLen should exactly match the writeBufLen because when matrixSslReceivedData was called below the record length was parsed off and the buffer was reallocated to the exact necessary length */ inBufLen = matrixSslGetReadbuf(receivingSide->ssl, &inBuf); dataSent = min(writeBufLen, inBufLen); memcpy(inBuf, writeBuf, dataSent); /* Now update the sending side that data has been "sent" */ matrixSslSentData(sendingSide->ssl, dataSent); /* Received data */ rc = matrixSslReceivedData(receivingSide->ssl, dataSent, &plaintextBuf, &ptLen); if (rc == MATRIXSSL_REQUEST_RECV) { goto SEND_MORE; } else if (rc == MATRIXSSL_APP_DATA) { if (matrixSslProcessedData(receivingSide->ssl, &plaintextBuf, &ptLen) != 0) { return PS_FAILURE; } } else { printf("Unexpected error in exchangeAppData: %d\n", rc); return PS_FAILURE; } return PS_SUCCESS; }
/* Recursive handshake */ static int32 performHandshake(sslConn_t *sendingSide, sslConn_t *receivingSide) { unsigned char *inbuf, *outbuf, *plaintextBuf; int32 inbufLen, outbufLen, rc, sdrc, dataSent; uint32 ptLen; #ifdef ENABLE_PERF_TIMING psTime_t start, end; #endif /* ENABLE_PERF_TIMING */ /* Sending side will have outdata ready */ #ifdef ENABLE_PERF_TIMING psGetTime(&start); #endif /* ENABLE_PERF_TIMING */ outbufLen = matrixSslGetOutdata(sendingSide->ssl, &outbuf); #ifdef ENABLE_PERF_TIMING psGetTime(&end); sendingSide->runningTime += psDiffMsecs(start, end); #endif /* ENABLE_PERF_TIMING */ /* Receiving side must ask for storage space to receive data into */ #ifdef ENABLE_PERF_TIMING psGetTime(&start); #endif /* ENABLE_PERF_TIMING */ inbufLen = matrixSslGetReadbuf(receivingSide->ssl, &inbuf); #ifdef ENABLE_PERF_TIMING psGetTime(&end); receivingSide->runningTime += psDiffMsecs(start, end); #endif /* ENABLE_PERF_TIMING */ /* The indata is the outdata from the sending side. copy it over */ dataSent = min(outbufLen, inbufLen); memcpy(inbuf, outbuf, dataSent); /* Now update the sending side that data has been "sent" */ #ifdef ENABLE_PERF_TIMING psGetTime(&start); #endif /* ENABLE_PERF_TIMING */ sdrc = matrixSslSentData(sendingSide->ssl, dataSent); #ifdef ENABLE_PERF_TIMING psGetTime(&end); sendingSide->runningTime += psDiffMsecs(start, end); #endif /* ENABLE_PERF_TIMING */ /* Received data */ #ifdef ENABLE_PERF_TIMING psGetTime(&start); #endif /* ENABLE_PERF_TIMING */ rc = matrixSslReceivedData(receivingSide->ssl, dataSent, &plaintextBuf, &ptLen); #ifdef ENABLE_PERF_TIMING psGetTime(&end); receivingSide->runningTime += psDiffMsecs(start, end); #endif /* ENABLE_PERF_TIMING */ if (rc == MATRIXSSL_REQUEST_SEND) { /* Success case. Switch roles and continue */ return performHandshake(receivingSide, sendingSide); } else if (rc == MATRIXSSL_REQUEST_RECV) { /* This pass didn't take care of it all. Don't switch rolls and try again */ return performHandshake(sendingSide, receivingSide); } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { return PS_SUCCESS; } else if (rc == MATRIXSSL_RECEIVED_ALERT) { /* Just continue if warning level alert */ if (plaintextBuf[0] == SSL_ALERT_LEVEL_WARNING) { if (matrixSslProcessedData(receivingSide->ssl, &plaintextBuf, &ptLen) != 0) { return PS_FAILURE; } return performHandshake(sendingSide, receivingSide); } else { return PS_FAILURE; } } else { printf("Unexpected error in performHandshake: %d\n", rc); return PS_FAILURE; } return PS_FAILURE; /* can't get here */ }
/* Make a secure HTTP request to a defined IP and port Connection is made in blocking socket mode The connection is considered successful if the SSL/TLS session is negotiated successfully, a request is sent, and a HTTP response is received. */ static int32 httpsClientConnection(sslKeys_t *keys, sslSessionId_t *sid) { int32 rc, transferred, len, complete; ssl_t *ssl; unsigned char *buf; httpConn_t cp; SOCKET fd; complete = 0; memset(&cp, 0x0, sizeof(httpConn_t)); fd = socketConnect(HTTPS_IP, HTTPS_PORT, &rc); if (fd == INVALID_SOCKET || rc != PS_SUCCESS) { _psTraceInt("Connect failed: %d. Exiting\n", rc); return PS_PLATFORM_FAIL; } rc = matrixSslNewClientSession(&ssl, keys, sid, 0, certCb, NULL, NULL); if (rc != MATRIXSSL_REQUEST_SEND) { _psTraceInt("New Client Session Failed: %d. Exiting\n", rc); close(fd); return PS_ARG_FAIL; } WRITE_MORE: while ((len = matrixSslGetOutdata(ssl, &buf)) > 0) { transferred = send(fd, buf, len, 0); if (transferred <= 0) { goto L_CLOSE_ERR; } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(ssl, transferred)) < 0) { goto L_CLOSE_ERR; } if (rc == MATRIXSSL_REQUEST_CLOSE) { closeConn(ssl, fd); return MATRIXSSL_SUCCESS; } if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If we sent the Finished SSL message, initiate the HTTP req */ /* (This occurs on a resumption handshake) */ if (httpWriteRequest(ssl) < 0) { goto L_CLOSE_ERR; } goto WRITE_MORE; } /* SSL_REQUEST_SEND is handled by loop logic */ } } READ_MORE: if ((len = matrixSslGetReadbuf(ssl, &buf)) <= 0) { goto L_CLOSE_ERR; } if ((transferred = recv(fd, buf, len, 0)) < 0) { goto L_CLOSE_ERR; } /* If EOF, remote socket closed. But we haven't received the HTTP response so we consider it an error in the case of an HTTP client */ if (transferred == 0) { goto L_CLOSE_ERR; } if ((rc = matrixSslReceivedData(ssl, (int32)transferred, &buf, (uint32*)&len)) < 0) { goto L_CLOSE_ERR; } PROCESS_MORE: switch (rc) { case MATRIXSSL_HANDSHAKE_COMPLETE: #ifdef REHANDSHAKE_TEST /* Test rehandshake capabilities of server. If a successful session resmption rehandshake occurs, this client will be last to send handshake data and MATRIXSSL_HANDSHAKE_COMPLETE will hit on the WRITE_MORE handler and httpWriteRequest will occur there. NOTE: If the server doesn't support session resumption it is possible to fall into an endless rehandshake loop */ if (matrixSslEncodeRehandshake(ssl, NULL, NULL, 0, 0) < 0) { goto L_CLOSE_ERR; } #else /* We got the Finished SSL message, initiate the HTTP req */ if (httpWriteRequest(ssl) < 0) { goto L_CLOSE_ERR; } #endif goto WRITE_MORE; case MATRIXSSL_APP_DATA: if ((rc = httpBasicParse(&cp, buf, len)) < 0) { closeConn(ssl, fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; return MATRIXSSL_ERROR; } if (rc == HTTPS_COMPLETE) { rc = matrixSslProcessedData(ssl, &buf, (uint32*)&len); closeConn(ssl, fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; if (rc < 0) { return MATRIXSSL_ERROR; } else { if (rc > 0) { _psTrace("HTTP data parsing not supported, ignoring.\n"); } _psTrace("SUCCESS: Received HTTP Response\n"); return MATRIXSSL_SUCCESS; } } /* We processed a partial HTTP message */ if ((rc = matrixSslProcessedData(ssl, &buf, (uint32*)&len)) == 0) { goto READ_MORE; } goto PROCESS_MORE; case MATRIXSSL_REQUEST_SEND: goto WRITE_MORE; case MATRIXSSL_REQUEST_RECV: goto READ_MORE; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*buf == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Fatal alert: %d, closing connection.\n", *(buf + 1)); goto L_CLOSE_ERR; } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { closeConn(ssl, fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; return MATRIXSSL_SUCCESS; } psTraceIntInfo("Warning alert: %d\n", *(buf + 1)); if ((rc = matrixSslProcessedData(ssl, &buf, (uint32*)&len)) == 0) { /* No more data in buffer. Might as well read for more. */ goto READ_MORE; } goto PROCESS_MORE; default: /* If rc <= 0 we fall here */ goto L_CLOSE_ERR; } L_CLOSE_ERR: _psTrace("FAIL: No HTTP Response\n"); matrixSslDeleteSession(ssl); close(fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; return MATRIXSSL_ERROR; }
int ssl_read_internal(int socket, char* data, size_t size) { int rc = -1; MATRIXSSL_PDEBUG_ALL("size: %i\n", size); unsigned char *sslData; SSL* ssl = NULL; SSL_Conext* sslContext = g_SSL_Driver.GetSSLContextBySocketHandle(socket); if (sslContext != NULL && sslContext->SslContext != NULL) { ssl = (SSL*) sslContext->SslContext; } else { return SOCK_SOCKET_ERROR; } rc = handleBufferdData(sslContext, data, size); if (rc > 0 || rc == SOCK_SOCKET_ERROR) { //if data was buffered, return it and wait for next call to get new data from socket return rc; } int len = 0; len = matrixSslGetReadbuf(sslContext->SslContext, &sslData); int rec = SOCK_recv(sslContext->SocketHandle, (char*) sslData, len, 0); MATRIXSSL_PDEBUG_ALL("SOCK_recv: %i\n", rec); if (rec == 0) { return rec; } rc = matrixSslReceivedData(sslContext->SslContext, (int32) rec, &sslData, (uint32*) &len); PRINT_RETURN_VALUE(rc); if (rc < 0) { PRINT_UNEXPECTED_RETURN_VALUE(rc); return SOCK_SOCKET_ERROR; } MATRIXSSL_PDEBUG_ALL("matrixSslReceivedData: %i\n", len); if (!(rc == MATRIXSSL_APP_DATA || rc == MATRIXSSL_REQUEST_RECV || rc == MATRIXSSL_REQUEST_SEND || rc == MATRIXSSL_RECEIVED_ALERT)) { PRINT_UNEXPECTED_RETURN_VALUE(rc); return SOCK_SOCKET_ERROR; } if (rc == MATRIXSSL_RECEIVED_ALERT) { if (len == 2) { unsigned char alertLevel = sslData[0]; unsigned char alertDescription = sslData[1]; MATRIXSSL_PDEBUG( "Alert: Level %i, Description: %i\n", alertLevel, alertDescription); if (alertDescription == SSL_ALERT_CLOSE_NOTIFY) { return 0; } } return SOCK_SOCKET_ERROR; } else if (rc == MATRIXSSL_REQUEST_SEND) { int rc = handleRequestSend(sslContext); if (rc == MATRIXSSL_SUCCESS) { return 0; } return SOCK_SOCKET_ERROR; } else if (rc == MATRIXSSL_APP_DATA) { return handleAppData(size, len, data, sslData, sslContext); } else { return SSL_RESULT__WOULD_BLOCK; } PRINT_UNEXPECTED_RETURN_VALUE(rc); return 0; }
static ssize processIncoming(Webs *wp, char *buf, ssize size, ssize nbytes, int *readMore) { Ms *ms; uchar *data, *obuf; ssize toWrite, written, copied, sofar; uint32 dlen; int rc; ms = (Ms*) wp->ssl; *readMore = 0; sofar = 0; /* Process the received data. If there is application data, it is returned in data/dlen */ rc = matrixSslReceivedData(ms->handle, (int) nbytes, &data, &dlen); while (1) { switch (rc) { case PS_SUCCESS: return sofar; case MATRIXSSL_REQUEST_SEND: toWrite = matrixSslGetOutdata(ms->handle, &obuf); if ((written = blockingWrite(wp, obuf, toWrite)) < 0) { error("MatrixSSL: Error in process"); return -1; } matrixSslSentData(ms->handle, (int) written); if (ms->handle->err != SSL_ALERT_NONE && ms->handle->err != SSL_ALLOW_ANON_CONNECTION) { return -1; } *readMore = 1; return 0; case MATRIXSSL_REQUEST_RECV: /* Partial read. More read data required */ *readMore = 1; ms->more = 1; return 0; case MATRIXSSL_HANDSHAKE_COMPLETE: *readMore = 0; return 0; case MATRIXSSL_RECEIVED_ALERT: assert(dlen == 2); if (data[0] == SSL_ALERT_LEVEL_FATAL) { return -1; } else if (data[1] == SSL_ALERT_CLOSE_NOTIFY) { // ignore - graceful close return 0; } rc = matrixSslProcessedData(ms->handle, &data, &dlen); break; case MATRIXSSL_APP_DATA: copied = min((ssize) dlen, size); memcpy(buf, data, copied); buf += copied; size -= copied; data += copied; dlen = dlen - (int) copied; sofar += copied; ms->more = ((ssize) dlen > size) ? 1 : 0; if (!ms->more) { /* The MatrixSSL buffer has been consumed, see if we can get more data */ rc = matrixSslProcessedData(ms->handle, &data, &dlen); break; } return sofar; default: return -1; } } }
void child_process(struct spead_client *cl, sslKeys_t *keys) { #define BUFF 1000 //#define REPLY "HTTP/1.1 200 OK\nServer: pshr\nConnection: Keep-Alive\nContent-Type: audio/mpeg\nCache-Control: no-cache\nPragma: no-cache\n\n" #define REPLY "HTTP/1.1 200 OK\nServer: pshr\nConnection: Keep-Alive\nContent-Type: text/plain\nCache-Control: no-cache\nPragma: no-cache\n\nhello world" #define MODE_CONNECTING 0 #define MODE_SENDING 1 //int bytes, mode = MODE_CONNECTING; //unsigned char data[BUFF]; unsigned char *data, *res; uint32 err, len ,rb, wb; ssl_t *ssl; //int fd = (-1), initial=1450-300; data = NULL; res = NULL; ssl = NULL; if (cl == NULL || keys == NULL){ #ifdef DEBUG fprintf(stderr, "%s: parameter error\n", __func__); #endif exit(EXIT_FAILURE); } #ifdef DEBUG fprintf(stderr, "%s: child created with fd[%d]\n", __func__, cl->c_fd); #endif err = matrixSslNewServerSession(&ssl, keys, NULL, 0); if (err != PS_SUCCESS){ #ifdef DEBUG switch(err){ case PS_ARG_FAIL: fprintf(stderr, "Bad input function parameter\n"); break; case PS_FAILURE: fprintf(stderr, "Internal memory allocation failure\n"); break; } #endif #if 0 matrixSslDeleteKeys(keys); matrixSslClose(); #endif exit(EXIT_FAILURE); } while(run){ READ_STATE: len = matrixSslGetReadbuf(ssl, &data); if (rb < 0){ #ifdef DEBUG fprintf(stderr, "%s: matrixssl getreadbuf error\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } #ifdef DEBUG fprintf(stderr, "%s: matrixssl getreadbuf rtn [%d]\n", __func__, len); #endif rb = read(cl->c_fd, data, len); if (rb == 0){ #ifdef DEBUG fprintf(stderr, "%s: read EOF\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } else if (rb < 0){ #ifdef DEBUG fprintf(stderr, "%s: read error (%s)\n", __func__, strerror(errno)); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } #ifdef DEBUG fprintf(stderr, "%s: read rtn [%d]\n", __func__, rb); #endif rb = matrixSslReceivedData(ssl, rb, &data, &len); if (rb < 0){ #ifdef DEBUG fprintf(stderr, "%s: matrixssl receiveddata error\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } else if (rb == 0){ #ifdef DEBUG fprintf(stderr, "%s: matrix ssl received 0 bytes (false start?)\n", __func__); #endif } else if (rb > 0){ switch(rb){ case MATRIXSSL_REQUEST_SEND: #ifdef DEBUG fprintf(stderr, "%s: RS req send\n", __func__); #endif goto WRITE_STATE; break; case MATRIXSSL_REQUEST_RECV: #ifdef DEBUG fprintf(stderr, "%s: RS req recv\n", __func__); #endif goto READ_STATE; break; case MATRIXSSL_HANDSHAKE_COMPLETE: #ifdef DEBUG fprintf(stderr, "%s: RS handshake complete\n", __func__); #endif goto READ_STATE; break; case MATRIXSSL_RECEIVED_ALERT: #ifdef DEBUG fprintf(stderr, "%s: RS rec alert\n", __func__); #endif break; case MATRIXSSL_APP_DATA: #ifdef DEBUG fprintf(stderr, "%s: RS app data\n", __func__); #endif #ifdef DEBUG fprintf(stderr, "%s: RS got data [%s]\n", __func__, data); #endif /*process client data here*/ res = get_resource_str(data); if (res == NULL){ #ifdef DEBUG fprintf(stderr, "%s: NULL RESOURCE\n", __func__); #endif run = 0; } #ifdef DEBUG fprintf(stderr, "%s: got resource [%s]\n", __func__, res); #endif unsigned char *tbuf; int32 tbuflen; tbuflen = matrixSslGetWritebuf(ssl, &tbuf, strlen(REPLY)); if (tbuflen < 0){ #ifdef DEBUG fprintf(stderr, "%s: matrixssl getwritebuf error\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } strncpy((char *) tbuf, REPLY, tbuflen); if (matrixSslEncodeWritebuf(ssl, strlen((char *) tbuf)) < 0){ matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } matrixSslEncodeClosureAlert(ssl); rb = matrixSslProcessedData(ssl, &data, &len); #ifdef DEBUG fprintf(stderr, "%s: processed data rtn [%d]\n", __func__, rb); #endif goto WRITE_STATE; break; } } WRITE_STATE: len = matrixSslGetOutdata(ssl, &data); if (len < 0){ #ifdef DEBUG fprintf(stderr, "%s: matrixssl getoutdata error\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } #ifdef DEBUG fprintf(stderr, "%s: getoutdata rtn [%d]\n", __func__, len); #endif wb = write(cl->c_fd, data, len); if (wb == 0){ #ifdef DEBUG fprintf(stderr, "%s: write 0\n", __func__); #endif goto READ_STATE; } else if (wb < 0){ #ifdef DEBUG fprintf(stderr, "%s: write error (%s)\n", __func__, strerror(errno)); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } #ifdef DEBUG fprintf(stderr, "%s: write rtn [%d]\n", __func__, wb); #endif wb = matrixSslSentData(ssl, wb); if (wb < 0) { #ifdef DEBUG fprintf(stderr, "%s: matrixssl sentdata error\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); } else if (wb > 0){ switch(wb){ case MATRIXSSL_REQUEST_SEND: #ifdef DEBUG fprintf(stderr, "%s: WS req send\n", __func__); #endif goto WRITE_STATE; break; case MATRIXSSL_REQUEST_CLOSE: #ifdef DEBUG fprintf(stderr, "%s: WS req close\n", __func__); #endif matrixSslDeleteSession(ssl); destroy_spead_client(cl); exit(EXIT_FAILURE); break; case MATRIXSSL_HANDSHAKE_COMPLETE: #ifdef DEBUG fprintf(stderr, "%s: WS handshake complete\n", __func__); #endif goto READ_STATE; /*note might need to jump to receiveddata*/ break; } } } matrixSslDeleteSession(ssl); #if 0 const char *filename = "/home/adam/live_audio_streaming/tenc/agoria-scala_original_mixwww.mp3vip.org.mp3"; //fd = open("/home/adam/build/lame-3.99.5/testcase.mp3", O_RDONLY); fd = open("/home/adam/live_audio_streaming/tenc/agoria-scala_original_mixwww.mp3vip.org.mp3", O_RDONLY); if (fd < 0){ #ifdef DEBUG fprintf(stderr, "%s: open error (%s)\n", __func__, strerror(errno)); #endif exit(EXIT_SUCCESS); } while((bytes = read_ssl(ssl, data, BUFF)) > 0){ #ifdef DEBUG fprintf(stderr, "%s: read [%d] [%s:%d] [%s]\n", __func__, bytes, get_client_address(cl), get_client_port(cl), data); #endif } #endif #if 0 while (run){ switch (mode){ case MODE_CONNECTING: bytes = read_ssl(ssl, data, BUFF); switch (bytes){ case 0: #ifdef DEBUG fprintf(stderr, "%s: read EOF client[%s:%d]\n", __func__, get_client_address(cl), get_client_port(cl)); #endif run = 0; break; case -1: #ifdef DEBUG fprintf(stderr, "%s: read error client[%s:%d] (%s)\n", __func__, get_client_address(cl), get_client_port(cl), strerror(errno)); #endif run = 0; break; } #ifdef DEBUG fprintf(stderr, "%s: [%s:%d] [%s]\n", __func__, get_client_address(cl), get_client_port(cl), data); #endif res = get_resource_str(data); if (res == NULL){ #ifdef DEBUG fprintf(stderr, "%s: NULL RESOURCE\n", __func__); #endif run = 0; } #ifdef DEBUG fprintf(stderr, "%s: got resource [%s]\n", __func__, res); #endif if (strncmp(res, "/sound", 6) == 0){ bytes = write_ssl(ssl, REPLY, sizeof(REPLY)); switch(bytes){ case -1: #ifdef DEBUG fprintf(stderr, "%s: write error client[%s:%d] (%s)\n", __func__, get_client_address(cl), get_client_port(cl), strerror(errno)); #endif run = 0; break; } mode = MODE_SENDING; } else { run = 0; } break; case MODE_SENDING: #if 0 bytes = sendfile(cl->c_fd, fd, NULL, BUFF+initial); if (bytes == 0){ close(fd); #if 0 fd = open("/srv/beats/Meditations On Afrocentrism EP/03 Down The Line (It Takes A Number).mp3", O_RDONLY); if (fd < 0){ #ifdef DEBUG fprintf(stderr, "%s: open error (%s)\n", __func__, strerror(errno)); #endif exit(EXIT_SUCCESS); } #endif } else if (bytes < 0){ #ifdef DEBUG fprintf(stderr, "%s: sendfile error client[%s:%d] (%s)\n", __func__, get_client_address(cl), get_client_port(cl), strerror(errno)); #endif exit(EXIT_SUCCESS); } if (initial > 1){ initial--; } #if 0 def DEBUG fprintf(stderr, "%s: [%s:%d] sent %d bytes\n", __func__, get_client_address(cl), get_client_port(cl), bytes); #endif usleep(9766/initial); #endif run = 0; break; } } #endif #ifdef DEBUG fprintf(stderr, "%s: child[%d] ending\n", __func__, getpid()); #endif #if 0 if (fd > 0){ close(fd); } #endif destroy_spead_client(cl); //shutdown(cl->c_fd, SHUT_RDWR); exit(EXIT_SUCCESS); }
/* 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; }
/* Non-blocking socket event handler Wait one time in select for events on any socket This will accept new connections, read and write to sockets that are connected, and close sockets as required. */ static int32 selectLoop(sslKeys_t *keys, SOCKET lfd) { httpConn_t *cp; psTime_t now; DLListEntry connsTmp; DLListEntry *pList; fd_set readfd, writefd; struct timeval timeout; SOCKET fd, maxfd; unsigned char *buf; int32 rc, len, transferred, val; unsigned char rSanity, wSanity, acceptSanity; sslSessOpts_t options; DLListInit(&connsTmp); rc = PS_SUCCESS; maxfd = INVALID_SOCKET; timeout.tv_sec = SELECT_TIME / 1000; timeout.tv_usec = (SELECT_TIME % 1000) * 1000; FD_ZERO(&readfd); FD_ZERO(&writefd); /* Always set readfd for listening socket */ FD_SET(lfd, &readfd); if (lfd > maxfd) { maxfd = lfd; } /* Check timeouts and set readfd and writefd for connections as required. We use connsTemp so that removal on error from the active iteration list doesn't interfere with list traversal */ psGetTime(&now, NULL); while (!DLListIsEmpty(&g_conns)) { pList = DLListGetHead(&g_conns); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&connsTmp, &cp->List); /* If timeout != 0 msec ith no new data, close */ if (cp->timeout && (psDiffMsecs(cp->time, now, NULL) > (int32)cp->timeout)) { closeConn(cp, PS_TIMEOUT_FAIL); continue; /* Next connection */ } /* Always select for read */ FD_SET(cp->fd, &readfd); /* Select for write if there's pending write data or connection */ if (matrixSslGetOutdata(cp->ssl, NULL) > 0) { FD_SET(cp->fd, &writefd); } /* Housekeeping for maxsock in select call */ if (cp->fd > maxfd) { maxfd = cp->fd; } } /* Use select to check for events on the sockets */ if ((val = select(maxfd + 1, &readfd, &writefd, NULL, &timeout)) <= 0) { /* On error, restore global connections list */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); } /* Select timeout */ if (val == 0) { return PS_TIMEOUT_FAIL; } /* Woke due to interrupt */ if (SOCKET_ERRNO == EINTR) { return PS_TIMEOUT_FAIL; } /* Should attempt to handle more errnos, such as EBADF */ return PS_PLATFORM_FAIL; } /* Check listener for new incoming socket connections */ if (FD_ISSET(lfd, &readfd)) { for (acceptSanity = 0; acceptSanity < ACCEPT_QUEUE; acceptSanity++) { fd = accept(lfd, NULL, NULL); if (fd == INVALID_SOCKET) { break; /* Nothing more to accept; next listener */ } setSocketOptions(fd); cp = malloc(sizeof(httpConn_t)); memset(cp, 0x0, sizeof(httpConn_t)); memset(&options, 0x0, sizeof(sslSessOpts_t)); options.versionFlag = g_proto; options.userPtr = keys; /* Just a test */ //options.truncHmac = -1; //options.maxFragLen = -1; //options.ecFlags |= SSL_OPT_SECP521R1; //options.ecFlags |= SSL_OPT_SECP224R1; //options.ecFlags |= SSL_OPT_SECP384R1; if ((rc = matrixSslNewServerSession(&cp->ssl, keys, certCb, &options)) < 0) { close(fd); fd = INVALID_SOCKET; continue; } #ifdef USE_SERVER_NAME_INDICATION /* Register extension callbacks to manage client connection opts */ matrixSslRegisterSNICallback(cp->ssl, SNI_callback); #endif #ifdef USE_ALPN matrixSslRegisterALPNCallback(cp->ssl, ALPN_callback); #endif cp->fd = fd; fd = INVALID_SOCKET; cp->timeout = SSL_TIMEOUT; psGetTime(&cp->time, NULL); cp->parsebuf = NULL; cp->parsebuflen = 0; DLListInsertTail(&connsTmp, &cp->List); /* Fake that there is read data available, no harm if there isn't */ FD_SET(cp->fd, &readfd); /* _psTraceInt("=== New Client %d ===\n", cp->fd); */ } } /* Check each connection for read/write activity */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); rSanity = wSanity = 0; /* See if there's pending data to send on this connection We could use FD_ISSET, but this is more reliable for the current state of data to send. */ WRITE_MORE: if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) { /* Could get a EWOULDBLOCK since we don't check FD_ISSET */ transferred = send(cp->fd, buf, len, MSG_DONTWAIT); if (transferred <= 0) { #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); continue; /* Next connection */ } } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if (rc == MATRIXSSL_REQUEST_CLOSE) { closeConn(cp, MATRIXSSL_REQUEST_CLOSE); continue; /* Next connection */ } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If the protocol is server initiated, send data here */ #ifdef ENABLE_FALSE_START /* OR this could be a Chrome browser using FALSE_START and the application data is already waiting in our inbuf for processing */ if ((rc = matrixSslReceivedData(cp->ssl, 0, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } if (rc > 0) { /* There was leftover data */ goto PROCESS_MORE; } #endif /* ENABLE_FALSE_START */ } /* Update activity time */ psGetTime(&cp->time, NULL); /* Try to send again if more data to send */ if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) { if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; } } } else if (len < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } /* Check the file descriptor returned from select to see if the connection has data to be read */ if (FD_ISSET(cp->fd, &readfd)) { READ_MORE: /* Get the ssl buffer and how much data it can accept */ /* Note 0 is a return failure, unlike with matrixSslGetOutdata */ if ((len = matrixSslGetReadbuf(cp->ssl, &buf)) <= 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if ((transferred = recv(cp->fd, buf, len, MSG_DONTWAIT)) < 0) { /* We could get EWOULDBLOCK despite the FD_ISSET on goto */ #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); } continue; /* Next connection */ } /* If EOF, remote socket closed. This is semi-normal closure. Officially, we should close on closure alert. */ if (transferred == 0) { /* psTraceIntInfo("Closing connection %d on EOF\n", cp->fd); */ closeConn(cp, 0); continue; /* Next connection */ } /* Notify SSL state machine that we've received more data into the ssl buffer retreived with matrixSslGetReadbuf. */ if ((rc = matrixSslReceivedData(cp->ssl, (int32)transferred, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } /* Update activity time */ psGetTime(&cp->time, NULL); PROCESS_MORE: /* Process any incoming plaintext application data */ switch (rc) { case MATRIXSSL_HANDSHAKE_COMPLETE: /* If the protocol is server initiated, send data here */ goto READ_MORE; case MATRIXSSL_APP_DATA: case MATRIXSSL_APP_DATA_COMPRESSED: //psTraceBytes("DATA", buf, len); /* Remember, must handle if len == 0! */ if ((rc = httpBasicParse(cp, buf, len, 0)) < 0) { _psTrace("Couldn't parse HTTP data. Closing conn.\n"); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } if (cp->parsebuf != NULL) { /* Test for one of our custom testing messages */ if (strncmp((const char*)cp->parsebuf, "MATRIX_SHUTDOWN", 15) == 0) { g_exitFlag = 1; matrixSslEncodeClosureAlert(cp->ssl); _psTrace("Got MATRIX_SHUTDOWN. Exiting\n"); goto WRITE_MORE; } } /* reply to /bytes?<byte count> syntax */ if (len > 11 && strncmp((char *)buf, "GET /bytes?", 11) == 0) { cp->bytes_requested = atoi((char *)buf + 11); if (cp->bytes_requested < strlen((char *)g_httpResponseHdr) || cp->bytes_requested > 1073741824) { cp->bytes_requested = strlen((char *)g_httpResponseHdr); } cp->bytes_sent = 0; } if (rc == HTTPS_COMPLETE) { if (httpWriteResponse(cp) < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* For HTTP, we assume no pipelined requests, so we close after parsing a single HTTP request */ /* Ignore return of closure alert, it's optional */ matrixSslEncodeClosureAlert(cp->ssl); rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len); if (rc > 0) { /* Additional data is available, but we ignore it */ _psTrace("HTTP data parsing not supported, ignoring.\n"); closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } else if (rc < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* rc == 0, write out our response and closure alert */ goto WRITE_MORE; } /* We processed a partial HTTP message */ if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { goto READ_MORE; } goto PROCESS_MORE; case MATRIXSSL_REQUEST_SEND: /* Prevent us from reading again after the write, although that wouldn't be the end of the world */ FD_CLR(cp->fd, &readfd); if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; break; case MATRIXSSL_REQUEST_RECV: if (rSanity++ < GOTO_SANITY) goto READ_MORE; break; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*buf == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Fatal alert: %d, closing connection.\n", *(buf + 1)); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } psTraceIntInfo("Warning alert: %d\n", *(buf + 1)); if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { /* No more data in buffer. Might as well read for more. */ goto READ_MORE; } goto PROCESS_MORE; default: /* If rc <= 0 we fall here */ closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Always try to read more if we processed some data */ if (rSanity++ < GOTO_SANITY) goto READ_MORE; } /* readfd handling */ } /* connection loop */ return PS_SUCCESS; } /******************************************************************************/ /* Create an HTTP response and encode it to the SSL buffer */ #define TEST_SIZE 16000 static int32 httpWriteResponse(httpConn_t *conn) { unsigned char *buf; ssl_t *cp; int32 available, len, rc; cp = conn->ssl; if (conn->bytes_requested) { /* The /bytes? syntax */ while (conn->bytes_sent < conn->bytes_requested) { len = conn->bytes_requested - conn->bytes_sent; if (len > RESPONSE_REC_LEN) { len = RESPONSE_REC_LEN; } psAssert(len > 0); rc = matrixSslGetWritebuf(cp, &buf, len); if (rc < len) { len = rc; /* could have been shortened due to max_frag */ } memset(buf, 'J', len); if (conn->bytes_sent == 0) { /* Overwrite first N bytes with HTTP header the first time */ strncpy((char *)buf, (char *)g_httpResponseHdr, strlen((char*)g_httpResponseHdr)); } if ((rc = matrixSslEncodeWritebuf(cp, len)) < 0) { printf("couldn't encode data %d\n", rc); } conn->bytes_sent += len; } return MATRIXSSL_REQUEST_SEND; } /* Usual reply */ if ((available = matrixSslGetWritebuf(cp, &buf, (uint32)strlen((char *)g_httpResponseHdr) + 1)) < 0) { return PS_MEM_FAIL; } strncpy((char *)buf, (char *)g_httpResponseHdr, available); //psTraceBytes("Replying", buf, (uint32)strlen((char *)buf)); if (matrixSslEncodeWritebuf(cp, (uint32)strlen((char *)buf)) < 0) { return PS_MEM_FAIL; } return MATRIXSSL_REQUEST_SEND; } /******************************************************************************/ /* Main non-blocking SSL server Initialize MatrixSSL and sockets layer, and loop on select */ int32 main(int32 argc, char **argv) { sslKeys_t *keys; SOCKET lfd; unsigned char *CAstream; int32 err, rc, CAstreamLen; #ifdef USE_STATELESS_SESSION_TICKETS unsigned char randKey[16]; #endif #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif keys = NULL; DLListInit(&g_conns); g_exitFlag = 0; lfd = INVALID_SOCKET; #ifdef POSIX if (sighandlers() < 0) { return PS_PLATFORM_FAIL; } #endif /* POSIX */ if ((rc = matrixSslOpen()) < 0) { _psTrace("MatrixSSL library init failure. Exiting\n"); return rc; } if (matrixSslNewKeys(&keys, NULL) < 0) { _psTrace("MatrixSSL library key init failure. Exiting\n"); return -1; } #ifdef USE_STATELESS_SESSION_TICKETS matrixSslSetSessionTicketCallback(keys, sessTicketCb); psGetEntropy(randKey, 16, NULL); if (matrixSslLoadSessionTicketKeys(keys, randKey, sessTicketSymKey, 32, sessTicketMacKey, 32) < 0) { _psTrace("Error loading session ticket encryption key\n"); } #endif #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 if ((rc = matrixSslLoadRsaKeysMem(keys, RSA1024, sizeof(RSA1024), RSA1024KEY, sizeof(RSA1024KEY), CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_ECDH_RSA_KEYS if ((rc = matrixSslLoadEcKeysMem(keys, ECDHRSA256, sizeof(ECDHRSA256), ECDHRSA256KEY, sizeof(ECDHRSA256KEY), CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_EC_KEYS if ((rc = matrixSslLoadEcKeysMem(keys, EC521, sizeof(EC521), EC521KEY, sizeof(EC521KEY), CAstream, CAstreamLen)) < 0) { // if ((rc = matrixSslLoadEcKeysMem(keys, EC256, sizeof(EC256), // EC256KEY, sizeof(EC256KEY), CAstream, CAstreamLen)) < 0) { // if ((rc = matrixSslLoadEcKeysMem(keys, EC192, sizeof(EC192), // EC192KEY, sizeof(EC192KEY), 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, dhParamBuf1024, sizeof(dhParamBuf1024)) < 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 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 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, NULL); 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, NULL); 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, NULL); 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, NULL); #endif /* USE_HEADER_KEYS */ #ifdef USE_PSK_CIPHER_SUITE /* The first one supports the 15-byte openssl PSK ID */ matrixSslLoadPsk(keys, pskTable[0].key, sizeof(pskTable[0].key), pskTable[rc].id, 15); for (rc = 0; rc < 8; rc++) { matrixSslLoadPsk(keys, pskTable[rc].key, sizeof(pskTable[rc].key), pskTable[rc].id, sizeof(pskTable[rc].id)); } #endif /* PSK */ if (argc == 2) { switch (atoi(argv[1])) { case 0: g_proto = SSL_FLAGS_SSLV3; break; case 1: g_proto = SSL_FLAGS_TLS_1_0; break; case 2: g_proto = SSL_FLAGS_TLS_1_1; break; case 3: g_proto = SSL_FLAGS_TLS_1_2; break; default: g_proto = SSL_FLAGS_TLS_1_0; break; } } else { g_proto = 0; } /* Create the listening socket that will accept incoming connections */ if ((lfd = socketListen(HTTPS_PORT, &err)) == INVALID_SOCKET) { _psTraceInt("Can't listen on port %d\n", HTTPS_PORT); goto L_EXIT; } /* Main select loop to handle sockets events */ while (!g_exitFlag) { selectLoop(keys, lfd); } L_EXIT: if (lfd != INVALID_SOCKET) close(lfd); if (keys) matrixSslDeleteKeys(keys); matrixSslClose(); return 0; }
/* return 0 on successful encryption/decryption communication return -1 on failed comm */ static int32 exchangeAppData(sslConn_t *sendingSide, sslConn_t *receivingSide) { int32 writeBufLen, inBufLen, dataSent, rc, sentRc; uint32 ptLen, totalProcessed, requestedLen, copyLen, halfReqLen; unsigned char *writeBuf, *inBuf, *plaintextBuf; unsigned char copyByte; /* Client sends the data */ requestedLen = APP_DATA_LEN; copyByte = 0x0; /* Split the data into two records sends. Exercises the API a bit more having the extra buffer management for the second record */ while (requestedLen > 1) { copyByte++; halfReqLen = requestedLen / 2; writeBufLen = matrixSslGetWritebuf(sendingSide->ssl, &writeBuf, halfReqLen); copyLen = min(halfReqLen, writeBufLen); memset(writeBuf, copyByte, copyLen); requestedLen -= copyLen; //psTraceBytes("sending", writeBuf, copyLen); writeBufLen = matrixSslEncodeWritebuf(sendingSide->ssl, copyLen); copyByte++; writeBufLen = matrixSslGetWritebuf(sendingSide->ssl, &writeBuf, halfReqLen); copyLen = min(halfReqLen, writeBufLen); memset(writeBuf, copyByte, copyLen); requestedLen -= copyLen; //psTraceBytes("sending", writeBuf, copyLen); writeBufLen = matrixSslEncodeWritebuf(sendingSide->ssl, copyLen); } totalProcessed = 0; SEND_MORE: writeBufLen = matrixSslGetOutdata(sendingSide->ssl, &writeBuf); /* Receiving side must ask for storage space to receive data into. A good optimization of the buffer management can be seen here if a second pass was required: the inBufLen should exactly match the writeBufLen because when matrixSslReceivedData was called below the record length was parsed off and the buffer was reallocated to the exact necessary length */ inBufLen = matrixSslGetReadbuf(receivingSide->ssl, &inBuf); dataSent = min(writeBufLen, inBufLen); memcpy(inBuf, writeBuf, dataSent); /* Now update the sending side that data has been "sent" */ sentRc = matrixSslSentData(sendingSide->ssl, dataSent); /* Received data */ rc = matrixSslReceivedData(receivingSide->ssl, dataSent, &plaintextBuf, &ptLen); if (rc == MATRIXSSL_REQUEST_RECV) { goto SEND_MORE; } else if (rc == MATRIXSSL_APP_DATA) { while (rc == MATRIXSSL_APP_DATA) { //psTraceBytes("received", plaintextBuf, ptLen); if ((rc = matrixSslProcessedData(receivingSide->ssl, &plaintextBuf, &ptLen)) != 0) { if (rc == MATRIXSSL_APP_DATA) { continue; } else if (rc == MATRIXSSL_REQUEST_RECV) { goto SEND_MORE; } else { return PS_FAILURE; } } } if (sentRc == MATRIXSSL_REQUEST_SEND) { goto SEND_MORE; } } else { printf("Unexpected error in exchangeAppData: %d\n", rc); return PS_FAILURE; } return PS_SUCCESS; }
int ssl_connect_internal(int socket, const char* szTargetHost, int sslContextHandle) { static int status = MATRIXSSL_REQUEST_SEND; int ret = SOCK_SOCKET_ERROR; int nonblock = 0; int done = 0; SSL *ssl = NULL; SSL_Conext* sslContext = g_SSL_Driver.GetSSLContextBySslIndex( sslContextHandle); if (sslContext != NULL && sslContext->SslContext != NULL) { ssl = (SSL*) sslContext->SslContext; } else { MATRIXSSL_PERROR("Context not valid\n"); return SOCK_SOCKET_ERROR; } unsigned char *sslData; // WARNING - SSL_Connect is asynchronous and will be called multiple times for 1 connection, therefore // we only want to set the CA store on the first call (when sslData == NULL) // // The first certificate is the device's outbound certificate int rc = 0; int sent = 0; int len = matrixSslGetOutdata(ssl, &sslData); do { if (len > 0) //(status == MATRIXSSL_REQUEST_SEND ) { ret = SOCK_TRY_AGAIN; sent = SOCK_send(socket, (const char *) sslData, len, 0); //TODO check if sent rc = matrixSslSentData(ssl, sent); done = 1; if (rc == MATRIXSSL_SUCCESS) { status = MATRIXSSL_REQUEST_RECV; } } else //if(status == MATRIXSSL_REQUEST_RECV && done == 0) { int len = 1000; len = matrixSslGetReadbuf(ssl, &sslData); len = SOCK_recv(socket, (char*) sslData, len, 0); ret = SOCK_TRY_AGAIN; if (len > 0) { rc = matrixSslReceivedData(ssl, (int32) len, &sslData, (uint32*) &len); if (rc == MATRIXSSL_REQUEST_RECV) { status = MATRIXSSL_REQUEST_RECV; } if (rc == MATRIXSSL_REQUEST_SEND) { status = MATRIXSSL_REQUEST_SEND; ret = SOCK_TRY_AGAIN; } if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { ret = 0; } } } if (done == 0) { len = matrixSslGetOutdata(ssl, &sslData); } else { len = 0; } } while (len); g_SSL_Driver.AddSslSocketHandle(sslContextHandle, socket); return ret; }