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