2a868d22ac37f7e0de240a850e13fb34f8fdc98b
[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 /* Accepts a connection from a particular socket */
45
46 int silc_net_accept_connection(int sock)
47 {
48   return accept(sock, 0, 0);
49 }
50
51 /* Sets a option for a socket. */
52
53 int silc_net_set_socket_opt(int sock, int level, int option, int on)
54 {
55   return setsockopt(sock, level, option, (void *)&on, sizeof(on));
56 }
57
58 /* Get socket options */
59
60 int silc_net_get_socket_opt(int sock, int level, int option,
61                             void *optval, int *opt_len)
62 {
63   return getsockopt(sock, level, option, optval, opt_len);
64 }
65
66 /* Checks whether IP address sent as argument is valid IPv4 address. */
67
68 SilcBool silc_net_is_ip4(const char *addr)
69 {
70   int count = 0;
71
72   while (*addr) {
73     if (*addr != '.' && !isdigit((int)*addr))
74       return FALSE;
75     if (*addr == '.')
76       count++;
77     addr++;
78   }
79
80   if (count != 3)
81     return FALSE;
82
83   return TRUE;
84 }
85
86 /* Checks whether IP address sent as argument is valid IPv6 address. */
87
88 SilcBool silc_net_is_ip6(const char *addr)
89 {
90   /* XXX does this work with all kinds of IPv6 addresses? */
91   while (*addr) {
92     if (*addr != ':' && !isxdigit((int)*addr))
93       return FALSE;
94     addr++;
95   }
96
97   return TRUE;
98 }
99
100 /* Checks whether IP address sent as argument is valid IP address. */
101
102 SilcBool silc_net_is_ip(const char *addr)
103 {
104   if (silc_net_is_ip4(addr))
105     return TRUE;
106   return silc_net_is_ip6(addr);
107 }
108
109 /* Internal context for async resolving */
110 typedef struct {
111   SilcNetResolveCallback completion;
112   void *context;
113   SilcBool prefer_ipv6;
114   SilcSchedule schedule;
115   char *input;
116   char *result;
117 } *SilcNetResolveContext;
118
119 SILC_TASK_CALLBACK(silc_net_resolve_completion)
120 {
121   SilcNetResolveContext r = (SilcNetResolveContext)context;
122
123   /* Call the completion callback */
124   if (r->completion)
125     (*r->completion)(r->result, r->context);
126
127   silc_free(r->input);
128   silc_free(r->result);
129   silc_free(r);
130 }
131
132 /* Thread function to resolve the address for hostname. */
133
134 static void *silc_net_gethostbyname_thread(void *context)
135 {
136   SilcNetResolveContext r = (SilcNetResolveContext)context;
137   SilcSchedule schedule = r->schedule;
138   char tmp[64];
139
140   if (silc_net_gethostbyname(r->input, r->prefer_ipv6, tmp, sizeof(tmp)))
141     r->result = strdup(tmp);
142
143   silc_schedule_task_add(schedule, 0, silc_net_resolve_completion, r, 0, 1,
144                          SILC_TASK_TIMEOUT);
145   silc_schedule_wakeup(schedule);
146   return NULL;
147 }
148
149 /* Thread function to resolve the hostname for address. */
150
151 static void *silc_net_gethostbyaddr_thread(void *context)
152 {
153   SilcNetResolveContext r = (SilcNetResolveContext)context;
154   SilcSchedule schedule = r->schedule;
155   char tmp[256];
156
157   if (silc_net_gethostbyaddr(r->input, tmp, sizeof(tmp)))
158     r->result = strdup(tmp);
159
160   silc_schedule_task_add(schedule, 0, silc_net_resolve_completion, r, 0, 1,
161                          SILC_TASK_TIMEOUT);
162   silc_schedule_wakeup(schedule);
163   return NULL;
164 }
165
166 /* Resolves IP address for hostname. */
167
168 SilcBool silc_net_gethostbyname(const char *name,
169                                 SilcBool prefer_ipv6, char *address,
170                                 SilcUInt32 address_len)
171 {
172 #ifdef HAVE_IPV6
173   struct addrinfo hints, *ai, *tmp, *ip4 = NULL, *ip6 = NULL;
174
175   memset(&hints, 0, sizeof(hints));
176   hints.ai_socktype = SOCK_STREAM;
177   if (getaddrinfo(name, NULL, &hints, &ai))
178     return FALSE;
179
180   for (tmp = ai; tmp; tmp = tmp->ai_next) {
181     if (tmp->ai_family == AF_INET6) {
182       ip6 = tmp;
183       if (ip4)
184         break;
185       continue;
186     }
187     if (tmp->ai_family == AF_INET) {
188       ip4 = tmp;
189       if (ip6)
190         break;
191       continue;
192     }
193   }
194
195   tmp = (prefer_ipv6 ? (ip6 ? ip6 : ip4) : (ip4 ? ip4 : ip6));
196   if (!tmp) {
197     freeaddrinfo(ai);
198     return FALSE;
199   }
200
201   if (getnameinfo(tmp->ai_addr, tmp->ai_addrlen, address,
202                   address_len, NULL, 0, NI_NUMERICHOST)) {
203     freeaddrinfo(ai);
204     return FALSE;
205   }
206
207   freeaddrinfo(ai);
208 #else
209   struct hostent *hp;
210   struct in_addr ip;
211   char *tmp;
212
213   hp = gethostbyname(name);
214   if (!hp)
215     return FALSE;
216
217   memcpy(&ip.s_addr, hp->h_addr_list[0], 4);
218   tmp = inet_ntoa(ip);
219   if (!tmp)
220     return FALSE;
221   if (address_len < strlen(tmp))
222     return FALSE;
223   memset(address, 0, address_len);
224   strncpy(address, tmp, strlen(tmp));
225 #endif
226
227   return TRUE;
228 }
229
230 /* Resolves IP address for hostname async. */
231
232 void silc_net_gethostbyname_async(const char *name,
233                                   SilcBool prefer_ipv6,
234                                   SilcSchedule schedule,
235                                   SilcNetResolveCallback completion,
236                                   void *context)
237 {
238   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
239
240   r->completion = completion;
241   r->context = context;
242   r->prefer_ipv6 = prefer_ipv6;
243   r->schedule = schedule;
244   r->input = strdup(name);
245
246   silc_thread_create(silc_net_gethostbyname_thread, r, FALSE);
247 }
248
249 /* Resolves hostname by IP address. */
250
251 SilcBool silc_net_gethostbyaddr(const char *addr, char *name, SilcUInt32 name_len)
252 {
253 #ifdef HAVE_IPV6
254   struct addrinfo req, *ai;
255
256   memset(&req, 0, sizeof(req));
257   req.ai_socktype = SOCK_STREAM;
258   req.ai_flags = AI_CANONNAME;
259
260   if (getaddrinfo(addr, NULL, &req, &ai))
261     return FALSE;
262   if (getnameinfo(ai->ai_addr, ai->ai_addrlen, name, name_len, NULL, 0, 0)) {
263     freeaddrinfo(ai);
264     return FALSE;
265   }
266   freeaddrinfo(ai);
267 #else
268   struct hostent *hp;
269   unsigned char a[16];
270
271   if (!silc_net_addr2bin(addr, a, sizeof(a)))
272     return FALSE;
273
274   hp = gethostbyaddr(a, 4, AF_INET);
275   if (!hp)
276     return FALSE;
277   if (name_len < strlen(hp->h_name))
278     return FALSE;
279   memset(name, 0, name_len);
280   strncpy(name, hp->h_name, strlen(hp->h_name));
281 #endif
282
283   return TRUE;
284 }
285
286 /* Resolves hostname by IP address async. */
287
288 void silc_net_gethostbyaddr_async(const char *addr,
289                                   SilcSchedule schedule,
290                                   SilcNetResolveCallback completion,
291                                   void *context)
292 {
293   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
294
295   r->completion = completion;
296   r->context = context;
297   r->schedule = schedule;
298   r->input = strdup(addr);
299
300   silc_thread_create(silc_net_gethostbyaddr_thread, r, FALSE);
301 }
302
303 /* Performs lookups for remote name and IP address. This peforms reverse
304    lookup as well to verify that the IP has FQDN. */
305
306 SilcBool silc_net_check_host_by_sock(int sock, char **hostname, char **ip)
307 {
308   char host[1024];
309   int rval, len;
310
311 #ifdef HAVE_IPV6
312   struct sockaddr_storage remote;
313   char s[NI_MAXHOST];
314
315   if (hostname)
316     *hostname = NULL;
317   *ip = NULL;
318
319   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
320
321   memset(&remote, 0, sizeof(remote));
322   memset(&s, 0, sizeof(s));
323   len = sizeof(remote);
324   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
325   if (rval < 0)
326     return FALSE;
327
328   if (getnameinfo((struct sockaddr *)&remote, len, s, sizeof(s), NULL, 0,
329                   NI_NUMERICHOST))
330     return FALSE;
331
332   *ip = silc_memdup(s, strlen(s));
333   if (*ip == NULL)
334     return FALSE;
335 #else
336   struct sockaddr_in remote;
337   char *host_ip;
338
339   if (hostname)
340     *hostname = NULL;
341   *ip = NULL;
342
343   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
344
345   memset(&remote, 0, sizeof(remote));
346   len = sizeof(remote);
347   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
348   if (rval < 0)
349     return FALSE;
350
351   host_ip = inet_ntoa(remote.sin_addr);
352   if (!host_ip)
353     return FALSE;
354
355   *ip = silc_memdup(host_ip, strlen(host_ip));
356   if (*ip == NULL)
357     return FALSE;
358 #endif
359
360   /* Do reverse lookup if we want hostname too. */
361   if (hostname) {
362     /* Get host by address */
363     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
364       return FALSE;
365
366     *hostname = silc_memdup(host, strlen(host));
367     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
368
369     /* Reverse */
370     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
371       return FALSE;
372
373     if (strcmp(*ip, host))
374       return FALSE;
375   }
376
377   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
378   return TRUE;
379 }
380
381 /* Performs lookups for local name and IP address. This peforms reverse
382    lookup as well to verify that the IP has FQDN. */
383
384 SilcBool silc_net_check_local_by_sock(int sock, char **hostname, char **ip)
385 {
386   char host[1024];
387   int rval, len;
388
389 #ifdef HAVE_IPV6
390   struct sockaddr_storage local;
391   char s[NI_MAXHOST];
392
393   if (hostname)
394     *hostname = NULL;
395   *ip = NULL;
396
397   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
398
399   memset(&local, 0, sizeof(local));
400   memset(&s, 0, sizeof(s));
401   len = sizeof(local);
402   rval = getsockname(sock, (struct sockaddr *)&local, &len);
403   if (rval < 0)
404     return FALSE;
405
406   if (getnameinfo((struct sockaddr *)&local, 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 local;
415   char *host_ip;
416
417   if (hostname)
418     *hostname = NULL;
419   *ip = NULL;
420
421   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
422
423   memset(&local, 0, sizeof(local));
424   len = sizeof(local);
425   rval = getsockname(sock, (struct sockaddr *)&local, &len);
426   if (rval < 0)
427     return FALSE;
428
429   host_ip = inet_ntoa(local.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 /* Return remote port by socket. */
460
461 SilcUInt16 silc_net_get_remote_port(int sock)
462 {
463 #ifdef HAVE_IPV6
464   struct sockaddr_storage remote;
465   int len;
466   char s[NI_MAXSERV];
467
468   memset(&remote, 0, sizeof(remote));
469   len = sizeof(remote);
470   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
471     return 0;
472
473   if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
474                   NI_NUMERICSERV))
475     return 0;
476
477   return atoi(s);
478 #else
479   struct sockaddr_in remote;
480   int len;
481
482   memset(&remote, 0, sizeof(remote));
483   len = sizeof(remote);
484   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
485     return 0;
486
487   return ntohs(remote.sin_port);
488 #endif
489 }
490
491 /* Return local port by socket. */
492
493 SilcUInt16 silc_net_get_local_port(int sock)
494 {
495 #ifdef HAVE_IPV6
496   struct sockaddr_storage local;
497   int len;
498   char s[NI_MAXSERV];
499
500   memset(&local, 0, sizeof(local));
501   len = sizeof(local);
502   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
503     return 0;
504
505   if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
506                   NI_NUMERICSERV))
507     return 0;
508
509   return atoi(s);
510 #else
511   struct sockaddr_in local;
512   int len;
513
514   memset(&local, 0, sizeof(local));
515   len = sizeof(local);
516   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
517     return 0;
518
519   return ntohs(local.sin_port);
520 #endif
521 }
522
523 /* Return name of localhost. */
524
525 char *silc_net_localhost(void)
526 {
527   char hostname[256], ip_addr[64];
528
529   if (gethostname(hostname, sizeof(hostname)))
530     return NULL;
531
532   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
533     return strdup(hostname);
534
535   silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
536   return strdup(hostname);
537 }
538
539 /* Returns local IP address */
540
541 char *silc_net_localip(void)
542 {
543   char hostname[256], ip_addr[64];
544
545   if (gethostname(hostname, sizeof(hostname)))
546     return NULL;
547
548   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
549     return NULL;
550
551   return strdup(ip_addr);
552 }