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