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 /************************ Static utility functions **************************/
25 static SilcBool silc_net_set_sockaddr(TInetAddr *addr, const char *ip_addr,
28 /* Check for IPv4 and IPv6 addresses */
30 if (!silc_net_is_ip(ip_addr)) {
31 SILC_LOG_ERROR(("%s is not IP address", ip_addr));
35 if (silc_net_is_ip4(ip_addr)) {
40 if (!silc_net_addr2bin(ip_addr, buf, sizeof(buf)))
43 SILC_GET32_MSB(a, buf);
48 SILC_LOG_ERROR(("IPv6 not supported"));
51 SILC_LOG_ERROR(("Operating System does not support IPv6"));
63 /****************************** TCP Listener ********************************/
65 class SilcSymbianTCPListener;
69 /* Deliver new stream to upper layer */
71 static void silc_net_accept_stream(SilcSocketStreamStatus status,
72 SilcStream stream, void *context)
74 SilcNetListener listener = (SilcNetListener)context;
76 /* In case of error, the socket has been destroyed already via
77 silc_stream_destroy. */
78 if (status != SILC_SOCKET_OK)
81 listener->callback(SILC_NET_OK, stream, listener->context);
86 /* TCP Listener class */
88 class SilcSymbianTCPListener : public CActive {
91 SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
93 CActiveScheduler::Add(this);
97 ~SilcSymbianTCPListener()
102 /* Listen for connection */
105 SILC_LOG_DEBUG(("Listen()"));
107 new_conn = new RSocket;
110 if (new_conn->Open(ss) != KErrNone) {
115 /* Start listenning */
116 sock.Accept(*new_conn, iStatus);
120 /* Listener callback */
123 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
125 if (iStatus != KErrNone) {
133 SILC_LOG_DEBUG(("Accept new connection"));
135 /* Set socket options */
136 new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
138 /* Create socket stream */
139 silc_socket_tcp_stream_create(
140 (SilcSocket)silc_create_symbian_socket(new_conn, NULL),
141 listener->lookup, listener->require_fqdn,
142 listener->schedule, silc_net_accept_stream,
146 /* Continue listenning */
151 virtual void DoCancel()
162 SilcNetListener listener;
167 /* Create TCP listener */
170 silc_net_tcp_create_listener(const char **local_ip_addr,
171 SilcUInt32 local_ip_count, int port,
172 SilcBool lookup, SilcBool require_fqdn,
173 SilcSchedule schedule,
174 SilcNetCallback callback, void *context)
176 SilcNetListener listener = NULL;
177 SilcSymbianTCPListener *l = NULL;
182 SILC_LOG_DEBUG(("Creating TCP listener"));
184 if (port < 0 || !schedule || !callback)
187 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
189 callback(SILC_NET_NO_MEMORY, NULL, context);
192 listener->schedule = schedule;
193 listener->callback = callback;
194 listener->context = context;
195 listener->require_fqdn = require_fqdn;
196 listener->lookup = lookup;
198 if (local_ip_count > 0) {
199 listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
200 sizeof(*listener->socks));
201 if (!listener->socks) {
202 callback(SILC_NET_NO_MEMORY, NULL, context);
206 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
207 if (!listener->socks) {
208 callback(SILC_NET_NO_MEMORY, NULL, context);
215 /* Bind to local addresses */
216 for (i = 0; i < local_ip_count; i++) {
217 SILC_LOG_DEBUG(("Binding to local address %s",
218 local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
220 l = new SilcSymbianTCPListener;
224 /* Connect to socket server */
225 ret = l->ss.Connect();
230 /* Make our socket shareable between threads */
232 #endif /* SILC_THREADS */
234 /* Set listener address */
235 if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
238 /* Create the socket */
239 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
240 if (ret != KErrNone) {
241 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
245 /* Set the socket options */
246 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
247 if (ret != KErrNone) {
248 SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
252 /* Bind the listener socket */
253 ret = l->sock.Bind(server);
254 if (ret != KErrNone) {
255 SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
259 /* Specify that we are listenning */
260 ret = l->sock.Listen(5);
261 if (ret != KErrNone) {
262 SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
267 l->listener = listener;
268 listener->socks[i] = (SilcSocket)l;
269 listener->socks_count++;
272 SILC_LOG_DEBUG(("TCP listener created"));
280 callback(SILC_NET_ERROR, NULL, context);
282 silc_net_close_listener(listener);
286 /* Create TCP listener, multiple ports */
289 silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports,
290 SilcUInt32 port_count,
291 SilcBool ignore_port_error,
292 SilcBool lookup, SilcBool require_fqdn,
293 SilcSchedule schedule,
294 SilcNetCallback callback, void *context)
296 SilcNetListener listener = NULL;
297 SilcSymbianTCPListener *l = NULL;
302 SILC_LOG_DEBUG(("Creating TCP listener"));
304 if (!schedule || !callback)
307 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
309 callback(SILC_NET_NO_MEMORY, NULL, context);
312 listener->schedule = schedule;
313 listener->callback = callback;
314 listener->context = context;
315 listener->require_fqdn = require_fqdn;
316 listener->lookup = lookup;
318 if (port_count > 0) {
319 listener->socks = (SilcSocket *)silc_calloc(port_count,
320 sizeof(*listener->socks));
321 if (!listener->socks) {
322 callback(SILC_NET_NO_MEMORY, NULL, context);
326 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
327 if (!listener->socks) {
328 callback(SILC_NET_NO_MEMORY, NULL, context);
336 for (i = 0; i < port_count; i++) {
337 SILC_LOG_DEBUG(("Binding to local address %s:%d",
338 local_ip_addr ? local_ip_addr : "0.0.0.0",
339 ports ? ports[i] : 0));
341 l = new SilcSymbianTCPListener;
345 /* Connect to socket server */
346 ret = l->ss.Connect();
351 /* Make our socket shareable between threads */
353 #endif /* SILC_THREADS */
355 /* Set listener address */
356 if (!silc_net_set_sockaddr(&server, local_ip_addr, ports ? ports[i] : 0)) {
357 if (ignore_port_error) {
364 /* Create the socket */
365 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
366 if (ret != KErrNone) {
367 if (ignore_port_error) {
371 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
375 /* Set the socket options */
376 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
377 if (ret != KErrNone) {
378 if (ignore_port_error) {
382 SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
386 /* Bind the listener socket */
387 ret = l->sock.Bind(server);
388 if (ret != KErrNone) {
389 if (ignore_port_error) {
393 SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
397 /* Specify that we are listenning */
398 ret = l->sock.Listen(5);
399 if (ret != KErrNone) {
400 if (ignore_port_error) {
404 SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
409 l->listener = listener;
410 listener->socks[i] = (SilcSocket)l;
411 listener->socks_count++;
414 if (ignore_port_error && !listener->socks_count) {
419 SILC_LOG_DEBUG(("TCP listener created"));
427 callback(SILC_NET_ERROR, NULL, context);
429 silc_net_close_listener(listener);
433 /* Close network listener */
435 void silc_net_close_listener(SilcNetListener listener)
439 SILC_LOG_DEBUG(("Closing network listener"));
444 for (i = 0; i < listener->socks_count; i++) {
445 SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
454 silc_free(listener->socks);
459 /**************************** TCP/IP connecting *****************************/
461 static void silc_net_connect_stream(SilcSocketStreamStatus status,
462 SilcStream stream, void *context);
466 /* TCP connecting class */
468 class SilcSymbianTCPConnect : public CActive {
471 SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
473 CActiveScheduler::Add(this);
477 ~SilcSymbianTCPConnect()
485 /* Connect to remote host */
486 void Connect(TSockAddr &addr)
488 SILC_LOG_DEBUG(("Connect()"));
489 sock->Connect(addr, iStatus);
493 /* Connection callback */
496 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
498 if (iStatus != KErrNone) {
500 callback(SILC_NET_ERROR, NULL, context);
501 sock->CancelConnect();
511 SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
515 silc_socket_tcp_stream_create(
516 (SilcSocket)silc_create_symbian_socket(sock, ss),
517 TRUE, FALSE, schedule, silc_net_connect_stream,
533 virtual void DoCancel()
540 sock->CancelConnect();
550 SilcAsyncOperation op;
551 SilcSchedule schedule;
552 SilcNetCallback callback;
558 /* TCP stream creation callback */
560 static void silc_net_connect_stream(SilcSocketStreamStatus status,
561 SilcStream stream, void *context)
563 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
564 SilcNetStatus net_status = SILC_NET_OK;
566 SILC_LOG_DEBUG(("Socket stream creation status %d", status));
568 if (status != SILC_SOCKET_OK) {
569 /* In case of error, the socket has been destroyed already via
570 silc_stream_destroy. */
571 if (status == SILC_SOCKET_UNKNOWN_IP)
572 net_status = SILC_NET_UNKNOWN_IP;
573 else if (status == SILC_SOCKET_UNKNOWN_HOST)
574 net_status = SILC_NET_UNKNOWN_HOST;
576 net_status = SILC_NET_ERROR;
579 /* Call connection callback */
581 conn->callback(net_status, stream, conn->context);
583 silc_stream_destroy(stream);
588 /* Connecting abort callback */
590 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
592 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
595 conn->callback = NULL;
598 conn->sock->CancelConnect();
601 /* Create TCP/IP connection */
603 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
604 const char *remote_ip_addr,
606 SilcSchedule schedule,
607 SilcNetCallback callback,
610 SilcSymbianTCPConnect *conn;
611 TInetAddr local, remote;
612 SilcNetStatus status;
615 if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
618 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
619 remote_ip_addr, remote_port));
621 conn = new SilcSymbianTCPConnect;
623 callback(SILC_NET_NO_MEMORY, NULL, context);
626 conn->schedule = schedule;
627 conn->callback = callback;
628 conn->context = context;
629 conn->port = remote_port;
630 conn->remote = strdup(remote_ip_addr);
632 status = SILC_NET_NO_MEMORY;
636 /* Allocate socket */
637 conn->sock = new RSocket;
639 status = SILC_NET_NO_MEMORY;
643 /* Allocate socket server */
644 conn->ss = new RSocketServ;
646 status = SILC_NET_NO_MEMORY;
650 /* Connect to socket server */
651 ret = conn->ss->Connect();
652 if (ret != KErrNone) {
653 SILC_LOG_ERROR(("Error connecting to socket server, error %d", ret));
654 status = SILC_NET_ERROR;
659 /* Make our socket shareable between threads */
660 conn->ss->ShareAuto();
661 #endif /* SILC_THREADS */
663 /* Start async operation */
664 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
666 status = SILC_NET_NO_MEMORY;
671 if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
672 sizeof(conn->remote_ip))) {
673 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
674 "host", conn->remote));
675 status = SILC_NET_HOST_UNREACHABLE;
679 /* Create the connection socket */
680 ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
681 if (ret != KErrNone) {
682 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
683 status = SILC_NET_ERROR;
687 /* Set appropriate options */
688 conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
689 conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
691 /* Bind to the local address if provided */
693 if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
694 conn->sock->Bind(local);
696 /* Connect to the host */
697 if (!silc_net_set_sockaddr(&remote, conn->remote_ip, remote_port)) {
698 SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
699 status = SILC_NET_ERROR;
702 conn->Connect(remote);
704 SILC_LOG_DEBUG(("Connection operation in progress"));
716 silc_free(conn->remote);
718 silc_async_free(conn->op);
719 callback(status, NULL, context);
724 /****************************** UDP routines ********************************/
726 /* Create UDP/IP connection */
728 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
729 const char *remote_ip_addr, int remote_port,
730 SilcSchedule schedule)
732 SilcSymbianSocket *s;
734 TInetAddr local, remote;
735 TRequestStatus status;
736 RSocket *sock = NULL;
737 RSocketServ *ss = NULL;
740 SILC_LOG_DEBUG(("Creating UDP stream"));
745 SILC_LOG_DEBUG(("Binding to local address %s",
746 local_ip_addr ? local_ip_addr : "0.0.0.0"));
752 ss = new RSocketServ;
756 /* Open socket server */
762 /* Make our socket shareable between threads */
764 #endif /* SILC_THREADS */
766 /* Get local bind address */
767 if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
770 /* Create the socket */
771 ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
772 if (ret != KErrNone) {
773 SILC_LOG_ERROR(("Cannot create socket"));
777 /* Set the socket options */
778 sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
780 /* Bind the listener socket */
781 ret = sock->Bind(local);
782 if (ret != KErrNone) {
783 SILC_LOG_DEBUG(("Cannot bind socket"));
787 /* Set to connected state if remote address is provided. */
788 if (remote_ip_addr && remote_port) {
789 if (silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) {
790 sock->Connect(remote, status);
791 if (status != KErrNone) {
792 SILC_LOG_DEBUG(("Cannot connect UDP stream"));
798 /* Encapsulate into socket stream */
799 s = silc_create_symbian_socket(sock, ss);
803 silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
804 silc_net_is_ip6(local_ip_addr) : FALSE,
805 remote_ip_addr ? TRUE : FALSE, schedule);
809 SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
822 /* Sets socket to non-blocking mode */
824 int silc_net_set_socket_nonblock(SilcSocket sock)
826 /* Nothing to do in Symbian where blocking socket mode is asynchronous
827 already (ie. non-blocking). */
831 /* Converts the IP number string from numbers-and-dots notation to
834 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
839 ret = inet_aton(addr, &tmp);
843 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
848 /* Get remote host and IP from socket */
850 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
853 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
862 s->sock->RemoteName(addr);
865 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
869 /* Do reverse lookup if we want hostname too. */
871 /* Get host by address */
872 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
875 *hostname = (char *)silc_memdup(host, strlen(host));
876 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
879 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
882 if (strcmp(*ip, host))
886 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
890 /* Get local host and IP from socket */
892 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
895 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
904 s->sock->LocalName(addr);
907 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
911 /* Do reverse lookup if we want hostname too. */
913 /* Get host by address */
914 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
917 *hostname = (char *)silc_memdup(host, strlen(host));
918 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
921 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
924 if (strcmp(*ip, host))
928 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
932 /* Get remote port from socket */
934 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
936 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
939 s->sock->RemoteName(addr);
940 return (SilcUInt16)addr.Port();
943 /* Get local port from socket */
945 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
947 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
950 s->sock->LocalName(addr);
951 return (SilcUInt16)addr.Port();