blob: 06165f37e8f487cfc83dbe3ae8be81038c1aa56d [file] [log] [blame]
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +00001/* $Id$ */
2/*
3 * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pj/ssl_sock.h>
20#include <pj/compat/socket.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
Nanang Izzuddin006cc012009-10-16 03:06:13 +000023#include <pj/math.h>
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +000024#include <pj/pool.h>
25#include <pj/sock.h>
26#include <pj/string.h>
27
28#include "os_symbian.h"
29#include <securesocket.h>
30#include <x509cert.h>
31#include <e32des8.h>
32
33#define THIS_FILE "ssl_sock_symbian.cpp"
34
35typedef void (*CPjSSLSocket_cb)(int err, void *key);
36
37class CPjSSLSocketReader : public CActive
38{
39public:
40 static CPjSSLSocketReader *NewL(CSecureSocket &sock)
41 {
42 CPjSSLSocketReader *self = new (ELeave)
43 CPjSSLSocketReader(sock);
44 CleanupStack::PushL(self);
45 self->ConstructL();
46 CleanupStack::Pop(self);
47 return self;
48 }
49
50 ~CPjSSLSocketReader() {
51 Cancel();
52 }
53
54 /* Asynchronous read from the socket. */
55 int Read(CPjSSLSocket_cb cb, void *key, TPtr8 &data, TUint flags)
56 {
57 PJ_ASSERT_RETURN(!IsActive(), PJ_EBUSY);
58
59 cb_ = cb;
60 key_ = key;
61 sock_.RecvOneOrMore(data, iStatus, len_received_);
62 SetActive();
63
64 return PJ_EPENDING;
65 }
66
67private:
68 CSecureSocket &sock_;
69 CPjSSLSocket_cb cb_;
70 void *key_;
71 TSockXfrLength len_received_; /* not really useful? */
72
73 void DoCancel() {
74 sock_.CancelAll();
75 }
76
77 void RunL() {
78 (*cb_)(iStatus.Int(), key_);
79 }
80
81 CPjSSLSocketReader(CSecureSocket &sock) :
82 CActive(0), sock_(sock), cb_(NULL), key_(NULL)
83 {}
84
85 void ConstructL() {
86 CActiveScheduler::Add(this);
87 }
88};
89
90class CPjSSLSocket : public CActive
91{
92public:
93 enum ssl_state {
94 SSL_STATE_NULL,
95 SSL_STATE_CONNECTING,
96 SSL_STATE_HANDSHAKING,
97 SSL_STATE_ESTABLISHED
98 };
99
100 static CPjSSLSocket *NewL(const TDesC8 &ssl_proto) {
101 CPjSSLSocket *self = new (ELeave) CPjSSLSocket();
102 CleanupStack::PushL(self);
103 self->ConstructL(ssl_proto);
104 CleanupStack::Pop(self);
105 return self;
106 }
107
108 ~CPjSSLSocket() {
109 Cancel();
110 CleanupSubObjects();
111 }
112
113 int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr,
114 const TInetAddr &rem_addr,
115 const TDesC8 &servername = TPtrC8(NULL,0));
116 int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags);
117 int SendSync(const TDesC8 &aDesc, TUint flags);
118
119 CPjSSLSocketReader* GetReader();
120 enum ssl_state GetState() const { return state_; }
121 const TInetAddr* GetLocalAddr() const { return &local_addr_; }
122 int GetCipher(TDes8 &cipher) const {
123 if (securesock_)
124 return securesock_->CurrentCipherSuite(cipher);
125 return KErrNotFound;
126 }
127
128private:
129 enum ssl_state state_;
130 pj_sock_t sock_;
131 CSecureSocket *securesock_;
132 bool is_connected_;
133 CPjSSLSocketReader *reader_;
134 TBuf<32> ssl_proto_;
135 TInetAddr rem_addr_;
136 TPtrC8 servername_;
137 TInetAddr local_addr_;
138 TSockXfrLength sent_len_;
139
140 CPjSSLSocket_cb cb_;
141 void *key_;
142
143 void DoCancel();
144 void RunL();
145
146 CPjSSLSocket() :
147 CActive(0), state_(SSL_STATE_NULL), sock_(PJ_INVALID_SOCKET),
148 securesock_(NULL),
149 is_connected_(false), reader_(NULL),
150 cb_(NULL), key_(NULL)
151 {}
152
153 void ConstructL(const TDesC8 &ssl_proto) {
154 ssl_proto_.Copy(ssl_proto);
155 CActiveScheduler::Add(this);
156 }
157
158 void CleanupSubObjects() {
159 delete reader_;
160 reader_ = NULL;
161 if (securesock_) {
162 securesock_->Close();
163 delete securesock_;
164 securesock_ = NULL;
165 }
166 if (sock_ != PJ_INVALID_SOCKET) {
167 delete (CPjSocket*)sock_;
168 sock_ = PJ_INVALID_SOCKET;
169 }
170 }
171};
172
173int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key,
174 const TInetAddr &local_addr,
175 const TInetAddr &rem_addr,
176 const TDesC8 &servername)
177{
178 pj_status_t status;
179
180 PJ_ASSERT_RETURN(state_ == SSL_STATE_NULL, PJ_EINVALIDOP);
181
182 status = pj_sock_socket(rem_addr.Family(), pj_SOCK_STREAM(), 0, &sock_);
183 if (status != PJ_SUCCESS)
184 return status;
185
186 RSocket &rSock = ((CPjSocket*)sock_)->Socket();
187
188 local_addr_ = local_addr;
189
190 if (!local_addr_.IsUnspecified()) {
191 TInt err = rSock.Bind(local_addr_);
192 if (err != KErrNone)
193 return PJ_RETURN_OS_ERROR(err);
194 }
195
196 cb_ = cb;
197 key_ = key;
198 rem_addr_ = rem_addr;
199 servername_.Set(servername);
200 state_ = SSL_STATE_CONNECTING;
201
202 rSock.Connect(rem_addr_, iStatus);
203 SetActive();
204
205 rSock.LocalName(local_addr_);
206
207 return PJ_EPENDING;
208}
209
210int CPjSSLSocket::Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc,
211 TUint flags)
212{
213 PJ_UNUSED_ARG(flags);
214
215 PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
216
217 if (IsActive())
218 return PJ_EBUSY;
219
220 cb_ = cb;
221 key_ = key;
222
223 securesock_->Send(aDesc, iStatus, sent_len_);
224 SetActive();
225
226 return PJ_EPENDING;
227}
228
229int CPjSSLSocket::SendSync(const TDesC8 &aDesc, TUint flags)
230{
231 PJ_UNUSED_ARG(flags);
232
233 PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
234
235 TRequestStatus reqStatus;
236 securesock_->Send(aDesc, reqStatus, sent_len_);
237 User::WaitForRequest(reqStatus);
238
239 return PJ_RETURN_OS_ERROR(reqStatus.Int());
240}
241
242CPjSSLSocketReader* CPjSSLSocket::GetReader()
243{
244 PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, NULL);
245
246 if (reader_)
247 return reader_;
248
249 TRAPD(err, reader_ = CPjSSLSocketReader::NewL(*securesock_));
250 if (err != KErrNone)
251 return NULL;
252
253 return reader_;
254}
255
256void CPjSSLSocket::DoCancel()
257{
258 /* Operation to be cancelled depends on current state */
259 switch (state_) {
260 case SSL_STATE_CONNECTING:
261 {
262 RSocket &rSock = ((CPjSocket*)sock_)->Socket();
263 rSock.CancelConnect();
264
265 CleanupSubObjects();
266
267 state_ = SSL_STATE_NULL;
268 }
269 break;
270 case SSL_STATE_HANDSHAKING:
271 {
272 securesock_->CancelHandshake();
273 securesock_->Close();
274
275 CleanupSubObjects();
276
277 state_ = SSL_STATE_NULL;
278 }
279 break;
280 case SSL_STATE_ESTABLISHED:
281 securesock_->CancelSend();
282 break;
283 default:
284 break;
285 }
286}
287
288void CPjSSLSocket::RunL()
289{
290 switch (state_) {
291 case SSL_STATE_CONNECTING:
292 if (iStatus != KErrNone) {
293 CleanupSubObjects();
294 state_ = SSL_STATE_NULL;
295 /* Dispatch connect failure notification */
296 if (cb_) (*cb_)(iStatus.Int(), key_);
297 } else {
298 RSocket &rSock = ((CPjSocket*)sock_)->Socket();
299
300 /* Get local addr */
301 rSock.LocalName(local_addr_);
302
303 /* Prepare and start handshake */
304 securesock_ = CSecureSocket::NewL(rSock, ssl_proto_);
305 securesock_->SetDialogMode(EDialogModeAttended);
306 if (servername_.Length() > 0)
307 securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL,
308 servername_);
309 securesock_->FlushSessionCache();
310 securesock_->StartClientHandshake(iStatus);
311 SetActive();
312 state_ = SSL_STATE_HANDSHAKING;
313 }
314 break;
315 case SSL_STATE_HANDSHAKING:
316 if (iStatus == KErrNone) {
317 state_ = SSL_STATE_ESTABLISHED;
318 } else {
319 state_ = SSL_STATE_NULL;
320 CleanupSubObjects();
321 }
322 /* Dispatch connect status notification */
323 if (cb_) (*cb_)(iStatus.Int(), key_);
324 break;
325 case SSL_STATE_ESTABLISHED:
326 /* Dispatch data sent notification */
327 if (cb_) (*cb_)(iStatus.Int(), key_);
328 break;
329 default:
330 pj_assert(0);
331 break;
332 }
333}
334
335typedef void (*CPjTimer_cb)(void *user_data);
336
337class CPjTimer : public CActive
338{
339public:
340 CPjTimer(const pj_time_val *delay, CPjTimer_cb cb, void *user_data) :
341 CActive(0), cb_(cb), user_data_(user_data)
342 {
343 CActiveScheduler::Add(this);
344
345 rtimer_.CreateLocal();
346 pj_int32_t interval = PJ_TIME_VAL_MSEC(*delay) * 1000;
347 if (interval < 0) {
348 interval = 0;
349 }
350 rtimer_.After(iStatus, interval);
351 SetActive();
352 }
353
354 ~CPjTimer() { Cancel(); }
355
356private:
357 RTimer rtimer_;
358 CPjTimer_cb cb_;
359 void *user_data_;
360
361 void RunL() { if (cb_) (*cb_)(user_data_); }
362 void DoCancel() { rtimer_.Cancel(); }
363};
364
365/*
366 * Structure of recv/read state.
367 */
368typedef struct read_state_t {
369 TPtr8 *read_buf;
370 TPtr8 *orig_buf;
371 pj_uint32_t flags;
372} read_state_t;
373
374/*
375 * Structure of send/write data.
376 */
377typedef struct write_data_t {
378 pj_size_t len;
379 pj_ioqueue_op_key_t *key;
380 pj_size_t data_len;
381 char data[1];
382} write_data_t;
383
384/*
385 * Structure of send/write state.
386 */
387typedef struct write_state_t {
388 char *buf;
389 pj_size_t max_len;
390 char *start;
391 pj_size_t len;
392 write_data_t *current_data;
393 TPtrC8 send_ptr;
394} write_state_t;
395
396/*
397 * Secure socket structure definition.
398 */
399struct pj_ssl_sock_t
400{
401 pj_ssl_sock_cb cb;
402 void *user_data;
403
404 pj_bool_t established;
405 write_state_t write_state;
406 read_state_t read_state;
407 CPjTimer *connect_timer;
408
409 CPjSSLSocket *sock;
410 int sock_af;
411 int sock_type;
412 pj_sockaddr local_addr;
413 pj_sockaddr rem_addr;
414
415 pj_ssl_sock_proto proto;
416 pj_time_val timeout;
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000417 unsigned ciphers_num;
418 pj_ssl_cipher *ciphers;
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000419 pj_str_t servername;
420};
421
422
423/*
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000424 * Get cipher list supported by SSL/TLS backend.
425 */
426PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[],
427 unsigned *cipher_num)
428{
429 /* Available ciphers */
430 static pj_ssl_cipher ciphers_[64];
431 static unsigned ciphers_num_ = 0;
432 unsigned i;
433
434 PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
435
436 if (ciphers_num_ == 0) {
437 RSocket sock;
438 CSecureSocket *secure_sock;
439 TPtrC16 proto(_L16("TLS1.0"));
440
441 secure_sock = CSecureSocket::NewL(sock, proto);
442 if (secure_sock) {
443 TBuf8<128> ciphers_buf(0);
444 secure_sock->AvailableCipherSuites(ciphers_buf);
445
446 ciphers_num_ = ciphers_buf.Length() / 2;
447 if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_))
448 ciphers_num_ = PJ_ARRAY_SIZE(ciphers_);
449 for (i = 0; i < ciphers_num_; ++i)
450 ciphers_[i] = (pj_ssl_cipher)ciphers_buf[i*2];
451 }
452
453 delete secure_sock;
454 }
455
456 if (ciphers_num_ == 0) {
457 return PJ_ENOTFOUND;
458 }
459
460 *cipher_num = PJ_MIN(*cipher_num, ciphers_num_);
461 for (i = 0; i < *cipher_num; ++i)
462 ciphers[i] = ciphers_[i];
463
464 return PJ_SUCCESS;
465}
466
467/*
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000468 * Create SSL socket instance.
469 */
470PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
471 const pj_ssl_sock_param *param,
472 pj_ssl_sock_t **p_ssock)
473{
474 pj_ssl_sock_t *ssock;
475
476 PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL);
477 PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
478
479 /* Allocate secure socket */
480 ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
481
482 /* Allocate write buffer */
483 ssock->write_state.buf = (char*)pj_pool_alloc(pool,
484 param->send_buffer_size);
485 ssock->write_state.max_len = param->send_buffer_size;
486 ssock->write_state.start = ssock->write_state.buf;
487
488 /* Init secure socket */
489 ssock->sock_af = param->sock_af;
490 ssock->sock_type = param->sock_type;
491 ssock->cb = param->cb;
492 ssock->user_data = param->user_data;
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000493 ssock->ciphers_num = param->ciphers_num;
494 if (param->ciphers_num > 0) {
495 unsigned i;
496 ssock->ciphers = (pj_ssl_cipher*)
497 pj_pool_calloc(pool, param->ciphers_num,
498 sizeof(pj_ssl_cipher));
499 for (i = 0; i < param->ciphers_num; ++i)
500 ssock->ciphers[i] = param->ciphers[i];
501 }
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000502 pj_strdup_with_null(pool, &ssock->servername, &param->servername);
503
504 /* Finally */
505 *p_ssock = ssock;
506
507 return PJ_SUCCESS;
508}
509
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000510
511PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
512 const pj_str_t *CA_file,
513 const pj_str_t *cert_file,
514 const pj_str_t *privkey_file,
515 const pj_str_t *privkey_pass,
516 pj_ssl_cert_t **p_cert)
517{
518 PJ_UNUSED_ARG(pool);
519 PJ_UNUSED_ARG(CA_file);
520 PJ_UNUSED_ARG(cert_file);
521 PJ_UNUSED_ARG(privkey_file);
522 PJ_UNUSED_ARG(privkey_pass);
523 PJ_UNUSED_ARG(p_cert);
524 return PJ_ENOTSUP;
525}
526
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000527/*
528 * Set SSL socket credential.
529 */
530PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
531 pj_ssl_sock_t *ssock,
532 pj_pool_t *pool,
533 const pj_ssl_cert_t *cert)
534{
535 PJ_UNUSED_ARG(ssock);
536 PJ_UNUSED_ARG(pool);
537 PJ_UNUSED_ARG(cert);
538 return PJ_ENOTSUP;
539}
540
541/*
542 * Close the SSL socket.
543 */
544PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
545{
546 PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
547
548 delete ssock->connect_timer;
549 ssock->connect_timer = NULL;
550
551 delete ssock->sock;
552 ssock->sock = NULL;
553
554 delete ssock->read_state.read_buf;
555 delete ssock->read_state.orig_buf;
556 ssock->read_state.read_buf = NULL;
557 ssock->read_state.orig_buf = NULL;
558
559 return PJ_SUCCESS;
560}
561
562
563/*
564 * Associate arbitrary data with the SSL socket.
565 */
566PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock,
567 void *user_data)
568{
569 PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
570
571 ssock->user_data = user_data;
572
573 return PJ_SUCCESS;
574}
575
576
577/*
578 * Retrieve the user data previously associated with this SSL
579 * socket.
580 */
581PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
582{
583 PJ_ASSERT_RETURN(ssock, NULL);
584
585 return ssock->user_data;
586}
587
588
589/*
590 * Retrieve the local address and port used by specified SSL socket.
591 */
592PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
593 pj_ssl_sock_info *info)
594{
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000595 PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL);
596
597 pj_bzero(info, sizeof(*info));
598
599 info->established = ssock->established;
600
601 /* Local address */
602 if (ssock->sock) {
603 const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr();
604 int addrlen = sizeof(pj_sockaddr);
605 pj_status_t status;
606
607 status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen);
608 if (status != PJ_SUCCESS)
609 return status;
610 } else {
611 pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
612 }
613
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000614 if (info->established) {
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000615 /* Cipher suite */
616 TBuf8<4> cipher;
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000617 if (ssock->sock->GetCipher(cipher) == KErrNone) {
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000618 info->cipher = (pj_ssl_cipher)cipher[1];
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000619 }
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000620
621 /* Remote address */
622 pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr,
623 (pj_sockaddr_t*)&ssock->rem_addr);
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000624 }
625
626 /* Protocol */
627 info->proto = ssock->proto;
628
629 return PJ_SUCCESS;
630}
631
632
633/*
634 * Starts read operation on this SSL socket.
635 */
636PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
637 pj_pool_t *pool,
638 unsigned buff_size,
639 pj_uint32_t flags)
640{
641 PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
642 PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
643
644 /* Reading is already started */
645 if (ssock->read_state.orig_buf) {
646 return PJ_SUCCESS;
647 }
648
649 void *readbuf[1];
650 readbuf[0] = pj_pool_alloc(pool, buff_size);
651 return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
652}
653
654static void read_cb(int err, void *key)
655{
656 pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
657 pj_status_t status;
658
659 status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
660
661 /* Check connection status */
662 if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() ||
663 !ssock->established)
664 {
665 status = PJ_EEOF;
666 }
667
668 /* Notify data arrival */
669 if (ssock->cb.on_data_read) {
670 pj_size_t remainder = 0;
671 char *data = (char*)ssock->read_state.orig_buf->Ptr();
672 pj_size_t data_len = ssock->read_state.read_buf->Length() +
673 ssock->read_state.read_buf->Ptr() -
674 ssock->read_state.orig_buf->Ptr();
675
676 if (data_len > 0) {
677 /* Notify received data */
678 pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len,
679 status, &remainder);
680 if (!ret) {
681 /* We've been destroyed */
682 return;
683 }
684
685 /* Calculate available data for next READ operation */
686 if (remainder > 0) {
687 pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength();
688
689 /* There is some data left unconsumed by application, we give
690 * smaller buffer for next READ operation.
691 */
692 ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0,
693 data_maxlen - remainder);
694 } else {
695 /* Give all buffer for next READ operation.
696 */
697 ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf);
698 }
699 }
700 }
701
702 if (status == PJ_SUCCESS) {
703 /* Perform the "next" READ operation */
704 CPjSSLSocketReader *reader = ssock->sock->GetReader();
705 ssock->read_state.read_buf->SetLength(0);
706 status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf,
707 ssock->read_state.flags);
708 if (status != PJ_EPENDING) {
709 /* Notify error */
710 (*ssock->cb.on_data_read)(ssock, NULL, 0, status, NULL);
711 }
712 }
713
714 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
715 /* Connection closed or something goes wrong */
716 delete ssock->read_state.read_buf;
717 delete ssock->read_state.orig_buf;
718 ssock->read_state.read_buf = NULL;
719 ssock->read_state.orig_buf = NULL;
720 ssock->established = PJ_FALSE;
721 }
722}
723
724/*
725 * Same as #pj_ssl_sock_start_read(), except that the application
726 * supplies the buffers for the read operation so that the acive socket
727 * does not have to allocate the buffers.
728 */
729PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
730 pj_pool_t *pool,
731 unsigned buff_size,
732 void *readbuf[],
733 pj_uint32_t flags)
734{
735 PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL);
736 PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
737
738 /* Return failure if access point is marked as down by app. */
739 PJ_SYMBIAN_CHECK_CONNECTION();
740
741 /* Reading is already started */
742 if (ssock->read_state.orig_buf) {
743 return PJ_SUCCESS;
744 }
745
746 PJ_UNUSED_ARG(pool);
747
748 /* Get reader instance */
749 CPjSSLSocketReader *reader = ssock->sock->GetReader();
750 if (!reader)
751 return PJ_ENOMEM;
752
753 /* We manage two buffer pointers here:
754 * 1. orig_buf keeps the orginal buffer address (and its max length).
755 * 2. read_buf provides buffer for READ operation, mind that there may be
756 * some remainder data left by application.
757 */
758 ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
759 ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
760 ssock->read_state.flags = flags;
761
762 pj_status_t status;
763 status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf,
764 ssock->read_state.flags);
765
766 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
767 delete ssock->read_state.read_buf;
768 delete ssock->read_state.orig_buf;
769 ssock->read_state.read_buf = NULL;
770 ssock->read_state.orig_buf = NULL;
771
772 return status;
773 }
774
775 return PJ_SUCCESS;
776}
777
778/*
779 * Same as pj_ssl_sock_start_read(), except that this function is used
780 * only for datagram sockets, and it will trigger \a on_data_recvfrom()
781 * callback instead.
782 */
783PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
784 pj_pool_t *pool,
785 unsigned buff_size,
786 pj_uint32_t flags)
787{
788 PJ_UNUSED_ARG(ssock);
789 PJ_UNUSED_ARG(pool);
790 PJ_UNUSED_ARG(buff_size);
791 PJ_UNUSED_ARG(flags);
792 return PJ_ENOTSUP;
793}
794
795/*
796 * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom()
797 * operation takes the buffer from the argument rather than creating
798 * new ones.
799 */
800PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
801 pj_pool_t *pool,
802 unsigned buff_size,
803 void *readbuf[],
804 pj_uint32_t flags)
805{
806 PJ_UNUSED_ARG(ssock);
807 PJ_UNUSED_ARG(pool);
808 PJ_UNUSED_ARG(buff_size);
809 PJ_UNUSED_ARG(readbuf);
810 PJ_UNUSED_ARG(flags);
811 return PJ_ENOTSUP;
812}
813
814static void send_cb(int err, void *key)
815{
816 pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
817 write_state_t *st = &ssock->write_state;
818
819 /* Check connection status */
820 if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() ||
821 !ssock->established)
822 {
823 ssock->established = PJ_FALSE;
824 return;
825 }
826
827 /* Remove sent data from buffer */
828 st->start += st->current_data->len;
829 st->len -= st->current_data->len;
830
831 /* Reset current outstanding send */
832 st->current_data = NULL;
833
834 /* Let's check if there is pending data to send */
835 if (st->len) {
836 write_data_t *wdata = (write_data_t*)st->start;
837 pj_status_t status;
838
839 st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
840 st->current_data = wdata;
841 status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0);
842 if (status != PJ_EPENDING) {
843 ssock->established = PJ_FALSE;
844 st->len = 0;
845 return;
846 }
847 }
848}
849
850/*
851 * Send data using the socket.
852 */
853PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
854 pj_ioqueue_op_key_t *send_key,
855 const void *data,
856 pj_ssize_t *size,
857 unsigned flags)
858{
859 PJ_CHECK_STACK();
860 PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL);
861 PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 ||
862 ssock->write_state.max_len >= (pj_size_t)*size,
863 PJ_ETOOSMALL);
864
865 /* Check connection status */
866 if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established)
867 {
868 ssock->established = PJ_FALSE;
869 return PJ_ECANCELLED;
870 }
871
872 write_state_t *st = &ssock->write_state;
873
874 /* Synchronous mode */
875 if (st->max_len == 0) {
876 st->send_ptr.Set((TUint8*)data, (TInt)*size);
877 return ssock->sock->SendSync(st->send_ptr, flags);
878 }
879
880 /* CSecureSocket only allows one outstanding send operation, so
881 * we use buffering mechanism to allow application to perform send
882 * operations at any time.
883 */
884
885 pj_size_t avail_len = st->max_len - st->len;
886 pj_size_t needed_len = *size + sizeof(write_data_t) - 1;
887
888 /* Align needed_len to be multiplication of 4 */
889 needed_len = ((needed_len + 3) >> 2) << 2;
890
891 /* Block until there is buffer slot available! */
892 while (needed_len >= avail_len) {
893 pj_symbianos_poll(-1, -1);
894 avail_len = st->max_len - st->len;
895 }
896
897 /* Ok, make sure the new data will not get wrapped */
898 if (st->start + st->len + needed_len > st->buf + st->max_len) {
899 /* Align buffer left */
900 pj_memmove(st->buf, st->start, st->len);
901 st->start = st->buf;
902 }
903
904 /* Push back the send data into the buffer */
905 write_data_t *wdata = (write_data_t*)(st->start + st->len);
906
907 wdata->len = needed_len;
908 wdata->key = send_key;
909 wdata->data_len = (pj_size_t)*size;
910 pj_memcpy(wdata->data, data, *size);
911 st->len += needed_len;
912
913 /* If no outstanding send, send it */
914 if (st->current_data == NULL) {
915 pj_status_t status;
916
917 wdata = (write_data_t*)st->start;
918 st->current_data = wdata;
919 st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
920 status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags);
921
922 if (status != PJ_EPENDING) {
923 *size = -status;
924 return status;
925 }
926 }
927
928 return PJ_SUCCESS;
929}
930
931/*
932 * Send datagram using the socket.
933 */
934PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
935 pj_ioqueue_op_key_t *send_key,
936 const void *data,
937 pj_ssize_t *size,
938 unsigned flags,
939 const pj_sockaddr_t *addr,
940 int addr_len)
941{
942 PJ_UNUSED_ARG(ssock);
943 PJ_UNUSED_ARG(send_key);
944 PJ_UNUSED_ARG(data);
945 PJ_UNUSED_ARG(size);
946 PJ_UNUSED_ARG(flags);
947 PJ_UNUSED_ARG(addr);
948 PJ_UNUSED_ARG(addr_len);
949 return PJ_ENOTSUP;
950}
951
952/*
953 * Starts asynchronous socket accept() operations on this SSL socket.
954 */
955PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
956 pj_pool_t *pool,
957 const pj_sockaddr_t *local_addr,
958 int addr_len)
959{
960 PJ_UNUSED_ARG(ssock);
961 PJ_UNUSED_ARG(pool);
962 PJ_UNUSED_ARG(local_addr);
963 PJ_UNUSED_ARG(addr_len);
964
965 return PJ_ENOTSUP;
966}
967
968static void connect_cb(int err, void *key)
969{
970 pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
971 pj_status_t status;
972
973 if (ssock->connect_timer) {
974 delete ssock->connect_timer;
975 ssock->connect_timer = NULL;
976 }
977
978 status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
979 if (status == PJ_SUCCESS) {
980 ssock->established = PJ_TRUE;
981 } else {
982 delete ssock->sock;
983 ssock->sock = NULL;
984 }
985
986 if (ssock->cb.on_connect_complete) {
987 pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status);
988 if (!ret) {
989 /* We've been destroyed */
990 return;
991 }
992 }
993}
994
995static void connect_timer_cb(void *key)
996{
997 connect_cb(KErrTimedOut, key);
998}
999
1000/*
1001 * Starts asynchronous socket connect() operation and SSL/TLS handshaking
1002 * for this socket. Once the connection is done (either successfully or not),
1003 * the \a on_connect_complete() callback will be called.
1004 */
1005PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock,
1006 pj_pool_t *pool,
1007 const pj_sockaddr_t *localaddr,
1008 const pj_sockaddr_t *remaddr,
1009 int addr_len)
1010{
1011 CPjSSLSocket *sock = NULL;
1012 pj_status_t status;
1013
1014 PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
1015 PJ_EINVAL);
1016
1017 /* Check connection status */
1018 PJ_SYMBIAN_CHECK_CONNECTION();
1019
1020 if (ssock->sock != NULL) {
1021 CPjSSLSocket::ssl_state state = ssock->sock->GetState();
1022 switch (state) {
1023 case CPjSSLSocket::SSL_STATE_ESTABLISHED:
1024 return PJ_SUCCESS;
1025 default:
1026 return PJ_EPENDING;
1027 }
1028 }
1029
1030 /* Set SSL protocol */
1031 TPtrC8 proto;
1032
1033 if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT)
1034 ssock->proto = PJ_SSL_SOCK_PROTO_TLS1;
1035
1036 /* CSecureSocket only support TLS1.0 and SSL3.0 */
1037 switch(ssock->proto) {
1038 case PJ_SSL_SOCK_PROTO_TLS1:
1039 proto.Set((const TUint8*)"TLS1.0", 6);
1040 break;
1041 case PJ_SSL_SOCK_PROTO_SSL3:
1042 proto.Set((const TUint8*)"SSL3.0", 6);
1043 break;
1044 default:
1045 return PJ_ENOTSUP;
1046 }
1047
1048 /* Prepare addresses */
1049 TInetAddr localaddr_, remaddr_;
1050 status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len,
1051 localaddr_);
1052 if (status != PJ_SUCCESS)
1053 return status;
1054
1055 status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len,
1056 remaddr_);
1057 if (status != PJ_SUCCESS)
1058 return status;
1059
1060 pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr);
1061
1062 /* Init SSL engine */
1063 TRAPD(err, sock = CPjSSLSocket::NewL(proto));
1064 if (err != KErrNone)
1065 return PJ_ENOMEM;
1066
1067 if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) {
1068 ssock->connect_timer = new CPjTimer(&ssock->timeout,
1069 &connect_timer_cb, ssock);
1070 }
1071
1072 /* Convert server name to Symbian descriptor */
1073 TPtrC8 servername_((TUint8*)ssock->servername.ptr,
1074 ssock->servername.slen);
1075
1076 /* Try to connect */
1077 status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_,
1078 servername_);
1079 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1080 delete sock;
1081 return status;
1082 }
1083
1084 ssock->sock = sock;
1085 return status;
1086}
1087