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 */
35 if (status != SILC_SOCKET_OK)
38 listener->callback(SILC_NET_OK, stream, listener->context);
41 /* TCP Listener class */
43 class SilcSymbianTCPListener : public CActive {
46 SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
48 CActiveScheduler::Add(this);
52 ~SilcSymbianTCPListener()
57 /* Listen for connection */
60 new_conn = new RSocket;
63 if (new_conn->Open(ss) != KErrNone) {
68 /* Start listenning */
69 sock.Accept(*new_conn, iStatus);
73 /* Listener callback */
76 if (iStatus != KErrNone) {
84 /* Set socket options */
85 new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
87 /* Create socket stream */
88 silc_socket_tcp_stream_create(
89 (SilcSocket)silc_create_symbian_socket(new_conn, NULL),
90 listener->lookup, listener->require_fqdn,
91 listener->schedule, silc_net_accept_stream,
95 /* Continue listenning */
100 virtual void DoCancel()
111 SilcNetListener listener;
114 /* Create TCP listener */
117 silc_net_tcp_create_listener(const char **local_ip_addr,
118 SilcUInt32 local_ip_count, int port,
119 SilcBool lookup, SilcBool require_fqdn,
120 SilcSchedule schedule,
121 SilcNetCallback callback, void *context)
123 SilcNetListener listener = NULL;
124 SilcSymbianTCPListener *l = NULL;
130 SILC_LOG_DEBUG(("Creating TCP listener"));
132 if (port < 0 || !schedule || !callback)
135 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
137 callback(SILC_NET_NO_MEMORY, NULL, context);
140 listener->schedule = schedule;
141 listener->callback = callback;
142 listener->context = context;
143 listener->require_fqdn = require_fqdn;
144 listener->lookup = lookup;
146 if (local_ip_count > 0) {
147 listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
148 sizeof(*listener->socks));
149 if (!listener->socks) {
150 callback(SILC_NET_NO_MEMORY, NULL, context);
154 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
155 if (!listener->socks) {
156 callback(SILC_NET_NO_MEMORY, NULL, context);
163 /* Bind to local addresses */
164 for (i = 0; i < local_ip_count; i++) {
165 SILC_LOG_DEBUG(("Binding to local address %s",
166 local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
168 l = new SilcSymbianTCPListener;
172 /* Connect to socket server */
173 ret = l->ss.Connect();
177 /* Set listener address */
179 server = TInetAddr(port);
180 tmp = (TText *)local_ip_addr[i];
181 ret = server.Input(tmp);
185 server = TInetAddr(KInetAddrAny, port);
188 /* Create the socket */
189 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
190 if (ret != KErrNone) {
191 SILC_LOG_ERROR(("Cannot create socket"));
195 /* Set the socket options */
196 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
197 if (ret != KErrNone) {
198 SILC_LOG_ERROR(("Cannot set socket options"));
202 /* Bind the listener socket */
203 ret = l->sock.Bind(server);
204 if (ret != KErrNone) {
205 SILC_LOG_DEBUG(("Cannot bind socket"));
209 /* Specify that we are listenning */
210 ret = l->sock.Listen(5);
211 if (ret != KErrNone) {
212 SILC_LOG_ERROR(("Cannot set socket listenning"));
217 l->listener = listener;
218 listener->socks[i] = (SilcSocket)l;
219 listener->socks_count++;
222 SILC_LOG_DEBUG(("TCP listener created"));
230 callback(SILC_NET_ERROR, NULL, context);
232 silc_net_close_listener(listener);
236 /* Close network listener */
238 void silc_net_close_listener(SilcNetListener listener)
242 SILC_LOG_DEBUG(("Closing network listener"));
244 for (i = 0; i < listener->socks_count; i++) {
245 SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
254 silc_free(listener->socks);
258 /**************************** TCP/IP connecting *****************************/
260 static void silc_net_connect_stream(SilcSocketStreamStatus status,
261 SilcStream stream, void *context);
263 /* TCP connecting class */
265 class SilcSymbianTCPConnect : public CActive {
268 SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
270 CActiveScheduler::Add(this);
274 ~SilcSymbianTCPConnect()
282 /* Connect to remote host */
283 void Connect(TSockAddr &addr)
285 sock->Connect(addr, iStatus);
289 /* Connection callback */
292 if (iStatus != KErrNone) {
294 callback(SILC_NET_ERROR, NULL, context);
295 sock->CancelConnect();
307 silc_socket_tcp_stream_create(
308 (SilcSocket)silc_create_symbian_socket(sock, ss),
309 TRUE, FALSE, schedule, silc_net_connect_stream,
324 virtual void DoCancel()
331 sock->CancelConnect();
341 SilcAsyncOperation op;
342 SilcSchedule schedule;
343 SilcNetCallback callback;
347 /* Stream creation callback */
349 static void silc_net_connect_stream(SilcSocketStreamStatus status,
350 SilcStream stream, void *context)
352 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
353 SilcNetStatus net_status = SILC_NET_OK;
355 if (status != SILC_SOCKET_OK) {
356 /* In case of error, the socket has been destroyed already */
357 if (status == SILC_SOCKET_UNKNOWN_IP)
358 net_status = SILC_NET_UNKNOWN_IP;
359 else if (status == SILC_SOCKET_UNKNOWN_HOST)
360 net_status = SILC_NET_UNKNOWN_HOST;
362 net_status = SILC_NET_ERROR;
365 /* Call connection callback */
367 conn->callback(net_status, stream, conn->context);
369 silc_stream_destroy(stream);
374 /* Connecting abort callback */
376 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
378 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
381 conn->callback = NULL;
384 sock->CancelConnect();
387 /* Create TCP/IP connection */
389 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
390 const char *remote_ip_addr,
392 SilcSchedule schedule,
393 SilcNetCallback callback,
396 SilcSymbianTCPConnect *conn;
397 TInetAddr local, remote;
398 SilcNetStatus status;
402 if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
405 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
406 remote_ip_addr, remote_port));
408 conn = new SilcSymbianTCPConnect;
410 callback(SILC_NET_NO_MEMORY, NULL, context);
413 conn->schedule = schedule;
414 conn->callback = callback;
415 conn->context = context;
416 conn->port = remote_port;
417 conn->remote = strdup(remote_ip_addr);
419 status = SILC_NET_NO_MEMORY;
423 /* Allocate socket */
424 conn->sock = new RSocket;
426 status = SILC_NET_NO_MEMORY;
430 /* Allocate socket server */
431 conn->ss = new RSocketServ;
433 status = SILC_NET_NO_MEMORY;
437 /* Connect to socket server */
438 ret = conn->ss->Connect();
439 if (ret != KErrNone) {
440 status = SILC_NET_ERROR;
444 /* Start async operation */
445 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
447 status = SILC_NET_NO_MEMORY;
452 if (!silc_net_is_ip(remote_ip_addr)) {
453 if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
454 sizeof(conn->remote_ip))) {
455 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
456 "host", conn->remote));
457 status = SILC_NET_HOST_UNREACHABLE;
461 strcpy(conn->remote_ip, remote_ip_addr);
464 /* Create the connection socket */
465 ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
466 if (ret != KErrNone) {
467 SILC_LOG_ERROR(("Cannot create socket"));
468 status = SILC_NET_ERROR;
472 /* Set appropriate options */
473 conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
474 conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
476 /* Bind to the local address if provided */
478 local = TInetAddr(0);
479 tmp = (TText *)local_ip_addr;
480 ret = local.Input(tmp);
482 ret = conn->sock->Bind(local);
485 /* Connect to the host */
486 remote = TInetAddr(remote_port);
487 tmp = (TText *)conn->remote_ip;
488 ret = remote.Input(tmp);
489 if (ret != KErrNone) {
490 SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
491 status = SILC_NET_ERROR;
494 conn->Connect(remote);
496 SILC_LOG_DEBUG(("Connection operation in progress"));
508 silc_free(conn->remote);
510 silc_async_free(conn->op);
511 callback(status, NULL, context);
516 /****************************** UDP routines ********************************/
518 /* Create UDP/IP connection */
520 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
521 const char *remote_ip_addr, int remote_port,
522 SilcSchedule schedule)
524 SilcSymbianSocket *s;
526 TInetAddr local, remote;
527 TRequestStatus status;
528 RSocket *sock = NULL;
529 RSocketServ *ss = NULL;
533 SILC_LOG_DEBUG(("Creating UDP stream"));
538 SILC_LOG_DEBUG(("Binding to local address %s",
539 local_ip_addr ? local_ip_addr : "0.0.0.0"));
545 ss = new RSocketServ;
549 /* Open socket server */
554 /* Get local bind address */
556 local = TInetAddr(local_port);
557 tmp = (TText *)local_ip_addr;
558 ret = local.Input(tmp);
562 local = TInetAddr(KInetAddrAny, local_port);
565 /* Create the socket */
566 ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
567 if (ret != KErrNone) {
568 SILC_LOG_ERROR(("Cannot create socket"));
572 /* Set the socket options */
573 sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
575 /* Bind the listener socket */
576 ret = sock->Bind(local);
577 if (ret != KErrNone) {
578 SILC_LOG_DEBUG(("Cannot bind socket"));
582 /* Set to connected state if remote address is provided. */
583 if (remote_ip_addr && remote_port) {
584 remote = TInetAddr(remote_port);
585 tmp = (TText *)remote_ip_addr;
586 ret = remote.Input(tmp);
590 sock->Connect(remote, status);
591 if (status != KErrNone) {
592 SILC_LOG_DEBUG(("Cannot connect UDP stream"));
597 /* Encapsulate into socket stream */
598 s = silc_create_symbian_socket(sock, ss);
602 silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
603 silc_net_is_ip6(local_ip_addr) : FALSE,
604 remote_ip_addr ? TRUE : FALSE, schedule);
608 SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
621 /* Sets socket to non-blocking mode */
623 int silc_net_set_socket_nonblock(SilcSocket sock)
625 /* Nothing to do in Symbian where blocking socket mode is asynchronous
626 already (ie. non-blocking). */
630 /* Converts the IP number string from numbers-and-dots notation to
633 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
638 ret = inet_aton(addr, &tmp);
642 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
647 /* Get remote host and IP from socket */
649 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
652 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
661 s->sock->RemoteName(addr);
664 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
668 /* Do reverse lookup if we want hostname too. */
670 /* Get host by address */
671 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
674 *hostname = (char *)silc_memdup(host, strlen(host));
675 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
678 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
681 if (strcmp(*ip, host))
685 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
689 /* Get local host and IP from socket */
691 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
694 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
703 s->sock->LocalName(addr);
706 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
710 /* Do reverse lookup if we want hostname too. */
712 /* Get host by address */
713 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
716 *hostname = (char *)silc_memdup(host, strlen(host));
717 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
720 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
723 if (strcmp(*ip, host))
727 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
731 /* Get remote port from socket */
733 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
735 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
738 s->sock->RemoteName(addr);
739 return (SilcUInt16)addr.Port();
742 /* Get local port from socket */
744 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
746 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
749 s->sock->LocalName(addr);
750 return (SilcUInt16)addr.Port();