From 5ef168f30582cfd441def20089c1d503128ec159 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Sat, 6 Mar 2010 02:04:52 +0000 Subject: Ticket #1043: - Fixed bug of unused timeout setting in Symbian SSL socket, ssl_sock_symbian.cpp. - Added an SSL test scenario of SSL connect timeout, SSL socket client tries to connect to non-SSL socket server. - Fixed OpenSSL-based SSL socket to start SSL timer before TCP connect (was started after TCP connected and before SSL handshake). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3117 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib/src/pj/ssl_sock_ossl.c | 28 ++--- pjlib/src/pj/ssl_sock_symbian.cpp | 24 +++-- pjlib/src/pjlib-test/ssl_sock.c | 213 +++++++++++++++++++++++++++++++++++++- 3 files changed, 238 insertions(+), 27 deletions(-) diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 7fe0c435..3528fe72 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -1165,7 +1165,7 @@ static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te) switch (timer_id) { case TIMER_HANDSHAKE_TIMEOUT: - PJ_LOG(1,(ssock->pool->obj_name, "SSL handshake timeout after %d.%ds", + PJ_LOG(1,(ssock->pool->obj_name, "SSL timeout after %d.%ds", ssock->param.timeout.sec, ssock->param.timeout.msec)); on_handshake_complete(ssock, PJ_ETIMEDOUT); @@ -1607,19 +1607,6 @@ static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, ssock->write_state.start = ssock->write_state.buf; ssock->write_state.len = 0; - /* Start handshake timer */ - if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || - ssock->param.timeout.msec != 0)) - { - pj_assert(ssock->timer.id == TIMER_NONE); - ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; - status = pj_timer_heap_schedule(ssock->param.timer_heap, - &ssock->timer, - &ssock->param.timeout); - if (status != PJ_SUCCESS) - ssock->timer.id = TIMER_NONE; - } - #ifdef SSL_set_tlsext_host_name /* Set server name to connect */ if (ssock->param.server_name.slen) { @@ -2290,6 +2277,19 @@ PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, /* Save remote address */ pj_sockaddr_cp(&ssock->rem_addr, remaddr); + /* Start timer */ + if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || + ssock->param.timeout.msec != 0)) + { + pj_assert(ssock->timer.id == TIMER_NONE); + ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; + status = pj_timer_heap_schedule(ssock->param.timer_heap, + &ssock->timer, + &ssock->param.timeout); + if (status != PJ_SUCCESS) + ssock->timer.id = TIMER_NONE; + } + status = pj_activesock_start_connect(ssock->asock, pool, remaddr, addr_len); diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp index 65916d73..9237ef13 100644 --- a/pjlib/src/pj/ssl_sock_symbian.cpp +++ b/pjlib/src/pj/ssl_sock_symbian.cpp @@ -171,7 +171,8 @@ private: delete reader_; reader_ = NULL; if (securesock_) { - securesock_->Close(); + if (state_ == SSL_STATE_ESTABLISHED) + securesock_->Close(); delete securesock_; securesock_ = NULL; } @@ -213,10 +214,9 @@ int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, key_ = key; rem_addr_ = rem_addr; servername_.Set(servername); - state_ = SSL_STATE_CONNECTING; - rSock.Connect(rem_addr_, iStatus); SetActive(); + state_ = SSL_STATE_CONNECTING; rSock.LocalName(local_addr_); @@ -276,20 +276,16 @@ void CPjSSLSocket::DoCancel() case SSL_STATE_CONNECTING: { RSocket &rSock = ((CPjSocket*)sock_)->Socket(); + rSock.CancelConnect(); - CleanupSubObjects(); - state_ = SSL_STATE_NULL; } break; case SSL_STATE_HANDSHAKING: { securesock_->CancelHandshake(); - securesock_->Close(); - CleanupSubObjects(); - state_ = SSL_STATE_NULL; } break; @@ -322,7 +318,15 @@ void CPjSSLSocket::RunL() if (servername_.Length() > 0) securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL, servername_); - securesock_->FlushSessionCache(); + + // FlushSessionCache() seems to also fire signals to all + // completed AOs (something like CActiveScheduler::RunIfReady()) + // which may cause problem, e.g: we've experienced that when + // SSL timeout is set to 1s, the SSL timeout timer fires up + // at this point and securesock_ instance gets deleted here! + // So be careful using this. And we don't think we need it here. + //securesock_->FlushSessionCache(); + securesock_->StartClientHandshake(iStatus); SetActive(); state_ = SSL_STATE_HANDSHAKING; @@ -647,6 +651,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, ssock->sock_type = param->sock_type; ssock->cb = param->cb; ssock->user_data = param->user_data; + ssock->timeout = param->timeout; ssock->ciphers_num = param->ciphers_num; if (param->ciphers_num > 0) { unsigned i; @@ -1153,6 +1158,7 @@ static void connect_cb(int err, void *key) } else { delete ssock->sock; ssock->sock = NULL; + if (err == KErrTimedOut) status = PJ_ETIMEDOUT; } if (ssock->cb.on_connect_complete) { diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index 68769501..ab3f9f3e 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -68,6 +68,7 @@ static int get_cipher_list(void) { 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 */ @@ -746,6 +747,47 @@ static pj_bool_t asock_on_connect_complete(pj_activesock_t *asock, 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 @@ -903,6 +945,158 @@ on_return: } +/* 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. */ @@ -1152,6 +1346,11 @@ int ssl_sock_test(void) //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, @@ -1195,18 +1394,24 @@ int ssl_sock_test(void) if (ret != 0) return ret; - PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)")); - ret = client_non_ssl(5000); + PJ_LOG(3,("", "..performance test")); + ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0); if (ret != 0) return ret; - PJ_LOG(3,("", "..performance test")); - ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0); + 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; } -- cgit v1.2.3