Added SILC Thread Queue API
[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 extern "C" {
68
69 /* Deliver new stream to upper layer */
70
71 static void silc_net_accept_stream(SilcResult status,
72                                    SilcStream stream, void *context)
73 {
74   SilcNetListener listener = (SilcNetListener)context;
75
76   /* In case of error, the socket has been destroyed already via
77      silc_stream_destroy. */
78   if (status != SILC_OK)
79     return;
80
81   listener->callback(SILC_OK, stream, listener->context);
82 }
83
84 } /* extern "C" */
85
86 /* TCP Listener class */
87
88 class SilcSymbianTCPListener : public CActive {
89 public:
90   /* Constructor */
91   SilcSymbianTCPListener() : CActive(CActive::EPriorityStandard)
92   {
93     CActiveScheduler::Add(this);
94   }
95
96   /* Destructor */
97   ~SilcSymbianTCPListener()
98   {
99     Cancel();
100   }
101
102   /* Listen for connection */
103   void Listen()
104   {
105     SILC_LOG_DEBUG(("Listen()"));
106
107     new_conn = new RSocket;
108     if (!new_conn)
109       return;
110     if (new_conn->Open(ss) != KErrNone) {
111       Listen();
112       return;
113     }
114
115     /* Start listenning */
116     sock.Accept(*new_conn, iStatus);
117     SetActive();
118   }
119
120   /* Listener callback */
121   virtual void RunL()
122   {
123     SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
124
125     if (iStatus != KErrNone) {
126       if (new_conn)
127         delete new_conn;
128       new_conn = NULL;
129       Listen();
130       return;
131     }
132
133     SILC_LOG_DEBUG(("Accept new connection"));
134
135     /* Set socket options */
136     new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
137
138     /* Create socket stream */
139     silc_socket_tcp_stream_create(
140                         (SilcSocket)silc_create_symbian_socket(new_conn, NULL),
141                         listener->lookup, listener->require_fqdn,
142                         listener->schedule, silc_net_accept_stream,
143                         (void *)listener);
144     new_conn = NULL;
145
146     /* Continue listenning */
147     Listen();
148   }
149
150   /* Cancel */
151   virtual void DoCancel()
152   {
153     sock.CancelAll();
154     ss.Close();
155     if (new_conn)
156       delete new_conn;
157   }
158
159   RSocket *new_conn;
160   RSocket sock;
161   RSocketServ ss;
162   SilcNetListener listener;
163 };
164
165 extern "C" {
166
167 /* Create TCP listener */
168
169 SilcNetListener
170 silc_net_tcp_create_listener(const char **local_ip_addr,
171                              SilcUInt32 local_ip_count, int port,
172                              SilcBool lookup, SilcBool require_fqdn,
173                              SilcSchedule schedule,
174                              SilcNetCallback callback, void *context)
175 {
176   SilcNetListener listener = NULL;
177   SilcSymbianTCPListener *l = NULL;
178   TInetAddr server;
179   TInt ret;
180   int i;
181
182   SILC_LOG_DEBUG(("Creating TCP listener"));
183
184   if (!schedule) {
185     schedule = silc_schedule_get_global();
186     if (!schedule) {
187       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
188       goto err;
189     }
190   }
191
192   if (port < 0 || !callback) {
193     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
194     goto err;
195   }
196
197   listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
198   if (!listener) {
199     callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
200     return NULL;
201   }
202   listener->schedule = schedule;
203   listener->callback = callback;
204   listener->context = context;
205   listener->require_fqdn = require_fqdn;
206   listener->lookup = lookup;
207
208   if (local_ip_count > 0) {
209     listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
210                                                 sizeof(*listener->socks));
211     if (!listener->socks) {
212       callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
213       return NULL;
214     }
215   } else {
216     listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
217     if (!listener->socks) {
218       callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
219       return NULL;
220     }
221
222     local_ip_count = 1;
223   }
224
225   /* Bind to local addresses */
226   for (i = 0; i < local_ip_count; i++) {
227     SILC_LOG_DEBUG(("Binding to local address %s",
228                     local_ip_addr ? local_ip_addr[i] : "0.0.0.0"));
229
230     l = new SilcSymbianTCPListener;
231     if (!l)
232       goto err;
233
234     /* Connect to socket server */
235     ret = l->ss.Connect();
236     if (ret != KErrNone)
237       goto err;
238
239 #ifdef SILC_THREADS
240     /* Make our socket shareable between threads */
241     l->ss.ShareAuto();
242 #endif /* SILC_THREADS */
243
244     /* Set listener address */
245     if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
246       goto err;
247
248     /* Create the socket */
249     ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
250     if (ret != KErrNone) {
251       SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
252       goto err;
253     }
254
255     /* Set the socket options */
256     ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
257     if (ret != KErrNone) {
258       SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
259       goto err;
260     }
261
262     /* Bind the listener socket */
263     ret = l->sock.Bind(server);
264     if (ret != KErrNone) {
265       SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
266       goto err;
267     }
268
269     /* Specify that we are listenning */
270     ret = l->sock.Listen(5);
271     if (ret != KErrNone) {
272       SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
273       goto err;
274     }
275     l->Listen();
276
277     l->listener = listener;
278     listener->socks[i] = (SilcSocket)l;
279     listener->socks_count++;
280   }
281
282   SILC_LOG_DEBUG(("TCP listener created"));
283
284   return listener;
285
286  err:
287   if (l)
288     delete l;
289   if (callback)
290     callback(SILC_ERR, NULL, context);
291   if (listener)
292     silc_net_close_listener(listener);
293   return NULL;
294 }
295
296 /* Create TCP listener, multiple ports */
297
298 SilcNetListener
299 silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports,
300                               SilcUInt32 port_count,
301                               SilcBool ignore_port_error,
302                               SilcBool lookup, SilcBool require_fqdn,
303                               SilcSchedule schedule,
304                               SilcNetCallback callback, void *context)
305 {
306   SilcNetListener listener = NULL;
307   SilcSymbianTCPListener *l = NULL;
308   TInetAddr server;
309   TInt ret;
310   int i;
311
312   SILC_LOG_DEBUG(("Creating TCP listener"));
313
314   if (!schedule) {
315     schedule = silc_schedule_get_global();
316     if (!schedule) {
317       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
318       goto err;
319     }
320   }
321
322   if (!callback) {
323     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
324     goto err;
325   }
326
327   listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
328   if (!listener) {
329     callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
330     return NULL;
331   }
332   listener->schedule = schedule;
333   listener->callback = callback;
334   listener->context = context;
335   listener->require_fqdn = require_fqdn;
336   listener->lookup = lookup;
337
338   if (port_count > 0) {
339     listener->socks = (SilcSocket *)silc_calloc(port_count,
340                                                 sizeof(*listener->socks));
341     if (!listener->socks) {
342       callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
343       return NULL;
344     }
345   } else {
346     listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
347     if (!listener->socks) {
348       callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
349       return NULL;
350     }
351
352     port_count = 1;
353   }
354
355   /* Bind to ports */
356   for (i = 0; i < port_count; i++) {
357     SILC_LOG_DEBUG(("Binding to local address %s:%d",
358                     local_ip_addr ? local_ip_addr : "0.0.0.0",
359                     ports ? ports[i] : 0));
360
361     l = new SilcSymbianTCPListener;
362     if (!l)
363       goto err;
364
365     /* Connect to socket server */
366     ret = l->ss.Connect();
367     if (ret != KErrNone)
368       goto err;
369
370 #ifdef SILC_THREADS
371     /* Make our socket shareable between threads */
372     l->ss.ShareAuto();
373 #endif /* SILC_THREADS */
374
375     /* Set listener address */
376     if (!silc_net_set_sockaddr(&server, local_ip_addr, ports ? ports[i] : 0)) {
377       if (ignore_port_error) {
378         delete l;
379         continue;
380       }
381       goto err;
382     }
383
384     /* Create the socket */
385     ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
386     if (ret != KErrNone) {
387       if (ignore_port_error) {
388         delete l;
389         continue;
390       }
391       SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
392       goto err;
393     }
394
395     /* Set the socket options */
396     ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
397     if (ret != KErrNone) {
398       if (ignore_port_error) {
399         delete l;
400         continue;
401       }
402       SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
403       goto err;
404     }
405
406     /* Bind the listener socket */
407     ret = l->sock.Bind(server);
408     if (ret != KErrNone) {
409       if (ignore_port_error) {
410         delete l;
411         continue;
412       }
413       SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
414       goto err;
415     }
416
417     /* Specify that we are listenning */
418     ret = l->sock.Listen(5);
419     if (ret != KErrNone) {
420       if (ignore_port_error) {
421         delete l;
422         continue;
423       }
424       SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
425       goto err;
426     }
427     l->Listen();
428
429     l->listener = listener;
430     listener->socks[i] = (SilcSocket)l;
431     listener->socks_count++;
432   }
433
434   if (ignore_port_error && !listener->socks_count) {
435     l = NULL;
436     goto err;
437   }
438
439   SILC_LOG_DEBUG(("TCP listener created"));
440
441   return listener;
442
443  err:
444   if (l)
445     delete l;
446   if (callback)
447     callback(SILC_ERR, NULL, context);
448   if (listener)
449     silc_net_close_listener(listener);
450   return NULL;
451 }
452
453 /* Close network listener */
454
455 void silc_net_close_listener(SilcNetListener listener)
456 {
457   int i;
458
459   SILC_LOG_DEBUG(("Closing network listener"));
460
461   if (!listener)
462     return;
463
464   for (i = 0; i < listener->socks_count; i++) {
465     SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
466     l->sock.CancelAll();
467     l->sock.Close();
468     l->ss.Close();
469     if (l->new_conn)
470       delete l->new_conn;
471     delete l;
472   }
473
474   silc_free(listener->socks);
475   silc_free(listener);
476 }
477
478
479 /**************************** TCP/IP connecting *****************************/
480
481 static void silc_net_connect_stream(SilcResult status,
482                                     SilcStream stream, void *context);
483
484 } /* extern "C" */
485
486 /* TCP connecting class */
487
488 class SilcSymbianTCPConnect : public CActive {
489 public:
490   /* Constructor */
491   SilcSymbianTCPConnect() : CActive(CActive::EPriorityStandard)
492   {
493     CActiveScheduler::Add(this);
494   }
495
496   /* Destructor */
497   ~SilcSymbianTCPConnect()
498   {
499     silc_free(remote);
500     if (op)
501       silc_async_free(op);
502     Cancel();
503   }
504
505   /* Connect to remote host */
506   void Connect(TSockAddr &addr)
507   {
508     SILC_LOG_DEBUG(("Connect()"));
509     sock->Connect(addr, iStatus);
510     SetActive();
511   }
512
513   /* Connection callback */
514   virtual void RunL()
515   {
516     SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
517
518     if (iStatus != KErrNone) {
519       if (callback)
520         callback(SILC_ERR, NULL, context);
521       sock->CancelConnect();
522       delete sock;
523       ss->Close();
524       delete ss;
525       sock = NULL;
526       ss = NULL;
527       delete this;
528       return;
529     }
530
531     SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
532
533     /* Create stream */
534     if (callback) {
535       silc_socket_tcp_stream_create(
536                              (SilcSocket)silc_create_symbian_socket(sock, ss),
537                              TRUE, FALSE, schedule, silc_net_connect_stream,
538                              (void *)this);
539       sock = NULL;
540       ss = NULL;
541     } else {
542       sock->Close();
543       delete sock;
544       ss->Close();
545       delete ss;
546       sock = NULL;
547       ss = NULL;
548       delete this;
549     }
550   }
551
552   /* Cancel */
553   virtual void DoCancel()
554   {
555     if (ss) {
556       ss->Close();
557       delete ss;
558     }
559     if (sock) {
560       sock->CancelConnect();
561       delete sock;
562     }
563   }
564
565   RSocket *sock;
566   RSocketServ *ss;
567   char *remote;
568   char remote_ip[64];
569   int port;
570   SilcAsyncOperation op;
571   SilcSchedule schedule;
572   SilcNetCallback callback;
573   void *context;
574 };
575
576 extern "C" {
577
578 /* TCP stream creation callback */
579
580 static void silc_net_connect_stream(SilcResult status,
581                                     SilcStream stream, void *context)
582 {
583   SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
584
585   SILC_LOG_DEBUG(("Socket stream creation status %d", status));
586
587   /* Call connection callback */
588   if (conn->callback)
589     conn->callback(status, stream, conn->context);
590   else if (stream)
591     silc_stream_destroy(stream);
592
593   delete conn;
594 }
595
596 /* Connecting abort callback */
597
598 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
599 {
600   SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
601
602   /* Abort */
603   conn->callback = NULL;
604   conn->op = NULL;
605   if (conn->sock)
606     conn->sock->CancelConnect();
607 }
608
609 /* Create TCP/IP connection */
610
611 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
612                                         const char *remote_ip_addr,
613                                         int remote_port,
614                                         SilcSchedule schedule,
615                                         SilcNetCallback callback,
616                                         void *context)
617 {
618   SilcSymbianTCPConnect *conn;
619   TInetAddr local, remote;
620   SilcResult status;
621   TInt ret;
622
623   if (!schedule) {
624     schedule = silc_schedule_get_global();
625     if (!schedule) {
626       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
627       return NULL;
628     }
629   }
630
631   if (!remote_ip_addr || remote_port < 1 || !callback) {
632     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
633     return NULL;
634   }
635
636   SILC_LOG_DEBUG(("Creating connection to host %s port %d",
637                   remote_ip_addr, remote_port));
638
639   conn = new SilcSymbianTCPConnect;
640   if (!conn) {
641     callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
642     return NULL;
643   }
644   conn->schedule = schedule;
645   conn->callback = callback;
646   conn->context = context;
647   conn->port = remote_port;
648   conn->remote = strdup(remote_ip_addr);
649   if (!conn->remote) {
650     status = SILC_ERR_OUT_OF_MEMORY;
651     goto err;
652   }
653
654   /* Allocate socket */
655   conn->sock = new RSocket;
656   if (!conn->sock) {
657     status = SILC_ERR_OUT_OF_MEMORY;
658     goto err;
659   }
660
661   /* Allocate socket server */
662   conn->ss = new RSocketServ;
663   if (!conn->ss) {
664     status = SILC_ERR_OUT_OF_MEMORY;
665     goto err;
666   }
667
668   /* Connect to socket server */
669   ret = conn->ss->Connect();
670   if (ret != KErrNone) {
671     SILC_LOG_ERROR(("Error connecting to socket server, error %d", ret));
672     status = SILC_ERR;
673     goto err;
674   }
675
676 #ifdef SILC_THREADS
677   /* Make our socket shareable between threads */
678   conn->ss->ShareAuto();
679 #endif /* SILC_THREADS */
680
681   /* Start async operation */
682   conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
683   if (!conn->op) {
684     status = SILC_ERR_OUT_OF_MEMORY;
685     goto err;
686   }
687
688   /* Do host lookup */
689   if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
690                               sizeof(conn->remote_ip))) {
691     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
692                     "host", conn->remote));
693     status = SILC_ERR_UNREACHABLE;
694     goto err;
695   }
696
697   /* Create the connection socket */
698   ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
699   if (ret != KErrNone) {
700     SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
701     status = SILC_ERR;
702     goto err;
703   }
704
705   /* Set appropriate options */
706   conn->sock->SetOpt(KSoTcpNoDelay, KSolInetTcp, 1);
707   conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
708
709   /* Bind to the local address if provided */
710   if (local_ip_addr)
711     if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
712       conn->sock->Bind(local);
713
714   /* Connect to the host */
715   if (!silc_net_set_sockaddr(&remote, conn->remote_ip, remote_port)) {
716     SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
717     status = SILC_ERR;
718     goto err;
719   }
720   conn->Connect(remote);
721
722   SILC_LOG_DEBUG(("Connection operation in progress"));
723
724   return conn->op;
725
726  err:
727   if (conn->ss) {
728     conn->ss->Close();
729     delete conn->ss;
730   }
731   if (conn->sock)
732     delete conn->sock;
733   if (conn->remote)
734     silc_free(conn->remote);
735   if (conn->op)
736     silc_async_free(conn->op);
737   callback(status, NULL, context);
738   delete conn;
739   return NULL;
740 }
741
742 /****************************** UDP routines ********************************/
743
744 /* Create UDP/IP connection */
745
746 SilcStream silc_net_udp_connect(const char *local_ip_addr, int local_port,
747                                 const char *remote_ip_addr, int remote_port,
748                                 SilcSchedule schedule)
749 {
750   SilcSymbianSocket *s;
751   SilcStream stream;
752   TInetAddr local, remote;
753   TRequestStatus status;
754   RSocket *sock = NULL;
755   RSocketServ *ss = NULL;
756   TInt ret;
757
758   SILC_LOG_DEBUG(("Creating UDP stream"));
759
760   if (!schedule) {
761     schedule = silc_schedule_get_global();
762     if (!schedule) {
763       silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
764       goto err;
765     }
766   }
767
768   SILC_LOG_DEBUG(("Binding to local address %s",
769                   local_ip_addr ? local_ip_addr : "0.0.0.0"));
770
771   sock = new RSocket;
772   if (!sock)
773     goto err;
774
775   ss = new RSocketServ;
776   if (!ss)
777     goto err;
778
779   /* Open socket server */
780   ret = ss->Connect();
781   if (ret != KErrNone)
782     goto err;
783
784 #ifdef SILC_THREADS
785   /* Make our socket shareable between threads */
786   ss->ShareAuto();
787 #endif /* SILC_THREADS */
788
789   /* Get local bind address */
790   if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
791     goto err;
792
793   /* Create the socket */
794   ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
795   if (ret != KErrNone) {
796     SILC_LOG_ERROR(("Cannot create socket"));
797     goto err;
798   }
799
800   /* Set the socket options */
801   sock->SetOpt(KSoReuseAddr, KSolInetIp, 1);
802
803   /* Bind the listener socket */
804   ret = sock->Bind(local);
805   if (ret != KErrNone) {
806     SILC_LOG_DEBUG(("Cannot bind socket"));
807     goto err;
808   }
809
810   /* Set to connected state if remote address is provided. */
811   if (remote_ip_addr && remote_port) {
812     if (silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) {
813       sock->Connect(remote, status);
814       if (status != KErrNone) {
815         SILC_LOG_DEBUG(("Cannot connect UDP stream"));
816         goto err;
817       }
818     }
819   }
820
821   /* Encapsulate into socket stream */
822   s = silc_create_symbian_socket(sock, ss);
823   if (!s)
824     goto err;
825   stream =
826     silc_socket_udp_stream_create((SilcSocket)s, local_ip_addr ?
827                                   silc_net_is_ip6(local_ip_addr) : FALSE,
828                                   remote_ip_addr ? TRUE : FALSE, schedule);
829   if (!stream)
830     goto err;
831
832   SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock));
833   return stream;
834
835  err:
836   if (sock)
837     delete sock;
838   if (ss) {
839     ss->Close();
840     delete ss;
841   }
842   return NULL;
843 }
844
845 /* Sets socket to non-blocking mode */
846
847 int silc_net_set_socket_nonblock(SilcSocket sock)
848 {
849   /* Nothing to do in Symbian where blocking socket mode is asynchronous
850      already (ie. non-blocking). */
851   return 0;
852 }
853
854 /* Converts the IP number string from numbers-and-dots notation to
855    binary form. */
856
857 SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
858 {
859   int ret = 0;
860   struct in_addr tmp;
861
862   ret = inet_aton(addr, &tmp);
863   if (bin_len < 4)
864     return FALSE;
865
866   memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
867
868   return ret != 0;
869 }
870
871 /* Get remote host and IP from socket */
872
873 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
874                                      char **ip)
875 {
876   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
877   TInetAddr addr;
878   char host[256];
879   TBuf<64> tmp;
880
881   if (hostname)
882     *hostname = NULL;
883   *ip = NULL;
884
885   s->sock->RemoteName(addr);
886   addr.Output(tmp);
887
888   *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
889   if (*ip == NULL)
890     return FALSE;
891
892   /* Do reverse lookup if we want hostname too. */
893   if (hostname) {
894     /* Get host by address */
895     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
896       return FALSE;
897
898     *hostname = (char *)silc_memdup(host, strlen(host));
899     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
900
901     /* Reverse */
902     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
903       return FALSE;
904
905     if (strcmp(*ip, host))
906       return FALSE;
907   }
908
909   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
910   return TRUE;
911 }
912
913 /* Get local host and IP from socket */
914
915 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
916                                       char **ip)
917 {
918   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
919   TInetAddr addr;
920   char host[256];
921   TBuf<64> tmp;
922
923   if (hostname)
924     *hostname = NULL;
925   *ip = NULL;
926
927   s->sock->LocalName(addr);
928   addr.Output(tmp);
929
930   *ip = (char *)silc_memdup(tmp.Ptr(), tmp.Length());
931   if (*ip == NULL)
932     return FALSE;
933
934   /* Do reverse lookup if we want hostname too. */
935   if (hostname) {
936     /* Get host by address */
937     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
938       return FALSE;
939
940     *hostname = (char *)silc_memdup(host, strlen(host));
941     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
942
943     /* Reverse */
944     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
945       return FALSE;
946
947     if (strcmp(*ip, host))
948       return FALSE;
949   }
950
951   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
952   return TRUE;
953 }
954
955 /* Get remote port from socket */
956
957 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
958 {
959   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
960   TInetAddr addr;
961
962   s->sock->RemoteName(addr);
963   return (SilcUInt16)addr.Port();
964 }
965
966 /* Get local port from socket */
967
968 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
969 {
970   SilcSymbianSocket *s = (SilcSymbianSocket *)sock;
971   TInetAddr addr;
972
973   s->sock->LocalName(addr);
974   return (SilcUInt16)addr.Port();
975 }
976
977 } /* extern "C" */