Newer
Older
Daniel Stenberg
committed
if(peer_CN)
OPENSSL_free(peer_CN);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
return res;
Daniel Stenberg
committed
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
and thus this cannot be done there. */
#ifdef SSL_CTRL_SET_MSG_CALLBACK
static const char *ssl_msg_type(int ssl_ver, int msg)
{
if (ssl_ver == SSL2_VERSION_MAJOR) {
switch (msg) {
case SSL2_MT_ERROR:
return "Error";
case SSL2_MT_CLIENT_HELLO:
return "Client hello";
case SSL2_MT_CLIENT_MASTER_KEY:
return "Client key";
case SSL2_MT_CLIENT_FINISHED:
return "Client finished";
case SSL2_MT_SERVER_HELLO:
return "Server hello";
case SSL2_MT_SERVER_VERIFY:
return "Server verify";
case SSL2_MT_SERVER_FINISHED:
return "Server finished";
case SSL2_MT_REQUEST_CERTIFICATE:
return "Request CERT";
case SSL2_MT_CLIENT_CERTIFICATE:
return "Client CERT";
}
}
else if (ssl_ver == SSL3_VERSION_MAJOR) {
switch (msg) {
case SSL3_MT_HELLO_REQUEST:
return "Hello request";
case SSL3_MT_CLIENT_HELLO:
return "Client hello";
case SSL3_MT_SERVER_HELLO:
return "Server hello";
case SSL3_MT_CERTIFICATE:
return "CERT";
case SSL3_MT_SERVER_KEY_EXCHANGE:
return "Server key exchange";
case SSL3_MT_CLIENT_KEY_EXCHANGE:
return "Client key exchange";
case SSL3_MT_CERTIFICATE_REQUEST:
return "Request CERT";
case SSL3_MT_SERVER_DONE:
return "Server finished";
case SSL3_MT_CERTIFICATE_VERIFY:
return "CERT verify";
case SSL3_MT_FINISHED:
return "Finished";
}
}
return "Unknown";
}
static const char *tls_rt_type(int type)
{
return (
type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
type == SSL3_RT_ALERT ? "TLS alert, " :
type == SSL3_RT_HANDSHAKE ? "TLS handshake, " :
type == SSL3_RT_APPLICATION_DATA ? "TLS app data, " :
"TLS Unknown, ");
}
/*
* Our callback from the SSL/TLS layers.
*/
static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
const void *buf, size_t len, const SSL *ssl,
struct connectdata *conn)
{
Daniel Stenberg
committed
const char *msg_name, *tls_rt_name;
char ssl_buf[1024];
int ver, msg_type, txt_len;
if (!conn || !conn->data || !conn->data->set.fdebug ||
(direction != 0 && direction != 1))
return;
data = conn->data;
ssl_ver >>= 8;
ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
/* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
* always pass-up content-type as 0. But the interesting message-type
Daniel Stenberg
committed
* is at 'buf[0]'.
*/
if (ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
tls_rt_name = tls_rt_type(content_type);
else
tls_rt_name = "";
msg_type = *(char*)buf;
msg_name = ssl_msg_type(ssl_ver, msg_type);
txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
ver, tls_rt_name, msg_name, msg_type);
Daniel Stenberg
committed
Curl_debug(data, CURLINFO_TEXT, ssl_buf, txt_len, NULL);
Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
Daniel Stenberg
committed
(void) ssl;
}
#endif
Daniel Stenberg
committed
#ifdef USE_SSLEAY
/* ====================================================== */
Daniel Stenberg
committed
CURLcode
Daniel Stenberg
committed
Curl_ossl_connect(struct connectdata *conn,
int sockindex)
Daniel Stenberg
committed
CURLcode retcode = CURLE_OK;
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
long lerr;
Daniel Stenberg
committed
int what;
SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
Daniel Stenberg
committed
void *ssl_sessionid=NULL;
ASN1_TIME *certdate;
curl_socket_t sockfd = conn->sock[sockindex];
Daniel Stenberg
committed
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
Daniel Stenberg
committed
if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
Daniel Stenberg
committed
/* Make funny stuff to get random input */
random_the_seed(data);
ssl_seeded = TRUE;
}
/* check to see if we've been told to use an explicit SSL/TLS version */
Daniel Stenberg
committed
switch(data->set.ssl.version) {
case CURL_SSLVERSION_DEFAULT:
/* we try to figure out version */
req_method = SSLv23_client_method();
break;
case CURL_SSLVERSION_TLSv1:
req_method = TLSv1_client_method();
break;
case CURL_SSLVERSION_SSLv2:
req_method = SSLv2_client_method();
break;
case CURL_SSLVERSION_SSLv3:
req_method = SSLv3_client_method();
break;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
connssl->ctx = SSL_CTX_new(req_method);
Daniel Stenberg
committed
if(!connssl->ctx) {
failf(data, "SSL: couldn't create a context!");
Daniel Stenberg
committed
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
#ifdef SSL_CTRL_SET_MSG_CALLBACK
if (data->set.fdebug) {
/* the SSL trace callback is only used for verbose logging so we only
inform about failures of setting it */
if (!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
infof(data, "SSL: couldn't set callback!");
else if (!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
conn)) {
infof(data, "SSL: couldn't set callback argument!");
Daniel Stenberg
committed
}
#endif
/* OpenSSL contains code to work-around lots of bugs and flaws in various
SSL-implementations. SSL_CTX_set_options() is used to enabled those
work-arounds. The man page for this option states that SSL_OP_ALL enables
ll the work-arounds and that "It is usually safe to use SSL_OP_ALL to
enable the bug workaround options if compatibility with somewhat broken
implementations is desired."
*/
Daniel Stenberg
committed
SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL);
Daniel Stenberg
committed
Daniel Stenberg
committed
#if 0
/*
* Not sure it's needed to tell SSL_connect() that socket is
* non-blocking. It doesn't seem to care, but just return with
* SSL_ERROR_WANT_x.
*/
if (data->state.used_interface == Curl_if_multi)
SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
#endif
Daniel Stenberg
committed
if(data->set.cert) {
if(!cert_stuff(conn,
Daniel Stenberg
committed
connssl->ctx,
data->set.cert,
data->set.cert_type,
data->set.key,
data->set.key_type)) {
/* failf() is already done in cert_stuff() */
return CURLE_SSL_CERTPROBLEM;
if(data->set.ssl.cipher_list) {
Daniel Stenberg
committed
if(!SSL_CTX_set_cipher_list(connssl->ctx,
data->set.ssl.cipher_list)) {
failf(data, "failed setting cipher list");
return CURLE_SSL_CIPHER;
if (data->set.ssl.CAfile || data->set.ssl.CApath) {
/* tell SSL where to find CA certificates that are used to verify
the servers certificate. */
Daniel Stenberg
committed
if (!SSL_CTX_load_verify_locations(connssl->ctx, data->set.ssl.CAfile,
data->set.ssl.CApath)) {
if (data->set.ssl.verifypeer) {
/* Fail if we insist on successfully verifying the server. */
failf(data,"error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s\n",
data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
data->set.ssl.CApath ? data->set.ssl.CApath : "none");
return CURLE_SSL_CACERT;
}
else {
/* Just continue with a warning if no strict certificate verification
is required. */
Daniel Stenberg
committed
infof(data, "error setting certificate verify locations,"
" continuing anyway:\n");
}
}
else {
/* Everything is fine. */
Daniel Stenberg
committed
infof(data, "successfully set certificate verify locations:\n");
Daniel Stenberg
committed
}
Daniel Stenberg
committed
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
data->set.ssl.CApath ? data->set.ssl.CApath : "none");
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
Daniel Stenberg
committed
SSL_CTX_set_verify(connssl->ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
cert_verify_callback);
Daniel Stenberg
committed
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
Daniel Stenberg
committed
retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
data->set.ssl.fsslctxp);
if(retcode) {
failf(data,"error signaled by ssl ctx callback");
return retcode;
}
}
/* Lets make an SSL structure */
Daniel Stenberg
committed
connssl->handle = SSL_new(connssl->ctx);
if (!connssl->handle) {
failf(data, "SSL: couldn't create a context (handle)!");
return CURLE_OUT_OF_MEMORY;
}
Daniel Stenberg
committed
SSL_set_connect_state(connssl->handle);
Daniel Stenberg
committed
connssl->server_cert = 0x0;
Daniel Stenberg
committed
/* Check if there's a cached ID we can/should use here! */
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
/* we got a session id, use it! */
if (!SSL_set_session(connssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
Daniel Stenberg
committed
/* Informational message */
infof (data, "SSL re-using session ID\n");
}
/* pass the raw socket into the SSL layers */
if (!SSL_set_fd(connssl->handle, sockfd)) {
failf(data, "SSL: SSL_set_fd failed: %s",
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
while(1) {
Daniel Stenberg
committed
long has_passed;
/* Find out if any timeout is set. If not, use 300 seconds.
Otherwise, figure out the most strict timeout of the two possible one
and then how much time that has elapsed to know how much time we
allow for the connect call */
if(data->set.timeout || data->set.connecttimeout) {
/* get the most strict timeout of the ones converted to milliseconds */
if(data->set.timeout &&
(data->set.timeout>data->set.connecttimeout))
timeout_ms = data->set.timeout*1000;
else
timeout_ms = data->set.connecttimeout*1000;
}
else
/* no particular time-out has been set */
timeout_ms= DEFAULT_CONNECT_TIMEOUT;
Daniel Stenberg
committed
/* Evaluate in milliseconds how much time that has passed */
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
/* subtract the passed time */
timeout_ms -= has_passed;
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEOUTED;
}
Daniel Stenberg
committed
readfd = CURL_SOCKET_BAD;
writefd = CURL_SOCKET_BAD;
Daniel Stenberg
committed
Daniel Stenberg
committed
err = SSL_connect(connssl->handle);
Daniel Stenberg
committed
/* 1 is fine
0 is "not successful but was shut down controlled"
<0 is "handshake was not successful, because a fatal error occurred" */
if(1 != err) {
Daniel Stenberg
committed
int detail = SSL_get_error(connssl->handle, err);
Daniel Stenberg
committed
if(SSL_ERROR_WANT_READ == detail)
Daniel Stenberg
committed
else if(SSL_ERROR_WANT_WRITE == detail)
Daniel Stenberg
committed
else {
/* untreated error */
unsigned long errdetail;
Daniel Stenberg
committed
char error_buffer[120]; /* OpenSSL documents that this must be at least
120 bytes long. */
Daniel Stenberg
committed
CURLcode rc;
const char *cert_problem = NULL;
errdetail = ERR_get_error(); /* Gets the earliest error code from the
thread's error queue and removes the
entry. */
switch(errdetail) {
case 0x1407E086:
/* 1407E086:
SSL routines:
SSL2_SET_CERTIFICATE:
certificate verify failed */
Daniel Stenberg
committed
/* fall-through */
case 0x14090086:
/* 14090086:
SSL routines:
SSL3_GET_SERVER_CERTIFICATE:
certificate verify failed */
Daniel Stenberg
committed
cert_problem = "SSL certificate problem, verify that the CA cert is"
" OK. Details:\n";
rc = CURLE_SSL_CACERT;
break;
default:
Daniel Stenberg
committed
rc = CURLE_SSL_CONNECT_ERROR;
break;
}
/* detail is already set to the SSL error above */
Daniel Stenberg
committed
/* If we e.g. use SSLv2 request-method and the server doesn't like us
* (RST connection etc.), OpenSSL gives no explanation whatsoever and
* the SO_ERROR is also lost.
*/
if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
failf(data, "Unknown SSL protocol error in connection to %s:%d ",
Daniel Stenberg
committed
conn->host.name, conn->port);
return rc;
}
/* Could be a CERT problem */
SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
Daniel Stenberg
committed
failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
return rc;
Daniel Stenberg
committed
}
}
else
/* we have been connected fine, get out of the connect loop */
break;
Daniel Stenberg
committed
while(1) {
what = Curl_select(readfd, writefd, (int)timeout_ms);
Daniel Stenberg
committed
if(what > 0)
/* reabable or writable, go loop in the outer loop */
break;
else if(0 == what) {
/* timeout */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
else {
Daniel Stenberg
committed
/* anything that gets here is fatally bad */
Daniel Stenberg
committed
failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
return CURLE_SSL_CONNECT_ERROR;
}
} /* while()-loop for the select() */
} /* while()-loop for the SSL_connect() */
/* Informational message */
infof (data, "SSL connection using %s\n",
Daniel Stenberg
committed
SSL_get_cipher(connssl->handle));
if(!ssl_sessionid) {
/* Since this is not a cached session ID, then we want to stach this one
in the cache! */
Daniel Stenberg
committed
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
SSL_SESSION *ssl_sessionid;
#ifdef HAVE_SSL_GET1_SESSION
ssl_sessionid = SSL_get1_session(connssl->handle);
/* SSL_get1_session() will increment the reference
count and the session will stay in memory until explicitly freed with
SSL_SESSION_free(3), regardless of its state.
This function was introduced in openssl 0.9.5a. */
#else
ssl_sessionid = SSL_get_session(connssl->handle);
/* if SSL_get1_session() is unavailable, use SSL_get_session().
This is an inferior option because the session can be flushed
at any time by openssl. It is included only so curl compiles
under versions of openssl < 0.9.5a.
WARNING: How curl behaves if it's session is flushed is
untested.
*/
#endif
retcode = Curl_ssl_addsessionid(conn, ssl_sessionid,
0 /* unknown size */);
Daniel Stenberg
committed
failf(data, "failed to store ssl session");
return retcode;
}
}
Daniel Stenberg
committed
/* Get server's certificate (note: beware of dynamic allocation) - opt */
/* major serious hack alert -- we should check certificates
* to authenticate the server; otherwise we risk man-in-the-middle
* attack
*/
Daniel Stenberg
committed
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
failf(data, "SSL: couldn't get peer certificate!");
Daniel Stenberg
committed
return CURLE_SSL_PEER_CERTIFICATE;
}
infof (data, "Server certificate:\n");
Daniel Stenberg
committed
Daniel Stenberg
committed
str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert),
NULL, 0);
if(!str) {
failf(data, "SSL: couldn't get X509-subject!");
Daniel Stenberg
committed
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
Daniel Stenberg
committed
return CURLE_SSL_CONNECT_ERROR;
}
infof(data, "\t subject: %s\n", str);
CRYPTO_free(str);
Daniel Stenberg
committed
certdate = X509_get_notBefore(connssl->server_cert);
Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);
Daniel Stenberg
committed
certdate = X509_get_notAfter(connssl->server_cert);
Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);
if(data->set.ssl.verifyhost) {
Daniel Stenberg
committed
retcode = verifyhost(conn, connssl->server_cert);
if(retcode) {
Daniel Stenberg
committed
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return retcode;
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert),
NULL, 0);
if(!str) {
failf(data, "SSL: couldn't get X509-issuer name!");
retcode = CURLE_SSL_CONNECT_ERROR;
else {
infof(data, "\t issuer: %s\n", str);
CRYPTO_free(str);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
Daniel Stenberg
committed
Daniel Stenberg
committed
lerr = data->set.ssl.certverifyresult=
SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
/* We probably never reach this, because SSL_connect() will fail
and we return earlyer if verifypeer is set? */
Daniel Stenberg
committed
failf(data, "SSL certificate verify result: %s (%ld)",
X509_verify_cert_error_string(lerr), lerr);
retcode = CURLE_SSL_PEER_CERTIFICATE;
}
else
Daniel Stenberg
committed
infof(data, "SSL certificate verify result: %s (%ld),"
" continuing anyway.\n",
X509_verify_cert_error_string(lerr), lerr);
Daniel Stenberg
committed
}
else
infof(data, "SSL certificate verify ok.\n");
Daniel Stenberg
committed
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
Daniel Stenberg
committed
return retcode;
Daniel Stenberg
committed
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
/* return number of sent (non-SSL) bytes */
int Curl_ossl_send(struct connectdata *conn,
int sockindex,
void *mem,
size_t len)
{
/* SSL_write() is said to return 'int' while write() and send() returns
'size_t' */
int err;
char error_buffer[120]; /* OpenSSL documents that this must be at least 120
bytes long. */
unsigned long sslerror;
int rc = SSL_write(conn->ssl[sockindex].handle, mem, (int)len);
if(rc < 0) {
err = SSL_get_error(conn->ssl[sockindex].handle, rc);
switch(err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* The operation did not complete; the same TLS/SSL I/O function
should be called again later. This is basicly an EWOULDBLOCK
equivalent. */
return 0;
case SSL_ERROR_SYSCALL:
failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n",
Curl_ourerrno());
return -1;
case SSL_ERROR_SSL:
/* A failure in the SSL library occurred, usually a protocol error.
The OpenSSL error queue contains more information on the error. */
sslerror = ERR_get_error();
failf(conn->data, "SSL_write() error: %s\n",
ERR_error_string(sslerror, error_buffer));
return -1;
}
/* a true error */
failf(conn->data, "SSL_write() return error %d\n", err);
return -1;
}
return rc; /* number of bytes */
}
/*
* If the read would block we return -1 and set 'wouldblock' to TRUE.
* Otherwise we return the amount of data read. Other errors should return -1
* and set 'wouldblock' to FALSE.
*/
ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */
int num, /* socketindex */
char *buf, /* store read data here */
size_t buffersize, /* max amount to read */
bool *wouldblock)
{
char error_buffer[120]; /* OpenSSL documents that this must be at
least 120 bytes long. */
unsigned long sslerror;
ssize_t nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf,
(int)buffersize);
*wouldblock = FALSE;
if(nread < 0) {
/* failed SSL_read */
int err = SSL_get_error(conn->ssl[num].handle, (int)nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
case SSL_ERROR_ZERO_RETURN: /* no more data */
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_read() */
*wouldblock = TRUE;
return -1; /* basically EWOULDBLOCK */
default:
/* openssl/ssl.h says "look at error stack/return value/errno" */
sslerror = ERR_get_error();
failf(conn->data, "SSL read: %s, errno %d",
ERR_error_string(sslerror, error_buffer),
Curl_ourerrno() );
return -1;
}
}
return nread;
}
size_t Curl_ossl_version(char *buffer, size_t size)
{
#if (SSLEAY_VERSION_NUMBER >= 0x905000)
{
char sub[2];
unsigned long ssleay_value;
sub[1]='\0';
ssleay_value=SSLeay();
if(ssleay_value < 0x906000) {
ssleay_value=SSLEAY_VERSION_NUMBER;
sub[0]='\0';
}
else {
if(ssleay_value&0xff0) {
sub[0]=(char)((ssleay_value>>4)&0xff) + 'a' -1;
}
else
sub[0]='\0';
}
return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx%s",
(ssleay_value>>28)&0xf,
(ssleay_value>>20)&0xff,
(ssleay_value>>12)&0xff,
sub);
}
#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
#if (SSLEAY_VERSION_NUMBER >= 0x900000)
return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx",
(SSLEAY_VERSION_NUMBER>>28)&0xff,
(SSLEAY_VERSION_NUMBER>>20)&0xff,
(SSLEAY_VERSION_NUMBER>>12)&0xf);
#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
{
char sub[2];
sub[1]='\0';
if(SSLEAY_VERSION_NUMBER&0x0f) {
sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
}
else
sub[0]='\0';
return snprintf(buffer, size, " SSL/%x.%x.%x%s",
(SSLEAY_VERSION_NUMBER>>12)&0xff,
(SSLEAY_VERSION_NUMBER>>8)&0xf,
(SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
}
#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
}
#endif /* USE_SSLEAY */