| /* $Id$ */ |
| /* |
| * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) |
| * |
| * 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 <pj/ssl_sock.h> |
| #include <pj/compat/socket.h> |
| #include <pj/assert.h> |
| #include <pj/errno.h> |
| #include <pj/math.h> |
| #include <pj/pool.h> |
| #include <pj/sock.h> |
| #include <pj/string.h> |
| |
| #include "os_symbian.h" |
| #include <securesocket.h> |
| #include <x509cert.h> |
| #include <e32des8.h> |
| |
| #define THIS_FILE "ssl_sock_symbian.cpp" |
| |
| |
| /* Cipher name structure */ |
| typedef struct cipher_name_t { |
| pj_ssl_cipher cipher; |
| const char *name; |
| } cipher_name_t; |
| |
| /* Cipher name constants */ |
| static cipher_name_t cipher_names[] = |
| { |
| {PJ_TLS_NULL_WITH_NULL_NULL, "NULL"}, |
| |
| /* TLS/SSLv3 */ |
| {PJ_TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"}, |
| {PJ_TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"}, |
| {PJ_TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"}, |
| {PJ_TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"}, |
| {PJ_TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"}, |
| {PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, |
| {PJ_TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, |
| {PJ_TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"}, |
| {PJ_TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"}, |
| {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"}, |
| {PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"}, |
| {PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"}, |
| {PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"}, |
| {PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"}, |
| {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"}, |
| {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"}, |
| {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"}, |
| {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, |
| {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"}, |
| {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"}, |
| {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"}, |
| {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, |
| {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"}, |
| {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"}, |
| {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"}, |
| {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, |
| {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"}, |
| {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"}, |
| {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}, |
| {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, |
| {PJ_TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"}, |
| {PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"}, |
| {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"}, |
| {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"}, |
| {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"}, |
| {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"}, |
| |
| /* TLS (deprecated) */ |
| {PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"}, |
| {PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"}, |
| {PJ_TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"}, |
| {PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"}, |
| {PJ_TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"}, |
| {PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"}, |
| {PJ_TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"}, |
| {PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"}, |
| {PJ_TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"}, |
| {PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"}, |
| {PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"}, |
| {PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"}, |
| {PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"}, |
| {PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"}, |
| {PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}, |
| {PJ_TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"}, |
| |
| /* SSLv3 */ |
| {PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"}, |
| {PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"}, |
| {PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"}, |
| |
| /* SSLv2 */ |
| {PJ_SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"}, |
| {PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"}, |
| {PJ_SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"}, |
| {PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"}, |
| {PJ_SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"}, |
| {PJ_SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"}, |
| {PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"} |
| }; |
| |
| |
| /* Get cipher name string */ |
| static const char* get_cipher_name(pj_ssl_cipher cipher) |
| { |
| unsigned i, n; |
| |
| n = PJ_ARRAY_SIZE(cipher_names); |
| for (i = 0; i < n; ++i) { |
| if (cipher == cipher_names[i].cipher) |
| return cipher_names[i].name; |
| } |
| |
| return "CIPHER_UNKNOWN"; |
| } |
| |
| typedef void (*CPjSSLSocket_cb)(int err, void *key); |
| |
| class CPjSSLSocketReader : public CActive |
| { |
| public: |
| static CPjSSLSocketReader *NewL(CSecureSocket &sock) |
| { |
| CPjSSLSocketReader *self = new (ELeave) |
| CPjSSLSocketReader(sock); |
| CleanupStack::PushL(self); |
| self->ConstructL(); |
| CleanupStack::Pop(self); |
| return self; |
| } |
| |
| ~CPjSSLSocketReader() { |
| Cancel(); |
| } |
| |
| /* Asynchronous read from the socket. */ |
| int Read(CPjSSLSocket_cb cb, void *key, TPtr8 &data, TUint flags) |
| { |
| PJ_ASSERT_RETURN(!IsActive(), PJ_EBUSY); |
| |
| cb_ = cb; |
| key_ = key; |
| sock_.RecvOneOrMore(data, iStatus, len_received_); |
| SetActive(); |
| |
| return PJ_EPENDING; |
| } |
| |
| private: |
| CSecureSocket &sock_; |
| CPjSSLSocket_cb cb_; |
| void *key_; |
| TSockXfrLength len_received_; /* not really useful? */ |
| |
| void DoCancel() { |
| sock_.CancelAll(); |
| } |
| |
| void RunL() { |
| (*cb_)(iStatus.Int(), key_); |
| } |
| |
| CPjSSLSocketReader(CSecureSocket &sock) : |
| CActive(0), sock_(sock), cb_(NULL), key_(NULL) |
| {} |
| |
| void ConstructL() { |
| CActiveScheduler::Add(this); |
| } |
| }; |
| |
| class CPjSSLSocket : public CActive |
| { |
| public: |
| enum ssl_state { |
| SSL_STATE_NULL, |
| SSL_STATE_CONNECTING, |
| SSL_STATE_HANDSHAKING, |
| SSL_STATE_ESTABLISHED |
| }; |
| |
| static CPjSSLSocket *NewL(const TDesC8 &ssl_proto, |
| pj_qos_type qos_type, |
| const pj_qos_params &qos_params) |
| { |
| CPjSSLSocket *self = new (ELeave) CPjSSLSocket(qos_type, qos_params); |
| CleanupStack::PushL(self); |
| self->ConstructL(ssl_proto); |
| CleanupStack::Pop(self); |
| return self; |
| } |
| |
| ~CPjSSLSocket() { |
| Cancel(); |
| CleanupSubObjects(); |
| } |
| |
| int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, |
| const TInetAddr &rem_addr, |
| const TDesC8 &servername = TPtrC8(NULL,0), |
| const TDesC8 &ciphers = TPtrC8(NULL,0)); |
| int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags); |
| int SendSync(const TDesC8 &aDesc, TUint flags); |
| |
| CPjSSLSocketReader* GetReader(); |
| enum ssl_state GetState() const { return state_; } |
| const TInetAddr* GetLocalAddr() const { return &local_addr_; } |
| int GetCipher(TDes8 &cipher) const { |
| if (securesock_) |
| return securesock_->CurrentCipherSuite(cipher); |
| return KErrNotFound; |
| } |
| const CX509Certificate *GetPeerCert() { |
| if (securesock_) |
| return securesock_->ServerCert(); |
| return NULL; |
| } |
| |
| private: |
| enum ssl_state state_; |
| pj_sock_t sock_; |
| CSecureSocket *securesock_; |
| bool is_connected_; |
| |
| pj_qos_type qos_type_; |
| pj_qos_params qos_params_; |
| |
| CPjSSLSocketReader *reader_; |
| TBuf<32> ssl_proto_; |
| TInetAddr rem_addr_; |
| TPtrC8 servername_; |
| TPtrC8 ciphers_; |
| TInetAddr local_addr_; |
| TSockXfrLength sent_len_; |
| |
| CPjSSLSocket_cb cb_; |
| void *key_; |
| |
| void DoCancel(); |
| void RunL(); |
| |
| CPjSSLSocket(pj_qos_type qos_type, const pj_qos_params &qos_params) : |
| CActive(0), state_(SSL_STATE_NULL), sock_(PJ_INVALID_SOCKET), |
| securesock_(NULL), is_connected_(false), |
| qos_type_(qos_type), qos_params_(qos_params), |
| reader_(NULL), cb_(NULL), key_(NULL) |
| {} |
| |
| void ConstructL(const TDesC8 &ssl_proto) { |
| ssl_proto_.Copy(ssl_proto); |
| CActiveScheduler::Add(this); |
| } |
| |
| void CleanupSubObjects() { |
| delete reader_; |
| reader_ = NULL; |
| if (securesock_) { |
| if (state_ == SSL_STATE_ESTABLISHED) |
| securesock_->Close(); |
| delete securesock_; |
| securesock_ = NULL; |
| } |
| if (sock_ != PJ_INVALID_SOCKET) { |
| pj_sock_close(sock_); |
| sock_ = PJ_INVALID_SOCKET; |
| } |
| } |
| }; |
| |
| int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, |
| const TInetAddr &local_addr, |
| const TInetAddr &rem_addr, |
| const TDesC8 &servername, |
| const TDesC8 &ciphers) |
| { |
| pj_status_t status; |
| |
| PJ_ASSERT_RETURN(state_ == SSL_STATE_NULL, PJ_EINVALIDOP); |
| |
| status = pj_sock_socket(rem_addr.Family(), pj_SOCK_STREAM(), 0, &sock_); |
| if (status != PJ_SUCCESS) |
| return status; |
| |
| // Apply QoS |
| status = pj_sock_apply_qos2(sock_, qos_type_, &qos_params_, |
| 2, THIS_FILE, NULL); |
| |
| RSocket &rSock = ((CPjSocket*)sock_)->Socket(); |
| |
| local_addr_ = local_addr; |
| |
| if (!local_addr_.IsUnspecified()) { |
| TInt err = rSock.Bind(local_addr_); |
| if (err != KErrNone) |
| return PJ_RETURN_OS_ERROR(err); |
| } |
| |
| cb_ = cb; |
| key_ = key; |
| rem_addr_ = rem_addr; |
| |
| /* Note: the following members only keep the pointer, not the data */ |
| servername_.Set(servername); |
| ciphers_.Set(ciphers); |
| |
| rSock.Connect(rem_addr_, iStatus); |
| SetActive(); |
| state_ = SSL_STATE_CONNECTING; |
| |
| rSock.LocalName(local_addr_); |
| |
| return PJ_EPENDING; |
| } |
| |
| int CPjSSLSocket::Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, |
| TUint flags) |
| { |
| PJ_UNUSED_ARG(flags); |
| |
| PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); |
| |
| if (IsActive()) |
| return PJ_EBUSY; |
| |
| cb_ = cb; |
| key_ = key; |
| |
| securesock_->Send(aDesc, iStatus, sent_len_); |
| SetActive(); |
| |
| return PJ_EPENDING; |
| } |
| |
| int CPjSSLSocket::SendSync(const TDesC8 &aDesc, TUint flags) |
| { |
| PJ_UNUSED_ARG(flags); |
| |
| PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); |
| |
| TRequestStatus reqStatus; |
| securesock_->Send(aDesc, reqStatus, sent_len_); |
| User::WaitForRequest(reqStatus); |
| |
| return PJ_RETURN_OS_ERROR(reqStatus.Int()); |
| } |
| |
| CPjSSLSocketReader* CPjSSLSocket::GetReader() |
| { |
| PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, NULL); |
| |
| if (reader_) |
| return reader_; |
| |
| TRAPD(err, reader_ = CPjSSLSocketReader::NewL(*securesock_)); |
| if (err != KErrNone) |
| return NULL; |
| |
| return reader_; |
| } |
| |
| void CPjSSLSocket::DoCancel() |
| { |
| /* Operation to be cancelled depends on current state */ |
| switch (state_) { |
| case SSL_STATE_CONNECTING: |
| { |
| RSocket &rSock = ((CPjSocket*)sock_)->Socket(); |
| |
| rSock.CancelConnect(); |
| CleanupSubObjects(); |
| state_ = SSL_STATE_NULL; |
| } |
| break; |
| case SSL_STATE_HANDSHAKING: |
| { |
| securesock_->CancelHandshake(); |
| CleanupSubObjects(); |
| state_ = SSL_STATE_NULL; |
| } |
| break; |
| case SSL_STATE_ESTABLISHED: |
| securesock_->CancelSend(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void CPjSSLSocket::RunL() |
| { |
| switch (state_) { |
| case SSL_STATE_CONNECTING: |
| if (iStatus != KErrNone) { |
| CleanupSubObjects(); |
| state_ = SSL_STATE_NULL; |
| /* Dispatch connect failure notification */ |
| if (cb_) (*cb_)(iStatus.Int(), key_); |
| } else { |
| RSocket &rSock = ((CPjSocket*)sock_)->Socket(); |
| |
| /* Get local addr */ |
| rSock.LocalName(local_addr_); |
| |
| /* Prepare and start handshake */ |
| securesock_ = CSecureSocket::NewL(rSock, ssl_proto_); |
| securesock_->SetDialogMode(EDialogModeAttended); |
| if (servername_.Length() > 0) |
| securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL, |
| servername_); |
| if (ciphers_.Length() > 0) |
| securesock_->SetAvailableCipherSuites(ciphers_); |
| |
| // 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; |
| } |
| break; |
| case SSL_STATE_HANDSHAKING: |
| if (iStatus == KErrNone) { |
| state_ = SSL_STATE_ESTABLISHED; |
| } else { |
| state_ = SSL_STATE_NULL; |
| CleanupSubObjects(); |
| } |
| /* Dispatch connect status notification */ |
| if (cb_) (*cb_)(iStatus.Int(), key_); |
| break; |
| case SSL_STATE_ESTABLISHED: |
| /* Dispatch data sent notification */ |
| if (cb_) (*cb_)(iStatus.Int(), key_); |
| break; |
| default: |
| pj_assert(0); |
| break; |
| } |
| } |
| |
| typedef void (*CPjTimer_cb)(void *user_data); |
| |
| class CPjTimer : public CActive |
| { |
| public: |
| CPjTimer(const pj_time_val *delay, CPjTimer_cb cb, void *user_data) : |
| CActive(0), cb_(cb), user_data_(user_data) |
| { |
| CActiveScheduler::Add(this); |
| |
| rtimer_.CreateLocal(); |
| pj_int32_t interval = PJ_TIME_VAL_MSEC(*delay) * 1000; |
| if (interval < 0) { |
| interval = 0; |
| } |
| rtimer_.After(iStatus, interval); |
| SetActive(); |
| } |
| |
| ~CPjTimer() { Cancel(); } |
| |
| private: |
| RTimer rtimer_; |
| CPjTimer_cb cb_; |
| void *user_data_; |
| |
| void RunL() { if (cb_) (*cb_)(user_data_); } |
| void DoCancel() { rtimer_.Cancel(); } |
| }; |
| |
| /* |
| * Structure of recv/read state. |
| */ |
| typedef struct read_state_t { |
| TPtr8 *read_buf; |
| TPtr8 *orig_buf; |
| pj_uint32_t flags; |
| } read_state_t; |
| |
| /* |
| * Structure of send/write data. |
| */ |
| typedef struct write_data_t { |
| pj_size_t len; |
| pj_ioqueue_op_key_t *key; |
| pj_size_t data_len; |
| char data[1]; |
| } write_data_t; |
| |
| /* |
| * Structure of send/write state. |
| */ |
| typedef struct write_state_t { |
| char *buf; |
| pj_size_t max_len; |
| char *start; |
| pj_size_t len; |
| write_data_t *current_data; |
| TPtrC8 send_ptr; |
| } write_state_t; |
| |
| /* |
| * Secure socket structure definition. |
| */ |
| struct pj_ssl_sock_t |
| { |
| pj_pool_t *pool; |
| pj_ssl_sock_cb cb; |
| void *user_data; |
| |
| pj_bool_t established; |
| write_state_t write_state; |
| read_state_t read_state; |
| CPjTimer *connect_timer; |
| |
| CPjSSLSocket *sock; |
| int sock_af; |
| int sock_type; |
| pj_sockaddr local_addr; |
| pj_sockaddr rem_addr; |
| |
| /* QoS settings */ |
| pj_qos_type qos_type; |
| pj_qos_params qos_params; |
| pj_bool_t qos_ignore_error; |
| |
| |
| pj_ssl_sock_proto proto; |
| pj_time_val timeout; |
| pj_str_t servername; |
| pj_str_t ciphers; |
| pj_ssl_cert_info remote_cert_info; |
| }; |
| |
| |
| static pj_str_t get_cert_name(char *buf, unsigned buf_len, |
| const CX500DistinguishedName &name) |
| { |
| TInt i; |
| TUint8 *p; |
| TInt l = buf_len; |
| |
| p = (TUint8*)buf; |
| for(i = 0; i < name.Count(); ++i) { |
| const CX520AttributeTypeAndValue &attr = name.Element(i); |
| |
| /* Print element separator */ |
| *p++ = '/'; |
| if (0 == --l) break; |
| |
| /* Print the type. */ |
| TPtr8 type(p, l); |
| type.Copy(attr.Type()); |
| p += type.Length(); |
| l -= type.Length(); |
| if (0 >= --l) break; |
| |
| /* Print equal sign */ |
| *p++ = '='; |
| if (0 == --l) break; |
| |
| /* Print the value. Let's just get the raw data here */ |
| TPtr8 value(p, l); |
| value.Copy(attr.EncodedValue().Mid(2)); |
| p += value.Length(); |
| l -= value.Length(); |
| if (0 >= --l) break; |
| } |
| |
| pj_str_t src; |
| pj_strset(&src, buf, buf_len - l); |
| |
| return src; |
| } |
| |
| /* Get certificate info from CX509Certificate. |
| */ |
| static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, |
| const CX509Certificate *x) |
| { |
| enum { tmp_buf_len = 512 }; |
| char *tmp_buf; |
| unsigned len; |
| |
| pj_assert(pool && ci && x); |
| |
| /* Init */ |
| tmp_buf = new char[tmp_buf_len]; |
| pj_bzero(ci, sizeof(*ci)); |
| |
| /* Version */ |
| ci->version = x->Version(); |
| |
| /* Serial number */ |
| len = x->SerialNumber().Length(); |
| if (len > sizeof(ci->serial_no)) |
| len = sizeof(ci->serial_no); |
| pj_memcpy(ci->serial_no + sizeof(ci->serial_no) - len, |
| x->SerialNumber().Ptr(), len); |
| |
| /* Subject */ |
| { |
| HBufC *subject = NULL; |
| TRAPD(err, subject = x->SubjectL()); |
| if (err == KErrNone) { |
| TPtr16 ptr16(subject->Des()); |
| len = ptr16.Length(); |
| TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len); |
| ptr8.Copy(ptr16); |
| pj_strset(&ci->subject.cn, (char*)ptr8.Ptr(), ptr8.Length()); |
| } |
| pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len, |
| x->SubjectName()); |
| pj_strdup(pool, &ci->subject.info, &tmp); |
| } |
| |
| /* Issuer */ |
| { |
| HBufC *issuer = NULL; |
| TRAPD(err, issuer = x->IssuerL()); |
| if (err == KErrNone) { |
| TPtr16 ptr16(issuer->Des()); |
| len = ptr16.Length(); |
| TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len); |
| ptr8.Copy(ptr16); |
| pj_strset(&ci->issuer.cn, (char*)ptr8.Ptr(), ptr8.Length()); |
| } |
| pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len, |
| x->IssuerName()); |
| pj_strdup(pool, &ci->issuer.info, &tmp); |
| } |
| |
| /* Validity */ |
| const CValidityPeriod &valid_period = x->ValidityPeriod(); |
| TTime base_time(TDateTime(1970, EJanuary, 0, 0, 0, 0, 0)); |
| TTimeIntervalSeconds tmp_sec; |
| valid_period.Start().SecondsFrom(base_time, tmp_sec); |
| ci->validity.start.sec = tmp_sec.Int(); |
| valid_period.Finish().SecondsFrom(base_time, tmp_sec); |
| ci->validity.end.sec = tmp_sec.Int(); |
| |
| /* Deinit */ |
| delete [] tmp_buf; |
| } |
| |
| |
| /* Update certificates info. This function should be called after handshake |
| * or renegotiation successfully completed. |
| */ |
| static void update_certs_info(pj_ssl_sock_t *ssock) |
| { |
| const CX509Certificate *x; |
| |
| pj_assert(ssock && ssock->sock && |
| ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED); |
| |
| /* Active remote certificate */ |
| x = ssock->sock->GetPeerCert(); |
| if (x) { |
| get_cert_info(ssock->pool, &ssock->remote_cert_info, x); |
| } else { |
| pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); |
| } |
| } |
| |
| |
| /* Available ciphers */ |
| static unsigned ciphers_num_ = 0; |
| static struct ciphers_t |
| { |
| pj_ssl_cipher id; |
| const char *name; |
| } ciphers_[64]; |
| |
| /* |
| * Get cipher list supported by SSL/TLS backend. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[], |
| unsigned *cipher_num) |
| { |
| unsigned i; |
| |
| PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); |
| |
| if (ciphers_num_ == 0) { |
| RSocket sock; |
| CSecureSocket *secure_sock; |
| TPtrC16 proto(_L16("TLS1.0")); |
| |
| secure_sock = CSecureSocket::NewL(sock, proto); |
| if (secure_sock) { |
| TBuf8<128> ciphers_buf(0); |
| secure_sock->AvailableCipherSuites(ciphers_buf); |
| |
| ciphers_num_ = ciphers_buf.Length() / 2; |
| if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_)) |
| ciphers_num_ = PJ_ARRAY_SIZE(ciphers_); |
| for (i = 0; i < ciphers_num_; ++i) { |
| ciphers_[i].id = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + |
| ciphers_buf[i*2+1]); |
| ciphers_[i].name = get_cipher_name(ciphers_[i].id); |
| } |
| } |
| |
| delete secure_sock; |
| } |
| |
| if (ciphers_num_ == 0) { |
| *cipher_num = 0; |
| return PJ_ENOTFOUND; |
| } |
| |
| *cipher_num = PJ_MIN(*cipher_num, ciphers_num_); |
| for (i = 0; i < *cipher_num; ++i) |
| ciphers[i] = ciphers_[i].id; |
| |
| return PJ_SUCCESS; |
| } |
| |
| |
| /* Get cipher name string */ |
| PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) |
| { |
| unsigned i; |
| |
| if (ciphers_num_ == 0) { |
| pj_ssl_cipher c[1]; |
| i = 0; |
| pj_ssl_cipher_get_availables(c, &i); |
| } |
| |
| for (i = 0; i < ciphers_num_; ++i) { |
| if (cipher == ciphers_[i].id) |
| return ciphers_[i].name; |
| } |
| |
| return NULL; |
| } |
| |
| |
| /* Check if the specified cipher is supported by SSL/TLS backend. */ |
| PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) |
| { |
| unsigned i; |
| |
| if (ciphers_num_ == 0) { |
| pj_ssl_cipher c[1]; |
| i = 0; |
| pj_ssl_cipher_get_availables(c, &i); |
| } |
| |
| for (i = 0; i < ciphers_num_; ++i) { |
| if (cipher == ciphers_[i].id) |
| return PJ_TRUE; |
| } |
| |
| return PJ_FALSE; |
| } |
| |
| |
| /* |
| * Create SSL socket instance. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, |
| const pj_ssl_sock_param *param, |
| pj_ssl_sock_t **p_ssock) |
| { |
| pj_ssl_sock_t *ssock; |
| |
| PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL); |
| PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); |
| |
| /* Allocate secure socket */ |
| ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); |
| |
| /* Allocate write buffer */ |
| ssock->write_state.buf = (char*)pj_pool_alloc(pool, |
| param->send_buffer_size); |
| ssock->write_state.max_len = param->send_buffer_size; |
| ssock->write_state.start = ssock->write_state.buf; |
| |
| /* Init secure socket */ |
| ssock->pool = pool; |
| ssock->sock_af = param->sock_af; |
| ssock->sock_type = param->sock_type; |
| ssock->cb = param->cb; |
| ssock->user_data = param->user_data; |
| ssock->timeout = param->timeout; |
| if (param->ciphers_num > 0) { |
| /* Cipher list in Symbian is represented as array of two-octets. */ |
| ssock->ciphers.slen = param->ciphers_num*2; |
| ssock->ciphers.ptr = (char*)pj_pool_alloc(pool, ssock->ciphers.slen); |
| pj_uint8_t *c = (pj_uint8_t*)ssock->ciphers.ptr; |
| for (unsigned i = 0; i < param->ciphers_num; ++i) { |
| *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF00) >> 8; |
| *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF); |
| } |
| } |
| pj_strdup_with_null(pool, &ssock->servername, ¶m->server_name); |
| |
| ssock->qos_type = param->qos_type; |
| ssock->qos_ignore_error = param->qos_ignore_error; |
| pj_memcpy(&ssock->qos_params, ¶m->qos_params, |
| sizeof(param->qos_params)); |
| |
| /* Finally */ |
| *p_ssock = ssock; |
| |
| return PJ_SUCCESS; |
| } |
| |
| |
| PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, |
| const pj_str_t *CA_file, |
| const pj_str_t *cert_file, |
| const pj_str_t *privkey_file, |
| const pj_str_t *privkey_pass, |
| pj_ssl_cert_t **p_cert) |
| { |
| PJ_UNUSED_ARG(pool); |
| PJ_UNUSED_ARG(CA_file); |
| PJ_UNUSED_ARG(cert_file); |
| PJ_UNUSED_ARG(privkey_file); |
| PJ_UNUSED_ARG(privkey_pass); |
| PJ_UNUSED_ARG(p_cert); |
| return PJ_ENOTSUP; |
| } |
| |
| /* |
| * Set SSL socket credential. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( |
| pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| const pj_ssl_cert_t *cert) |
| { |
| PJ_UNUSED_ARG(ssock); |
| PJ_UNUSED_ARG(pool); |
| PJ_UNUSED_ARG(cert); |
| return PJ_ENOTSUP; |
| } |
| |
| /* |
| * Close the SSL socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) |
| { |
| PJ_ASSERT_RETURN(ssock, PJ_EINVAL); |
| |
| delete ssock->connect_timer; |
| ssock->connect_timer = NULL; |
| |
| delete ssock->sock; |
| ssock->sock = NULL; |
| |
| delete ssock->read_state.read_buf; |
| delete ssock->read_state.orig_buf; |
| ssock->read_state.read_buf = NULL; |
| ssock->read_state.orig_buf = NULL; |
| |
| return PJ_SUCCESS; |
| } |
| |
| |
| /* |
| * Associate arbitrary data with the SSL socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock, |
| void *user_data) |
| { |
| PJ_ASSERT_RETURN(ssock, PJ_EINVAL); |
| |
| ssock->user_data = user_data; |
| |
| return PJ_SUCCESS; |
| } |
| |
| |
| /* |
| * Retrieve the user data previously associated with this SSL |
| * socket. |
| */ |
| PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) |
| { |
| PJ_ASSERT_RETURN(ssock, NULL); |
| |
| return ssock->user_data; |
| } |
| |
| |
| /* |
| * Retrieve the local address and port used by specified SSL socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, |
| pj_ssl_sock_info *info) |
| { |
| PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL); |
| |
| pj_bzero(info, sizeof(*info)); |
| |
| info->established = ssock->established; |
| |
| /* Local address */ |
| if (ssock->sock) { |
| const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr(); |
| int addrlen = sizeof(pj_sockaddr); |
| pj_status_t status; |
| |
| status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen); |
| if (status != PJ_SUCCESS) |
| return status; |
| } else { |
| pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); |
| } |
| |
| if (info->established) { |
| /* Cipher suite */ |
| TBuf8<4> cipher; |
| if (ssock->sock->GetCipher(cipher) == KErrNone) { |
| info->cipher = (pj_ssl_cipher)cipher[1]; |
| } |
| |
| /* Remote address */ |
| pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, |
| (pj_sockaddr_t*)&ssock->rem_addr); |
| |
| /* Certificates info */ |
| info->remote_cert_info = &ssock->remote_cert_info; |
| } |
| |
| /* Protocol */ |
| info->proto = ssock->proto; |
| |
| return PJ_SUCCESS; |
| } |
| |
| |
| /* |
| * Starts read operation on this SSL socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| unsigned buff_size, |
| pj_uint32_t flags) |
| { |
| PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); |
| PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP); |
| |
| /* Reading is already started */ |
| if (ssock->read_state.orig_buf) { |
| return PJ_SUCCESS; |
| } |
| |
| void *readbuf[1]; |
| readbuf[0] = pj_pool_alloc(pool, buff_size); |
| return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags); |
| } |
| |
| static void read_cb(int err, void *key) |
| { |
| pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; |
| pj_status_t status; |
| |
| status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err); |
| |
| /* Check connection status */ |
| if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() || |
| !ssock->established) |
| { |
| status = PJ_EEOF; |
| } |
| |
| /* Notify data arrival */ |
| if (ssock->cb.on_data_read) { |
| pj_size_t remainder = 0; |
| char *data = (char*)ssock->read_state.orig_buf->Ptr(); |
| pj_size_t data_len = ssock->read_state.read_buf->Length() + |
| ssock->read_state.read_buf->Ptr() - |
| ssock->read_state.orig_buf->Ptr(); |
| |
| if (data_len > 0) { |
| /* Notify received data */ |
| pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len, |
| status, &remainder); |
| if (!ret) { |
| /* We've been destroyed */ |
| return; |
| } |
| |
| /* Calculate available data for next READ operation */ |
| if (remainder > 0) { |
| pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength(); |
| |
| /* There is some data left unconsumed by application, we give |
| * smaller buffer for next READ operation. |
| */ |
| ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0, |
| data_maxlen - remainder); |
| } else { |
| /* Give all buffer for next READ operation. |
| */ |
| ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf); |
| } |
| } |
| } |
| |
| if (status == PJ_SUCCESS) { |
| /* Perform the "next" READ operation */ |
| CPjSSLSocketReader *reader = ssock->sock->GetReader(); |
| ssock->read_state.read_buf->SetLength(0); |
| status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, |
| ssock->read_state.flags); |
| } |
| |
| /* Connection closed or something goes wrong */ |
| if (status != PJ_SUCCESS && status != PJ_EPENDING) { |
| /* Notify error */ |
| if (ssock->cb.on_data_read) { |
| pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, NULL, 0, |
| status, NULL); |
| if (!ret) { |
| /* We've been destroyed */ |
| return; |
| } |
| } |
| |
| delete ssock->read_state.read_buf; |
| delete ssock->read_state.orig_buf; |
| ssock->read_state.read_buf = NULL; |
| ssock->read_state.orig_buf = NULL; |
| ssock->established = PJ_FALSE; |
| } |
| } |
| |
| /* |
| * Same as #pj_ssl_sock_start_read(), except that the application |
| * supplies the buffers for the read operation so that the acive socket |
| * does not have to allocate the buffers. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| unsigned buff_size, |
| void *readbuf[], |
| pj_uint32_t flags) |
| { |
| PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL); |
| PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP); |
| |
| /* Return failure if access point is marked as down by app. */ |
| PJ_SYMBIAN_CHECK_CONNECTION(); |
| |
| /* Reading is already started */ |
| if (ssock->read_state.orig_buf) { |
| return PJ_SUCCESS; |
| } |
| |
| PJ_UNUSED_ARG(pool); |
| |
| /* Get reader instance */ |
| CPjSSLSocketReader *reader = ssock->sock->GetReader(); |
| if (!reader) |
| return PJ_ENOMEM; |
| |
| /* We manage two buffer pointers here: |
| * 1. orig_buf keeps the orginal buffer address (and its max length). |
| * 2. read_buf provides buffer for READ operation, mind that there may be |
| * some remainder data left by application. |
| */ |
| ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size); |
| ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size); |
| ssock->read_state.flags = flags; |
| |
| pj_status_t status; |
| status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, |
| ssock->read_state.flags); |
| |
| if (status != PJ_SUCCESS && status != PJ_EPENDING) { |
| delete ssock->read_state.read_buf; |
| delete ssock->read_state.orig_buf; |
| ssock->read_state.read_buf = NULL; |
| ssock->read_state.orig_buf = NULL; |
| |
| return status; |
| } |
| |
| return PJ_SUCCESS; |
| } |
| |
| /* |
| * Same as pj_ssl_sock_start_read(), except that this function is used |
| * only for datagram sockets, and it will trigger \a on_data_recvfrom() |
| * callback instead. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| unsigned buff_size, |
| pj_uint32_t flags) |
| { |
| PJ_UNUSED_ARG(ssock); |
| PJ_UNUSED_ARG(pool); |
| PJ_UNUSED_ARG(buff_size); |
| PJ_UNUSED_ARG(flags); |
| return PJ_ENOTSUP; |
| } |
| |
| /* |
| * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() |
| * operation takes the buffer from the argument rather than creating |
| * new ones. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| unsigned buff_size, |
| void *readbuf[], |
| pj_uint32_t flags) |
| { |
| PJ_UNUSED_ARG(ssock); |
| PJ_UNUSED_ARG(pool); |
| PJ_UNUSED_ARG(buff_size); |
| PJ_UNUSED_ARG(readbuf); |
| PJ_UNUSED_ARG(flags); |
| return PJ_ENOTSUP; |
| } |
| |
| static void send_cb(int err, void *key) |
| { |
| pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; |
| write_state_t *st = &ssock->write_state; |
| |
| /* Check connection status */ |
| if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() || |
| !ssock->established) |
| { |
| ssock->established = PJ_FALSE; |
| return; |
| } |
| |
| /* Remove sent data from buffer */ |
| st->start += st->current_data->len; |
| st->len -= st->current_data->len; |
| |
| /* Reset current outstanding send */ |
| st->current_data = NULL; |
| |
| /* Let's check if there is pending data to send */ |
| if (st->len) { |
| write_data_t *wdata = (write_data_t*)st->start; |
| pj_status_t status; |
| |
| st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len); |
| st->current_data = wdata; |
| status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0); |
| if (status != PJ_EPENDING) { |
| ssock->established = PJ_FALSE; |
| st->len = 0; |
| return; |
| } |
| } else { |
| /* Buffer empty, reset the start position */ |
| st->start = st->buf; |
| } |
| } |
| |
| /* |
| * Send data using the socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock, |
| pj_ioqueue_op_key_t *send_key, |
| const void *data, |
| pj_ssize_t *size, |
| unsigned flags) |
| { |
| PJ_CHECK_STACK(); |
| PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL); |
| PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 || |
| ssock->write_state.max_len >= (pj_size_t)*size, |
| PJ_ETOOSMALL); |
| |
| /* Check connection status */ |
| if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) |
| { |
| ssock->established = PJ_FALSE; |
| return PJ_ECANCELLED; |
| } |
| |
| write_state_t *st = &ssock->write_state; |
| |
| /* Synchronous mode */ |
| if (st->max_len == 0) { |
| st->send_ptr.Set((TUint8*)data, (TInt)*size); |
| return ssock->sock->SendSync(st->send_ptr, flags); |
| } |
| |
| /* CSecureSocket only allows one outstanding send operation, so |
| * we use buffering mechanism to allow application to perform send |
| * operations at any time. |
| */ |
| |
| pj_size_t needed_len = *size + sizeof(write_data_t) - 1; |
| |
| /* Align needed_len to be multiplication of 4 */ |
| needed_len = ((needed_len + 3) >> 2) << 2; |
| |
| /* Block until there is buffer slot available and contiguous! */ |
| while (st->start + st->len + needed_len > st->buf + st->max_len) { |
| pj_symbianos_poll(-1, -1); |
| } |
| |
| /* Push back the send data into the buffer */ |
| write_data_t *wdata = (write_data_t*)(st->start + st->len); |
| |
| wdata->len = needed_len; |
| wdata->key = send_key; |
| wdata->data_len = (pj_size_t)*size; |
| pj_memcpy(wdata->data, data, *size); |
| st->len += needed_len; |
| |
| /* If no outstanding send, send it */ |
| if (st->current_data == NULL) { |
| pj_status_t status; |
| |
| wdata = (write_data_t*)st->start; |
| st->current_data = wdata; |
| st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len); |
| status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags); |
| |
| if (status != PJ_EPENDING) { |
| *size = -status; |
| return status; |
| } |
| } |
| |
| return PJ_SUCCESS; |
| } |
| |
| /* |
| * Send datagram using the socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, |
| pj_ioqueue_op_key_t *send_key, |
| const void *data, |
| pj_ssize_t *size, |
| unsigned flags, |
| const pj_sockaddr_t *addr, |
| int addr_len) |
| { |
| PJ_UNUSED_ARG(ssock); |
| PJ_UNUSED_ARG(send_key); |
| PJ_UNUSED_ARG(data); |
| PJ_UNUSED_ARG(size); |
| PJ_UNUSED_ARG(flags); |
| PJ_UNUSED_ARG(addr); |
| PJ_UNUSED_ARG(addr_len); |
| return PJ_ENOTSUP; |
| } |
| |
| /* |
| * Starts asynchronous socket accept() operations on this SSL socket. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| const pj_sockaddr_t *local_addr, |
| int addr_len) |
| { |
| PJ_UNUSED_ARG(ssock); |
| PJ_UNUSED_ARG(pool); |
| PJ_UNUSED_ARG(local_addr); |
| PJ_UNUSED_ARG(addr_len); |
| |
| return PJ_ENOTSUP; |
| } |
| |
| static void connect_cb(int err, void *key) |
| { |
| pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; |
| pj_status_t status; |
| |
| if (ssock->connect_timer) { |
| delete ssock->connect_timer; |
| ssock->connect_timer = NULL; |
| } |
| |
| status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err); |
| if (status == PJ_SUCCESS) { |
| ssock->established = PJ_TRUE; |
| update_certs_info(ssock); |
| } else { |
| delete ssock->sock; |
| ssock->sock = NULL; |
| if (err == KErrTimedOut) status = PJ_ETIMEDOUT; |
| } |
| |
| if (ssock->cb.on_connect_complete) { |
| pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status); |
| if (!ret) { |
| /* We've been destroyed */ |
| return; |
| } |
| } |
| } |
| |
| static void connect_timer_cb(void *key) |
| { |
| connect_cb(KErrTimedOut, key); |
| } |
| |
| /* |
| * Starts asynchronous socket connect() operation and SSL/TLS handshaking |
| * for this socket. Once the connection is done (either successfully or not), |
| * the \a on_connect_complete() callback will be called. |
| */ |
| PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock, |
| pj_pool_t *pool, |
| const pj_sockaddr_t *localaddr, |
| const pj_sockaddr_t *remaddr, |
| int addr_len) |
| { |
| CPjSSLSocket *sock = NULL; |
| pj_status_t status; |
| |
| PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, |
| PJ_EINVAL); |
| |
| /* Check connection status */ |
| PJ_SYMBIAN_CHECK_CONNECTION(); |
| |
| if (ssock->sock != NULL) { |
| CPjSSLSocket::ssl_state state = ssock->sock->GetState(); |
| switch (state) { |
| case CPjSSLSocket::SSL_STATE_ESTABLISHED: |
| return PJ_SUCCESS; |
| default: |
| return PJ_EPENDING; |
| } |
| } |
| |
| /* Set SSL protocol */ |
| TPtrC8 proto; |
| |
| if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT) |
| ssock->proto = PJ_SSL_SOCK_PROTO_TLS1; |
| |
| /* CSecureSocket only support TLS1.0 and SSL3.0 */ |
| switch(ssock->proto) { |
| case PJ_SSL_SOCK_PROTO_TLS1: |
| proto.Set((const TUint8*)"TLS1.0", 6); |
| break; |
| case PJ_SSL_SOCK_PROTO_SSL3: |
| proto.Set((const TUint8*)"SSL3.0", 6); |
| break; |
| default: |
| return PJ_ENOTSUP; |
| } |
| |
| /* Prepare addresses */ |
| TInetAddr localaddr_, remaddr_; |
| status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len, |
| localaddr_); |
| if (status != PJ_SUCCESS) |
| return status; |
| |
| status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len, |
| remaddr_); |
| if (status != PJ_SUCCESS) |
| return status; |
| |
| pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr); |
| |
| /* Init SSL engine */ |
| TRAPD(err, sock = CPjSSLSocket::NewL(proto, ssock->qos_type, |
| ssock->qos_params)); |
| if (err != KErrNone) |
| return PJ_ENOMEM; |
| |
| if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) { |
| ssock->connect_timer = new CPjTimer(&ssock->timeout, |
| &connect_timer_cb, ssock); |
| } |
| |
| /* Convert server name to Symbian descriptor */ |
| TPtrC8 servername_((TUint8*)ssock->servername.ptr, |
| ssock->servername.slen); |
| |
| /* Convert cipher list to Symbian descriptor */ |
| TPtrC8 ciphers_((TUint8*)ssock->ciphers.ptr, |
| ssock->ciphers.slen); |
| |
| /* Try to connect */ |
| status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_, |
| servername_, ciphers_); |
| if (status != PJ_SUCCESS && status != PJ_EPENDING) { |
| delete sock; |
| return status; |
| } |
| |
| ssock->sock = sock; |
| return status; |
| } |
| |
| |
| PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) |
| { |
| PJ_UNUSED_ARG(ssock); |
| return PJ_ENOTSUP; |
| } |