Added silc_net_bit2addr, silc_htonl, silc_ntohl, silc_htons, silc_ntohs
[runtime.git] / lib / silcutil / silcnet.c
1 /*
2
3   silcnet.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2008 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 "silcruntime.h"
21
22 /* Returns bound port from listener */
23
24 SilcUInt16 *silc_net_listener_get_port(SilcNetListener listener,
25                                        SilcUInt32 *port_count)
26 {
27   SilcUInt16 *ports;
28   int i;
29
30   ports = silc_calloc(listener->socks_count, sizeof(*ports));
31   if (!ports)
32     return NULL;
33
34   for (i = 0; i < listener->socks_count; i++)
35     ports[i] = silc_net_get_local_port(listener->socks[i]);
36
37   if (port_count)
38     *port_count = listener->socks_count;
39
40   return ports;
41 }
42
43 /* Return bound IP from listener */
44
45 char **silc_net_listener_get_ip(SilcNetListener listener,
46                                 SilcUInt32 *ip_count)
47 {
48   char **ips = NULL, *ip;
49   int i, k;
50
51   ips = silc_calloc(listener->socks_count, sizeof(*ips));
52   if (!ips)
53     return NULL;
54
55   for (i = 0, k = 0; i < listener->socks_count; i++) {
56     if (silc_net_check_local_by_sock(listener->socks[i], NULL, &ip))
57       ips[k++] = ip;
58   }
59
60   if (ip_count)
61     *ip_count = k;
62
63   return ips;
64 }
65
66 /* Return bound hostname from listener */
67
68 char **silc_net_listener_get_hostname(SilcNetListener listener,
69                                       SilcUInt32 *hostname_count)
70 {
71   char **hs = NULL, *h;
72   int i, k;
73
74   hs = silc_calloc(listener->socks_count, sizeof(*hs));
75   if (!hs)
76     return NULL;
77
78   for (i = 0, k = 0; i < listener->socks_count; i++) {
79     if (silc_net_check_local_by_sock(listener->socks[i], &h, NULL))
80       hs[k++] = h;
81   }
82
83   if (hostname_count)
84     *hostname_count = k;
85
86   return hs;
87 }
88
89 /* Accepts a connection from a particular socket */
90
91 int silc_net_accept_connection(int sock)
92 {
93   int ret = accept(sock, 0, 0);
94   if (ret < 0)
95     silc_set_errno_posix(errno);
96   return ret;
97 }
98
99 /* Sets a option for a socket. */
100
101 int silc_net_set_socket_opt(int sock, int level, int option, int on)
102 {
103   int ret = setsockopt(sock, level, option, (void *)&on, sizeof(on));
104   if (ret < 0)
105     silc_set_errno_posix(errno);
106   return ret;
107 }
108
109 /* Get socket options */
110
111 int silc_net_get_socket_opt(int sock, int level, int option,
112                             void *optval, int *opt_len)
113 {
114   int ret = getsockopt(sock, level, option, optval, (unsigned int *)opt_len);
115   if (ret < 0)
116     silc_set_errno_posix(errno);
117   return ret;
118 }
119
120 /* Checks whether IP address sent as argument is valid IPv4 address. */
121
122 SilcBool silc_net_is_ip4(const char *addr)
123 {
124   int count = 0;
125
126   while (*addr) {
127     if (*addr != '.' && !isdigit((int)*addr))
128       return FALSE;
129     if (*addr == '.')
130       count++;
131     addr++;
132   }
133
134   if (count != 3)
135     return FALSE;
136
137   return TRUE;
138 }
139
140 /* Checks whether IP address sent as argument is valid IPv6 address. */
141
142 SilcBool silc_net_is_ip6(const char *addr)
143 {
144   /* XXX does this work with all kinds of IPv6 addresses? */
145   while (*addr) {
146     if (*addr != ':' && !isxdigit((int)*addr))
147       return FALSE;
148     addr++;
149   }
150
151   return TRUE;
152 }
153
154 /* Checks whether IP address sent as argument is valid IP address. */
155
156 SilcBool silc_net_is_ip(const char *addr)
157 {
158   if (silc_net_is_ip4(addr))
159     return TRUE;
160   return silc_net_is_ip6(addr);
161 }
162
163 /* Internal context for async resolving */
164 typedef struct {
165   SilcNetResolveCallback completion;
166   void *context;
167   SilcBool prefer_ipv6;
168   SilcSchedule schedule;
169   char *input;
170   char *result;
171 } *SilcNetResolveContext;
172
173 SILC_TASK_CALLBACK(silc_net_resolve_completion)
174 {
175   SilcNetResolveContext r = (SilcNetResolveContext)context;
176
177   /* Call the completion callback */
178   if (r->completion)
179     (*r->completion)(r->result, r->context);
180
181   silc_free(r->input);
182   silc_free(r->result);
183   silc_free(r);
184 }
185
186 /* Thread function to resolve the address for hostname. */
187
188 static void *silc_net_gethostbyname_thread(void *context)
189 {
190   SilcNetResolveContext r = (SilcNetResolveContext)context;
191   SilcSchedule schedule = r->schedule;
192   char tmp[64];
193
194   if (silc_net_gethostbyname(r->input, r->prefer_ipv6, tmp, sizeof(tmp)))
195     r->result = silc_strdup(tmp);
196
197   silc_schedule_task_add(schedule, 0, silc_net_resolve_completion, r, 0, 1,
198                          SILC_TASK_TIMEOUT);
199   silc_schedule_wakeup(schedule);
200   return NULL;
201 }
202
203 /* Thread function to resolve the hostname for address. */
204
205 static void *silc_net_gethostbyaddr_thread(void *context)
206 {
207   SilcNetResolveContext r = (SilcNetResolveContext)context;
208   SilcSchedule schedule = r->schedule;
209   char tmp[256];
210
211   if (silc_net_gethostbyaddr(r->input, tmp, sizeof(tmp)))
212     r->result = silc_strdup(tmp);
213
214   silc_schedule_task_add(schedule, 0, silc_net_resolve_completion, r, 0, 1,
215                          SILC_TASK_TIMEOUT);
216   silc_schedule_wakeup(schedule);
217   return NULL;
218 }
219
220 /* Resolves IP address for hostname. */
221
222 SilcBool silc_net_gethostbyname(const char *name,
223                                 SilcBool prefer_ipv6, char *address,
224                                 SilcUInt32 address_len)
225 {
226 #ifdef HAVE_IPV6
227   struct addrinfo hints, *ai, *tmp, *ip4 = NULL, *ip6 = NULL;
228
229   memset(&hints, 0, sizeof(hints));
230   hints.ai_socktype = SOCK_STREAM;
231   if (getaddrinfo(name, NULL, &hints, &ai))
232     return FALSE;
233
234   for (tmp = ai; tmp; tmp = tmp->ai_next) {
235     if (tmp->ai_family == AF_INET6) {
236       ip6 = tmp;
237       if (ip4)
238         break;
239       continue;
240     }
241     if (tmp->ai_family == AF_INET) {
242       ip4 = tmp;
243       if (ip6)
244         break;
245       continue;
246     }
247   }
248
249   tmp = (prefer_ipv6 ? (ip6 ? ip6 : ip4) : (ip4 ? ip4 : ip6));
250   if (!tmp) {
251     freeaddrinfo(ai);
252     return FALSE;
253   }
254
255   if (getnameinfo(tmp->ai_addr, tmp->ai_addrlen, address,
256                   address_len, NULL, 0, NI_NUMERICHOST)) {
257     freeaddrinfo(ai);
258     return FALSE;
259   }
260
261   freeaddrinfo(ai);
262 #else
263   struct hostent *hp;
264   struct in_addr ip;
265   char *tmp;
266
267   if (silc_net_is_ip4(name)) {
268     memset(address, 0, address_len);
269     if (address_len < strlen(name))
270       return FALSE;
271     strncpy(address, name, strlen(name));
272     return TRUE;
273   }
274
275   hp = gethostbyname(name);
276   if (!hp)
277     return FALSE;
278
279   memcpy(&ip.s_addr, hp->h_addr_list[0], 4);
280   tmp = inet_ntoa(ip);
281   if (!tmp)
282     return FALSE;
283   if (address_len < strlen(tmp))
284     return FALSE;
285   memset(address, 0, address_len);
286   strncpy(address, tmp, strlen(tmp));
287 #endif
288
289   return TRUE;
290 }
291
292 /* Resolves IP address for hostname async. */
293
294 void silc_net_gethostbyname_async(const char *name,
295                                   SilcBool prefer_ipv6,
296                                   SilcSchedule schedule,
297                                   SilcNetResolveCallback completion,
298                                   void *context)
299 {
300   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
301
302   if (!schedule) {
303     schedule = silc_schedule_get_global();
304     SILC_VERIFY(schedule);
305   }
306
307   r->completion = completion;
308   r->context = context;
309   r->prefer_ipv6 = prefer_ipv6;
310   r->schedule = schedule;
311   r->input = silc_strdup(name);
312
313   silc_thread_create(silc_net_gethostbyname_thread, r, FALSE);
314 }
315
316 /* Resolves hostname by IP address. */
317
318 SilcBool silc_net_gethostbyaddr(const char *addr, char *name,
319                                 SilcUInt32 name_len)
320 {
321 #ifdef HAVE_IPV6
322   struct addrinfo req, *ai;
323
324   memset(&req, 0, sizeof(req));
325   req.ai_socktype = SOCK_STREAM;
326   req.ai_flags = AI_CANONNAME;
327
328   if (getaddrinfo(addr, NULL, &req, &ai))
329     return FALSE;
330   if (getnameinfo(ai->ai_addr, ai->ai_addrlen, name, name_len, NULL, 0, 0)) {
331     freeaddrinfo(ai);
332     return FALSE;
333   }
334   freeaddrinfo(ai);
335 #else
336   struct hostent *hp;
337   unsigned char a[4];
338
339   if (!silc_net_addr2bin(addr, a, sizeof(a)))
340     return FALSE;
341
342   hp = gethostbyaddr(a, 4, AF_INET);
343   if (!hp)
344     return FALSE;
345   if (name_len < strlen(hp->h_name))
346     return FALSE;
347   memset(name, 0, name_len);
348   strncpy(name, hp->h_name, strlen(hp->h_name));
349 #endif
350
351   return TRUE;
352 }
353
354 /* Resolves hostname by IP address async. */
355
356 void silc_net_gethostbyaddr_async(const char *addr,
357                                   SilcSchedule schedule,
358                                   SilcNetResolveCallback completion,
359                                   void *context)
360 {
361   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
362
363   if (!schedule) {
364     schedule = silc_schedule_get_global();
365     SILC_VERIFY(schedule);
366   }
367
368   r->completion = completion;
369   r->context = context;
370   r->schedule = schedule;
371   r->input = silc_strdup(addr);
372
373   silc_thread_create(silc_net_gethostbyaddr_thread, r, FALSE);
374 }
375
376 #ifndef SILC_SYMBIAN
377
378 /* Performs lookups for remote name and IP address. This peforms reverse
379    lookup as well to verify that the IP has FQDN. */
380
381 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
382                                      char **ip)
383 {
384   char host[1024];
385   int rval;
386   unsigned int len;
387
388 #ifdef HAVE_IPV6
389   struct sockaddr_storage remote;
390   char s[NI_MAXHOST];
391
392   if (hostname)
393     *hostname = NULL;
394   *ip = NULL;
395
396   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
397
398   memset(&remote, 0, sizeof(remote));
399   memset(&s, 0, sizeof(s));
400   len = sizeof(remote);
401   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
402   if (rval < 0)
403     return FALSE;
404
405   if (getnameinfo((struct sockaddr *)&remote, len, s, sizeof(s), NULL, 0,
406                   NI_NUMERICHOST))
407     return FALSE;
408
409   *ip = silc_memdup(s, strlen(s));
410   if (*ip == NULL)
411     return FALSE;
412 #else
413   struct sockaddr_in remote;
414   char *host_ip;
415
416   if (hostname)
417     *hostname = NULL;
418   *ip = NULL;
419
420   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
421
422   memset(&remote, 0, sizeof(remote));
423   len = sizeof(remote);
424   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
425   if (rval < 0)
426     return FALSE;
427
428   host_ip = inet_ntoa(remote.sin_addr);
429   if (!host_ip)
430     return FALSE;
431
432   *ip = silc_memdup(host_ip, strlen(host_ip));
433   if (*ip == NULL)
434     return FALSE;
435 #endif
436
437   /* Do reverse lookup if we want hostname too. */
438   if (hostname) {
439     /* Get host by address */
440     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
441       return FALSE;
442
443     *hostname = silc_memdup(host, strlen(host));
444     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
445
446     /* Reverse */
447     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
448       return FALSE;
449
450     if (strcmp(*ip, host))
451       return FALSE;
452   }
453
454   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
455   return TRUE;
456 }
457
458 /* Performs lookups for local name and IP address. This peforms reverse
459    lookup as well to verify that the IP has FQDN. */
460
461 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
462                                       char **ip)
463 {
464   char host[1024];
465   int rval;
466   unsigned int len;
467
468 #ifdef HAVE_IPV6
469   struct sockaddr_storage local;
470   char s[NI_MAXHOST];
471
472   if (hostname)
473     *hostname = NULL;
474   *ip = NULL;
475
476   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
477
478   memset(&local, 0, sizeof(local));
479   memset(&s, 0, sizeof(s));
480   len = sizeof(local);
481   rval = getsockname(sock, (struct sockaddr *)&local, &len);
482   if (rval < 0)
483     return FALSE;
484
485   if (getnameinfo((struct sockaddr *)&local, len, s, sizeof(s), NULL, 0,
486                   NI_NUMERICHOST))
487     return FALSE;
488
489   *ip = silc_memdup(s, strlen(s));
490   if (*ip == NULL)
491     return FALSE;
492 #else
493   struct sockaddr_in local;
494   char *host_ip;
495
496   if (hostname)
497     *hostname = NULL;
498   *ip = NULL;
499
500   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
501
502   memset(&local, 0, sizeof(local));
503   len = sizeof(local);
504   rval = getsockname(sock, (struct sockaddr *)&local, &len);
505   if (rval < 0)
506     return FALSE;
507
508   host_ip = inet_ntoa(local.sin_addr);
509   if (!host_ip)
510     return FALSE;
511
512   *ip = silc_memdup(host_ip, strlen(host_ip));
513   if (*ip == NULL)
514     return FALSE;
515 #endif
516
517   /* Do reverse lookup if we want hostname too. */
518   if (hostname) {
519     /* Get host by address */
520     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
521       return FALSE;
522
523     *hostname = silc_memdup(host, strlen(host));
524     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
525
526     /* Reverse */
527     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
528       return FALSE;
529
530     if (strcmp(*ip, host))
531       return FALSE;
532   }
533
534   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
535   return TRUE;
536 }
537
538 /* Return remote port by socket. */
539
540 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
541 {
542 #ifdef HAVE_IPV6
543   struct sockaddr_storage remote;
544   unsigned int len;
545   char s[NI_MAXSERV];
546
547   memset(&remote, 0, sizeof(remote));
548   len = sizeof(remote);
549   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
550     return 0;
551
552   if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
553                   NI_NUMERICSERV))
554     return 0;
555
556   return atoi(s);
557 #else
558   struct sockaddr_in remote;
559   unsigned int len;
560
561   memset(&remote, 0, sizeof(remote));
562   len = sizeof(remote);
563   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
564     return 0;
565
566   return ntohs(remote.sin_port);
567 #endif
568 }
569
570 /* Return local port by socket. */
571
572 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
573 {
574 #ifdef HAVE_IPV6
575   struct sockaddr_storage local;
576   unsigned int len;
577   char s[NI_MAXSERV];
578
579   memset(&local, 0, sizeof(local));
580   len = sizeof(local);
581   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
582     return 0;
583
584   if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
585                   NI_NUMERICSERV))
586     return 0;
587
588   return atoi(s);
589 #else
590   struct sockaddr_in local;
591   unsigned int len;
592
593   memset(&local, 0, sizeof(local));
594   len = sizeof(local);
595   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
596     return 0;
597
598   return ntohs(local.sin_port);
599 #endif
600 }
601 #endif /* !SILC_SYMBIAN */
602
603 /* Return name of localhost. */
604
605 char *silc_net_localhost(void)
606 {
607   char hostname[256], ip_addr[64];
608
609   if (gethostname(hostname, sizeof(hostname)))
610     return NULL;
611
612   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
613     return silc_strdup(hostname);
614
615   silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
616   return silc_strdup(hostname);
617 }
618
619 /* Returns local IP address */
620
621 char *silc_net_localip(void)
622 {
623   char hostname[256], ip_addr[64];
624
625   if (gethostname(hostname, sizeof(hostname)))
626     return NULL;
627
628   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
629     return NULL;
630
631   return silc_strdup(ip_addr);
632 }
633
634 /* Convert network byte order IP to string */
635
636 SilcBool silc_net_bin2addr(const void *bin, SilcUInt32 bin_len,
637                            char *addr, SilcUInt32 addr_size)
638 {
639   if (!addr || !addr_size)
640     return TRUE;
641
642   if (bin_len == 16) {
643 #ifdef HAVE_IPV6
644     struct sockaddr_in6 ipv6;
645     memset(&ipv6, 0, sizeof(ipv6));
646     ipv6.sin6_family = AF_INET6;
647     memcpy(&ipv6.sin6_addr, bin, sizeof(ipv6.sin6_addr));
648     if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
649                      addr, addr_size, NULL, 0, NI_NUMERICHOST))
650     return TRUE;
651 #else
652     return FALSE;
653 #endif /* HAVE_IPV6 */
654   } else if (bin_len == 4) {
655     struct in_addr ipv4;
656     char *a;
657
658     memcpy(&ipv4.s_addr, bin, 4);
659     a = inet_ntoa(ipv4);
660     if (!a)
661       return FALSE;
662
663     silc_snprintf(addr, addr_size, a);
664     return TRUE;
665   }
666
667   return FALSE;
668 }
669
670 /* Host to network byte order */
671
672 SilcUInt32 silc_htonl(SilcUInt32 host)
673 {
674 #ifdef WORDS_BIGENDIAN
675   return host;
676 #else
677   return SILC_SWAB_32(host);
678 #endif /* WORDS_BIGENDIAN */
679 }
680
681 /* Network to host byte order */
682
683 SilcUInt32 silc_ntohl(SilcUInt32 net)
684 {
685 #ifdef WORDS_BIGENDIAN
686   return net;
687 #else
688   return SILC_SWAB_32(net);
689 #endif /* WORDS_BIGENDIAN */
690 }
691
692 /* Host to network byte order */
693
694 SilcUInt16 silc_htons(SilcUInt16 host)
695 {
696 #ifdef WORDS_BIGENDIAN
697   return net;
698 #else
699   return SILC_SWAB_16(host);
700 #endif /* WORDS_BIGENDIAN */
701 }
702
703 /* Network to host byte order */
704
705 SilcUInt16 silc_ntohs(SilcUInt16 net)
706 {
707 #ifdef WORDS_BIGENDIAN
708   return net;
709 #else
710   return SILC_SWAB_16(net);
711 #endif /* WORDS_BIGENDIAN */
712 }