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(SilcResult 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_OK)
81 listener->callback(SILC_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"));
185 schedule = silc_schedule_get_global();
187 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
192 if (port < 0 || !callback) {
193 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
197 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
199 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
202 listener->schedule = schedule;
203 listener->callback = callback;
204 listener->context = context;
205 listener->require_fqdn = require_fqdn;
206 listener->lookup = lookup;
208 if (local_ip_count > 0) {
209 listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
210 sizeof(*listener->socks));
211 if (!listener->socks) {
212 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
216 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
217 if (!listener->socks) {
218 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
225 /* Bind to local addresses */
226 for (i = 0; i < local_ip_count; i++) {
227 SILC_LOG_DEBUG(("Binding to local address %s",
228 local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
230 l = new SilcSymbianTCPListener;
234 /* Connect to socket server */
235 ret = l->ss.Connect();
240 /* Make our socket shareable between threads */
242 #endif /* SILC_THREADS */
244 /* Set listener address */
245 if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
248 /* Create the socket */
249 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
250 if (ret != KErrNone) {
251 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
255 /* Set the socket options */
256 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
257 if (ret != KErrNone) {
258 SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
262 /* Bind the listener socket */
263 ret = l->sock.Bind(server);
264 if (ret != KErrNone) {
265 SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
269 /* Specify that we are listenning */
270 ret = l->sock.Listen(5);
271 if (ret != KErrNone) {
272 SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
277 l->listener = listener;
278 listener->socks[i] = (SilcSocket)l;
279 listener->socks_count++;
282 SILC_LOG_DEBUG(("TCP listener created"));
290 callback(SILC_ERR, NULL, context);
292 silc_net_close_listener(listener);
296 /* Create TCP listener, multiple ports */
299 silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports,
300 SilcUInt32 port_count,
301 SilcBool ignore_port_error,
302 SilcBool lookup, SilcBool require_fqdn,
303 SilcSchedule schedule,
304 SilcNetCallback callback, void *context)
306 SilcNetListener listener = NULL;
307 SilcSymbianTCPListener *l = NULL;
312 SILC_LOG_DEBUG(("Creating TCP listener"));
315 schedule = silc_schedule_get_global();
317 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
323 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
327 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
329 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
332 listener->schedule = schedule;
333 listener->callback = callback;
334 listener->context = context;
335 listener->require_fqdn = require_fqdn;
336 listener->lookup = lookup;
338 if (port_count > 0) {
339 listener->socks = (SilcSocket *)silc_calloc(port_count,
340 sizeof(*listener->socks));
341 if (!listener->socks) {
342 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
346 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
347 if (!listener->socks) {
348 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
356 for (i = 0; i < port_count; i++) {
357 SILC_LOG_DEBUG(("Binding to local address %s:%d",
358 local_ip_addr ? local_ip_addr : "0.0.0.0",
359 ports ? ports[i] : 0));
361 l = new SilcSymbianTCPListener;
365 /* Connect to socket server */
366 ret = l->ss.Connect();
371 /* Make our socket shareable between threads */
373 #endif /* SILC_THREADS */
375 /* Set listener address */
376 if (!silc_net_set_sockaddr(&server, local_ip_addr, ports ? ports[i] : 0)) {
377 if (ignore_port_error) {
384 /* Create the socket */
385 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
386 if (ret != KErrNone) {
387 if (ignore_port_error) {
391 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
395 /* Set the socket options */
396 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
397 if (ret != KErrNone) {
398 if (ignore_port_error) {
402 SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
406 /* Bind the listener socket */
407 ret = l->sock.Bind(server);
408 if (ret != KErrNone) {
409 if (ignore_port_error) {
413 SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
417 /* Specify that we are listenning */
418 ret = l->sock.Listen(5);
419 if (ret != KErrNone) {
420 if (ignore_port_error) {
424 SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
429 l->listener = listener;
430 listener->socks[i] = (SilcSocket)l;
431 listener->socks_count++;
434 if (ignore_port_error && !listener->socks_count) {
439 SILC_LOG_DEBUG(("TCP listener created"));
447 callback(SILC_ERR, NULL, context);
449 silc_net_close_listener(listener);
453 /* Close network listener */
455 void silc_net_close_listener(SilcNetListener listener)
459 SILC_LOG_DEBUG(("Closing network listener"));
464 for (i = 0; i < listener->socks_count; i++) {
465 SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
474 silc_free(listener->socks);
479 /**************************** TCP/IP connecting *****************************/
481 static void silc_net_connect_stream(SilcResult status,
482 SilcStream stream, void *context);
486 /* TCP connecting class */
488 class SilcSymbianTCPConnect : public CActive {
491 SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
493 CActiveScheduler::Add(this);
497 ~SilcSymbianTCPConnect()
505 /* Connect to remote host */
506 void Connect(TSockAddr &addr)
508 SILC_LOG_DEBUG(("Connect()"));
509 sock->Connect(addr, iStatus);
513 /* Connection callback */
516 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
518 if (iStatus != KErrNone) {
520 callback(SILC_ERR, NULL, context);
521 sock->CancelConnect();
531 SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
535 silc_socket_tcp_stream_create(
536 (SilcSocket)silc_create_symbian_socket(sock, ss),
537 TRUE, FALSE, schedule, silc_net_connect_stream,
553 virtual void DoCancel()
560 sock->CancelConnect();
570 SilcAsyncOperation op;
571 SilcSchedule schedule;
572 SilcNetCallback callback;
578 /* TCP stream creation callback */
580 static void silc_net_connect_stream(SilcResult status,
581 SilcStream stream, void *context)
583 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
585 SILC_LOG_DEBUG(("Socket stream creation status %d", status));
587 /* Call connection callback */
589 conn->callback(status, stream, conn->context);
591 silc_stream_destroy(stream);
596 /* Connecting abort callback */
598 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
600 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
603 conn->callback = NULL;
606 conn->sock->CancelConnect();
609 /* Create TCP/IP connection */
611 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
612 const char *remote_ip_addr,
614 SilcSchedule schedule,
615 SilcNetCallback callback,
618 SilcSymbianTCPConnect *conn;
619 TInetAddr local, remote;
624 schedule = silc_schedule_get_global();
626 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
631 if (!remote_ip_addr || remote_port < 1 || !callback) {
632 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
636 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
637 remote_ip_addr, remote_port));
639 conn = new SilcSymbianTCPConnect;
641 callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
644 conn->schedule = schedule;
645 conn->callback = callback;
646 conn->context = context;
647 conn->port = remote_port;
648 conn->remote = strdup(remote_ip_addr);
650 status = SILC_ERR_OUT_OF_MEMORY;
654 /* Allocate socket */
655 conn->sock = new RSocket;
657 status = SILC_ERR_OUT_OF_MEMORY;
661 /* Allocate socket server */
662 conn->ss = new RSocketServ;
664 status = SILC_ERR_OUT_OF_MEMORY;
668 /* Connect to socket server */
669 ret = conn->ss->Connect();
670 if (ret != KErrNone) {
671 SILC_LOG_ERROR(("Error connecting to socket server, error %d", ret));
677 /* Make our socket shareable between threads */
678 conn->ss->ShareAuto();
679 #endif /* SILC_THREADS */
681 /* Start async operation */
682 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
684 status = SILC_ERR_OUT_OF_MEMORY;
689 if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
690 sizeof(conn->remote_ip))) {
691 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
692 "host", conn->remote));
693 status = SILC_ERR_UNREACHABLE;
697 /* Create the connection socket */
698 ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
699 if (ret != KErrNone) {
700 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
705 /* Set appropriate options */
706 conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
707 conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
709 /* Bind to the local address if provided */
711 if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
712 conn->sock->Bind(local);
714 /* Connect to the host */
715 if (!silc_net_set_sockaddr(&remote, conn->remote_ip, remote_port)) {
716 SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
720 conn->Connect(remote);
722 SILC_LOG_DEBUG(("Connection operation in progress"));
734 silc_free(conn->remote);
736 silc_async_free(conn->op);
737 callback(status, NULL, context);
742 /****************************** UDP routines ********************************/
744 /* Create UDP/IP connection */
746 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
747 const char *remote_ip_addr, int remote_port,
748 SilcSchedule schedule)
750 SilcSymbianSocket *s;
752 TInetAddr local, remote;
753 TRequestStatus status;
754 RSocket *sock = NULL;
755 RSocketServ *ss = NULL;
758 SILC_LOG_DEBUG(("Creating UDP stream"));
761 schedule = silc_schedule_get_global();
763 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
768 SILC_LOG_DEBUG(("Binding to local address %s",
769 local_ip_addr ? local_ip_addr : "0.0.0.0"));
775 ss = new RSocketServ;
779 /* Open socket server */
785 /* Make our socket shareable between threads */
787 #endif /* SILC_THREADS */
789 /* Get local bind address */
790 if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
793 /* Create the socket */
794 ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
795 if (ret != KErrNone) {
796 SILC_LOG_ERROR(("Cannot create socket"));
800 /* Set the socket options */
801 sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
803 /* Bind the listener socket */
804 ret = sock->Bind(local);
805 if (ret != KErrNone) {
806 SILC_LOG_DEBUG(("Cannot bind socket"));
810 /* Set to connected state if remote address is provided. */
811 if (remote_ip_addr && remote_port) {
812 if (silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) {
813 sock->Connect(remote, status);
814 if (status != KErrNone) {
815 SILC_LOG_DEBUG(("Cannot connect UDP stream"));
821 /* Encapsulate into socket stream */
822 s = silc_create_symbian_socket(sock, ss);
826 silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
827 silc_net_is_ip6(local_ip_addr) : FALSE,
828 remote_ip_addr ? TRUE : FALSE, schedule);
832 SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
845 /* Sets socket to non-blocking mode */
847 int silc_net_set_socket_nonblock(SilcSocket sock)
849 /* Nothing to do in Symbian where blocking socket mode is asynchronous
850 already (ie. non-blocking). */
854 /* Converts the IP number string from numbers-and-dots notation to
857 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
862 ret = inet_aton(addr, &tmp);
866 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
871 /* Get remote host and IP from socket */
873 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
876 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
885 s->sock->RemoteName(addr);
888 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
892 /* Do reverse lookup if we want hostname too. */
894 /* Get host by address */
895 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
898 *hostname = (char *)silc_memdup(host, strlen(host));
899 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
902 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
905 if (strcmp(*ip, host))
909 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
913 /* Get local host and IP from socket */
915 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
918 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
927 s->sock->LocalName(addr);
930 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
934 /* Do reverse lookup if we want hostname too. */
936 /* Get host by address */
937 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
940 *hostname = (char *)silc_memdup(host, strlen(host));
941 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
944 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
947 if (strcmp(*ip, host))
951 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
955 /* Get remote port from socket */
957 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
959 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
962 s->sock->RemoteName(addr);
963 return (SilcUInt16)addr.Port();
966 /* Get local port from socket */
968 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
970 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
973 s->sock->LocalName(addr);
974 return (SilcUInt16)addr.Port();