Porting Toolkit to Symbian. It should work while some sporadic
[silc.git] / lib / silcutil / symbian / silcsymbiannet.cpp
1 /*
2
3   silcsymbiannet.cpp
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2006 - 2007 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 #include "silc.h"
21 #include "silcsymbiansocketstream.h"
22
23 /************************ Static utility functions **************************/
24
25 static SilcBool silc_net_set_sockaddr(TInetAddr *addr, const char *ip_addr,
26                                       int port)
27 {
28   /* Check for IPv4 and IPv6 addresses */
29   if (ip_addr) {
30     if (!silc_net_is_ip(ip_addr)) {
31       SILC_LOG_ERROR(("%s is not IP address", ip_addr));
32       return FALSE;
33     }
34
35     if (silc_net_is_ip4(ip_addr)) {
36       /* IPv4 address */
37       unsigned char buf[4];
38       TUint32 a;
39
40       if (!silc_net_addr2bin(ip_addr, buf, sizeof(buf)))
41         return FALSE;
42
43       SILC_GET32_MSB(a, buf);
44       addr->SetAddress(a);
45       addr->SetPort(port);
46     } else {
47 #ifdef HAVE_IPV6
48       SILC_LOG_ERROR(("IPv6 not supported"));
49       return FALSE;
50 #else
51       SILC_LOG_ERROR(("Operating System does not support IPv6"));
52       return FALSE;
53 #endif
54     }
55   } else {
56     addr->SetAddress(0);
57     addr->SetPort(port);
58   }
59
60   return TRUE;
61 }
62
63 /****************************** TCP Listener ********************************/
64
65 class SilcSymbianTCPListener;
66
67 /* Deliver new stream to upper layer */
68
69 static void silc_net_accept_stream(SilcSocketStreamStatus status,
70                                    SilcStream stream, void *context)
71 {
72   SilcNetListener listener = (SilcNetListener)context;
73
74   /* In case of error, the socket has been destroyed already via
75      silc_stream_destroy. */
76   if (status != SILC_SOCKET_OK)
77     return;
78
79   listener->callback(SILC_NET_OK, stream, listener->context);
80 }
81
82 /* TCP Listener class */
83
84 class SilcSymbianTCPListener : public CActive {
85 public:
86   /* Constructor */
87   SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
88   {
89     CActiveScheduler::Add(this);
90   }
91
92   /* Destructor */
93   ~SilcSymbianTCPListener()
94   {
95     Cancel();
96   }
97
98   /* Listen for connection */
99   void Listen()
100   {
101     SILC_LOG_DEBUG(("Listen()"));
102
103     new_conn = new RSocket;
104     if (!new_conn)
105       return;
106     if (new_conn->Open(ss) != KErrNone) {
107       Listen();
108       return;
109     }
110
111     /* Start listenning */
112     sock.Accept(*new_conn, iStatus);
113     SetActive();
114   }
115
116   /* Listener callback */
117   virtual void RunL()
118   {
119     SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
120
121     if (iStatus != KErrNone) {
122       if (new_conn)
123         delete new_conn;
124       new_conn = NULL;
125       Listen();
126       return;
127     }
128
129     SILC_LOG_DEBUG(("Accept new connection"));
130
131     /* Set socket options */
132     new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
133
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,
139                         (void *)listener);
140     new_conn = NULL;
141
142     /* Continue listenning */
143     Listen();
144   }
145
146   /* Cancel */
147   virtual void DoCancel()
148   {
149     sock.CancelAll();
150     ss.Close();
151     if (new_conn)
152       delete new_conn;
153   }
154
155   RSocket *new_conn;
156   RSocket sock;
157   RSocketServ ss;
158   SilcNetListener listener;
159 };
160
161 extern "C" {
162
163 /* Create TCP listener */
164
165 SilcNetListener
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)
171 {
172   SilcNetListener listener = NULL;
173   SilcSymbianTCPListener *l = NULL;
174   TInetAddr server;
175   TInt ret;
176   int i;
177
178   SILC_LOG_DEBUG(("Creating TCP listener"));
179
180   if (port < 0 || !schedule || !callback)
181     goto err;
182
183   listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
184   if (!listener) {
185     callback(SILC_NET_NO_MEMORY, NULL, context);
186     return NULL;
187   }
188   listener->schedule = schedule;
189   listener->callback = callback;
190   listener->context = context;
191   listener->require_fqdn = require_fqdn;
192   listener->lookup = lookup;
193
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);
199       return NULL;
200     }
201   } else {
202     listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
203     if (!listener->socks) {
204       callback(SILC_NET_NO_MEMORY, NULL, context);
205       return NULL;
206     }
207
208     local_ip_count = 1;
209   }
210
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"));
215
216     l = new SilcSymbianTCPListener;
217     if (!l)
218       goto err;
219
220     /* Connect to socket server */
221     ret = l->ss.Connect();
222     if (ret != KErrNone)
223       goto err;
224
225 #ifdef SILC_THREADS
226     /* Make our socket shareable between threads */
227     l->ss.ShareAuto();
228 #endif /* SILC_THREADS */
229
230     /* Set listener address */
231     if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
232       goto err;
233
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));
238       goto err;
239     }
240
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));
245       goto err;
246     }
247
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));
252       goto err;
253     }
254
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));
259       goto err;
260     }
261     l->Listen();
262
263     l->listener = listener;
264     listener->socks[i] = (SilcSocket)l;
265     listener->socks_count++;
266   }
267
268   SILC_LOG_DEBUG(("TCP listener created"));
269
270   return listener;
271
272  err:
273   if (l)
274     delete l;
275   if (callback)
276     callback(SILC_NET_ERROR, NULL, context);
277   if (listener)
278     silc_net_close_listener(listener);
279   return NULL;
280 }
281
282 /* Close network listener */
283
284 void silc_net_close_listener(SilcNetListener listener)
285 {
286   int i;
287
288   SILC_LOG_DEBUG(("Closing network listener"));
289
290   if (!listener)
291     return;
292
293   for (i = 0; i < listener->socks_count; i++) {
294     SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
295     l->sock.CancelAll();
296     l->sock.Close();
297     l->ss.Close();
298     if (l->new_conn)
299       delete l->new_conn;
300     delete l;
301   }
302
303   silc_free(listener->socks);
304   silc_free(listener);
305 }
306
307 } /* extern "C" */
308
309 /**************************** TCP/IP connecting *****************************/
310
311 static void silc_net_connect_stream(SilcSocketStreamStatus status,
312             SilcStream stream, void *context);
313
314 /* TCP connecting class */
315
316 class SilcSymbianTCPConnect : public CActive {
317 public:
318   /* Constructor */
319   SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
320   {
321     CActiveScheduler::Add(this);
322   }
323
324   /* Destructor */
325   ~SilcSymbianTCPConnect()
326   {
327     silc_free(remote);
328     if (op)
329       silc_async_free(op);
330     Cancel();
331   }
332
333   /* Connect to remote host */
334   void Connect(TSockAddr &addr)
335   {
336     SILC_LOG_DEBUG(("Connect()"));
337     sock->Connect(addr, iStatus);
338     SetActive();
339   }
340
341   /* Connection callback */
342   virtual void RunL()
343   {
344     SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
345
346     if (iStatus != KErrNone) {
347       if (callback)
348         callback(SILC_NET_ERROR, NULL, context);
349       sock->CancelConnect();
350       delete sock;
351       ss->Close();
352       delete ss;
353       sock = NULL;
354       ss = NULL;
355       delete this;
356       return;
357     }
358
359     SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
360
361     /* Create stream */
362     if (callback) {
363       silc_socket_tcp_stream_create(
364                              (SilcSocket)silc_create_symbian_socket(sock, ss),
365                              TRUE, FALSE, schedule, silc_net_connect_stream,
366                              (void *)this);
367       sock = NULL;
368       ss = NULL;
369     } else {
370       sock->Close();
371       delete sock;
372       ss->Close();
373       delete ss;
374       sock = NULL;
375       ss = NULL;
376       delete this;
377     }
378   }
379
380   /* Cancel */
381   virtual void DoCancel()
382   {
383     if (ss) {
384       ss->Close();
385       delete ss;
386     }
387     if (sock) {
388       sock->CancelConnect();
389       delete sock;
390     }
391   }
392
393   RSocket *sock;
394   RSocketServ *ss;
395   char *remote;
396   char remote_ip[64];
397   int port;
398   SilcAsyncOperation op;
399   SilcSchedule schedule;
400   SilcNetCallback callback;
401   void *context;
402 };
403
404 extern "C" {
405
406 /* TCP stream creation callback */
407
408 static void silc_net_connect_stream(SilcSocketStreamStatus status,
409                                     SilcStream stream, void *context)
410 {
411   SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
412   SilcNetStatus net_status = SILC_NET_OK;
413
414   SILC_LOG_DEBUG(("Socket stream creation status %d", status));
415
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;
423     else
424       net_status = SILC_NET_ERROR;
425   }
426
427   /* Call connection callback */
428   if (conn->callback)
429     conn->callback(net_status, stream, conn->context);
430   else if (stream)
431     silc_stream_destroy(stream);
432
433   delete conn;
434 }
435
436 /* Connecting abort callback */
437
438 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
439 {
440   SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
441
442   /* Abort */
443   conn->callback = NULL;
444   conn->op = NULL;
445   if (conn->sock)
446     conn->sock->CancelConnect();
447 }
448
449 /* Create TCP/IP connection */
450
451 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
452                                         const char *remote_ip_addr,
453                                         int remote_port,
454                                         SilcSchedule schedule,
455                                         SilcNetCallback callback,
456                                         void *context)
457 {
458   SilcSymbianTCPConnect *conn;
459   TInetAddr local, remote;
460   SilcNetStatus status;
461   TInt ret;
462
463   if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
464     return NULL;
465
466   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
467                   remote_ip_addr, remote_port));
468
469   conn = new SilcSymbianTCPConnect;
470   if (!conn) {
471     callback(SILC_NET_NO_MEMORY, NULL, context);
472     return NULL;
473   }
474   conn->schedule = schedule;
475   conn->callback = callback;
476   conn->context = context;
477   conn->port = remote_port;
478   conn->remote = strdup(remote_ip_addr);
479   if (!conn->remote) {
480     status = SILC_NET_NO_MEMORY;
481     goto err;
482   }
483
484   /* Allocate socket */
485   conn->sock = new RSocket;
486   if (!conn->sock) {
487     status = SILC_NET_NO_MEMORY;
488     goto err;
489   }
490
491   /* Allocate socket server */
492   conn->ss = new RSocketServ;
493   if (!conn->ss) {
494     status = SILC_NET_NO_MEMORY;
495     goto err;
496   }
497
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;
503     goto err;
504   }
505
506 #ifdef SILC_THREADS
507   /* Make our socket shareable between threads */
508   conn->ss->ShareAuto();
509 #endif /* SILC_THREADS */
510
511   /* Start async operation */
512   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
513   if (!conn->op) {
514     status = SILC_NET_NO_MEMORY;
515     goto err;
516   }
517
518   /* Do host lookup */
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;
524     goto err;
525   }
526
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;
532     goto err;
533   }
534
535   /* Set appropriate options */
536   conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
537   conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
538
539   /* Bind to the local address if provided */
540   if (local_ip_addr)
541     if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
542       conn->sock->Bind(local);
543
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;
548     goto err;
549   }
550   conn->Connect(remote);
551
552   SILC_LOG_DEBUG(("Connection operation in progress"));
553
554   return conn->op;
555
556  err:
557   if (conn->ss) {
558     conn->ss->Close();
559     delete conn->ss;
560   }
561   if (conn->sock)
562     delete conn->sock;
563   if (conn->remote)
564     silc_free(conn->remote);
565   if (conn->op)
566     silc_async_free(conn->op);
567   callback(status, NULL, context);
568   delete conn;
569   return NULL;
570 }
571
572 /****************************** UDP routines ********************************/
573
574 /* Create UDP/IP connection */
575
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)
579 {
580   SilcSymbianSocket *s;
581   SilcStream stream;
582   TInetAddr local, remote;
583   TRequestStatus status;
584   RSocket *sock = NULL;
585   RSocketServ *ss = NULL;
586   TInt ret;
587
588   SILC_LOG_DEBUG(("Creating UDP stream"));
589
590   if (!schedule)
591     goto err;
592
593   SILC_LOG_DEBUG(("Binding to local address %s",
594                   local_ip_addr ? local_ip_addr : "0.0.0.0"));
595
596   sock = new RSocket;
597   if (!sock)
598     goto err;
599
600   ss = new RSocketServ;
601   if (!ss)
602     goto err;
603
604   /* Open socket server */
605   ret = ss->Connect();
606   if (ret != KErrNone)
607     goto err;
608
609 #ifdef SILC_THREADS
610   /* Make our socket shareable between threads */
611   ss->ShareAuto();
612 #endif /* SILC_THREADS */
613
614   /* Get local bind address */
615   if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
616     goto err;
617
618   /* Create the socket */
619   ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
620   if (ret != KErrNone) {
621     SILC_LOG_ERROR(("Cannot create socket"));
622     goto err;
623   }
624
625   /* Set the socket options */
626   sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
627
628   /* Bind the listener socket */
629   ret = sock->Bind(local);
630   if (ret != KErrNone) {
631     SILC_LOG_DEBUG(("Cannot bind socket"));
632     goto err;
633   }
634
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"));
641         goto err;
642       }
643     }
644   }
645
646   /* Encapsulate into socket stream */
647   s = silc_create_symbian_socket(sock, ss);
648   if (!s)
649     goto err;
650   stream =
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);
654   if (!stream)
655     goto err;
656
657   SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
658   return stream;
659
660  err:
661   if (sock)
662     delete sock;
663   if (ss) {
664     ss->Close();
665     delete ss;
666   }
667   return NULL;
668 }
669
670 /* Sets socket to non-blocking mode */
671
672 int silc_net_set_socket_nonblock(SilcSocket sock)
673 {
674   /* Nothing to do in Symbian where blocking socket mode is asynchronous
675      already (ie. non-blocking). */
676   return 0;
677 }
678
679 /* Converts the IP number string from numbers-and-dots notation to
680    binary form. */
681
682 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
683 {
684   int ret = 0;
685   struct in_addr tmp;
686
687   ret = inet_aton(addr, &tmp);
688   if (bin_len < 4)
689     return FALSE;
690
691   memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
692
693   return ret != 0;
694 }
695
696 /* Get remote host and IP from socket */
697
698 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
699                                      char **ip)
700 {
701   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
702   TInetAddr addr;
703   char host[256];
704   TBuf<64> tmp;
705
706   if (hostname)
707     *hostname = NULL;
708   *ip = NULL;
709
710   s->sock->RemoteName(addr);
711   addr.Output(tmp);
712
713   *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
714   if (*ip == NULL)
715     return FALSE;
716
717   /* Do reverse lookup if we want hostname too. */
718   if (hostname) {
719     /* Get host by address */
720     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
721       return FALSE;
722
723     *hostname = (char *)silc_memdup(host, strlen(host));
724     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
725
726     /* Reverse */
727     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
728       return FALSE;
729
730     if (strcmp(*ip, host))
731       return FALSE;
732   }
733
734   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
735   return TRUE;
736 }
737
738 /* Get local host and IP from socket */
739
740 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
741                                       char **ip)
742 {
743   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
744   TInetAddr addr;
745   char host[256];
746   TBuf<64> tmp;
747
748   if (hostname)
749     *hostname = NULL;
750   *ip = NULL;
751
752   s->sock->LocalName(addr);
753   addr.Output(tmp);
754
755   *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
756   if (*ip == NULL)
757     return FALSE;
758
759   /* Do reverse lookup if we want hostname too. */
760   if (hostname) {
761     /* Get host by address */
762     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
763       return FALSE;
764
765     *hostname = (char *)silc_memdup(host, strlen(host));
766     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
767
768     /* Reverse */
769     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
770       return FALSE;
771
772     if (strcmp(*ip, host))
773       return FALSE;
774   }
775
776   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
777   return TRUE;
778 }
779
780 /* Get remote port from socket */
781
782 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
783 {
784   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
785   TInetAddr addr;
786
787   s->sock->RemoteName(addr);
788   return (SilcUInt16)addr.Port();
789 }
790
791 /* Get local port from socket */
792
793 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
794 {
795   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
796   TInetAddr addr;
797
798   s->sock->LocalName(addr);
799   return (SilcUInt16)addr.Port();
800 }
801
802 } /* extern "C" */