blob: 18dfd7c0712d4a23524a2d41e0d2f0e9ae742813 [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)
Nanang Izzuddineef9b8d2009-11-06 13:31:37 +0000450 ciphers_[i] = (pj_ssl_cipher)(ciphers_buf[i*2]*10 +
451 ciphers_buf[i*2+1]);
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000452 }
453
454 delete secure_sock;
455 }
456
457 if (ciphers_num_ == 0) {
458 return PJ_ENOTFOUND;
459 }
460
461 *cipher_num = PJ_MIN(*cipher_num, ciphers_num_);
462 for (i = 0; i < *cipher_num; ++i)
463 ciphers[i] = ciphers_[i];
464
465 return PJ_SUCCESS;
466}
467
468/*
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000469 * Create SSL socket instance.
470 */
471PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
472 const pj_ssl_sock_param *param,
473 pj_ssl_sock_t **p_ssock)
474{
475 pj_ssl_sock_t *ssock;
476
477 PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL);
478 PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
479
480 /* Allocate secure socket */
481 ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
482
483 /* Allocate write buffer */
484 ssock->write_state.buf = (char*)pj_pool_alloc(pool,
485 param->send_buffer_size);
486 ssock->write_state.max_len = param->send_buffer_size;
487 ssock->write_state.start = ssock->write_state.buf;
488
489 /* Init secure socket */
490 ssock->sock_af = param->sock_af;
491 ssock->sock_type = param->sock_type;
492 ssock->cb = param->cb;
493 ssock->user_data = param->user_data;
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000494 ssock->ciphers_num = param->ciphers_num;
495 if (param->ciphers_num > 0) {
496 unsigned i;
497 ssock->ciphers = (pj_ssl_cipher*)
498 pj_pool_calloc(pool, param->ciphers_num,
499 sizeof(pj_ssl_cipher));
500 for (i = 0; i < param->ciphers_num; ++i)
501 ssock->ciphers[i] = param->ciphers[i];
502 }
Nanang Izzuddinea6d3c42009-10-26 15:47:52 +0000503 pj_strdup_with_null(pool, &ssock->servername, &param->server_name);
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000504
505 /* Finally */
506 *p_ssock = ssock;
507
508 return PJ_SUCCESS;
509}
510
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000511
512PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
513 const pj_str_t *CA_file,
514 const pj_str_t *cert_file,
515 const pj_str_t *privkey_file,
516 const pj_str_t *privkey_pass,
517 pj_ssl_cert_t **p_cert)
518{
519 PJ_UNUSED_ARG(pool);
520 PJ_UNUSED_ARG(CA_file);
521 PJ_UNUSED_ARG(cert_file);
522 PJ_UNUSED_ARG(privkey_file);
523 PJ_UNUSED_ARG(privkey_pass);
524 PJ_UNUSED_ARG(p_cert);
525 return PJ_ENOTSUP;
526}
527
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000528/*
529 * Set SSL socket credential.
530 */
531PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
532 pj_ssl_sock_t *ssock,
533 pj_pool_t *pool,
534 const pj_ssl_cert_t *cert)
535{
536 PJ_UNUSED_ARG(ssock);
537 PJ_UNUSED_ARG(pool);
538 PJ_UNUSED_ARG(cert);
539 return PJ_ENOTSUP;
540}
541
542/*
543 * Close the SSL socket.
544 */
545PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
546{
547 PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
548
549 delete ssock->connect_timer;
550 ssock->connect_timer = NULL;
551
552 delete ssock->sock;
553 ssock->sock = NULL;
554
555 delete ssock->read_state.read_buf;
556 delete ssock->read_state.orig_buf;
557 ssock->read_state.read_buf = NULL;
558 ssock->read_state.orig_buf = NULL;
559
560 return PJ_SUCCESS;
561}
562
563
564/*
565 * Associate arbitrary data with the SSL socket.
566 */
567PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock,
568 void *user_data)
569{
570 PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
571
572 ssock->user_data = user_data;
573
574 return PJ_SUCCESS;
575}
576
577
578/*
579 * Retrieve the user data previously associated with this SSL
580 * socket.
581 */
582PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
583{
584 PJ_ASSERT_RETURN(ssock, NULL);
585
586 return ssock->user_data;
587}
588
589
590/*
591 * Retrieve the local address and port used by specified SSL socket.
592 */
593PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
594 pj_ssl_sock_info *info)
595{
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000596 PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL);
597
598 pj_bzero(info, sizeof(*info));
599
600 info->established = ssock->established;
601
602 /* Local address */
603 if (ssock->sock) {
604 const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr();
605 int addrlen = sizeof(pj_sockaddr);
606 pj_status_t status;
607
608 status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen);
609 if (status != PJ_SUCCESS)
610 return status;
611 } else {
612 pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
613 }
614
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000615 if (info->established) {
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000616 /* Cipher suite */
617 TBuf8<4> cipher;
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000618 if (ssock->sock->GetCipher(cipher) == KErrNone) {
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000619 info->cipher = (pj_ssl_cipher)cipher[1];
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000620 }
Nanang Izzuddin006cc012009-10-16 03:06:13 +0000621
622 /* Remote address */
623 pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr,
624 (pj_sockaddr_t*)&ssock->rem_addr);
Nanang Izzuddin6c62bf42009-08-27 19:55:13 +0000625 }
626
627 /* Protocol */
628 info->proto = ssock->proto;
629
630 return PJ_SUCCESS;
631}
632
633
634/*
635 * Starts read operation on this SSL socket.
636 */
637PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
638 pj_pool_t *pool,
639 unsigned buff_size,
640 pj_uint32_t flags)
641{
642 PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
643 PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
644
645 /* Reading is already started */
646 if (ssock->read_state.orig_buf) {
647 return PJ_SUCCESS;
648 }
649
650 void *readbuf[1];
651 readbuf[0] = pj_pool_alloc(pool, buff_size);
652 return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
653}
654
655static void read_cb(int err, void *key)
656{
657 pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
658 pj_status_t status;
659
660 status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
661
662 /* Check connection status */
663 if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() ||
664 !ssock->established)
665 {
666 status = PJ_EEOF;
667 }
668
669 /* Notify data arrival */
670 if (ssock->cb.on_data_read) {
671 pj_size_t remainder = 0;
672 char *data = (char*)ssock->read_state.orig_buf->Ptr();
673 pj_size_t data_len = ssock->read_state.read_buf->Length() +
674 ssock->read_state.read_buf->Ptr() -
675 ssock->read_state.orig_buf->Ptr();
676
677 if (data_len > 0) {
678 /* Notify received data */
679 pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len,
680 status, &remainder);
681 if (!ret) {
682 /* We've been destroyed */
683 return;
684 }
685
686 /* Calculate available data for next READ operation */
687 if (remainder > 0) {
688 pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength();
689
690 /* There is some data left unconsumed by application, we give
691 * smaller buffer for next READ operation.
692 */
693 ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0,
694 data_maxlen - remainder);
695 } else {
696 /* Give all buffer for next READ operation.
697 */
698 ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf);
699 }
700 }
701 }
702
703 if (status == PJ_SUCCESS) {
704 /* Perform the "next" READ operation */
705 CPjSSLSocketReader *reader = ssock->sock->GetReader();
706 ssock->read_state.read_buf->SetLength(0);
707 status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf,
708 ssock->read_state.flags);
709 if (status != PJ_EPENDING) {
710 /* Notify error */
711 (*ssock->cb.on_data_read)(ssock, NULL, 0, status, NULL);
712 }
713 }
714
715 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
716 /* Connection closed or something goes wrong */
717 delete ssock->read_state.read_buf;
718 delete ssock->read_state.orig_buf;
719 ssock->read_state.read_buf = NULL;
720 ssock->read_state.orig_buf = NULL;
721 ssock->established = PJ_FALSE;
722 }
723}
724
725/*
726 * Same as #pj_ssl_sock_start_read(), except that the application
727 * supplies the buffers for the read operation so that the acive socket
728 * does not have to allocate the buffers.
729 */
730PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
731 pj_pool_t *pool,
732 unsigned buff_size,
733 void *readbuf[],
734 pj_uint32_t flags)
735{
736 PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL);
737 PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
738
739 /* Return failure if access point is marked as down by app. */
740 PJ_SYMBIAN_CHECK_CONNECTION();
741
742 /* Reading is already started */
743 if (ssock->read_state.orig_buf) {
744 return PJ_SUCCESS;
745 }
746
747 PJ_UNUSED_ARG(pool);
748
749 /* Get reader instance */
750 CPjSSLSocketReader *reader = ssock->sock->GetReader();
751 if (!reader)
752 return PJ_ENOMEM;
753
754 /* We manage two buffer pointers here:
755 * 1. orig_buf keeps the orginal buffer address (and its max length).
756 * 2. read_buf provides buffer for READ operation, mind that there may be
757 * some remainder data left by application.
758 */
759 ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
760 ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
761 ssock->read_state.flags = flags;
762
763 pj_status_t status;
764 status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf,
765 ssock->read_state.flags);
766
767 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
768 delete ssock->read_state.read_buf;
769 delete ssock->read_state.orig_buf;
770 ssock->read_state.read_buf = NULL;
771 ssock->read_state.orig_buf = NULL;
772
773 return status;
774 }
775
776 return PJ_SUCCESS;
777}
778
779/*
780 * Same as pj_ssl_sock_start_read(), except that this function is used
781 * only for datagram sockets, and it will trigger \a on_data_recvfrom()
782 * callback instead.
783 */
784PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
785 pj_pool_t *pool,
786 unsigned buff_size,
787 pj_uint32_t flags)
788{
789 PJ_UNUSED_ARG(ssock);
790 PJ_UNUSED_ARG(pool);
791 PJ_UNUSED_ARG(buff_size);
792 PJ_UNUSED_ARG(flags);
793 return PJ_ENOTSUP;
794}
795
796/*
797 * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom()
798 * operation takes the buffer from the argument rather than creating
799 * new ones.
800 */
801PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
802 pj_pool_t *pool,
803 unsigned buff_size,
804 void *readbuf[],
805 pj_uint32_t flags)
806{
807 PJ_UNUSED_ARG(ssock);
808 PJ_UNUSED_ARG(pool);
809 PJ_UNUSED_ARG(buff_size);
810 PJ_UNUSED_ARG(readbuf);
811 PJ_UNUSED_ARG(flags);
812 return PJ_ENOTSUP;
813}
814
815static void send_cb(int err, void *key)
816{
817 pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
818 write_state_t *st = &ssock->write_state;
819
820 /* Check connection status */
821 if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() ||
822 !ssock->established)
823 {
824 ssock->established = PJ_FALSE;
825 return;
826 }
827
828 /* Remove sent data from buffer */
829 st->start += st->current_data->len;
830 st->len -= st->current_data->len;
831
832 /* Reset current outstanding send */
833 st->current_data = NULL;
834
835 /* Let's check if there is pending data to send */
836 if (st->len) {
837 write_data_t *wdata = (write_data_t*)st->start;
838 pj_status_t status;
839
840 st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
841 st->current_data = wdata;
842 status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0);
843 if (status != PJ_EPENDING) {
844 ssock->established = PJ_FALSE;
845 st->len = 0;
846 return;
847 }
848 }
849}
850
851/*
852 * Send data using the socket.
853 */
854PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
855 pj_ioqueue_op_key_t *send_key,
856 const void *data,
857 pj_ssize_t *size,
858 unsigned flags)
859{
860 PJ_CHECK_STACK();
861 PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL);
862 PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 ||
863 ssock->write_state.max_len >= (pj_size_t)*size,
864 PJ_ETOOSMALL);
865
866 /* Check connection status */
867 if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established)
868 {
869 ssock->established = PJ_FALSE;
870 return PJ_ECANCELLED;
871 }
872
873 write_state_t *st = &ssock->write_state;
874
875 /* Synchronous mode */
876 if (st->max_len == 0) {
877 st->send_ptr.Set((TUint8*)data, (TInt)*size);
878 return ssock->sock->SendSync(st->send_ptr, flags);
879 }
880
881 /* CSecureSocket only allows one outstanding send operation, so
882 * we use buffering mechanism to allow application to perform send
883 * operations at any time.
884 */
885
886 pj_size_t avail_len = st->max_len - st->len;
887 pj_size_t needed_len = *size + sizeof(write_data_t) - 1;
888
889 /* Align needed_len to be multiplication of 4 */
890 needed_len = ((needed_len + 3) >> 2) << 2;
891
892 /* Block until there is buffer slot available! */
893 while (needed_len >= avail_len) {
894 pj_symbianos_poll(-1, -1);
895 avail_len = st->max_len - st->len;
896 }
897
898 /* Ok, make sure the new data will not get wrapped */
899 if (st->start + st->len + needed_len > st->buf + st->max_len) {
900 /* Align buffer left */
901 pj_memmove(st->buf, st->start, st->len);
902 st->start = st->buf;
903 }
904
905 /* Push back the send data into the buffer */
906 write_data_t *wdata = (write_data_t*)(st->start + st->len);
907
908 wdata->len = needed_len;
909 wdata->key = send_key;
910 wdata->data_len = (pj_size_t)*size;
911 pj_memcpy(wdata->data, data, *size);
912 st->len += needed_len;
913
914 /* If no outstanding send, send it */
915 if (st->current_data == NULL) {
916 pj_status_t status;
917
918 wdata = (write_data_t*)st->start;
919 st->current_data = wdata;
920 st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
921 status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags);
922
923 if (status != PJ_EPENDING) {
924 *size = -status;
925 return status;
926 }
927 }
928
929 return PJ_SUCCESS;
930}
931
932/*
933 * Send datagram using the socket.
934 */
935PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
936 pj_ioqueue_op_key_t *send_key,
937 const void *data,
938 pj_ssize_t *size,
939 unsigned flags,
940 const pj_sockaddr_t *addr,
941 int addr_len)
942{
943 PJ_UNUSED_ARG(ssock);
944 PJ_UNUSED_ARG(send_key);
945 PJ_UNUSED_ARG(data);
946 PJ_UNUSED_ARG(size);
947 PJ_UNUSED_ARG(flags);
948 PJ_UNUSED_ARG(addr);
949 PJ_UNUSED_ARG(addr_len);
950 return PJ_ENOTSUP;
951}
952
953/*
954 * Starts asynchronous socket accept() operations on this SSL socket.
955 */
956PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
957 pj_pool_t *pool,
958 const pj_sockaddr_t *local_addr,
959 int addr_len)
960{
961 PJ_UNUSED_ARG(ssock);
962 PJ_UNUSED_ARG(pool);
963 PJ_UNUSED_ARG(local_addr);
964 PJ_UNUSED_ARG(addr_len);
965
966 return PJ_ENOTSUP;
967}
968
969static void connect_cb(int err, void *key)
970{
971 pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
972 pj_status_t status;
973
974 if (ssock->connect_timer) {
975 delete ssock->connect_timer;
976 ssock->connect_timer = NULL;
977 }
978
979 status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
980 if (status == PJ_SUCCESS) {
981 ssock->established = PJ_TRUE;
982 } else {
983 delete ssock->sock;
984 ssock->sock = NULL;
985 }
986
987 if (ssock->cb.on_connect_complete) {
988 pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status);
989 if (!ret) {
990 /* We've been destroyed */
991 return;
992 }
993 }
994}
995
996static void connect_timer_cb(void *key)
997{
998 connect_cb(KErrTimedOut, key);
999}
1000
1001/*
1002 * Starts asynchronous socket connect() operation and SSL/TLS handshaking
1003 * for this socket. Once the connection is done (either successfully or not),
1004 * the \a on_connect_complete() callback will be called.
1005 */
1006PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock,
1007 pj_pool_t *pool,
1008 const pj_sockaddr_t *localaddr,
1009 const pj_sockaddr_t *remaddr,
1010 int addr_len)
1011{
1012 CPjSSLSocket *sock = NULL;
1013 pj_status_t status;
1014
1015 PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
1016 PJ_EINVAL);
1017
1018 /* Check connection status */
1019 PJ_SYMBIAN_CHECK_CONNECTION();
1020
1021 if (ssock->sock != NULL) {
1022 CPjSSLSocket::ssl_state state = ssock->sock->GetState();
1023 switch (state) {
1024 case CPjSSLSocket::SSL_STATE_ESTABLISHED:
1025 return PJ_SUCCESS;
1026 default:
1027 return PJ_EPENDING;
1028 }
1029 }
1030
1031 /* Set SSL protocol */
1032 TPtrC8 proto;
1033
1034 if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT)
1035 ssock->proto = PJ_SSL_SOCK_PROTO_TLS1;
1036
1037 /* CSecureSocket only support TLS1.0 and SSL3.0 */
1038 switch(ssock->proto) {
1039 case PJ_SSL_SOCK_PROTO_TLS1:
1040 proto.Set((const TUint8*)"TLS1.0", 6);
1041 break;
1042 case PJ_SSL_SOCK_PROTO_SSL3:
1043 proto.Set((const TUint8*)"SSL3.0", 6);
1044 break;
1045 default:
1046 return PJ_ENOTSUP;
1047 }
1048
1049 /* Prepare addresses */
1050 TInetAddr localaddr_, remaddr_;
1051 status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len,
1052 localaddr_);
1053 if (status != PJ_SUCCESS)
1054 return status;
1055
1056 status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len,
1057 remaddr_);
1058 if (status != PJ_SUCCESS)
1059 return status;
1060
1061 pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr);
1062
1063 /* Init SSL engine */
1064 TRAPD(err, sock = CPjSSLSocket::NewL(proto));
1065 if (err != KErrNone)
1066 return PJ_ENOMEM;
1067
1068 if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) {
1069 ssock->connect_timer = new CPjTimer(&ssock->timeout,
1070 &connect_timer_cb, ssock);
1071 }
1072
1073 /* Convert server name to Symbian descriptor */
1074 TPtrC8 servername_((TUint8*)ssock->servername.ptr,
1075 ssock->servername.slen);
1076
1077 /* Try to connect */
1078 status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_,
1079 servername_);
1080 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1081 delete sock;
1082 return status;
1083 }
1084
1085 ssock->sock = sock;
1086 return status;
1087}
1088
Nanang Izzuddinea6d3c42009-10-26 15:47:52 +00001089
1090PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
1091{
1092 PJ_UNUSED_ARG(ssock);
1093 return PJ_ENOTSUP;
1094}