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