Files
mars-matrixssl/apps/ssl/server.c
2016-05-03 17:36:14 -07:00

838 lines
24 KiB
C

/**
* @file server.c
* @version $Format:%h%d$
*
* Simple non-blocking MatrixSSL server example for multiple connections.
* Uses a single, hardcoded RSA identity. No client authentication.
*/
/*
* Copyright (c) 2013-2016 INSIDE Secure Corporation
* Copyright (c) PeerSec Networks, 2002-2011
* All Rights Reserved
*
* The latest version of this code is available at http://www.matrixssl.org
*
* This software is open source; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This General Public License does NOT permit incorporating this software
* into proprietary programs. If you are unable to comply with the GPL, a
* commercial license for this software may be purchased from INSIDE at
* http://www.insidesecure.com/
*
* This program is distributed in WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* http://www.gnu.org/copyleft/gpl.html
*/
/******************************************************************************/
#include "app.h"
#include "matrixssl/matrixsslApi.h"
#ifdef USE_SERVER_SIDE_SSL
#include <signal.h> /* Defines SIGTERM, etc. */
#ifdef WIN32
#pragma message("DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS.")
#else
#warning "DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS."
#endif
#define ALLOW_ANON_CONNECTIONS 1
#define SEND_CLOSURE_ALERT
/* Directory path to MatrixSSL testkeys */
#define KEY_DIR "../../"
const static char certFile[] = "testkeys/RSA/2048_RSA.pem";
const static char privkeyFile[] = "testkeys/RSA/2048_RSA_KEY.pem";
#ifdef REQUIRE_DH_PARAMS
const static char dhParamFile[] = "testkeys/DH/1024_DH_PARAMS.pem";
#endif
/********************************** Defines ***********************************/
#define SSL_TIMEOUT 45000 /* In milliseconds */
#define SELECT_TIME 1000 /* In milliseconds */
#define RESPONSE_REC_LEN SSL_MAX_PLAINTEXT_LEN
#define GOTO_SANITY 32 /* Must be <= 255 */
/*
The ACCEPT_QUEUE is an optimization mechanism that allows the server to
accept() up to this many connections before serving any of them. The
reason is that the timeout waiting for the accept() is much shorter
than the timeout for the actual processing.
*/
#define ACCEPT_QUEUE 16
/********************************** Globals ***********************************/
static DLListEntry g_conns;
static int32 g_exitFlag;
static unsigned char g_httpResponseHdr[] = "HTTP/1.0 200 OK\r\n"
"Server: MatrixSSL/" MATRIXSSL_VERSION "\r\n"
"Pragma: no-cache\r\n"
"Cache-Control: no-cache\r\n"
"Content-type: text/plain\r\n"
"Content-length: 9\r\n"
"\r\n"
"MatrixSSL";
/****************************** Local Functions *******************************/
static int32 selectLoop(sslKeys_t *keys, SOCKET lfd);
static int32 httpWriteResponse(httpConn_t *conn);
static int setSocketOptions(SOCKET fd);
static SOCKET lsocketListen(short port, int32 *err);
static void closeConn(httpConn_t *cp, int32 reason);
#ifdef POSIX
static void sigsegv_handler(int i);
static void sigintterm_handler(int i);
static int32 sighandlers(void);
#endif /* POSIX */
/******************************************************************************/
/**
Display connections per second (if more than 0), at most once per second
*/
static uint64_t g_handshakes = 0;
static void displayStats(void)
{
static uint64_t s_handshakes = 0; /* last value displayed */
static time_t s_t = (time_t)0; /* last time displayed */
time_t t;
if (g_handshakes > s_handshakes) {
t = time(NULL);
if (t > s_t) {
printf("%u CPS\n",
(uint32_t)(g_handshakes - s_handshakes) / (uint32_t)(t - s_t));
s_handshakes = g_handshakes;
s_t = t;
}
}
}
/******************************************************************************/
/*
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, specialAppData;
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 */
}
if (setSocketOptions(fd) < 0) {
close(fd);
return PS_PLATFORM_FAIL;
}
cp = malloc(sizeof(httpConn_t));
if (cp == NULL) {
close(fd);
return PS_MEM_FAIL;
}
memset(cp, 0x0, sizeof(httpConn_t));
memset(&options, 0x0, sizeof(sslSessOpts_t));
options.userPtr = keys;
if ((rc = matrixSslNewServerSession(&cp->ssl, keys, NULL,
&options)) < 0) {
close(fd);
continue;
}
cp->fd = fd;
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 = (int32)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 */
g_handshakes++;
#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 */
}
/* If we are sending response data and it's all encoded and sent, close conn */
if (cp->bytes_requested > 0 &&
cp->bytes_requested == cp->bytes_sent &&
matrixSslGetOutdata(cp->ssl, &buf) <= 0) {
closeConn(cp, PS_SUCCESS);
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 = (int32)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:
g_handshakes++;
/* 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);
/* First test to see if this is one of the special data
requests used for testing.
First is a "GET /bytes?<byteCount>" format
*/
specialAppData = 0;
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 =
(uint32)strlen((char *)g_httpResponseHdr);
}
cp->bytes_sent = 0;
specialAppData = 1;
}
/* A special test for TLS 1.0 where BEAST workaround used */
if (len > 10 &&
strncmp((char *)buf, "ET /bytes?", 10) == 0) {
cp->bytes_requested = atoi((char *)buf + 10);
if (cp->bytes_requested <
strlen((char *)g_httpResponseHdr) ||
cp->bytes_requested > 1073741824) {
cp->bytes_requested =
(uint32)strlen((char *)g_httpResponseHdr);
}
cp->bytes_sent = 0;
specialAppData = 1;
}
/* Shutdown the server */
if (len >= 15 &&
strncmp((char*)buf, "MATRIX_SHUTDOWN", 15) == 0) {
g_exitFlag = 1;
rc = matrixSslEncodeClosureAlert(cp->ssl);
psAssert(rc >= 0);
_psTrace("Got MATRIX_SHUTDOWN. Exiting\n");
goto WRITE_MORE;
}
if (specialAppData == 0) {
if ((rc = httpBasicParse(cp, buf, len, 0)) < 0) {
_psTrace("Couldn't parse HTTP data. Closing...\n");
closeConn(cp, PS_PROTOCOL_FAIL);
continue; /* Next connection */
}
}
if (rc == HTTPS_COMPLETE || specialAppData == 1) {
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 */
#ifdef SEND_CLOSURE_ALERT
// rc = matrixSslEncodeClosureAlert(cp->ssl);
// psAssert(rc >= 0);
#endif
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 *cp)
{
unsigned char *buf;
ssl_t *ssl;
int32 available, len, rc;
ssl = cp->ssl;
/* The /bytes? HTTP request assigns bytes_requested */
if (cp->bytes_requested) {
/*
Generate TLS records for all the requested bytes.
This can put a lot of data in ssl outbuf, so we attempt
to flush it out at the bottom of the loop.
Anything left over will be sent out in the main server loop.
*/
while (cp->bytes_sent < cp->bytes_requested) {
len = cp->bytes_requested - cp->bytes_sent;
if (len < 0) {
return PS_MEM_FAIL;
}
if (len > RESPONSE_REC_LEN) {
len = RESPONSE_REC_LEN;
}
if ((rc = matrixSslGetWritebuf(ssl, &buf, len)) < 1) {
return PS_MEM_FAIL;
}
if (rc < len) {
len = rc; /* could have been shortened due to max_frag */
}
memset(buf, 'J', len);
if (cp->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(ssl, len)) < 0) {
printf("couldn't encode data %d\n", rc);
}
cp->bytes_sent += len;
/*
Do a quick, non-blocking send here to start flushing the
generated records. We could flush after each record encode,
or only on a multiple of record encodes.
*/
if (matrixSslGetOutdata(ssl, &buf) > (RESPONSE_REC_LEN * 4)) {
if ((len = (int32)send(cp->fd, buf, len, MSG_DONTWAIT)) > 0) {
rc = matrixSslSentData(ssl, len);
// psAssert(rc != MATRIXSSL_REQUEST_SEND); /* Some data remains */
}
}
}
return MATRIXSSL_REQUEST_SEND;
}
/* Usual reply */
if ((available = matrixSslGetWritebuf(ssl, &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(ssl, (uint32)strlen((char *)buf)) < 0) {
return PS_MEM_FAIL;
}
return MATRIXSSL_REQUEST_SEND;
}
static void usage(void)
{
printf( "This application takes no runtime parameters.\n"
"Configuration is through defines in the source.\n");
}
/******************************************************************************/
/*
Main non-blocking SSL server
Initialize MatrixSSL and sockets layer, and loop on select
*/
int32 main(int32 argc, char **argv)
{
SOCKET lfd;
int32 err, rc;
#ifdef WIN32
WSADATA wsaData;
#endif
char certpath[FILENAME_MAX];
char keypath[FILENAME_MAX];
sslKeys_t *keys = NULL;
if (argc > 1) {
usage();
return 0;
}
#ifdef WIN32
WSAStartup(MAKEWORD(1, 1), &wsaData);
#endif
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) {
return -1;
}
snprintf(certpath, FILENAME_MAX - 1, "%s/%s", KEY_DIR, certFile);
snprintf(keypath, FILENAME_MAX - 1, "%s/%s", KEY_DIR, privkeyFile);
if ((rc = matrixSslLoadRsaKeys(keys, certpath, keypath, NULL, NULL)) < 0) {
_psTrace("Unable to load static key material. Exiting\n");
return rc;
}
#ifdef REQUIRE_DH_PARAMS
snprintf(certpath, FILENAME_MAX - 1, "%s/%s", KEY_DIR, dhParamFile);
if ((rc = matrixSslLoadDhParams(keys, certpath)) < 0) {
_psTrace("Unable to load static key material. Exiting\n");
return rc;
}
#endif
/* Create the listening socket that will accept incoming connections */
if ((lfd = lsocketListen(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);
displayStats();
}
/* Close any active connections */
while (!DLListIsEmpty(&g_conns)) {
httpConn_t *cp;
DLListEntry *pList;
pList = DLListGetHead(&g_conns);
cp = DLListGetContainer(pList, httpConn_t, List);
closeConn(cp, PS_SUCCESS);
}
L_EXIT:
if (lfd != INVALID_SOCKET) close(lfd);
matrixSslClose();
return 0;
}
/******************************************************************************/
/*
Close a socket and free associated SSL context and buffers
*/
static void closeConn(httpConn_t *cp, int32 reason)
{
#ifdef SEND_CLOSURE_ALERT
unsigned char *buf;
#endif
int32 len;
DLListRemove(&cp->List);
#ifdef SEND_CLOSURE_ALERT
/* Quick attempt to send a closure alert, don't worry about failure */
if (matrixSslEncodeClosureAlert(cp->ssl) >= 0) {
if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) {
//psTraceBytes("closure alert", buf, len);
if ((len = (int32)send(cp->fd, buf, len, MSG_DONTWAIT)) > 0) {
matrixSslSentData(cp->ssl, len);
}
}
}
#endif
if (cp->parsebuf != NULL) {
psAssert(cp->parsebuflen > 0);
free(cp->parsebuf);
cp->parsebuflen = 0;
}
matrixSslDeleteSession(cp->ssl);
if (cp->fd != INVALID_SOCKET) {
close(cp->fd);
}
if (reason >= 0) {
/* _psTraceInt("=== Closing Client %d ===\n", cp->fd); */
} else {
_psTraceInt("=== Closing Client %d on Error ===\n", cp->fd);
}
free(cp);
}
/******************************************************************************/
/*
Establish a listening socket for incomming connections
*/
static SOCKET lsocketListen(short port, int32 *err)
{
struct sockaddr_in addr = { 0 };
SOCKET fd;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
_psTrace("Error creating listen socket\n");
*err = SOCKET_ERRNO;
return INVALID_SOCKET;
}
if (setSocketOptions(fd) < 0) {
close(fd);
return INVALID_SOCKET;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(fd);
_psTrace("Can't bind socket. Port in use or insufficient privilege\n");
*err = SOCKET_ERRNO;
return INVALID_SOCKET;
}
if (listen(fd, SOMAXCONN) < 0) {
close(fd);
_psTrace("Error listening on socket\n");
*err = SOCKET_ERRNO;
return INVALID_SOCKET;
}
_psTraceInt("Listening on port %d\n", port);
return fd;
}
/******************************************************************************/
/*
Make sure the socket is not inherited by exec'd processes
Set the REUSE flag to minimize the number of sockets in TIME_WAIT
Then we set REUSEADDR, NODELAY and NONBLOCK on the socket
*/
static int setSocketOptions(SOCKET fd)
{
int rc;
#ifdef POSIX
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
return PS_PLATFORM_FAIL;
}
#endif
rc = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)) < 0) {
return PS_PLATFORM_FAIL;
}
#ifdef POSIX
rc = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&rc, sizeof(rc)) < 0) {
return PS_PLATFORM_FAIL;
}
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0) {
return PS_PLATFORM_FAIL;
}
#elif defined(WIN32)
rc = 1; /* 1 for non-block, 0 for block */
if (ioctlsocket(fd, FIONBIO, &rc) < 0) {
return PS_PLATFORM_FAIL;
}
#endif
#ifdef __APPLE__ /* MAC OS X */
rc = 1;
if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&rc, sizeof(rc)) < 0) {
return PS_PLATFORM_FAIL;
}
#endif
return PS_SUCCESS;
}
#ifdef POSIX
/******************************************************************************/
/*
Handle some signals on POSIX platforms
Lets ctrl-c do a clean exit of the server.
*/
static int32 sighandlers(void)
{
if (signal(SIGINT, sigintterm_handler) == SIG_ERR ||
signal(SIGTERM, sigintterm_handler) == SIG_ERR ||
signal(SIGPIPE, SIG_IGN) == SIG_ERR ||
signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
return PS_PLATFORM_FAIL;
}
return 0;
}
/* Warn on segmentation violation */
static void sigsegv_handler(int unused)
{
printf("Segfault! Please report this as a bug to support@peersec.com\n");
exit(EXIT_FAILURE);
}
/* catch ctrl-c or sigterm */
static void sigintterm_handler(int unused)
{
g_exitFlag = 1; /* Rudimentary exit flagging */
printf("Exiting due to interrupt.\n");
}
#endif /* POSIX */
#else
/******************************************************************************/
/*
Stub main for compiling without server enabled
*/
int32 main(int32 argc, char **argv)
{
printf("USE_SERVER_SIDE_SSL must be enabled in matrixsslConfig.h at build" \
" time to run this application\n");
return -1;
}
#endif /* USE_SERVER_SIDE_SSL */
/******************************************************************************/