Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[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   if (silc_net_is_ip4(name)) {
280     memset(address, 0, address_len);
281     if (address_len < strlen(name))
282       return FALSE;
283     strncpy(address, name, strlen(name));
284     return TRUE;
285   }
286
287   hp = gethostbyname(name);
288   if (!hp)
289     return FALSE;
290
291   memcpy(&ip.s_addr, hp->h_addr_list[0], 4);
292   tmp = inet_ntoa(ip);
293   if (!tmp)
294     return FALSE;
295   if (address_len < strlen(tmp))
296     return FALSE;
297   memset(address, 0, address_len);
298   strncpy(address, tmp, strlen(tmp));
299 #endif
300
301   return TRUE;
302 }
303
304 /* Resolves IP address for hostname async. */
305
306 void silc_net_gethostbyname_async(const char *name,
307                                   SilcBool prefer_ipv6,
308                                   SilcSchedule schedule,
309                                   SilcNetResolveCallback completion,
310                                   void *context)
311 {
312   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
313
314   r->completion = completion;
315   r->context = context;
316   r->prefer_ipv6 = prefer_ipv6;
317   r->schedule = schedule;
318   r->input = strdup(name);
319
320   silc_thread_create(silc_net_gethostbyname_thread, r, FALSE);
321 }
322
323 /* Resolves hostname by IP address. */
324
325 SilcBool silc_net_gethostbyaddr(const char *addr, char *name,
326                                 SilcUInt32 name_len)
327 {
328 #ifdef HAVE_IPV6
329   struct addrinfo req, *ai;
330
331   memset(&req, 0, sizeof(req));
332   req.ai_socktype = SOCK_STREAM;
333   req.ai_flags = AI_CANONNAME;
334
335   if (getaddrinfo(addr, NULL, &req, &ai))
336     return FALSE;
337   if (getnameinfo(ai->ai_addr, ai->ai_addrlen, name, name_len, NULL, 0, 0)) {
338     freeaddrinfo(ai);
339     return FALSE;
340   }
341   freeaddrinfo(ai);
342 #else
343   struct hostent *hp;
344   unsigned char a[4];
345
346   if (!silc_net_addr2bin(addr, a, sizeof(a)))
347     return FALSE;
348
349   hp = gethostbyaddr(a, 4, AF_INET);
350   if (!hp)
351     return FALSE;
352   if (name_len < strlen(hp->h_name))
353     return FALSE;
354   memset(name, 0, name_len);
355   strncpy(name, hp->h_name, strlen(hp->h_name));
356 #endif
357
358   return TRUE;
359 }
360
361 /* Resolves hostname by IP address async. */
362
363 void silc_net_gethostbyaddr_async(const char *addr,
364                                   SilcSchedule schedule,
365                                   SilcNetResolveCallback completion,
366                                   void *context)
367 {
368   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
369
370   r->completion = completion;
371   r->context = context;
372   r->schedule = schedule;
373   r->input = strdup(addr);
374
375   silc_thread_create(silc_net_gethostbyaddr_thread, r, FALSE);
376 }
377
378 #ifndef SILC_SYMBIAN
379
380 /* Performs lookups for remote name and IP address. This peforms reverse
381    lookup as well to verify that the IP has FQDN. */
382
383 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
384                                      char **ip)
385 {
386   char host[1024];
387   int rval, len;
388
389 #ifdef HAVE_IPV6
390   struct sockaddr_storage remote;
391   char s[NI_MAXHOST];
392
393   if (hostname)
394     *hostname = NULL;
395   *ip = NULL;
396
397   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
398
399   memset(&remote, 0, sizeof(remote));
400   memset(&s, 0, sizeof(s));
401   len = sizeof(remote);
402   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
403   if (rval < 0)
404     return FALSE;
405
406   if (getnameinfo((struct sockaddr *)&remote, len, s, sizeof(s), NULL, 0,
407                   NI_NUMERICHOST))
408     return FALSE;
409
410   *ip = silc_memdup(s, strlen(s));
411   if (*ip == NULL)
412     return FALSE;
413 #else
414   struct sockaddr_in remote;
415   char *host_ip;
416
417   if (hostname)
418     *hostname = NULL;
419   *ip = NULL;
420
421   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
422
423   memset(&remote, 0, sizeof(remote));
424   len = sizeof(remote);
425   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
426   if (rval < 0)
427     return FALSE;
428
429   host_ip = inet_ntoa(remote.sin_addr);
430   if (!host_ip)
431     return FALSE;
432
433   *ip = silc_memdup(host_ip, strlen(host_ip));
434   if (*ip == NULL)
435     return FALSE;
436 #endif
437
438   /* Do reverse lookup if we want hostname too. */
439   if (hostname) {
440     /* Get host by address */
441     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
442       return FALSE;
443
444     *hostname = silc_memdup(host, strlen(host));
445     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
446
447     /* Reverse */
448     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
449       return FALSE;
450
451     if (strcmp(*ip, host))
452       return FALSE;
453   }
454
455   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
456   return TRUE;
457 }
458
459 /* Performs lookups for local name and IP address. This peforms reverse
460    lookup as well to verify that the IP has FQDN. */
461
462 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
463                                       char **ip)
464 {
465   char host[1024];
466   int rval, 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   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   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   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   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 strdup(hostname);
614
615   silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
616   return 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 strdup(ip_addr);
632 }