Static analyzer bug fixes
[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   if (ip)
396     *ip = NULL;
397
398   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
399
400   memset(&remote, 0, sizeof(remote));
401   memset(&s, 0, sizeof(s));
402   len = sizeof(remote);
403   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
404   if (rval < 0)
405     return FALSE;
406
407   if (getnameinfo((struct sockaddr *)&remote, len, s, sizeof(s), NULL, 0,
408                   NI_NUMERICHOST))
409     return FALSE;
410
411   if (ip) {
412     *ip = silc_memdup(s, strlen(s));
413     if (*ip == NULL)
414       return FALSE;
415   }
416 #else
417   struct sockaddr_in remote;
418   char *host_ip;
419
420   if (hostname)
421     *hostname = NULL;
422   if (ip)
423     *ip = NULL;
424
425   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
426
427   memset(&remote, 0, sizeof(remote));
428   len = sizeof(remote);
429   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
430   if (rval < 0)
431     return FALSE;
432
433   host_ip = inet_ntoa(remote.sin_addr);
434   if (!host_ip)
435     return FALSE;
436
437   if (ip) {
438     *ip = silc_memdup(host_ip, strlen(host_ip));
439     if (*ip == NULL)
440       return FALSE;
441   }
442 #endif
443
444   /* Do reverse lookup if we want hostname too. */
445   if (hostname) {
446     /* Get host by address */
447     if (!ip || !silc_net_gethostbyaddr(*ip, host, sizeof(host)))
448       return FALSE;
449
450     *hostname = silc_memdup(host, strlen(host));
451     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
452
453     /* Reverse */
454     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
455       return FALSE;
456
457     if (strcmp(*ip, host))
458       return FALSE;
459   }
460
461   if (ip)
462     SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
463   return TRUE;
464 }
465
466 /* Performs lookups for local name and IP address. This peforms reverse
467    lookup as well to verify that the IP has FQDN. */
468
469 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
470                                       char **ip)
471 {
472   char host[1024];
473   int rval, len;
474
475 #ifdef HAVE_IPV6
476   struct sockaddr_storage local;
477   char s[NI_MAXHOST];
478
479   if (hostname)
480     *hostname = NULL;
481   if (ip)
482     *ip = NULL;
483
484   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
485
486   memset(&local, 0, sizeof(local));
487   memset(&s, 0, sizeof(s));
488   len = sizeof(local);
489   rval = getsockname(sock, (struct sockaddr *)&local, &len);
490   if (rval < 0)
491     return FALSE;
492
493   if (getnameinfo((struct sockaddr *)&local, len, s, sizeof(s), NULL, 0,
494                   NI_NUMERICHOST))
495     return FALSE;
496
497   if (ip) {
498     *ip = silc_memdup(s, strlen(s));
499     if (*ip == NULL)
500       return FALSE;
501   }
502 #else
503   struct sockaddr_in local;
504   char *host_ip;
505
506   if (hostname)
507     *hostname = NULL;
508   if (ip)
509     *ip = NULL;
510
511   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
512
513   memset(&local, 0, sizeof(local));
514   len = sizeof(local);
515   rval = getsockname(sock, (struct sockaddr *)&local, &len);
516   if (rval < 0)
517     return FALSE;
518
519   host_ip = inet_ntoa(local.sin_addr);
520   if (!host_ip)
521     return FALSE;
522
523   if (ip) {
524     *ip = silc_memdup(host_ip, strlen(host_ip));
525     if (*ip == NULL)
526       return FALSE;
527   }
528 #endif
529
530   /* Do reverse lookup if we want hostname too. */
531   if (hostname) {
532     /* Get host by address */
533     if (!ip || !silc_net_gethostbyaddr(*ip, host, sizeof(host)))
534       return FALSE;
535
536     *hostname = silc_memdup(host, strlen(host));
537     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
538
539     /* Reverse */
540     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
541       return FALSE;
542
543     if (strcmp(*ip, host))
544       return FALSE;
545   }
546
547   if (ip)
548     SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
549   return TRUE;
550 }
551
552 /* Return remote port by socket. */
553
554 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
555 {
556 #ifdef HAVE_IPV6
557   struct sockaddr_storage remote;
558   int len;
559   char s[NI_MAXSERV];
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   if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
567                   NI_NUMERICSERV))
568     return 0;
569
570   return atoi(s);
571 #else
572   struct sockaddr_in remote;
573   int len;
574
575   memset(&remote, 0, sizeof(remote));
576   len = sizeof(remote);
577   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
578     return 0;
579
580   return ntohs(remote.sin_port);
581 #endif
582 }
583
584 /* Return local port by socket. */
585
586 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
587 {
588 #ifdef HAVE_IPV6
589   struct sockaddr_storage local;
590   int len;
591   char s[NI_MAXSERV];
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   if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
599                   NI_NUMERICSERV))
600     return 0;
601
602   return atoi(s);
603 #else
604   struct sockaddr_in local;
605   int len;
606
607   memset(&local, 0, sizeof(local));
608   len = sizeof(local);
609   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
610     return 0;
611
612   return ntohs(local.sin_port);
613 #endif
614 }
615 #endif /* !SILC_SYMBIAN */
616
617 /* Return name of localhost. */
618
619 char *silc_net_localhost(void)
620 {
621   char hostname[256], ip_addr[64];
622
623   if (gethostname(hostname, sizeof(hostname)))
624     return NULL;
625
626   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
627     return strdup(hostname);
628
629   silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
630   return strdup(hostname);
631 }
632
633 /* Returns local IP address */
634
635 char *silc_net_localip(void)
636 {
637   char hostname[256], ip_addr[64];
638
639   if (gethostname(hostname, sizeof(hostname)))
640     return NULL;
641
642   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
643     return NULL;
644
645   return strdup(ip_addr);
646 }