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