1 /*
2 * server.c
3 * Release $Name: MATRIXSSL-3-3-0-OPEN $
4 *
5 * Non-blocking MatrixSSL server example supporting multiple connections
6 */
7 /*
8 * Copyright (c) AuthenTec, Inc. 2011-2012
9 * Copyright (c) PeerSec Networks, 2002-2011
10 * All Rights Reserved
11 *
12 * The latest version of this code is available at http://www.matrixssl.org
13 *
14 * This software is open source; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This General Public License does NOT permit incorporating this software
20 * into proprietary programs. If you are unable to comply with the GPL, a
21 * commercial license for this software may be purchased from AuthenTec at
22 * http://www.authentec.com/Products/EmbeddedSecurity/SecurityToolkits.aspx
23 *
24 * This program is distributed in WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26 * See the GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 * http://www.gnu.org/copyleft/gpl.html
32 */
33 /******************************************************************************/
34
35 #include "app.h"
36 #include "matrixssl/matrixsslApi.h"
37
38 #ifdef USE_SERVER_SIDE_SSL
39
40 #include <signal.h> /* Defines SIGTERM, etc. */
41
42 #ifdef WIN32
43 #pragma message("DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS.")
44 #else
45 #warning "DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS."
46 #endif
47
48
49 #define USE_HEADER_KEYS
50
51 #ifdef USE_HEADER_KEYS
52 #include "sampleCerts/certSrv.h"
53 #include "sampleCerts/privkeySrv.h"
54 #else
55 static char certSrvFile[] = "certSrv.pem";
56 static char privkeySrvFile[] = "privkeySrv.pem";
57 #endif /* USE_HEADER_KEYS */
58
59
60 /********************************** Defines ***********************************/
61
62 #define SSL_TIMEOUT 45000
63 #define SELECT_TIME 1000
64
65 #define GOTO_SANITY 32 /* Must be <= 255 */
66 /*
67 The ACCEPT_QUEUE is an optimization mechanism that allows the server to
68 accept() up to this many connections before serving any of them. The
69 reason is that the timeout waiting for the accept() is much shorter
70 than the timeout for the actual processing.
71 */
72 #define ACCEPT_QUEUE 16
73
74 /********************************** Globals ***********************************/
75
76 static DLListEntry g_conns;
77 static int32 g_exitFlag;
78 static unsigned char g_httpResponseHdr[] = "HTTP/1.0 200 OK\r\n"
79 "Server: AuthenTec MatrixSSL/" MATRIXSSL_VERSION "\r\n"
80 "Pragma: no-cache\r\n"
81 "Cache-Control: no-cache\r\n"
82 "Content-type: text/plain\r\n"
83 "Content-length: 9\r\n"
84 "\r\n"
85 "MatrixSSL";
86
87
88 /****************************** Local Functions *******************************/
89
90 static int32 selectLoop(sslKeys_t *keys, SOCKET lfd);
91 static int32 httpWriteResponse(ssl_t *cp);
92 static void setSocketOptions(SOCKET fd);
93 static SOCKET socketListen(short port, int32 *err);
94 static void closeConn(httpConn_t *cp, int32 reason);
95
96 #ifdef POSIX
97 static void sigsegv_handler(int i);
98 static void sigintterm_handler(int i);
99 static int32 sighandlers(void);
100 #endif /* POSIX */
101
102
103 #define certCb NULL
104
105 /******************************************************************************/
106 /*
107 Non-blocking socket event handler
108 Wait one time in select for events on any socket
109 This will accept new connections, read and write to sockets that are
110 connected, and close sockets as required.
111 */
selectLoop(sslKeys_t * keys,SOCKET lfd)112 static int32 selectLoop(sslKeys_t *keys, SOCKET lfd)
113 {
114 httpConn_t *cp;
115 psTime_t now;
116 DLListEntry connsTmp;
117 DLListEntry *pList;
118
119 fd_set readfd, writefd;
120 struct timeval timeout;
121 SOCKET fd, maxfd;
122
123 unsigned char *buf;
124 int32 rc, len, transferred, val;
125 unsigned char rSanity, wSanity, acceptSanity;
126
127 DLListInit(&connsTmp);
128 rc = PS_SUCCESS;
129 maxfd = INVALID_SOCKET;
130 timeout.tv_sec = SELECT_TIME / 1000;
131 timeout.tv_usec = (SELECT_TIME % 1000) * 1000;
132 FD_ZERO(&readfd);
133 FD_ZERO(&writefd);
134
135 /* Always set readfd for listening socket */
136 FD_SET(lfd, &readfd);
137 if (lfd > maxfd) {
138 maxfd = lfd;
139 }
140 /*
141 Check timeouts and set readfd and writefd for connections as required.
142 We use connsTemp so that removal on error from the active iteration list
143 doesn't interfere with list traversal
144 */
145 psGetTime(&now);
146 while (!DLListIsEmpty(&g_conns)) {
147 pList = DLListGetHead(&g_conns);
148 cp = DLListGetContainer(pList, httpConn_t, List);
149 DLListInsertTail(&connsTmp, &cp->List);
150 /* If timeout != 0 msec ith no new data, close */
151 if (cp->timeout && (psDiffMsecs(cp->time, now) > (int32)cp->timeout)) {
152 closeConn(cp, PS_TIMEOUT_FAIL);
153 continue; /* Next connection */
154 }
155 /* Always select for read */
156 FD_SET(cp->fd, &readfd);
157 /* Select for write if there's pending write data or connection */
158 if (matrixSslGetOutdata(cp->ssl, NULL) > 0) {
159 FD_SET(cp->fd, &writefd);
160 }
161 /* Housekeeping for maxsock in select call */
162 if (cp->fd > maxfd) {
163 maxfd = cp->fd;
164 }
165 }
166
167 /* Use select to check for events on the sockets */
168 if ((val = select(maxfd + 1, &readfd, &writefd, NULL, &timeout)) <= 0) {
169 /* On error, restore global connections list */
170 while (!DLListIsEmpty(&connsTmp)) {
171 pList = DLListGetHead(&connsTmp);
172 cp = DLListGetContainer(pList, httpConn_t, List);
173 DLListInsertTail(&g_conns, &cp->List);
174 }
175 /* Select timeout */
176 if (val == 0) {
177 return PS_TIMEOUT_FAIL;
178 }
179 /* Woke due to interrupt */
180 if (SOCKET_ERRNO == EINTR) {
181 return PS_TIMEOUT_FAIL;
182 }
183 /* Should attempt to handle more errnos, such as EBADF */
184 return PS_PLATFORM_FAIL;
185 }
186
187 /* Check listener for new incoming socket connections */
188 if (FD_ISSET(lfd, &readfd)) {
189 for (acceptSanity = 0; acceptSanity < ACCEPT_QUEUE; acceptSanity++) {
190 fd = accept(lfd, NULL, NULL);
191 if (fd == INVALID_SOCKET) {
192 break; /* Nothing more to accept; next listener */
193 }
194 setSocketOptions(fd);
195 cp = malloc(sizeof(httpConn_t));
196 if ((rc = matrixSslNewServerSession(&cp->ssl, keys, certCb)) < 0) {
197 close(fd); fd = INVALID_SOCKET;
198 continue;
199 }
200 cp->fd = fd;
201 fd = INVALID_SOCKET;
202 cp->timeout = SSL_TIMEOUT;
203 psGetTime(&cp->time);
204 cp->parsebuf = NULL;
205 cp->parsebuflen = 0;
206 DLListInsertTail(&connsTmp, &cp->List);
207 /* Fake that there is read data available, no harm if there isn't */
208 FD_SET(cp->fd, &readfd);
209 /* _psTraceInt("=== New Client %d ===\n", cp->fd); */
210 }
211 }
212
213 /* Check each connection for read/write activity */
214 while (!DLListIsEmpty(&connsTmp)) {
215 pList = DLListGetHead(&connsTmp);
216 cp = DLListGetContainer(pList, httpConn_t, List);
217 DLListInsertTail(&g_conns, &cp->List);
218
219 rSanity = wSanity = 0;
220 /*
221 See if there's pending data to send on this connection
222 We could use FD_ISSET, but this is more reliable for the current
223 state of data to send.
224 */
225 WRITE_MORE:
226 if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) {
227 /* Could get a EWOULDBLOCK since we don't check FD_ISSET */
228 transferred = send(cp->fd, buf, len, MSG_DONTWAIT);
229 if (transferred <= 0) {
230 #ifdef WIN32
231 if (SOCKET_ERRNO != EWOULDBLOCK &&
232 SOCKET_ERRNO != WSAEWOULDBLOCK) {
233
234 #else
235 if (SOCKET_ERRNO != EWOULDBLOCK) {
236 #endif
237 closeConn(cp, PS_PLATFORM_FAIL);
238 continue; /* Next connection */
239 }
240 } else {
241 /* Indicate that we've written > 0 bytes of data */
242 if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) {
243 closeConn(cp, PS_ARG_FAIL);
244 continue; /* Next connection */
245 }
246 if (rc == MATRIXSSL_REQUEST_CLOSE) {
247 closeConn(cp, MATRIXSSL_REQUEST_CLOSE);
248 continue; /* Next connection */
249 } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) {
250 /* If the protocol is server initiated, send data here */
251 #ifdef ENABLE_FALSE_START
252 /* OR this could be a Chrome browser using
253 FALSE_START and the application data is already
254 waiting in our inbuf for processing */
255 if ((rc = matrixSslReceivedData(cp->ssl, 0,
256 &buf, (uint32*)&len)) < 0) {
257 closeConn(cp, 0);
258 continue; /* Next connection */
259 }
260 if (rc > 0) { /* There was leftover data */
261 goto PROCESS_MORE;
262 }
263 #endif /* ENABLE_FALSE_START */
264
265 }
266 /* Update activity time */
267 psGetTime(&cp->time);
268 /* Try to send again if more data to send */
269 if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) {
270 if (wSanity++ < GOTO_SANITY) goto WRITE_MORE;
271 }
272 }
273 } else if (len < 0) {
274 closeConn(cp, PS_ARG_FAIL);
275 continue; /* Next connection */
276 }
277
278 /*
279 Check the file descriptor returned from select to see if the connection
280 has data to be read
281 */
282 if (FD_ISSET(cp->fd, &readfd)) {
283 READ_MORE:
284 /* Get the ssl buffer and how much data it can accept */
285 /* Note 0 is a return failure, unlike with matrixSslGetOutdata */
286 if ((len = matrixSslGetReadbuf(cp->ssl, &buf)) <= 0) {
287 closeConn(cp, PS_ARG_FAIL);
288 continue; /* Next connection */
289 }
290 if ((transferred = recv(cp->fd, buf, len, MSG_DONTWAIT)) < 0) {
291 /* We could get EWOULDBLOCK despite the FD_ISSET on goto */
292 #ifdef WIN32
293 if (SOCKET_ERRNO != EWOULDBLOCK &&
294 SOCKET_ERRNO != WSAEWOULDBLOCK) {
295
296 #else
297 if (SOCKET_ERRNO != EWOULDBLOCK) {
298 #endif
299 closeConn(cp, PS_PLATFORM_FAIL);
300 }
301 continue; /* Next connection */
302 }
303 /* If EOF, remote socket closed. This is semi-normal closure.
304 Officially, we should close on closure alert. */
305 if (transferred == 0) {
306 /* psTraceIntInfo("Closing connection %d on EOF\n", cp->fd); */
307 closeConn(cp, 0);
308 continue; /* Next connection */
309 }
310 /*
311 Notify SSL state machine that we've received more data into the
312 ssl buffer retreived with matrixSslGetReadbuf.
313 */
314 if ((rc = matrixSslReceivedData(cp->ssl, (int32)transferred, &buf,
315 (uint32*)&len)) < 0) {
316 closeConn(cp, 0);
317 continue; /* Next connection */
318 }
319 /* Update activity time */
320 psGetTime(&cp->time);
321
322 PROCESS_MORE:
323 /* Process any incoming plaintext application data */
324 switch (rc) {
325 case MATRIXSSL_HANDSHAKE_COMPLETE:
326 /* If the protocol is server initiated, send data here */
327 goto READ_MORE;
328 case MATRIXSSL_APP_DATA:
329 /* Remember, must handle if len == 0! */
330 if ((rc = httpBasicParse(cp, buf, len)) < 0) {
331 _psTrace("Couldn't parse HTTP data. Closing conn.\n");
332 closeConn(cp, PS_PROTOCOL_FAIL);
333 continue; /* Next connection */
334 }
335 if (rc == HTTPS_COMPLETE) {
336 if (httpWriteResponse(cp->ssl) < 0) {
337 closeConn(cp, PS_PROTOCOL_FAIL);
338 continue; /* Next connection */
339 }
340 /* For HTTP, we assume no pipelined requests, so we
341 close after parsing a single HTTP request */
342 /* Ignore return of closure alert, it's optional */
343 matrixSslEncodeClosureAlert(cp->ssl);
344 rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len);
345 if (rc > 0) {
346 /* Additional data is available, but we ignore it */
347 _psTrace("HTTP data parsing not supported, ignoring.\n");
348 closeConn(cp, PS_SUCCESS);
349 continue; /* Next connection */
350 } else if (rc < 0) {
351 closeConn(cp, PS_PROTOCOL_FAIL);
352 continue; /* Next connection */
353 }
354 /* rc == 0, write out our response and closure alert */
355 goto WRITE_MORE;
356 }
357 /* We processed a partial HTTP message */
358 if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) {
359 goto READ_MORE;
360 }
361 goto PROCESS_MORE;
362 case MATRIXSSL_REQUEST_SEND:
363 /* Prevent us from reading again after the write,
364 although that wouldn't be the end of the world */
365 FD_CLR(cp->fd, &readfd);
366 if (wSanity++ < GOTO_SANITY) goto WRITE_MORE;
367 break;
368 case MATRIXSSL_REQUEST_RECV:
369 if (rSanity++ < GOTO_SANITY) goto READ_MORE;
370 break;
371 case MATRIXSSL_RECEIVED_ALERT:
372 /* The first byte of the buffer is the level */
373 /* The second byte is the description */
374 if (*buf == SSL_ALERT_LEVEL_FATAL) {
375 psTraceIntInfo("Fatal alert: %d, closing connection.\n",
376 *(buf + 1));
377 closeConn(cp, PS_PROTOCOL_FAIL);
378 continue; /* Next connection */
379 }
380 /* Closure alert is normal (and best) way to close */
381 if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) {
382 closeConn(cp, PS_SUCCESS);
383 continue; /* Next connection */
384 }
385 psTraceIntInfo("Warning alert: %d\n", *(buf + 1));
386 if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) {
387 /* No more data in buffer. Might as well read for more. */
388 goto READ_MORE;
389 }
390 goto PROCESS_MORE;
391
392 default:
393 /* If rc <= 0 we fall here */
394 closeConn(cp, PS_PROTOCOL_FAIL);
395 continue; /* Next connection */
396 }
397 /* Always try to read more if we processed some data */
398 if (rSanity++ < GOTO_SANITY) goto READ_MORE;
399 } /* readfd handling */
400 } /* connection loop */
401 return PS_SUCCESS;
402 }
403
404 /******************************************************************************/
405 /*
406 Create an HTTP response and encode it to the SSL buffer
407 */
408 #define TEST_SIZE 16000
409 static int32 httpWriteResponse(ssl_t *cp)
410 {
411 unsigned char *buf;
412 int32 available;
413
414 if ((available = matrixSslGetWritebuf(cp, &buf,
415 strlen((char *)g_httpResponseHdr) + 1)) < 0) {
416 return PS_MEM_FAIL;
417 }
418 strncpy((char *)buf, (char *)g_httpResponseHdr, available);
419 if (matrixSslEncodeWritebuf(cp, strlen((char *)buf)) < 0) {
420 return PS_MEM_FAIL;
421 }
422 return MATRIXSSL_REQUEST_SEND;
423 }
424
425 /******************************************************************************/
426 /*
427 Main non-blocking SSL server
428 Initialize MatrixSSL and sockets layer, and loop on select
429 */
430 int32 main(int32 argc, char **argv)
431 {
432 sslKeys_t *keys;
433 SOCKET lfd;
434 int32 err, rc;
435 #ifdef WIN32
436 WSADATA wsaData;
437 WSAStartup(MAKEWORD(1, 1), &wsaData);
438 #endif
439
440
441 keys = NULL;
442 DLListInit(&g_conns);
443 g_exitFlag = 0;
444 lfd = INVALID_SOCKET;
445
446 #ifdef POSIX
447 if (sighandlers() < 0) {
448 return PS_PLATFORM_FAIL;
449 }
450 #endif /* POSIX */
451 if ((rc = matrixSslOpen()) < 0) {
452 _psTrace("MatrixSSL library init failure. Exiting\n");
453 return rc;
454 }
455 if (matrixSslNewKeys(&keys) < 0) {
456 _psTrace("MatrixSSL library key init failure. Exiting\n");
457 return -1;
458 }
459
460 #ifdef USE_HEADER_KEYS
461 /*
462 In-memory based keys
463 */
464 if ((rc = matrixSslLoadRsaKeysMem(keys, certSrvBuf, sizeof(certSrvBuf),
465 privkeySrvBuf, sizeof(privkeySrvBuf), NULL, 0)) < 0) {
466 _psTrace("No certificate material loaded. Exiting\n");
467 matrixSslDeleteKeys(keys);
468 matrixSslClose();
469 return rc;
470 }
471 #else /* USE_HEADER_KEYS */
472 /*
473 File based keys
474 */
475 if ((rc = matrixSslLoadRsaKeys(keys, certSrvFile, privkeySrvFile, NULL,
476 NULL)) < 0) {
477 _psTrace("No certificate material loaded. Exiting\n");
478 matrixSslDeleteKeys(keys);
479 matrixSslClose();
480 return rc;
481 }
482 #endif /* USE_HEADER_KEYS */
483
484
485 /* Create the listening socket that will accept incoming connections */
486 if ((lfd = socketListen(HTTPS_PORT, &err)) == INVALID_SOCKET) {
487 _psTraceInt("Can't listen on port %d\n", HTTPS_PORT);
488 goto L_EXIT;
489 }
490
491 /* Main select loop to handle sockets events */
492 while (!g_exitFlag) {
493 selectLoop(keys, lfd);
494 }
495
496 L_EXIT:
497 if (lfd != INVALID_SOCKET) close(lfd);
498 if (keys) matrixSslDeleteKeys(keys);
499 matrixSslClose();
500
501 return 0;
502 }
503
504 /******************************************************************************/
505 /*
506 Close a socket and free associated SSL context and buffers
507 */
508 static void closeConn(httpConn_t *cp, int32 reason)
509 {
510 unsigned char *buf;
511 int32 len;
512
513 DLListRemove(&cp->List);
514 /* Quick attempt to send a closure alert, don't worry about failure */
515 if (matrixSslEncodeClosureAlert(cp->ssl) >= 0) {
516 if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) {
517 if ((len = send(cp->fd, buf, len, MSG_DONTWAIT)) > 0) {
518 matrixSslSentData(cp->ssl, len);
519 }
520 }
521 }
522 if (cp->parsebuf != NULL) {
523 psAssert(cp->parsebuflen > 0);
524 free(cp->parsebuf);
525 cp->parsebuflen = 0;
526 }
527 matrixSslDeleteSession(cp->ssl);
528 if (cp->fd != INVALID_SOCKET) {
529 close(cp->fd);
530 }
531 if (reason >= 0) {
532 /* _psTraceInt("=== Closing Client %d ===\n", cp->fd); */
533 } else {
534 _psTraceInt("=== Closing Client %d on Error ===\n", cp->fd);
535 }
536 free(cp);
537 }
538
539 /******************************************************************************/
540 /*
541 Establish a listening socket for incomming connections
542 */
543 static SOCKET socketListen(short port, int32 *err)
544 {
545 struct sockaddr_in addr;
546 SOCKET fd;
547
548 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
549 _psTrace("Error creating listen socket\n");
550 *err = SOCKET_ERRNO;
551 return INVALID_SOCKET;
552 }
553
554 setSocketOptions(fd);
555
556 addr.sin_family = AF_INET;
557 addr.sin_port = htons(port);
558 addr.sin_addr.s_addr = INADDR_ANY;
559 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
560 _psTrace("Can't bind socket. Port in use or insufficient privilege\n");
561 *err = SOCKET_ERRNO;
562 return INVALID_SOCKET;
563 }
564 if (listen(fd, SOMAXCONN) < 0) {
565 _psTrace("Error listening on socket\n");
566 *err = SOCKET_ERRNO;
567 return INVALID_SOCKET;
568 }
569 _psTraceInt("Listening on port %d\n", port);
570 return fd;
571 }
572
573 /******************************************************************************/
574 /*
575 Make sure the socket is not inherited by exec'd processes
576 Set the REUSE flag to minimize the number of sockets in TIME_WAIT
577 Then we set REUSEADDR, NODELAY and NONBLOCK on the socket
578 */
579 static void setSocketOptions(SOCKET fd)
580 {
581 int32 rc;
582
583 #ifdef POSIX
584 fcntl(fd, F_SETFD, FD_CLOEXEC);
585 #endif
586 rc = 1;
587 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc));
588 #ifdef POSIX
589 rc = 1;
590 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&rc, sizeof(rc));
591 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
592 #elif defined(WIN32)
593 rc = 1; /* 1 for non-block, 0 for block */
594 ioctlsocket(fd, FIONBIO, &rc);
595 #endif
596 #ifdef __APPLE__ /* MAC OS X */
597 rc = 1;
598 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&rc, sizeof(rc));
599 #endif
600 }
601
602 #ifdef POSIX
603 /******************************************************************************/
604 /*
605 Handle some signals on POSIX platforms
606 Lets ctrl-c do a clean exit of the server.
607 */
608 static int32 sighandlers(void)
609 {
610 if (signal(SIGINT, sigintterm_handler) == SIG_ERR ||
611 signal(SIGTERM, sigintterm_handler) == SIG_ERR ||
612 signal(SIGPIPE, SIG_IGN) == SIG_ERR ||
613 signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
614 return PS_PLATFORM_FAIL;
615 }
616 return 0;
617 }
618
619 /* Warn on segmentation violation */
620 static void sigsegv_handler(int unused)
621 {
622 printf("Segfault! Please report this as a bug to support@peersec.com\n");
623 exit(EXIT_FAILURE);
624 }
625
626 /* catch ctrl-c or sigterm */
627 static void sigintterm_handler(int unused)
628 {
629 g_exitFlag = 1; /* Rudimentary exit flagging */
630 printf("Exiting due to interrupt.\n");
631 }
632 #endif /* POSIX */
633
634
635 #else
636
637 /******************************************************************************/
638 /*
639 Stub main for compiling without server enabled
640 */
641 int32 main(int32 argc, char **argv)
642 {
643 printf("USE_SERVER_SIDE_SSL must be enabled in matrixsslConfig.h at build" \
644 " time to run this application\n");
645 return -1;
646 }
647 #endif /* USE_SERVER_SIDE_SSL */
648
649 /******************************************************************************/
650
651