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