pjproject/pjlib/src/pjlib-test/ssl_sock.c

1639 lines
49 KiB
C

/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; 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 program is distributed in the hope that it will be useful,
* but 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
*/
#include "test.h"
#include <pjlib.h>
#define CERT_DIR "../build/"
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_DARWIN) || \
(PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_APPLE)
/* If we use Darwin SSL, use the cert in DER format. */
# define CERT_CA_FILE CERT_DIR "cacert.der"
#else
# define CERT_CA_FILE CERT_DIR "cacert.pem"
#endif
#define CERT_FILE CERT_DIR "cacert.pem"
#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem"
#define CERT_PRIVKEY_PASS "privkeypass"
#define TEST_LOAD_FROM_FILES 1
#if INCLUDE_SSLSOCK_TEST
/* Global vars */
static int clients_num;
struct send_key {
pj_ioqueue_op_key_t op_key;
};
static int get_cipher_list(void) {
pj_status_t status;
pj_ssl_cipher ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
unsigned cipher_num;
unsigned i;
cipher_num = PJ_ARRAY_SIZE(ciphers);
status = pj_ssl_cipher_get_availables(ciphers, &cipher_num);
if (status != PJ_SUCCESS) {
app_perror("...FAILED to get available ciphers", status);
return status;
}
PJ_LOG(3, ("", "...Found %u ciphers:", cipher_num));
for (i = 0; i < cipher_num; ++i) {
const char* st;
st = pj_ssl_cipher_name(ciphers[i]);
if (st == NULL)
st = "[Unknown]";
PJ_LOG(3, ("", "...%3u: 0x%08x=%s", i+1, ciphers[i], st));
}
return PJ_SUCCESS;
}
struct test_state
{
pj_pool_t *pool; /* pool */
pj_ioqueue_t *ioqueue; /* ioqueue */
pj_bool_t is_server; /* server role flag */
pj_bool_t is_verbose; /* verbose flag, e.g: cert info */
pj_bool_t echo; /* echo received data */
pj_status_t err; /* error flag */
pj_size_t sent; /* bytes sent */
pj_size_t recv; /* bytes received */
pj_uint8_t read_buf[256]; /* read buffer */
pj_bool_t done; /* test done flag */
char *send_str; /* data to send once connected */
pj_size_t send_str_len; /* send data length */
pj_bool_t check_echo; /* flag to compare sent & echoed data */
const char *check_echo_ptr; /* pointer/cursor for comparing data */
struct send_key send_key; /* send op key */
};
static void dump_ssl_info(const pj_ssl_sock_info *si)
{
const char *tmp_st;
/* Print cipher name */
tmp_st = pj_ssl_cipher_name(si->cipher);
if (tmp_st == NULL)
tmp_st = "[Unknown]";
PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
/* Print remote certificate info and verification result */
if (si->remote_cert_info && si->remote_cert_info->subject.info.slen)
{
char buf[2048];
const char *verif_msgs[32];
unsigned verif_msg_cnt;
/* Dump remote TLS certificate info */
PJ_LOG(3, ("", ".....Remote certificate info:"));
pj_ssl_cert_info_dump(si->remote_cert_info, " ", buf, sizeof(buf));
PJ_LOG(3,("", "\n%s", buf));
/* Dump remote TLS certificate verification result */
verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
pj_ssl_cert_get_verify_status_strings(si->verify_status,
verif_msgs, &verif_msg_cnt);
PJ_LOG(3,("", ".....Remote certificate verification result: %s",
(verif_msg_cnt == 1? verif_msgs[0]:"")));
if (verif_msg_cnt > 1) {
unsigned i;
for (i = 0; i < verif_msg_cnt; ++i)
PJ_LOG(3,("", "..... - %s", verif_msgs[i]));
}
}
}
static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
pj_status_t status)
{
struct test_state *st = (struct test_state*)
pj_ssl_sock_get_user_data(ssock);
void *read_buf[1];
pj_ssl_sock_info info;
char buf1[64], buf2[64];
if (status != PJ_SUCCESS) {
app_perror("...ERROR ssl_on_connect_complete()", status);
goto on_return;
}
status = pj_ssl_sock_get_info(ssock, &info);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_get_info()", status);
goto on_return;
}
pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf1, sizeof(buf1), 1);
pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf2, sizeof(buf2), 1);
PJ_LOG(3, ("", "...Connected %s -> %s!", buf1, buf2));
if (st->is_verbose)
dump_ssl_info(&info);
/* Start reading data */
read_buf[0] = st->read_buf;
status = pj_ssl_sock_start_read2(ssock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_start_read2()", status);
goto on_return;
}
/* Start sending data */
while (st->sent < st->send_str_len) {
pj_ssize_t size;
size = st->send_str_len - st->sent;
status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key,
st->send_str + st->sent, &size, 0);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
app_perror("...ERROR pj_ssl_sock_send()", status);
goto on_return;
}
if (status == PJ_SUCCESS) {
st->sent += size;
} else {
if (status == PJ_EPENDING)
status = PJ_SUCCESS;
break;
}
}
on_return:
st->err = status;
if (st->err != PJ_SUCCESS) {
pj_ssl_sock_close(ssock);
clients_num--;
return PJ_FALSE;
}
return PJ_TRUE;
}
static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
pj_ssl_sock_t *newsock,
const pj_sockaddr_t *src_addr,
int src_addr_len,
pj_status_t accept_status)
{
struct test_state *parent_st = (struct test_state*)
pj_ssl_sock_get_user_data(ssock);
struct test_state *st;
void *read_buf[1];
pj_ssl_sock_info info;
char buf[64];
pj_status_t status;
PJ_UNUSED_ARG(src_addr_len);
if (accept_status != PJ_SUCCESS) {
if (newsock) {
st = (struct test_state*) pj_ssl_sock_get_user_data(newsock);
st->err = accept_status;
}
return PJ_FALSE;
}
/* Duplicate parent test state to newly accepted test state */
st = (struct test_state*)pj_pool_zalloc(parent_st->pool, sizeof(struct test_state));
*st = *parent_st;
pj_ssl_sock_set_user_data(newsock, st);
status = pj_ssl_sock_get_info(newsock, &info);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_get_info()", status);
goto on_return;
}
pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
PJ_LOG(3, ("", "...Accepted connection from %s", buf));
if (st->is_verbose)
dump_ssl_info(&info);
/* Start reading data */
read_buf[0] = st->read_buf;
status = pj_ssl_sock_start_read2(newsock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_start_read2()", status);
goto on_return;
}
/* Start sending data */
while (st->sent < st->send_str_len) {
pj_ssize_t size;
size = st->send_str_len - st->sent;
status = pj_ssl_sock_send(newsock, (pj_ioqueue_op_key_t*)&st->send_key,
st->send_str + st->sent, &size, 0);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
app_perror("...ERROR pj_ssl_sock_send()", status);
goto on_return;
}
if (status == PJ_SUCCESS) {
st->sent += size;
} else {
if (status == PJ_EPENDING)
status = PJ_SUCCESS;
break;
}
}
on_return:
st->err = status;
if (st->err != PJ_SUCCESS) {
pj_ssl_sock_close(newsock);
return PJ_FALSE;
}
return PJ_TRUE;
}
static pj_bool_t ssl_on_data_read(pj_ssl_sock_t *ssock,
void *data,
pj_size_t size,
pj_status_t status,
pj_size_t *remainder)
{
struct test_state *st = (struct test_state*)
pj_ssl_sock_get_user_data(ssock);
PJ_UNUSED_ARG(remainder);
PJ_UNUSED_ARG(data);
if (size > 0) {
pj_size_t consumed;
/* Set random remainder */
*remainder = pj_rand() % 100;
/* Apply zero remainder if:
* - remainder is less than size, or
* - connection closed/error
* - echo/check_eco set
*/
if (*remainder > size || status != PJ_SUCCESS || st->echo || st->check_echo)
*remainder = 0;
consumed = size - *remainder;
st->recv += consumed;
//printf("%.*s", consumed, (char*)data);
pj_memmove(data, (char*)data + consumed, *remainder);
/* Echo data when specified to */
if (st->echo) {
pj_ssize_t size_ = consumed;
status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, data, &size_, 0);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
app_perror("...ERROR pj_ssl_sock_send()", status);
goto on_return;
}
if (status == PJ_SUCCESS) {
st->sent += size_;
} else if (status == PJ_EPENDING) {
status = PJ_SUCCESS;
}
}
/* Verify echoed data when specified to */
if (st->check_echo) {
if (!st->check_echo_ptr)
st->check_echo_ptr = st->send_str;
if (pj_memcmp(st->check_echo_ptr, data, consumed)) {
status = PJ_EINVAL;
app_perror("...ERROR echoed data not exact", status);
goto on_return;
}
st->check_echo_ptr += consumed;
/* Echo received completely */
if (st->send_str_len == st->recv) {
pj_ssl_sock_info info;
char buf[64];
status = pj_ssl_sock_get_info(ssock, &info);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_get_info()", status);
goto on_return;
}
pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf, sizeof(buf), 1);
PJ_LOG(3, ("", "...%s successfully recv %lu bytes echo", buf,
(unsigned long)st->recv));
st->done = PJ_TRUE;
}
}
}
if (status != PJ_SUCCESS) {
if (status == PJ_EEOF) {
status = PJ_SUCCESS;
st->done = PJ_TRUE;
} else {
app_perror("...ERROR ssl_on_data_read()", status);
}
}
on_return:
st->err = status;
if (st->err != PJ_SUCCESS || st->done) {
pj_ssl_sock_close(ssock);
if (!st->is_server)
clients_num--;
return PJ_FALSE;
}
return PJ_TRUE;
}
static pj_bool_t ssl_on_data_sent(pj_ssl_sock_t *ssock,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t sent)
{
struct test_state *st = (struct test_state*)
pj_ssl_sock_get_user_data(ssock);
PJ_UNUSED_ARG(op_key);
if (sent < 0) {
st->err = (pj_status_t)-sent;
} else {
st->sent += sent;
/* Send more if any */
while (st->sent < st->send_str_len) {
pj_ssize_t size;
pj_status_t status;
size = st->send_str_len - st->sent;
status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key,
st->send_str + st->sent, &size, 0);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
app_perror("...ERROR pj_ssl_sock_send()", status);
st->err = status;
break;
}
if (status == PJ_SUCCESS)
st->sent += size;
else
break;
}
}
if (st->err != PJ_SUCCESS) {
pj_ssl_sock_close(ssock);
if (!st->is_server)
clients_num--;
return PJ_FALSE;
}
return PJ_TRUE;
}
#define HTTP_SERVER_ADDR "trac.pjsip.org"
#define HTTP_SERVER_PORT 443
#define HTTP_REQ "GET https://" HTTP_SERVER_ADDR "/ HTTP/1.0\r\n\r\n";
static int https_client_test(unsigned ms_timeout)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
pj_timer_heap_t *timer = NULL;
pj_ssl_sock_t *ssock = NULL;
pj_ssl_sock_param param;
pj_status_t status;
struct test_state state = {0};
pj_sockaddr local_addr, rem_addr;
pj_str_t tmp_st;
pool = pj_pool_create(mem, "https_get", 256, 256, NULL);
status = pj_ioqueue_create(pool, 4, &ioqueue);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_timer_heap_create(pool, 4, &timer);
if (status != PJ_SUCCESS) {
goto on_return;
}
state.pool = pool;
state.send_str = HTTP_REQ;
state.send_str_len = pj_ansi_strlen(state.send_str);
state.is_verbose = PJ_TRUE;
pj_ssl_sock_param_default(&param);
param.cb.on_connect_complete = &ssl_on_connect_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.user_data = &state;
param.server_name = pj_str((char*)HTTP_SERVER_ADDR);
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
param.proto = PJ_SSL_SOCK_PROTO_SSL23;
pj_time_val_normalize(&param.timeout);
status = pj_ssl_sock_create(pool, &param, &ssock);
if (status != PJ_SUCCESS) {
goto on_return;
}
pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0);
pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, HTTP_SERVER_ADDR), HTTP_SERVER_PORT);
status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, sizeof(rem_addr));
if (status == PJ_SUCCESS) {
ssl_on_connect_complete(ssock, PJ_SUCCESS);
} else if (status == PJ_EPENDING) {
status = PJ_SUCCESS;
} else {
goto on_return;
}
/* Wait until everything has been sent/received */
while (state.err == PJ_SUCCESS && !state.done) {
#ifdef PJ_SYMBIAN
pj_symbianos_poll(-1, 1000);
#else
pj_time_val delay = {0, 100};
pj_ioqueue_poll(ioqueue, &delay);
pj_timer_heap_poll(timer, &delay);
#endif
}
if (state.err) {
status = state.err;
goto on_return;
}
PJ_LOG(3, ("", "...Done!"));
PJ_LOG(3, ("", ".....Sent/recv: %lu/%lu bytes", (unsigned long)state.sent,
(unsigned long)state.recv));
on_return:
if (ssock && !state.err && !state.done)
pj_ssl_sock_close(ssock);
if (ioqueue)
pj_ioqueue_destroy(ioqueue);
if (timer)
pj_timer_heap_destroy(timer);
if (pool)
pj_pool_release(pool);
return status;
}
#if !(defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
static pj_status_t load_cert_to_buf(pj_pool_t *pool, const pj_str_t *file_name,
pj_ssl_cert_buffer *buf)
{
pj_status_t status;
pj_oshandle_t fd = 0;
pj_ssize_t size = (pj_ssize_t)pj_file_size(file_name->ptr);
status = pj_file_open(pool, file_name->ptr, PJ_O_RDONLY, &fd);
if (status != PJ_SUCCESS)
return status;
buf->ptr = (char*)pj_pool_zalloc(pool, size+1);
status = pj_file_read(fd, buf->ptr, &size);
buf->slen = size;
pj_file_close(fd);
fd = NULL;
return status;
}
#endif
static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher,
pj_bool_t req_client_cert, pj_bool_t client_provide_cert)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
pj_timer_heap_t *timer = NULL;
pj_ssl_sock_t *ssock_serv = NULL;
pj_ssl_sock_t *ssock_cli = NULL;
pj_ssl_sock_param param;
struct test_state state_serv = { 0 };
struct test_state state_cli = { 0 };
pj_sockaddr addr, listen_addr;
pj_ssl_cipher ciphers[1];
pj_ssl_cert_t *cert = NULL;
pj_status_t status;
pool = pj_pool_create(mem, "ssl_echo", 256, 256, NULL);
status = pj_ioqueue_create(pool, 4, &ioqueue);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_timer_heap_create(pool, 4, &timer);
if (status != PJ_SUCCESS) {
goto on_return;
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_connect_complete = &ssl_on_connect_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.ciphers = ciphers;
/* Init default bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
/* === SERVER === */
param.proto = srv_proto;
param.user_data = &state_serv;
param.ciphers_num = (srv_cipher == -1)? 0 : 1;
param.require_client_cert = req_client_cert;
ciphers[0] = srv_cipher;
state_serv.pool = pool;
state_serv.echo = PJ_TRUE;
state_serv.is_server = PJ_TRUE;
state_serv.is_verbose = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Set server cert */
{
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_FILE);
pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE);
pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS);
#if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file,
&privkey_file, &privkey_pass,
&cert);
#else
pj_ssl_cert_buffer ca_buf, cert_buf, privkey_buf;
status = load_cert_to_buf(pool, &ca_file, &ca_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = load_cert_to_buf(pool, &cert_file, &cert_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = load_cert_to_buf(pool, &privkey_file, &privkey_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_cert_load_from_buffer(pool, &ca_buf, &cert_buf,
&privkey_buf, &privkey_pass,
&cert);
#endif
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
}
status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Get listener address */
{
pj_ssl_sock_info info;
pj_ssl_sock_get_info(ssock_serv, &info);
pj_sockaddr_cp(&listen_addr, &info.local_addr);
}
/* === CLIENT === */
param.proto = cli_proto;
param.user_data = &state_cli;
param.ciphers_num = (cli_cipher == -1)? 0 : 1;
ciphers[0] = cli_cipher;
state_cli.pool = pool;
state_cli.check_echo = PJ_TRUE;
state_cli.is_verbose = PJ_TRUE;
{
pj_time_val now;
pj_gettimeofday(&now);
pj_srand((unsigned)now.sec);
state_cli.send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024;
}
state_cli.send_str = (char*)pj_pool_alloc(pool, state_cli.send_str_len);
{
unsigned i;
for (i = 0; i < state_cli.send_str_len; ++i)
state_cli.send_str[i] = (char)(pj_rand() % 256);
}
status = pj_ssl_sock_create(pool, &param, &ssock_cli);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Set cert for client */
{
if (!client_provide_cert) {
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t null_str = pj_str("");
#if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
status = pj_ssl_cert_load_from_files(pool, &ca_file, &null_str,
&null_str, &null_str, &cert);
#else
pj_ssl_cert_buffer null_buf, ca_buf;
null_buf.slen = 0;
status = load_cert_to_buf(pool, &ca_file, &ca_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_cert_load_from_buffer(pool, &ca_buf, &null_buf,
&null_buf, &null_str, &cert);
#endif
if (status != PJ_SUCCESS) {
goto on_return;
}
}
status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
}
status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
if (status == PJ_SUCCESS) {
ssl_on_connect_complete(ssock_cli, PJ_SUCCESS);
} else if (status == PJ_EPENDING) {
status = PJ_SUCCESS;
} else {
goto on_return;
}
/* Wait until everything has been sent/received or error */
while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done)
{
#ifdef PJ_SYMBIAN
pj_symbianos_poll(-1, 1000);
#else
pj_time_val delay = {0, 100};
pj_ioqueue_poll(ioqueue, &delay);
#endif
}
/* Clean up sockets */
{
pj_time_val delay = {0, 100};
while (pj_ioqueue_poll(ioqueue, &delay) > 0);
}
if (state_serv.err || state_cli.err) {
if (state_serv.err != PJ_SUCCESS)
status = state_serv.err;
else
status = state_cli.err;
goto on_return;
}
PJ_LOG(3, ("", "...Done!"));
PJ_LOG(3, ("", ".....Sent/recv: %lu/%lu bytes", (unsigned long)state_cli.sent,
(unsigned long)state_cli.recv));
on_return:
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_DARWIN) || \
(PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_APPLE)
if (status != PJ_SUCCESS) {
PJ_LOG(3, ("", "Apple SSL requires the private key to be "
"inside the Keychain. So double click on "
"the file pjlib/build/privkey.p12 to "
"place it in the Keychain. "
"The password is \"pjsip\"."));
}
#endif
if (ssock_serv)
pj_ssl_sock_close(ssock_serv);
if (ssock_cli && !state_cli.err && !state_cli.done)
pj_ssl_sock_close(ssock_cli);
if (ioqueue)
pj_ioqueue_destroy(ioqueue);
if (timer)
pj_timer_heap_destroy(timer);
if (pool)
pj_pool_release(pool);
return status;
}
static pj_bool_t asock_on_data_read(pj_activesock_t *asock,
void *data,
pj_size_t size,
pj_status_t status,
pj_size_t *remainder)
{
struct test_state *st = (struct test_state*)
pj_activesock_get_user_data(asock);
PJ_UNUSED_ARG(data);
PJ_UNUSED_ARG(size);
PJ_UNUSED_ARG(remainder);
if (status != PJ_SUCCESS) {
if (status == PJ_EEOF) {
status = PJ_SUCCESS;
st->done = PJ_TRUE;
} else {
app_perror("...ERROR asock_on_data_read()", status);
}
}
st->err = status;
if (st->err != PJ_SUCCESS || st->done) {
pj_activesock_close(asock);
if (!st->is_server)
clients_num--;
return PJ_FALSE;
}
return PJ_TRUE;
}
static pj_bool_t asock_on_connect_complete(pj_activesock_t *asock,
pj_status_t status)
{
struct test_state *st = (struct test_state*)
pj_activesock_get_user_data(asock);
if (status == PJ_SUCCESS) {
void *read_buf[1];
/* Start reading data */
read_buf[0] = st->read_buf;
status = pj_activesock_start_read2(asock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_start_read2()", status);
}
}
st->err = status;
if (st->err != PJ_SUCCESS) {
pj_activesock_close(asock);
if (!st->is_server)
clients_num--;
return PJ_FALSE;
}
return PJ_TRUE;
}
static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock,
pj_sock_t newsock,
const pj_sockaddr_t *src_addr,
int src_addr_len)
{
struct test_state *st;
void *read_buf[1];
pj_activesock_t *new_asock;
pj_activesock_cb asock_cb = { 0 };
pj_status_t status;
PJ_UNUSED_ARG(src_addr);
PJ_UNUSED_ARG(src_addr_len);
st = (struct test_state*) pj_activesock_get_user_data(asock);
asock_cb.on_data_read = &asock_on_data_read;
status = pj_activesock_create(st->pool, newsock, pj_SOCK_STREAM(), NULL,
st->ioqueue, &asock_cb, st, &new_asock);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Start reading data */
read_buf[0] = st->read_buf;
status = pj_activesock_start_read2(new_asock, st->pool,
sizeof(st->read_buf),
(void**)read_buf, 0);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_start_read2()", status);
}
on_return:
st->err = status;
if (st->err != PJ_SUCCESS)
pj_activesock_close(new_asock);
return PJ_TRUE;
}
/* Raw TCP socket try to connect to SSL socket server, once
* connection established, it will just do nothing, SSL socket
* server should be able to close the connection after specified
* timeout period (set ms_timeout to 0 to disable timer).
*/
static int client_non_ssl(unsigned ms_timeout)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
pj_timer_heap_t *timer = NULL;
pj_ssl_sock_t *ssock_serv = NULL;
pj_activesock_t *asock_cli = NULL;
pj_activesock_cb asock_cb = { 0 };
pj_sock_t sock = PJ_INVALID_SOCKET;
pj_ssl_sock_param param;
struct test_state state_serv = { 0 };
struct test_state state_cli = { 0 };
pj_sockaddr listen_addr;
pj_ssl_cert_t *cert = NULL;
pj_status_t status;
pool = pj_pool_create(mem, "ssl_accept_raw_tcp", 256, 256, NULL);
status = pj_ioqueue_create(pool, 4, &ioqueue);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_timer_heap_create(pool, 4, &timer);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Set cert */
{
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_FILE);
pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE);
pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS);
#if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file,
&privkey_file, &privkey_pass,
&cert);
#else
pj_ssl_cert_buffer ca_buf, cert_buf, privkey_buf;
status = load_cert_to_buf(pool, &ca_file, &ca_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = load_cert_to_buf(pool, &cert_file, &cert_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = load_cert_to_buf(pool, &privkey_file, &privkey_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_cert_load_from_buffer(pool, &ca_buf, &cert_buf,
&privkey_buf, &privkey_pass,
&cert);
#endif
if (status != PJ_SUCCESS) {
goto on_return;
}
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
pj_time_val_normalize(&param.timeout);
/* SERVER */
param.user_data = &state_serv;
state_serv.pool = pool;
state_serv.is_server = PJ_TRUE;
state_serv.is_verbose = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Init bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
status = pj_ssl_sock_start_accept(ssock_serv, pool, &listen_addr, pj_sockaddr_get_len(&listen_addr));
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Update listener address */
{
pj_ssl_sock_info info;
pj_ssl_sock_get_info(ssock_serv, &info);
pj_sockaddr_cp(&listen_addr, &info.local_addr);
}
/* CLIENT */
state_cli.pool = pool;
status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
if (status != PJ_SUCCESS) {
goto on_return;
}
asock_cb.on_connect_complete = &asock_on_connect_complete;
asock_cb.on_data_read = &asock_on_data_read;
status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL,
ioqueue, &asock_cb, &state_cli, &asock_cli);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_activesock_start_connect(asock_cli, pool, (pj_sockaddr_t*)&listen_addr,
pj_sockaddr_get_len(&listen_addr));
if (status == PJ_SUCCESS) {
asock_on_connect_complete(asock_cli, PJ_SUCCESS);
} else if (status == PJ_EPENDING) {
status = PJ_SUCCESS;
} else {
goto on_return;
}
/* Wait until everything has been sent/received or error */
while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done)
{
#ifdef PJ_SYMBIAN
pj_symbianos_poll(-1, 1000);
#else
pj_time_val delay = {0, 100};
pj_ioqueue_poll(ioqueue, &delay);
pj_timer_heap_poll(timer, &delay);
#endif
}
if (state_serv.err || state_cli.err) {
if (state_serv.err != PJ_SUCCESS)
status = state_serv.err;
else
status = state_cli.err;
goto on_return;
}
PJ_LOG(3, ("", "...Done!"));
on_return:
if (ssock_serv)
pj_ssl_sock_close(ssock_serv);
if (asock_cli && !state_cli.err && !state_cli.done)
pj_activesock_close(asock_cli);
if (timer)
pj_timer_heap_destroy(timer);
if (ioqueue)
pj_ioqueue_destroy(ioqueue);
if (pool)
pj_pool_release(pool);
return status;
}
/* SSL socket try to connect to raw TCP socket server, once
* connection established, SSL socket will try to perform SSL
* handshake. SSL client socket should be able to close the
* connection after specified timeout period (set ms_timeout to
* 0 to disable timer).
*/
static int server_non_ssl(unsigned ms_timeout)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
pj_timer_heap_t *timer = NULL;
pj_activesock_t *asock_serv = NULL;
pj_ssl_sock_t *ssock_cli = NULL;
pj_activesock_cb asock_cb = { 0 };
pj_sock_t sock = PJ_INVALID_SOCKET;
pj_ssl_sock_param param;
struct test_state state_serv = { 0 };
struct test_state state_cli = { 0 };
pj_sockaddr addr, listen_addr;
pj_status_t status;
pool = pj_pool_create(mem, "ssl_connect_raw_tcp", 256, 256, NULL);
status = pj_ioqueue_create(pool, 4, &ioqueue);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_timer_heap_create(pool, 4, &timer);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* SERVER */
state_serv.pool = pool;
state_serv.ioqueue = ioqueue;
status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Init bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
status = pj_sock_bind(sock, (pj_sockaddr_t*)&listen_addr,
pj_sockaddr_get_len((pj_sockaddr_t*)&listen_addr));
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_sock_listen(sock, PJ_SOMAXCONN);
if (status != PJ_SUCCESS) {
goto on_return;
}
asock_cb.on_accept_complete = &asock_on_accept_complete;
status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL,
ioqueue, &asock_cb, &state_serv, &asock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_activesock_start_accept(asock_serv, pool);
if (status != PJ_SUCCESS)
goto on_return;
/* Update listener address */
{
int addr_len;
addr_len = sizeof(listen_addr);
pj_sock_getsockname(sock, (pj_sockaddr_t*)&listen_addr, &addr_len);
}
/* CLIENT */
pj_ssl_sock_param_default(&param);
param.cb.on_connect_complete = &ssl_on_connect_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
pj_time_val_normalize(&param.timeout);
param.user_data = &state_cli;
state_cli.pool = pool;
state_cli.is_server = PJ_FALSE;
state_cli.is_verbose = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_cli);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Init default bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
status = pj_ssl_sock_start_connect(ssock_cli, pool,
(pj_sockaddr_t*)&addr,
(pj_sockaddr_t*)&listen_addr,
pj_sockaddr_get_len(&listen_addr));
if (status != PJ_EPENDING) {
goto on_return;
}
/* Wait until everything has been sent/received or error */
while ((!state_serv.err && !state_serv.done) || (!state_cli.err && !state_cli.done))
{
#ifdef PJ_SYMBIAN
pj_symbianos_poll(-1, 1000);
#else
pj_time_val delay = {0, 100};
pj_ioqueue_poll(ioqueue, &delay);
pj_timer_heap_poll(timer, &delay);
#endif
}
if (state_serv.err || state_cli.err) {
if (state_cli.err != PJ_SUCCESS)
status = state_cli.err;
else
status = state_serv.err;
goto on_return;
}
PJ_LOG(3, ("", "...Done!"));
on_return:
if (asock_serv) {
pj_activesock_close(asock_serv);
} else if (sock != PJ_INVALID_SOCKET) {
pj_sock_close(sock);
}
if (ssock_cli && !state_cli.err && !state_cli.done)
pj_ssl_sock_close(ssock_cli);
if (timer)
pj_timer_heap_destroy(timer);
if (ioqueue)
pj_ioqueue_destroy(ioqueue);
if (pool)
pj_pool_release(pool);
return status;
}
#if WITH_BENCHMARK
/* Test will perform multiple clients trying to connect to single server.
* Once SSL connection established, echo test will be performed.
*/
static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
pj_timer_heap_t *timer = NULL;
pj_ssl_sock_t *ssock_serv = NULL;
pj_ssl_sock_t **ssock_cli = NULL;
pj_ssl_sock_param param;
struct test_state state_serv = { 0 };
struct test_state *state_cli = NULL;
pj_sockaddr addr, listen_addr;
pj_ssl_cert_t *cert = NULL;
pj_status_t status;
unsigned i, cli_err = 0;
pj_size_t tot_sent = 0, tot_recv = 0;
pj_time_val start;
pool = pj_pool_create(mem, "ssl_perf", 256, 256, NULL);
status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioqueue);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_timer_heap_create(pool, PJ_IOQUEUE_MAX_HANDLES, &timer);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Set cert */
{
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_FILE);
pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE);
pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS);
#if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file,
&privkey_file, &privkey_pass,
&cert);
#else
pj_ssl_cert_buffer ca_buf, cert_buf, privkey_buf;
status = load_cert_to_buf(pool, &ca_file, &ca_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = load_cert_to_buf(pool, &cert_file, &cert_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = load_cert_to_buf(pool, &privkey_file, &privkey_buf);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_cert_load_from_buffer(pool, &ca_buf, &cert_buf,
&privkey_buf, &privkey_pass,
&cert);
#endif
if (status != PJ_SUCCESS) {
goto on_return;
}
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_connect_complete = &ssl_on_connect_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_handshake_timeout;
pj_time_val_normalize(&param.timeout);
/* Init default bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
/* SERVER */
param.user_data = &state_serv;
state_serv.pool = pool;
state_serv.echo = PJ_TRUE;
state_serv.is_server = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Get listening address for clients to connect to */
{
pj_ssl_sock_info info;
char buf[64];
pj_ssl_sock_get_info(ssock_serv, &info);
pj_sockaddr_cp(&listen_addr, &info.local_addr);
pj_sockaddr_print((pj_sockaddr_t*)&listen_addr, buf, sizeof(buf), 1);
PJ_LOG(3, ("", "...Listener ready at %s", buf));
}
/* CLIENTS */
clients_num = clients;
param.timeout.sec = 0;
param.timeout.msec = 0;
/* Init random seed */
{
pj_time_val now;
pj_gettimeofday(&now);
pj_srand((unsigned)now.sec);
}
/* Allocate SSL socket pointers and test state */
ssock_cli = (pj_ssl_sock_t**)pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*));
state_cli = (struct test_state*)pj_pool_calloc(pool, clients, sizeof(struct test_state));
/* Get start timestamp */
pj_gettimeofday(&start);
/* Setup clients */
for (i = 0; i < clients; ++i) {
param.user_data = &state_cli[i];
state_cli[i].pool = pool;
state_cli[i].check_echo = PJ_TRUE;
state_cli[i].send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024;
state_cli[i].send_str = (char*)pj_pool_alloc(pool, state_cli[i].send_str_len);
{
unsigned j;
for (j = 0; j < state_cli[i].send_str_len; ++j)
state_cli[i].send_str[j] = (char)(pj_rand() % 256);
}
status = pj_ssl_sock_create(pool, &param, &ssock_cli[i]);
if (status != PJ_SUCCESS) {
app_perror("...ERROR pj_ssl_sock_create()", status);
cli_err++;
clients_num--;
continue;
}
status = pj_ssl_sock_start_connect(ssock_cli[i], pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
if (status == PJ_SUCCESS) {
ssl_on_connect_complete(ssock_cli[i], PJ_SUCCESS);
} else if (status == PJ_EPENDING) {
status = PJ_SUCCESS;
} else {
app_perror("...ERROR pj_ssl_sock_create()", status);
pj_ssl_sock_close(ssock_cli[i]);
ssock_cli[i] = NULL;
clients_num--;
cli_err++;
continue;
}
/* Give chance to server to accept this client */
{
unsigned n = 5;
#ifdef PJ_SYMBIAN
while(n && pj_symbianos_poll(-1, 1000))
n--;
#else
pj_time_val delay = {0, 100};
while(n && pj_ioqueue_poll(ioqueue, &delay) > 0)
n--;
#endif
}
}
/* Wait until everything has been sent/received or error */
while (clients_num)
{
#ifdef PJ_SYMBIAN
pj_symbianos_poll(-1, 1000);
#else
pj_time_val delay = {0, 100};
pj_ioqueue_poll(ioqueue, &delay);
pj_timer_heap_poll(timer, &delay);
#endif
}
/* Clean up sockets */
{
pj_time_val delay = {0, 500};
while (pj_ioqueue_poll(ioqueue, &delay) > 0);
}
if (state_serv.err != PJ_SUCCESS) {
status = state_serv.err;
goto on_return;
}
PJ_LOG(3, ("", "...Done!"));
/* SSL setup and data transfer duration */
{
pj_time_val stop;
pj_gettimeofday(&stop);
PJ_TIME_VAL_SUB(stop, start);
PJ_LOG(3, ("", ".....Setup & data transfer duration: %ld.%03lds", stop.sec, stop.msec));
}
/* Check clients status */
for (i = 0; i < clients; ++i) {
if (state_cli[i].err != PJ_SUCCESS)
cli_err++;
tot_sent += state_cli[1].sent;
tot_recv += state_cli[1].recv;
}
PJ_LOG(3, ("", ".....Clients: %d (%d errors)", clients, cli_err));
PJ_LOG(3, ("", ".....Total sent/recv: %lu/%lu bytes",
(unsigned long)tot_sent, (unsigned long)tot_recv));
on_return:
if (ssock_serv)
pj_ssl_sock_close(ssock_serv);
if (ssock_cli && state_cli) {
for (i = 0; i < clients; ++i) {
if (ssock_cli[i] && !state_cli[i].err && !state_cli[i].done)
pj_ssl_sock_close(ssock_cli[i]);
}
}
if (ioqueue)
pj_ioqueue_destroy(ioqueue);
if (pool)
pj_pool_release(pool);
return status;
}
#endif
#if 0 && (!defined(PJ_SYMBIAN) || PJ_SYMBIAN==0)
pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool);
static int ossl_test_send_buf()
{
pj_pool_t *pool;
pj_status_t status;
pool = pj_pool_create(mem, "send_buf", 256, 256, NULL);
status = pj_ssl_sock_ossl_test_send_buf(pool);
pj_pool_release(pool);
return status;
}
#else
static int ossl_test_send_buf()
{
return 0;
}
#endif
int ssl_sock_test(void)
{
int ret;
PJ_LOG(3,("", "..test ossl send buf"));
ret = ossl_test_send_buf();
if (ret != 0)
return ret;
PJ_LOG(3,("", "..get cipher list test"));
ret = get_cipher_list();
if (ret != 0)
return ret;
PJ_LOG(3,("", "..https client test"));
ret = https_client_test(30000);
// Ignore test result as internet connection may not be available.
//if (ret != 0)
//return ret;
#ifndef PJ_SYMBIAN
/* On Symbian platforms, SSL socket is implemented using CSecureSocket,
* and it hasn't supported server mode, so exclude the following tests,
* which require SSL server, for now.
*/
PJ_LOG(3,("", "..echo test w/ TLSv1 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1,
PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ SSLv23 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23,
PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2 vs client TLSv1.2"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_2,
-1, -1,
PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2+1.3 vs client TLSv1.3"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_3, PJ_SSL_SOCK_PROTO_TLS1_3,
-1, -1,
PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ incompatible proto: server TLSv1 vs client SSL3"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_SSL3,
PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_CBC_SHA,
PJ_FALSE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
/* We can't set min/max proto for TLS protocol higher than 1.0. */
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_DARWIN)
PJ_LOG(3,("", "..echo test w/ incompatible proto: server TLSv1.2 vs client TLSv1.3"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_3,
-1, -1,
PJ_FALSE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
#endif
/* We can't seem to enable certain ciphers only. SSLSetEnabledCiphers() is
* deprecated and we only have sec_protocol_options_append_tls_ciphersuite(),
* but there's no API to remove certain or all ciphers.
*/
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_APPLE)
PJ_LOG(3,("", "..echo test w/ incompatible ciphers"));
ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
PJ_FALSE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
#endif
PJ_LOG(3,("", "..echo test w/ client cert required but not provided"));
ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
PJ_TRUE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
PJ_LOG(3,("", "..echo test w/ client cert required and provided"));
ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
PJ_TRUE, PJ_TRUE);
if (ret != 0)
return ret;
#if WITH_BENCHMARK
PJ_LOG(3,("", "..performance test"));
ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0);
if (ret != 0)
return ret;
#endif
PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)"));
ret = client_non_ssl(5000);
if (ret != PJ_ETIMEDOUT)
return ret;
#endif
PJ_LOG(3,("", "..server non-SSL (handshake timeout 5 secs)"));
ret = server_non_ssl(5000);
if (ret != PJ_ETIMEDOUT)
return ret;
return 0;
}
#else /* INCLUDE_SSLSOCK_TEST */
/* To prevent warning about "translation unit is empty"
* when this test is disabled.
*/
int dummy_ssl_sock_test;
#endif /* INCLUDE_SSLSOCK_TEST */