1 /*
2 
3   silcnet.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 1997 - 2006 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
17 
18 */
19 /* $Id$ */
20 
21 #include "silc.h"
22 
23 /* Returns bound port from listener */
24 
silc_net_listener_get_port(SilcNetListener listener,SilcUInt32 * port_count)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 
silc_net_listener_get_ip(SilcNetListener listener,SilcUInt32 * ip_count)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 
silc_net_listener_get_hostname(SilcNetListener listener,SilcUInt32 * hostname_count)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 
silc_net_get_error_string(SilcNetStatus error)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 
silc_net_accept_connection(int sock)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 
silc_net_set_socket_opt(int sock,int level,int option,int on)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 
silc_net_get_socket_opt(int sock,int level,int option,void * optval,int * opt_len)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 
silc_net_is_ip4(const char * addr)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 
silc_net_is_ip6(const char * addr)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 
silc_net_is_ip(const char * addr)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 
SILC_TASK_CALLBACK(silc_net_resolve_completion)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 
silc_net_gethostbyname_thread(void * context)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 
silc_net_gethostbyaddr_thread(void * context)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 
silc_net_gethostbyname(const char * name,SilcBool prefer_ipv6,char * address,SilcUInt32 address_len)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 
silc_net_gethostbyname_async(const char * name,SilcBool prefer_ipv6,SilcSchedule schedule,SilcNetResolveCallback completion,void * context)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 
silc_net_gethostbyaddr(const char * addr,char * name,SilcUInt32 name_len)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 
silc_net_gethostbyaddr_async(const char * addr,SilcSchedule schedule,SilcNetResolveCallback completion,void * context)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 
silc_net_check_host_by_sock(SilcSocket sock,char ** hostname,char ** ip)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 
silc_net_check_local_by_sock(SilcSocket sock,char ** hostname,char ** ip)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 
silc_net_get_remote_port(SilcSocket sock)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 
silc_net_get_local_port(SilcSocket sock)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 
silc_net_localhost(void)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 
silc_net_localip(void)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 }
647