1639 lines
49 KiB
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(¶m);
|
|
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(¶m.timeout);
|
|
|
|
status = pj_ssl_sock_create(pool, ¶m, &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(¶m);
|
|
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, ¶m, &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, ¶m, &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(¶m);
|
|
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(¶m.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, ¶m, &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(¶m);
|
|
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(¶m.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, ¶m, &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(¶m);
|
|
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(¶m.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, ¶m, &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, ¶m, &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 */
|
|
|