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