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;
67 /* Deliver new stream to upper layer */
69 static void silc_net_accept_stream(SilcSocketStreamStatus status,
70 SilcStream stream, void *context)
72 SilcNetListener listener = (SilcNetListener)context;
74 /* In case of error, the socket has been destroyed already via
75 silc_stream_destroy. */
76 if (status != SILC_SOCKET_OK)
79 listener->callback(SILC_NET_OK, stream, listener->context);
82 /* TCP Listener class */
84 class SilcSymbianTCPListener : public CActive {
87 SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
89 CActiveScheduler::Add(this);
93 ~SilcSymbianTCPListener()
98 /* Listen for connection */
101 SILC_LOG_DEBUG(("Listen()"));
103 new_conn = new RSocket;
106 if (new_conn->Open(ss) != KErrNone) {
111 /* Start listenning */
112 sock.Accept(*new_conn, iStatus);
116 /* Listener callback */
119 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
121 if (iStatus != KErrNone) {
129 SILC_LOG_DEBUG(("Accept new connection"));
131 /* Set socket options */
132 new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
134 /* Create socket stream */
135 silc_socket_tcp_stream_create(
136 (SilcSocket)silc_create_symbian_socket(new_conn, NULL),
137 listener->lookup, listener->require_fqdn,
138 listener->schedule, silc_net_accept_stream,
142 /* Continue listenning */
147 virtual void DoCancel()
158 SilcNetListener listener;
163 /* Create TCP listener */
166 silc_net_tcp_create_listener(const char **local_ip_addr,
167 SilcUInt32 local_ip_count, int port,
168 SilcBool lookup, SilcBool require_fqdn,
169 SilcSchedule schedule,
170 SilcNetCallback callback, void *context)
172 SilcNetListener listener = NULL;
173 SilcSymbianTCPListener *l = NULL;
178 SILC_LOG_DEBUG(("Creating TCP listener"));
180 if (port < 0 || !schedule || !callback)
183 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
185 callback(SILC_NET_NO_MEMORY, NULL, context);
188 listener->schedule = schedule;
189 listener->callback = callback;
190 listener->context = context;
191 listener->require_fqdn = require_fqdn;
192 listener->lookup = lookup;
194 if (local_ip_count > 0) {
195 listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
196 sizeof(*listener->socks));
197 if (!listener->socks) {
198 callback(SILC_NET_NO_MEMORY, NULL, context);
202 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
203 if (!listener->socks) {
204 callback(SILC_NET_NO_MEMORY, NULL, context);
211 /* Bind to local addresses */
212 for (i = 0; i < local_ip_count; i++) {
213 SILC_LOG_DEBUG(("Binding to local address %s",
214 local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
216 l = new SilcSymbianTCPListener;
220 /* Connect to socket server */
221 ret = l->ss.Connect();
226 /* Make our socket shareable between threads */
228 #endif /* SILC_THREADS */
230 /* Set listener address */
231 if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
234 /* Create the socket */
235 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
236 if (ret != KErrNone) {
237 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
241 /* Set the socket options */
242 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
243 if (ret != KErrNone) {
244 SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
248 /* Bind the listener socket */
249 ret = l->sock.Bind(server);
250 if (ret != KErrNone) {
251 SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
255 /* Specify that we are listenning */
256 ret = l->sock.Listen(5);
257 if (ret != KErrNone) {
258 SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
263 l->listener = listener;
264 listener->socks[i] = (SilcSocket)l;
265 listener->socks_count++;
268 SILC_LOG_DEBUG(("TCP listener created"));
276 callback(SILC_NET_ERROR, NULL, context);
278 silc_net_close_listener(listener);
282 /* Close network listener */
284 void silc_net_close_listener(SilcNetListener listener)
288 SILC_LOG_DEBUG(("Closing network listener"));
293 for (i = 0; i < listener->socks_count; i++) {
294 SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
303 silc_free(listener->socks);
309 /**************************** TCP/IP connecting *****************************/
311 static void silc_net_connect_stream(SilcSocketStreamStatus status,
312 SilcStream stream, void *context);
314 /* TCP connecting class */
316 class SilcSymbianTCPConnect : public CActive {
319 SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
321 CActiveScheduler::Add(this);
325 ~SilcSymbianTCPConnect()
333 /* Connect to remote host */
334 void Connect(TSockAddr &addr)
336 SILC_LOG_DEBUG(("Connect()"));
337 sock->Connect(addr, iStatus);
341 /* Connection callback */
344 SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
346 if (iStatus != KErrNone) {
348 callback(SILC_NET_ERROR, NULL, context);
349 sock->CancelConnect();
359 SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
363 silc_socket_tcp_stream_create(
364 (SilcSocket)silc_create_symbian_socket(sock, ss),
365 TRUE, FALSE, schedule, silc_net_connect_stream,
381 virtual void DoCancel()
388 sock->CancelConnect();
398 SilcAsyncOperation op;
399 SilcSchedule schedule;
400 SilcNetCallback callback;
406 /* TCP stream creation callback */
408 static void silc_net_connect_stream(SilcSocketStreamStatus status,
409 SilcStream stream, void *context)
411 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
412 SilcNetStatus net_status = SILC_NET_OK;
414 SILC_LOG_DEBUG(("Socket stream creation status %d", status));
416 if (status != SILC_SOCKET_OK) {
417 /* In case of error, the socket has been destroyed already via
418 silc_stream_destroy. */
419 if (status == SILC_SOCKET_UNKNOWN_IP)
420 net_status = SILC_NET_UNKNOWN_IP;
421 else if (status == SILC_SOCKET_UNKNOWN_HOST)
422 net_status = SILC_NET_UNKNOWN_HOST;
424 net_status = SILC_NET_ERROR;
427 /* Call connection callback */
429 conn->callback(net_status, stream, conn->context);
431 silc_stream_destroy(stream);
436 /* Connecting abort callback */
438 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
440 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
443 conn->callback = NULL;
446 conn->sock->CancelConnect();
449 /* Create TCP/IP connection */
451 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
452 const char *remote_ip_addr,
454 SilcSchedule schedule,
455 SilcNetCallback callback,
458 SilcSymbianTCPConnect *conn;
459 TInetAddr local, remote;
460 SilcNetStatus status;
463 if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
466 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
467 remote_ip_addr, remote_port));
469 conn = new SilcSymbianTCPConnect;
471 callback(SILC_NET_NO_MEMORY, NULL, context);
474 conn->schedule = schedule;
475 conn->callback = callback;
476 conn->context = context;
477 conn->port = remote_port;
478 conn->remote = strdup(remote_ip_addr);
480 status = SILC_NET_NO_MEMORY;
484 /* Allocate socket */
485 conn->sock = new RSocket;
487 status = SILC_NET_NO_MEMORY;
491 /* Allocate socket server */
492 conn->ss = new RSocketServ;
494 status = SILC_NET_NO_MEMORY;
498 /* Connect to socket server */
499 ret = conn->ss->Connect();
500 if (ret != KErrNone) {
501 SILC_LOG_ERROR(("Error connecting to socket server, error %d", ret));
502 status = SILC_NET_ERROR;
507 /* Make our socket shareable between threads */
508 conn->ss->ShareAuto();
509 #endif /* SILC_THREADS */
511 /* Start async operation */
512 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
514 status = SILC_NET_NO_MEMORY;
519 if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
520 sizeof(conn->remote_ip))) {
521 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
522 "host", conn->remote));
523 status = SILC_NET_HOST_UNREACHABLE;
527 /* Create the connection socket */
528 ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
529 if (ret != KErrNone) {
530 SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
531 status = SILC_NET_ERROR;
535 /* Set appropriate options */
536 conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
537 conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
539 /* Bind to the local address if provided */
541 if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
542 conn->sock->Bind(local);
544 /* Connect to the host */
545 if (!silc_net_set_sockaddr(&remote, conn->remote_ip, remote_port)) {
546 SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
547 status = SILC_NET_ERROR;
550 conn->Connect(remote);
552 SILC_LOG_DEBUG(("Connection operation in progress"));
564 silc_free(conn->remote);
566 silc_async_free(conn->op);
567 callback(status, NULL, context);
572 /****************************** UDP routines ********************************/
574 /* Create UDP/IP connection */
576 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
577 const char *remote_ip_addr, int remote_port,
578 SilcSchedule schedule)
580 SilcSymbianSocket *s;
582 TInetAddr local, remote;
583 TRequestStatus status;
584 RSocket *sock = NULL;
585 RSocketServ *ss = NULL;
588 SILC_LOG_DEBUG(("Creating UDP stream"));
593 SILC_LOG_DEBUG(("Binding to local address %s",
594 local_ip_addr ? local_ip_addr : "0.0.0.0"));
600 ss = new RSocketServ;
604 /* Open socket server */
610 /* Make our socket shareable between threads */
612 #endif /* SILC_THREADS */
614 /* Get local bind address */
615 if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
618 /* Create the socket */
619 ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
620 if (ret != KErrNone) {
621 SILC_LOG_ERROR(("Cannot create socket"));
625 /* Set the socket options */
626 sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
628 /* Bind the listener socket */
629 ret = sock->Bind(local);
630 if (ret != KErrNone) {
631 SILC_LOG_DEBUG(("Cannot bind socket"));
635 /* Set to connected state if remote address is provided. */
636 if (remote_ip_addr && remote_port) {
637 if (silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) {
638 sock->Connect(remote, status);
639 if (status != KErrNone) {
640 SILC_LOG_DEBUG(("Cannot connect UDP stream"));
646 /* Encapsulate into socket stream */
647 s = silc_create_symbian_socket(sock, ss);
651 silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
652 silc_net_is_ip6(local_ip_addr) : FALSE,
653 remote_ip_addr ? TRUE : FALSE, schedule);
657 SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
670 /* Sets socket to non-blocking mode */
672 int silc_net_set_socket_nonblock(SilcSocket sock)
674 /* Nothing to do in Symbian where blocking socket mode is asynchronous
675 already (ie. non-blocking). */
679 /* Converts the IP number string from numbers-and-dots notation to
682 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
687 ret = inet_aton(addr, &tmp);
691 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
696 /* Get remote host and IP from socket */
698 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
701 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
710 s->sock->RemoteName(addr);
713 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
717 /* Do reverse lookup if we want hostname too. */
719 /* Get host by address */
720 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
723 *hostname = (char *)silc_memdup(host, strlen(host));
724 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
727 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
730 if (strcmp(*ip, host))
734 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
738 /* Get local host and IP from socket */
740 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
743 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
752 s->sock->LocalName(addr);
755 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
759 /* Do reverse lookup if we want hostname too. */
761 /* Get host by address */
762 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
765 *hostname = (char *)silc_memdup(host, strlen(host));
766 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
769 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
772 if (strcmp(*ip, host))
776 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
780 /* Get remote port from socket */
782 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
784 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
787 s->sock->RemoteName(addr);
788 return (SilcUInt16)addr.Port();
791 /* Get local port from socket */
793 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
795 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
798 s->sock->LocalName(addr);
799 return (SilcUInt16)addr.Port();