5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2006 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcsymbiansocketstream.h"
23 /****************************** TCP Listener ********************************/
25 class SilcSymbianTCPListener;
27 /* Deliver new stream to upper layer */
29 static void silc_net_accept_stream(SilcSocketStreamStatus status,
30 SilcStream stream, void *context)
32 SilcNetListener listener = (SilcNetListener)context;
34 /* In case of error, the socket has been destroyed already via
35 silc_stream_destroy. */
36 if (status != SILC_SOCKET_OK)
39 listener->callback(SILC_NET_OK, stream, listener->context);
42 /* TCP Listener class */
44 class SilcSymbianTCPListener : public CActive {
47 SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
49 CActiveScheduler::Add(this);
53 ~SilcSymbianTCPListener()
58 /* Listen for connection */
61 SILC_LOG_DEBUG(("Listen()"));
63 new_conn = new RSocket;
66 if (new_conn->Open(ss) != KErrNone) {
71 /* Start listenning */
72 sock.Accept(*new_conn, iStatus);
76 /* Listener callback */
79 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
81 if (iStatus != KErrNone) {
89 SILC_LOG_DEBUG(("Accept new connection"));
91 /* Set socket options */
92 new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
94 /* Create socket stream */
95 silc_socket_tcp_stream_create(
96 (SilcSocket)silc_create_symbian_socket(new_conn, NULL),
97 listener->lookup, listener->require_fqdn,
98 listener->schedule, silc_net_accept_stream,
102 /* Continue listenning */
107 virtual void DoCancel()
118 SilcNetListener listener;
123 /* Create TCP listener */
126 silc_net_tcp_create_listener(const char **local_ip_addr,
127 SilcUInt32 local_ip_count, int port,
128 SilcBool lookup, SilcBool require_fqdn,
129 SilcSchedule schedule,
130 SilcNetCallback callback, void *context)
132 SilcNetListener listener = NULL;
133 SilcSymbianTCPListener *l = NULL;
139 SILC_LOG_DEBUG(("Creating TCP listener"));
141 if (port < 0 || !schedule || !callback)
144 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
146 callback(SILC_NET_NO_MEMORY, NULL, context);
149 listener->schedule = schedule;
150 listener->callback = callback;
151 listener->context = context;
152 listener->require_fqdn = require_fqdn;
153 listener->lookup = lookup;
155 if (local_ip_count > 0) {
156 listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
157 sizeof(*listener->socks));
158 if (!listener->socks) {
159 callback(SILC_NET_NO_MEMORY, NULL, context);
163 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
164 if (!listener->socks) {
165 callback(SILC_NET_NO_MEMORY, NULL, context);
172 /* Bind to local addresses */
173 for (i = 0; i < local_ip_count; i++) {
174 SILC_LOG_DEBUG(("Binding to local address %s",
175 local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
177 l = new SilcSymbianTCPListener;
181 /* Connect to socket server */
182 ret = l->ss.Connect();
186 /* Set listener address */
188 server = TInetAddr(port);
189 tmp = (TText *)local_ip_addr[i];
190 ret = server.Input(tmp);
194 server = TInetAddr(KInetAddrAny, port);
197 /* Create the socket */
198 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
199 if (ret != KErrNone) {
200 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
204 /* Set the socket options */
205 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
206 if (ret != KErrNone) {
207 SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
211 /* Bind the listener socket */
212 ret = l->sock.Bind(server);
213 if (ret != KErrNone) {
214 SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
218 /* Specify that we are listenning */
219 ret = l->sock.Listen(5);
220 if (ret != KErrNone) {
221 SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
226 l->listener = listener;
227 listener->socks[i] = (SilcSocket)l;
228 listener->socks_count++;
231 SILC_LOG_DEBUG(("TCP listener created"));
239 callback(SILC_NET_ERROR, NULL, context);
241 silc_net_close_listener(listener);
245 /* Close network listener */
247 void silc_net_close_listener(SilcNetListener listener)
251 SILC_LOG_DEBUG(("Closing network listener"));
256 for (i = 0; i < listener->socks_count; i++) {
257 SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
266 silc_free(listener->socks);
272 /**************************** TCP/IP connecting *****************************/
274 static void silc_net_connect_stream(SilcSocketStreamStatus status,
275 SilcStream stream, void *context);
277 /* TCP connecting class */
279 class SilcSymbianTCPConnect : public CActive {
282 SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
284 CActiveScheduler::Add(this);
288 ~SilcSymbianTCPConnect()
296 /* Connect to remote host */
297 void Connect(TSockAddr &addr)
299 SILC_LOG_DEBUG(("Connect()"));
300 sock->Connect(addr, iStatus);
304 /* Connection callback */
307 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
309 if (iStatus != KErrNone) {
311 callback(SILC_NET_ERROR, NULL, context);
312 sock->CancelConnect();
322 SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
326 silc_socket_tcp_stream_create(
327 (SilcSocket)silc_create_symbian_socket(sock, ss),
328 TRUE, FALSE, schedule, silc_net_connect_stream,
342 virtual void DoCancel()
349 sock->CancelConnect();
359 SilcAsyncOperation op;
360 SilcSchedule schedule;
361 SilcNetCallback callback;
367 /* TCP stream creation callback */
369 static void silc_net_connect_stream(SilcSocketStreamStatus status,
370 SilcStream stream, void *context)
372 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
373 SilcNetStatus net_status = SILC_NET_OK;
375 SILC_LOG_DEBUG(("Socket stream creation status %d", status));
377 if (status != SILC_SOCKET_OK) {
378 /* In case of error, the socket has been destroyed already via
379 silc_stream_destroy. */
380 if (status == SILC_SOCKET_UNKNOWN_IP)
381 net_status = SILC_NET_UNKNOWN_IP;
382 else if (status == SILC_SOCKET_UNKNOWN_HOST)
383 net_status = SILC_NET_UNKNOWN_HOST;
385 net_status = SILC_NET_ERROR;
388 /* Call connection callback */
390 conn->callback(net_status, stream, conn->context);
392 silc_stream_destroy(stream);
397 /* Connecting abort callback */
399 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
401 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
404 conn->callback = NULL;
407 conn->sock->CancelConnect();
410 /* Create TCP/IP connection */
412 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
413 const char *remote_ip_addr,
415 SilcSchedule schedule,
416 SilcNetCallback callback,
419 SilcSymbianTCPConnect *conn;
420 TInetAddr local, remote;
421 SilcNetStatus status;
425 if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
428 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
429 remote_ip_addr, remote_port));
431 conn = new SilcSymbianTCPConnect;
433 callback(SILC_NET_NO_MEMORY, NULL, context);
436 conn->schedule = schedule;
437 conn->callback = callback;
438 conn->context = context;
439 conn->port = remote_port;
440 conn->remote = strdup(remote_ip_addr);
442 status = SILC_NET_NO_MEMORY;
446 /* Allocate socket */
447 conn->sock = new RSocket;
449 status = SILC_NET_NO_MEMORY;
453 /* Allocate socket server */
454 conn->ss = new RSocketServ;
456 status = SILC_NET_NO_MEMORY;
460 /* Connect to socket server */
461 ret = conn->ss->Connect();
462 if (ret != KErrNone) {
463 status = SILC_NET_ERROR;
467 /* Start async operation */
468 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
470 status = SILC_NET_NO_MEMORY;
475 if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
476 sizeof(conn->remote_ip))) {
477 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
478 "host", conn->remote));
479 status = SILC_NET_HOST_UNREACHABLE;
483 /* Create the connection socket */
484 ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
485 if (ret != KErrNone) {
486 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
487 status = SILC_NET_ERROR;
491 /* Set appropriate options */
492 conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
493 conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
495 /* Bind to the local address if provided */
497 local = TInetAddr(0);
498 tmp = (TText *)local_ip_addr;
499 ret = local.Input(tmp);
501 ret = conn->sock->Bind(local);
504 /* Connect to the host */
505 remote = TInetAddr(remote_port);
506 tmp = (TText *)conn->remote_ip;
507 ret = remote.Input(tmp);
508 if (ret != KErrNone) {
509 SILC_LOG_ERROR(("Cannot connect (cannot set address), error %d", ret));
510 status = SILC_NET_ERROR;
513 conn->Connect(remote);
515 SILC_LOG_DEBUG(("Connection operation in progress"));
527 silc_free(conn->remote);
529 silc_async_free(conn->op);
530 callback(status, NULL, context);
535 /****************************** UDP routines ********************************/
537 /* Create UDP/IP connection */
539 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
540 const char *remote_ip_addr, int remote_port,
541 SilcSchedule schedule)
543 SilcSymbianSocket *s;
545 TInetAddr local, remote;
546 TRequestStatus status;
547 RSocket *sock = NULL;
548 RSocketServ *ss = NULL;
552 SILC_LOG_DEBUG(("Creating UDP stream"));
557 SILC_LOG_DEBUG(("Binding to local address %s",
558 local_ip_addr ? local_ip_addr : "0.0.0.0"));
564 ss = new RSocketServ;
568 /* Open socket server */
573 /* Get local bind address */
575 local = TInetAddr(local_port);
576 tmp = (TText *)local_ip_addr;
577 ret = local.Input(tmp);
581 local = TInetAddr(KInetAddrAny, local_port);
584 /* Create the socket */
585 ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
586 if (ret != KErrNone) {
587 SILC_LOG_ERROR(("Cannot create socket"));
591 /* Set the socket options */
592 sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
594 /* Bind the listener socket */
595 ret = sock->Bind(local);
596 if (ret != KErrNone) {
597 SILC_LOG_DEBUG(("Cannot bind socket"));
601 /* Set to connected state if remote address is provided. */
602 if (remote_ip_addr && remote_port) {
603 remote = TInetAddr(remote_port);
604 tmp = (TText *)remote_ip_addr;
605 ret = remote.Input(tmp);
609 sock->Connect(remote, status);
610 if (status != KErrNone) {
611 SILC_LOG_DEBUG(("Cannot connect UDP stream"));
616 /* Encapsulate into socket stream */
617 s = silc_create_symbian_socket(sock, ss);
621 silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
622 silc_net_is_ip6(local_ip_addr) : FALSE,
623 remote_ip_addr ? TRUE : FALSE, schedule);
627 SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
640 /* Sets socket to non-blocking mode */
642 int silc_net_set_socket_nonblock(SilcSocket sock)
644 /* Nothing to do in Symbian where blocking socket mode is asynchronous
645 already (ie. non-blocking). */
649 /* Converts the IP number string from numbers-and-dots notation to
652 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
657 ret = inet_aton(addr, &tmp);
661 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
666 /* Get remote host and IP from socket */
668 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
671 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
680 s->sock->RemoteName(addr);
683 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
687 /* Do reverse lookup if we want hostname too. */
689 /* Get host by address */
690 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
693 *hostname = (char *)silc_memdup(host, strlen(host));
694 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
697 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
700 if (strcmp(*ip, host))
704 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
708 /* Get local host and IP from socket */
710 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
713 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
722 s->sock->LocalName(addr);
725 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
729 /* Do reverse lookup if we want hostname too. */
731 /* Get host by address */
732 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
735 *hostname = (char *)silc_memdup(host, strlen(host));
736 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
739 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
742 if (strcmp(*ip, host))
746 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
750 /* Get remote port from socket */
752 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
754 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
757 s->sock->RemoteName(addr);
758 return (SilcUInt16)addr.Port();
761 /* Get local port from socket */
763 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
765 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
768 s->sock->LocalName(addr);
769 return (SilcUInt16)addr.Port();