5 Author: Pekka Riikonen <priikone@silcnet.org>
\r
7 Copyright (C) 2006 Pekka Riikonen
\r
9 This program is free software; you can redistribute it and/or modify
\r
10 it under the terms of the GNU General Public License as published by
\r
11 the Free Software Foundation; version 2 of the License.
\r
13 This program is distributed in the hope that it will be useful,
\r
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 GNU General Public License for more details.
\r
21 #include "silcsymbiansocketstream.h"
\r
23 /****************************** TCP Listener ********************************/
\r
25 class SilcSymbianTCPListener;
\r
27 /* Deliver new stream to upper layer */
\r
29 static void silc_net_accept_stream(SilcSocketStreamStatus status,
\r
30 SilcStream stream, void *context)
\r
32 SilcNetListener listener = (SilcNetListener)context;
\r
34 /* In case of error, the socket has been destroyed already */
\r
35 if (status != SILC_SOCKET_OK)
\r
38 listener->callback(SILC_NET_OK, stream, listener->context);
\r
41 /* TCP Listener class */
\r
43 class SilcSymbianTCPListener : public CActive {
\r
46 SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
\r
48 CActiveScheduler::Add(this);
\r
52 ~SilcSymbianTCPListener()
\r
57 /* Listen for connection */
\r
60 new_conn = new RSocket;
\r
63 User::LeaveIfError(new_conn->Open(ss));
\r
65 /* Start listenning */
\r
66 sock.Accept(*new_conn, iStatus);
\r
70 /* Listener callback */
\r
73 if (iStatus != KErrNone) {
\r
81 /* Set socket options */
\r
82 new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
\r
84 /* Create socket stream */
\r
85 silc_socket_tcp_stream_create(
\r
86 (SilcSocket)silc_create_symbian_socket(new_conn, NULL),
\r
87 listener->lookup, listener->require_fqdn,
\r
88 listener->schedule, silc_net_accept_stream,
\r
91 /* Continue listenning */
\r
107 SilcNetListener listener;
\r
110 /* Create TCP listener */
\r
113 silc_net_tcp_create_listener(const char **local_ip_addr,
\r
114 SilcUInt32 local_ip_count, int port,
\r
115 SilcBool lookup, SilcBool require_fqdn,
\r
116 SilcSchedule schedule,
\r
117 SilcNetCallback callback, void *context)
\r
119 SilcNetListener listener = NULL;
\r
120 SilcSymbianTCPListener *l = NULL;
\r
126 SILC_LOG_DEBUG(("Creating TCP listener"));
\r
128 if (port < 0 || !schedule || !callback)
\r
131 listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
\r
133 callback(SILC_NET_NO_MEMORY, NULL, context);
\r
136 listener->schedule = schedule;
\r
137 listener->callback = callback;
\r
138 listener->context = context;
\r
139 listener->require_fqdn = require_fqdn;
\r
140 listener->lookup = lookup;
\r
142 if (local_ip_count > 0) {
\r
143 listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
\r
144 sizeof(*listener->socks));
\r
145 if (!listener->socks) {
\r
146 callback(SILC_NET_NO_MEMORY, NULL, context);
\r
150 listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
\r
151 if (!listener->socks) {
\r
152 callback(SILC_NET_NO_MEMORY, NULL, context);
\r
156 local_ip_count = 1;
\r
159 /* Bind to local addresses */
\r
160 for (i = 0; i < local_ip_count; i++) {
\r
161 SILC_LOG_DEBUG(("Binding to local address %s",
\r
162 local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
\r
164 l = new SilcSymbianTCPListener;
\r
168 /* Connect to socket server */
\r
169 ret = l->ss.Connect();
\r
170 if (ret != KErrNone)
\r
173 /* Set listener address */
\r
174 if (local_ip_addr) {
\r
175 server = TInetAddr(port);
\r
176 tmp = (TText *)local_ip_addr[i];
\r
177 ret = server.Input(tmp);
\r
178 if (ret != KErrNone)
\r
181 server = TInetAddr(KInetAddrAny, port);
\r
184 /* Create the socket */
\r
185 ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
\r
186 if (ret != KErrNone) {
\r
187 SILC_LOG_ERROR(("Cannot create socket"));
\r
191 /* Set the socket options */
\r
192 ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
\r
193 if (ret != KErrNone) {
\r
194 SILC_LOG_ERROR(("Cannot set socket options"));
\r
198 /* Bind the listener socket */
\r
199 ret = l->sock.Bind(server);
\r
200 if (ret != KErrNone) {
\r
201 SILC_LOG_DEBUG(("Cannot bind socket"));
\r
205 /* Specify that we are listenning */
\r
206 ret = l->sock.Listen(5);
\r
207 if (ret != KErrNone) {
\r
208 SILC_LOG_ERROR(("Cannot set socket listenning"));
\r
213 l->listener = listener;
\r
214 listener->socks[i] = (SilcSocket)l;
\r
215 listener->socks_count++;
\r
218 SILC_LOG_DEBUG(("TCP listener created"));
\r
226 callback(SILC_NET_ERROR, NULL, context);
\r
228 silc_net_close_listener(listener);
\r
232 /* Close network listener */
\r
234 void silc_net_close_listener(SilcNetListener listener)
\r
238 SILC_LOG_DEBUG(("Closing network listener"));
\r
240 for (i = 0; i < listener->socks_count; i++) {
\r
241 SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
\r
242 l->sock.CancelAll();
\r
246 delete l->new_conn;
\r
250 silc_free(listener->socks);
\r
251 silc_free(listener);
\r
254 /**************************** TCP/IP connecting *****************************/
\r
256 static void silc_net_connect_stream(SilcSocketStreamStatus status,
\r
257 SilcStream stream, void *context);
\r
259 /* TCP connecting class */
\r
261 class SilcSymbianTCPConnect : public CActive {
\r
264 SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
\r
266 CActiveScheduler::Add(this);
\r
270 ~SilcSymbianTCPConnect()
\r
274 silc_async_free(op);
\r
278 /* Connect to remote host */
\r
279 void Connect(TSockAddr &addr)
\r
281 sock->Connect(addr, iStatus);
\r
285 /* Connection callback */
\r
288 if (iStatus != KErrNone) {
\r
290 callback(SILC_NET_ERROR, NULL, context);
\r
291 sock->CancelConnect();
\r
298 /* Create stream */
\r
300 silc_socket_tcp_stream_create(
\r
301 (SilcSocket)silc_create_symbian_socket(sock, ss),
\r
302 FALSE, FALSE, schedule, silc_net_connect_stream,
\r
317 sock->CancelConnect();
\r
327 char remote_ip[64];
\r
329 SilcAsyncOperation op;
\r
330 SilcSchedule schedule;
\r
331 SilcNetCallback callback;
\r
335 /* Stream creation callback */
\r
337 static void silc_net_connect_stream(SilcSocketStreamStatus status,
\r
338 SilcStream stream, void *context)
\r
340 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
\r
341 SilcNetStatus net_status = SILC_NET_OK;
\r
343 if (status != SILC_SOCKET_OK) {
\r
344 /* In case of error, the socket has been destroyed already */
\r
345 if (status == SILC_SOCKET_UNKNOWN_IP)
\r
346 net_status = SILC_NET_UNKNOWN_IP;
\r
347 else if (status == SILC_SOCKET_UNKNOWN_HOST)
\r
348 net_status = SILC_NET_UNKNOWN_HOST;
\r
350 net_status = SILC_NET_ERROR;
\r
353 /* Set stream information */
\r
354 if (stream && conn->callback)
\r
355 silc_socket_stream_set_info(stream,
\r
356 !silc_net_is_ip(conn->remote) ? conn->remote :
\r
357 conn->remote_ip, conn->remote_ip, conn->port);
\r
359 /* Call connection callback */
\r
360 if (conn->callback)
\r
361 conn->callback(net_status, stream, conn->context);
\r
363 silc_stream_destroy(stream);
\r
368 /* Connecting abort callback */
\r
370 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
\r
372 SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
\r
375 conn->callback = NULL;
\r
380 /* Create TCP/IP connection */
\r
382 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
\r
383 const char *remote_ip_addr,
\r
385 SilcSchedule schedule,
\r
386 SilcNetCallback callback,
\r
389 SilcSymbianTCPConnect *conn;
\r
390 TInetAddr local, remote;
\r
391 SilcNetStatus status;
\r
395 if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
\r
398 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
\r
399 remote_ip_addr, remote_port));
\r
401 conn = new SilcSymbianTCPConnect;
\r
403 callback(SILC_NET_NO_MEMORY, NULL, context);
\r
406 conn->schedule = schedule;
\r
407 conn->callback = callback;
\r
408 conn->context = context;
\r
409 conn->port = remote_port;
\r
410 conn->remote = strdup(remote_ip_addr);
\r
411 if (!conn->remote) {
\r
412 status = SILC_NET_NO_MEMORY;
\r
416 /* Allocate socket */
\r
417 conn->sock = new RSocket;
\r
419 status = SILC_NET_NO_MEMORY;
\r
423 /* Allocate socket server */
\r
424 conn->ss = new RSocketServ;
\r
426 status = SILC_NET_NO_MEMORY;
\r
430 /* Connect to socket server */
\r
431 ret = conn->ss->Connect();
\r
432 if (ret != KErrNone) {
\r
433 status = SILC_NET_ERROR;
\r
437 /* Start async operation */
\r
438 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
\r
440 status = SILC_NET_NO_MEMORY;
\r
444 /* Do host lookup */
\r
445 if (!silc_net_is_ip(remote_ip_addr)) {
\r
446 if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
\r
447 sizeof(conn->remote_ip))) {
\r
448 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
\r
449 "host", conn->remote));
\r
450 status = SILC_NET_HOST_UNREACHABLE;
\r
454 strcpy(conn->remote_ip, remote_ip_addr);
\r
457 /* Create the connection socket */
\r
458 ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
\r
459 if (ret != KErrNone) {
\r
460 SILC_LOG_ERROR(("Cannot create socket"));
\r
461 status = SILC_NET_ERROR;
\r
465 /* Set appropriate options */
\r
466 conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
\r
467 conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
\r
469 /* Bind to the local address if provided */
\r
470 if (local_ip_addr) {
\r
471 local = TInetAddr(0);
\r
472 tmp = (TText *)local_ip_addr;
\r
473 ret = local.Input(tmp);
\r
474 if (ret == KErrNone)
\r
475 ret = conn->sock->Bind(local);
\r
478 /* Connect to the host */
\r
479 remote = TInetAddr(remote_port);
\r
480 tmp = (TText *)conn->remote_ip;
\r
481 ret = remote.Input(tmp);
\r
482 if (ret != KErrNone) {
\r
483 SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
\r
484 status = SILC_NET_ERROR;
\r
487 conn->Connect(remote);
\r
489 SILC_LOG_DEBUG(("Connection operation in progress"));
\r
501 silc_free(conn->remote);
\r
503 silc_async_free(conn->op);
\r
504 callback(status, NULL, context);
\r
509 /****************************** UDP routines ********************************/
\r
511 /* Create UDP/IP connection */
\r
513 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
\r
514 const char *remote_ip_addr, int remote_port,
\r
515 SilcSchedule schedule)
\r
517 SilcSymbianSocket *s;
\r
519 TInetAddr local, remote;
\r
520 TRequestStatus status;
\r
521 RSocket *sock = NULL;
\r
522 RSocketServ *ss = NULL;
\r
526 SILC_LOG_DEBUG(("Creating UDP stream"));
\r
531 SILC_LOG_DEBUG(("Binding to local address %s",
\r
532 local_ip_addr ? local_ip_addr : "0.0.0.0"));
\r
534 sock = new RSocket;
\r
538 ss = new RSocketServ;
\r
542 /* Open socket server */
\r
543 ret = ss->Connect();
\r
544 if (ret != KErrNone)
\r
547 /* Get local bind address */
\r
548 if (local_ip_addr) {
\r
549 local = TInetAddr(local_port);
\r
550 tmp = (TText *)local_ip_addr;
\r
551 ret = local.Input(tmp);
\r
552 if (ret != KErrNone)
\r
555 local = TInetAddr(KInetAddrAny, local_port);
\r
558 /* Create the socket */
\r
559 ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
\r
560 if (ret != KErrNone) {
\r
561 SILC_LOG_ERROR(("Cannot create socket"));
\r
565 /* Set the socket options */
\r
566 sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
\r
568 /* Bind the listener socket */
\r
569 ret = sock->Bind(local);
\r
570 if (ret != KErrNone) {
\r
571 SILC_LOG_DEBUG(("Cannot bind socket"));
\r
575 /* Set to connected state if remote address is provided. */
\r
576 if (remote_ip_addr && remote_port) {
\r
577 remote = TInetAddr(remote_port);
\r
578 tmp = (TText *)remote_ip_addr;
\r
579 ret = remote.Input(tmp);
\r
580 if (ret != KErrNone)
\r
583 sock->Connect(remote, status);
\r
584 if (status != KErrNone) {
\r
585 SILC_LOG_DEBUG(("Cannot connect UDP stream"));
\r
590 /* Encapsulate into socket stream */
\r
591 s = silc_create_symbian_socket(sock, ss);
\r
595 silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
\r
596 silc_net_is_ip6(local_ip_addr) : FALSE,
\r
597 remote_ip_addr ? TRUE : FALSE, schedule);
\r
601 SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
\r
614 /* Sets socket to non-blocking mode */
\r
616 int silc_net_set_socket_nonblock(SilcSocket sock)
\r
618 /* Nothing to do in Symbian where blocking socket mode is asynchronous
\r
619 already (ie. non-blocking). */
\r
623 /* Converts the IP number string from numbers-and-dots notation to
\r
626 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
\r
630 struct in_addr tmp;
\r
631 ret = inet_aton(addr, &tmp);
\r
635 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
\r
640 /* Get remote host and IP from socket */
\r
642 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
\r
645 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
\r
654 s->sock->RemoteName(addr);
\r
657 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
\r
661 /* Do reverse lookup if we want hostname too. */
\r
663 /* Get host by address */
\r
664 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
\r
667 *hostname = (char *)silc_memdup(host, strlen(host));
\r
668 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
\r
671 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
\r
674 if (strcmp(*ip, host))
\r
678 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
\r
682 /* Get local host and IP from socket */
\r
684 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
\r
687 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
\r
696 s->sock->LocalName(addr);
\r
699 *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
\r
703 /* Do reverse lookup if we want hostname too. */
\r
705 /* Get host by address */
\r
706 if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
\r
709 *hostname = (char *)silc_memdup(host, strlen(host));
\r
710 SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
\r
713 if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
\r
716 if (strcmp(*ip, host))
\r
720 SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
\r
724 /* Get remote port from socket */
\r
726 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
\r
728 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
\r
731 s->sock->RemoteName(addr);
\r
732 return (SilcUInt16)addr.Port();
\r
735 /* Get local port from socket */
\r
737 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
\r
739 SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
\r
742 s->sock->LocalName(addr);
\r
743 return (SilcUInt16)addr.Port();
\r