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