From f3ab456a17af1c89a6e3be4d20c5944853df1cb0 Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Mon, 7 Jan 2013 14:24:28 -0600 Subject: Import pjproject-2.0.1 --- pjlib/src/pjlib-test/ssl_sock.c | 1424 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1424 insertions(+) create mode 100644 pjlib/src/pjlib-test/ssl_sock.c (limited to 'pjlib/src/pjlib-test/ssl_sock.c') diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c new file mode 100644 index 0000000..d05a4db --- /dev/null +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -0,0 +1,1424 @@ +/* $Id: ssl_sock.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + + +#define CERT_DIR "../build/" +#define CERT_CA_FILE CERT_DIR "cacert.pem" +#define CERT_FILE CERT_DIR "cacert.pem" +#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem" +#define CERT_PRIVKEY_PASS "" + + +#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[100]; + 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 */ + unsigned sent; /* bytes sent */ + unsigned 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 */ + unsigned 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 + 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) +{ + 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); + + /* 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 + 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_; + } + + /* 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 %d bytes echo", buf, 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 = -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_REQ "GET / HTTP/1.0\r\n\r\n"; +#define HTTP_SERVER_ADDR "trac.pjsip.org" +#define HTTP_SERVER_PORT 443 + +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: %d/%d bytes", state.sent, 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; +} + + +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_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; + } + + pj_ssl_sock_param_default(¶m); + param.cb.on_accept_complete = &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.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 tmp1, tmp2, tmp3, tmp4; + + status = pj_ssl_cert_load_from_files(pool, + pj_strset2(&tmp1, (char*)CERT_CA_FILE), + pj_strset2(&tmp2, (char*)CERT_FILE), + pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), + pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), + &cert); + 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 tmp1, tmp2; + + pj_strset2(&tmp1, (char*)CERT_CA_FILE); + pj_strset2(&tmp2, NULL); + status = pj_ssl_cert_load_from_files(pool, + &tmp1, &tmp2, &tmp2, &tmp2, + &cert); + 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: %d/%d bytes", state_cli.sent, state_cli.recv)); + +on_return: + 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 (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 tmp1, tmp2, tmp3, tmp4; + status = pj_ssl_cert_load_from_files(pool, + pj_strset2(&tmp1, (char*)CERT_CA_FILE), + pj_strset2(&tmp2, (char*)CERT_FILE), + pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), + pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), + &cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + } + + pj_ssl_sock_param_default(¶m); + param.cb.on_accept_complete = &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); + 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; +} + + +/* 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, 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 tmp1, tmp2, tmp3, tmp4; + + status = pj_ssl_cert_load_from_files(pool, + pj_strset2(&tmp1, (char*)CERT_CA_FILE), + pj_strset2(&tmp2, (char*)CERT_FILE), + pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), + pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), + &cert); + if (status != PJ_SUCCESS) { + goto on_return; + } + } + + pj_ssl_sock_param_default(¶m); + param.cb.on_accept_complete = &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: %d.%03ds", 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: %d/%d bytes", tot_sent, tot_recv)); + +on_return: + if (ssock_serv) + pj_ssl_sock_close(ssock_serv); + + 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; +} + + +int ssl_sock_test(void) +{ + int 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_DES_CBC_SHA cipher")); + ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1, + PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_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/ incompatible proto")); + 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; + + 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; + + 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; + + PJ_LOG(3,("", "..performance test")); + ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0); + if (ret != 0) + return ret; + + PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)")); + ret = client_non_ssl(5000); + /* PJ_TIMEDOUT won't be returned as accepted socket is deleted silently */ + if (ret != 0) + 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 */ + -- cgit v1.2.3