/* 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; }
/* Create an HTTP request and encode it to the SSL buffer */ static int32 httpWriteRequest(ssl_t *ssl) { unsigned char *buf; uint32 available, requested; requested = strlen((char *)g_httpRequestHdr) + 1; if ((available = matrixSslGetWritebuf(ssl, &buf, requested)) < 0) { return PS_MEM_FAIL; } requested = min(requested, available); strncpy((char *)buf, (char *)g_httpRequestHdr, requested); _psTraceStr("SEND: [%s]\n", (char*)buf); if (matrixSslEncodeWritebuf(ssl, strlen((char *)buf)) < 0) { return PS_MEM_FAIL; } return MATRIXSSL_REQUEST_SEND; }
int ssl_write_internal(int socket, const char* data, size_t size) { unsigned char *sslData; int rc = 0; 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; } MATRIXSSL_PDEBUG_ALL("------------- called ----------------: Socket: %i\n", socket); /* SSL* ssl = (SSL*) g_SSL_Driver.m_sslContextArray[0].SslContext; if (ssl == NULL) { return SOCK_SOCKET_ERROR; }*/ int available = 0, requested = size, sent = 0; if ((available = matrixSslGetWritebuf(ssl, &sslData, size)) < 0) { return SOCK_SOCKET_ERROR; } requested = min(requested, available); TINYCLR_SSL_MEMCPY(sslData, data, requested); rc = matrixSslEncodeWritebuf(ssl, requested); //PRINT_RETURN_VALUE(rc); //if > 0 number of bytes encoded if (rc < 0) { PRINT_UNEXPECTED_RETURN_VALUE(rc); return SOCK_SOCKET_ERROR; } int len = matrixSslGetOutdata(ssl, &sslData); sent = SOCK_send(socket, (const char *) sslData, len, 0); rc = matrixSslSentData(ssl, sent); PRINT_RETURN_VALUE(rc); if (rc == MATRIXSSL_SUCCESS) { return sent; } PRINT_UNEXPECTED_RETURN_VALUE(rc); return SOCK_SOCKET_ERROR; }
/* 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; }
/* sslWrite encodes 'data' as a single SSL record. Return codes: -1 Internal failure. caller should free sess and close socket 0 WOULDBLOCK. caller should call back with same input later > 0 success status. number of plaintext bytes encoded and sent In order for non-blocking sockets to work transparently to the upper layers in webs, we manage the buffering of outgoing data and fudge the number of bytes sent until the entire SSL record is sent. This is because the encoded SSL record will be longer than 'len' with the SSL header prepended and the MAC and padding appended. There is no easy way to indicate to the caller if only a partial record was sent via the webs socket API. For example, if the caller specifies one byte of 'data' ('len' == 1), the SSL record could be 33 bytes long. If the socket send is able to write 10 bytes of data, how would we indicate this to the caller, which expects only 1 byte, 0 bytes or a negative error code as a result of this call? The solution here is to always return 0 from this function, until we have flushed out all the bytes in the SSL record (33 in this example), and when the record has all been sent, return the originally requested length, which is 1 in this example. This assumes that on a 0 return, the caller will wait for a writable event on the socket and re-call this api with the same 'data' and 'len' as previously sent. Essentially a 0 return means "retry the same sslWrite call later". */ int sslWrite(sslConn_t *cp, char *data, int len) { unsigned char *buf; int rc, transferred, ctLen; /* If sendBlocked is set, then the previous time into sslWrite with this cp could not send all the requested data, and zero was returned to the caller. In this case, the data has already been encoded into the SSL outdata buffer, and we don't need to re-encode it here. This assumes that the caller is sending the same 'len' and contents of 'data' as they did last time. */ if (!cp->sendBlocked) { if (matrixSslGetWritebuf(cp->ssl, &buf, len) < len) { /* SSL buffer must hold requested plaintext + SSL overhead */ return -1; } memcpy((char *)buf, data, len); if (matrixSslEncodeWritebuf(cp->ssl, len) < 0) { return -1; } } else { /* Not all previously encoded data could be sent without blocking and 0 bytes was previously returned to caller as sent. Ensure caller is retrying with same len (and presumably the same data). */ if (len != cp->ptReqBytes) { a_assert(len != cp->ptReqBytes); return -1; } } WRITE_MORE: /* There is a small chance that we are here with sendBlocked set, and yet there is no buffered outdata to send. This happens because a send can also happen in sslRead, and that could have flushed the outgoing buffer in addition to a handshake message reply. */ ctLen = matrixSslGetOutdata(cp->ssl, &buf); if (ctLen > 0) { transferred = send(cp->fd, buf, ctLen, MSG_NOSIGNAL); if (transferred <= 0) { #ifdef USE_NONBLOCKING_SSL_SOCKETS if (socketGetError() != EWOULDBLOCK) { return -1; } if (!cp->sendBlocked) { cp->sendBlocked = 1; cp->ptReqBytes = len; } else { } return 0; #else return -1; #endif } /* Update the SSL buffer that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) { return -1; } /* There is more data in the SSL buffer to send */ if (rc == MATRIXSSL_REQUEST_SEND) { goto WRITE_MORE; } } cp->sendBlocked = 0; cp->ptReqBytes = 0; return len; }
/* 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; }
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); }
/* 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; }