Integer type name change.
[silc.git] / lib / silcutil / silcnet.c
1 /*
2
3   silcnet.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcnet.h"
24
25 /* Accepts a connection from a particular socket */
26
27 int silc_net_accept_connection(int sock)
28 {
29   return accept(sock, 0, 0);
30 }
31
32 /* Sets a option for a socket. */
33
34 int silc_net_set_socket_opt(int sock, int level, int option, int on)
35 {
36   return setsockopt(sock, level, option, (void *)&on, sizeof(on));
37 }
38
39 /* Get socket options */
40
41 int silc_net_get_socket_opt(int sock, int level, int option, 
42                             void *optval, int *opt_len)
43 {
44   return getsockopt(sock, level, option, optval, opt_len);
45 }
46
47 /* Checks whether IP address sent as argument is valid IPv4 address. */
48
49 bool silc_net_is_ip4(const char *addr)
50 {
51   int count = 0;
52
53   while (*addr) {
54     if (*addr != '.' && !isdigit(*addr))
55       return FALSE;
56     if (*addr == '.')
57       count++;
58     addr++;
59   }
60
61   if (count != 3)
62     return FALSE;
63   
64   return TRUE;
65 }
66
67 /* Checks whether IP address sent as argument is valid IPv6 address. */
68
69 bool silc_net_is_ip6(const char *addr)
70 {
71   /* XXX does this work with all kinds of IPv6 addresses? */
72   while (*addr) {
73     if (*addr != ':' && !isxdigit(*addr))
74       return FALSE;
75     addr++;
76   }
77   
78   return TRUE;
79 }
80
81 /* Checks whether IP address sent as argument is valid IP address. */
82
83 bool silc_net_is_ip(const char *addr)
84 {
85   if (silc_net_is_ip4(addr))
86     return TRUE;
87   return silc_net_is_ip6(addr);
88 }
89
90 /* Internal context for async resolving */
91 typedef struct {
92   SilcNetResolveCallback completion;
93   void *context;
94   bool prefer_ipv6;
95   SilcSchedule schedule;
96   char *input;
97   char *result;
98 } *SilcNetResolveContext;
99
100 SILC_TASK_CALLBACK(silc_net_resolve_completion)
101 {
102   SilcNetResolveContext r = (SilcNetResolveContext)context;
103
104   /* Call the completion callback */
105   if (r->completion)
106     (*r->completion)(r->result, r->context);
107
108   silc_free(r->input);
109   silc_free(r->result);
110   silc_free(r);
111 }
112
113 /* Thread function to resolve the address for hostname. */
114
115 static void *silc_net_gethostbyname_thread(void *context)
116 {
117   SilcNetResolveContext r = (SilcNetResolveContext)context;
118   char tmp[64];
119
120   if (silc_net_gethostbyname(r->input, r->prefer_ipv6, tmp, sizeof(tmp)))
121     r->result = strdup(tmp);
122
123   silc_schedule_task_add(r->schedule, 0, silc_net_resolve_completion, r, 0, 1,
124                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
125   silc_schedule_wakeup(r->schedule);
126   return NULL;
127 }
128
129 /* Thread function to resolve the hostname for address. */
130
131 static void *silc_net_gethostbyaddr_thread(void *context)
132 {
133   SilcNetResolveContext r = (SilcNetResolveContext)context;
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(r->schedule, 0, silc_net_resolve_completion, r, 0, 1,
140                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
141   silc_schedule_wakeup(r->schedule);
142   return NULL;
143 }
144
145 /* Resolves IP address for hostname. */
146
147 bool silc_net_gethostbyname(const char *name, bool 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                                   bool 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 bool 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
248   hp = gethostbyaddr(addr, strlen(addr), AF_INET);
249   if (!hp)
250     return FALSE;
251   if (name_len < strlen(hp->h_name))
252     return FALSE;
253   memset(name, 0, name_len);
254   strncpy(name, hp->h_name, strlen(hp->h_name));
255 #endif
256   
257   return TRUE;
258 }
259
260 /* Resolves hostname by IP address async. */
261
262 void silc_net_gethostbyaddr_async(const char *addr, 
263                                   SilcSchedule schedule,
264                                   SilcNetResolveCallback completion,
265                                   void *context)
266 {
267   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
268
269   r->completion = completion;
270   r->context = context;
271   r->schedule = schedule;
272   r->input = strdup(addr);
273
274   silc_thread_create(silc_net_gethostbyaddr_thread, r, FALSE);
275 }
276
277 /* Performs lookups for remote name and IP address. This peforms reverse
278    lookup as well to verify that the IP has FQDN. */
279
280 bool silc_net_check_host_by_sock(int sock, char **hostname, char **ip)
281 {
282   struct sockaddr_in remote;
283   struct hostent *dest;
284   char *host_ip = NULL;
285   char host_name[1024];
286   int rval, len;
287   int i;
288
289   *hostname = NULL;
290   *ip = NULL;
291
292   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
293
294   memset(&remote, 0, sizeof(remote));
295   len = sizeof(remote);
296   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
297   if (rval < 0)
298     return FALSE;
299
300   host_ip = inet_ntoa(remote.sin_addr);
301   if (!host_ip)
302     return FALSE;
303
304   *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
305   memcpy(*ip, host_ip, strlen(host_ip));
306
307   /* Get host by address */
308   dest = gethostbyaddr((char *)&remote.sin_addr, 
309                        sizeof(struct in_addr), AF_INET);
310   if (!dest)
311     return FALSE;
312
313   /* Get same host by name to see that the remote host really is
314      the who it says it is */
315   memset(host_name, 0, sizeof(host_name));
316   memcpy(host_name, dest->h_name, strlen(dest->h_name));
317
318   *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
319   memcpy(*hostname, host_name, strlen(host_name));
320   SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
321
322   dest = gethostbyname(host_name);
323   if (!dest)
324     return FALSE;
325
326   /* Find the address from list */
327   for (i = 0; dest->h_addr_list[i]; i++)
328     if (!memcmp(dest->h_addr_list[i], &remote.sin_addr, 
329                 sizeof(struct in_addr)))
330       break;
331   if (!dest->h_addr_list[i])
332     return FALSE;
333
334   silc_free(*ip);
335   *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
336   memcpy(*ip, host_ip, strlen(host_ip));
337   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
338
339   return TRUE;
340 }
341
342 /* Performs lookups for local name and IP address. This peforms reverse
343    lookup as well to verify that the IP has FQDN. */
344
345 bool silc_net_check_local_by_sock(int sock, char **hostname, char **ip)
346 {
347   struct sockaddr_in local;
348   struct hostent *dest;
349   char *host_ip = NULL;
350   char host_name[1024];
351   int rval, len;
352   int i;
353
354   *hostname = NULL;
355   *ip = NULL;
356
357   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
358
359   memset(&local, 0, sizeof(local));
360   len = sizeof(local);
361   rval = getsockname(sock, (struct sockaddr *)&local, &len);
362   if (rval < 0)
363     return FALSE;
364
365   host_ip = inet_ntoa(local.sin_addr);
366   if (!host_ip)
367     return FALSE;
368
369   *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
370   memcpy(*ip, host_ip, strlen(host_ip));
371
372   /* Get host by address */
373   dest = gethostbyaddr((char *)&local.sin_addr, 
374                        sizeof(struct in_addr), AF_INET);
375   if (!dest)
376     return FALSE;
377
378   /* Get same host by name to see that the local host really is
379      the who it says it is */
380   memset(host_name, 0, sizeof(host_name));
381   memcpy(host_name, dest->h_name, strlen(dest->h_name));
382
383   *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
384   memcpy(*hostname, host_name, strlen(host_name));
385   SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
386
387   dest = gethostbyname(host_name);
388   if (!dest)
389     return FALSE;
390
391   /* Find the address from list */
392   for (i = 0; dest->h_addr_list[i]; i++)
393     if (!memcmp(dest->h_addr_list[i], &local.sin_addr, 
394                sizeof(struct in_addr)))
395       break;
396   if (!dest->h_addr_list[i])
397     return FALSE;
398
399   silc_free(*ip);
400   *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
401   memcpy(*ip, host_ip, strlen(host_ip));
402   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
403
404   return TRUE;
405 }
406
407 /* Return remote port by socket. */
408
409 SilcUInt16 silc_net_get_remote_port(int sock)
410 {
411 #ifdef HAVE_IPV6
412   struct sockaddr_storage remote;
413   int len;
414   char s[NI_MAXSERV];
415
416   memset(&remote, 0, sizeof(remote));
417   len = sizeof(remote);
418   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
419     return 0;
420
421   if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
422       NI_NUMERICSERV))
423     return 0;
424   
425   return atoi(s);
426 #else
427   struct sockaddr_in remote;
428   int len;
429
430   memset(&remote, 0, sizeof(remote));
431   len = sizeof(remote);
432   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
433     return 0;
434
435   return ntohs(remote.sin_port);
436 #endif
437 }
438
439 /* Return local port by socket. */
440
441 SilcUInt16 silc_net_get_local_port(int sock)
442 {
443 #ifdef HAVE_IPV6
444   struct sockaddr_storage local;
445   int len;
446   char s[NI_MAXSERV];
447
448   memset(&local, 0, sizeof(local));
449   len = sizeof(local);
450   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
451     return 0;
452
453   if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
454       NI_NUMERICSERV))
455     return 0;
456   
457   return atoi(s);
458 #else
459   struct sockaddr_in local;
460   int len;
461
462   memset(&local, 0, sizeof(local));
463   len = sizeof(local);
464   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
465     return 0;
466
467   return ntohs(local.sin_port);
468 #endif
469 }
470
471 /* Return name of localhost. */
472
473 char *silc_net_localhost(void)
474 {
475   char hostname[256], ip_addr[64];
476
477   if (gethostname(hostname, sizeof(hostname)))
478     return NULL;
479
480   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
481     return strdup(hostname);
482
483   silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
484   return strdup(hostname);
485 }
486
487 /* Returns local IP address */
488
489 char *silc_net_localip(void)
490 {
491   char hostname[256], ip_addr[64];
492
493   if (gethostname(hostname, sizeof(hostname)))
494     return NULL;
495
496   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
497     return NULL;
498
499   return strdup(ip_addr);
500 }