Added preliminary Symbian support.
[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,
252                                 SilcUInt32 name_len)
253 {
254 #ifdef HAVE_IPV6
255   struct addrinfo req, *ai;
256
257   memset(&req, 0, sizeof(req));
258   req.ai_socktype = SOCK_STREAM;
259   req.ai_flags = AI_CANONNAME;
260
261   if (getaddrinfo(addr, NULL, &req, &ai))
262     return FALSE;
263   if (getnameinfo(ai->ai_addr, ai->ai_addrlen, name, name_len, NULL, 0, 0)) {
264     freeaddrinfo(ai);
265     return FALSE;
266   }
267   freeaddrinfo(ai);
268 #else
269   struct hostent *hp;
270   unsigned char a[16];
271
272   if (!silc_net_addr2bin(addr, a, sizeof(a)))
273     return FALSE;
274
275   hp = gethostbyaddr(a, 4, AF_INET);
276   if (!hp)
277     return FALSE;
278   if (name_len < strlen(hp->h_name))
279     return FALSE;
280   memset(name, 0, name_len);
281   strncpy(name, hp->h_name, strlen(hp->h_name));
282 #endif
283
284   return TRUE;
285 }
286
287 /* Resolves hostname by IP address async. */
288
289 void silc_net_gethostbyaddr_async(const char *addr,
290                                   SilcSchedule schedule,
291                                   SilcNetResolveCallback completion,
292                                   void *context)
293 {
294   SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
295
296   r->completion = completion;
297   r->context = context;
298   r->schedule = schedule;
299   r->input = strdup(addr);
300
301   silc_thread_create(silc_net_gethostbyaddr_thread, r, FALSE);
302 }
303
304 #ifndef SILC_SYMBIAN
305
306 /* Performs lookups for remote name and IP address. This peforms reverse
307    lookup as well to verify that the IP has FQDN. */
308
309 SilcBool silc_net_check_host_by_sock(SilcSocket sock, char **hostname,
310                                      char **ip)
311 {
312   char host[1024];
313   int rval, len;
314
315 #ifdef HAVE_IPV6
316   struct sockaddr_storage remote;
317   char s[NI_MAXHOST];
318
319   if (hostname)
320     *hostname = NULL;
321   *ip = NULL;
322
323   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
324
325   memset(&remote, 0, sizeof(remote));
326   memset(&s, 0, sizeof(s));
327   len = sizeof(remote);
328   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
329   if (rval < 0)
330     return FALSE;
331
332   if (getnameinfo((struct sockaddr *)&remote, len, s, sizeof(s), NULL, 0,
333                   NI_NUMERICHOST))
334     return FALSE;
335
336   *ip = silc_memdup(s, strlen(s));
337   if (*ip == NULL)
338     return FALSE;
339 #else
340   struct sockaddr_in remote;
341   char *host_ip;
342
343   if (hostname)
344     *hostname = NULL;
345   *ip = NULL;
346
347   SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
348
349   memset(&remote, 0, sizeof(remote));
350   len = sizeof(remote);
351   rval = getpeername(sock, (struct sockaddr *)&remote, &len);
352   if (rval < 0)
353     return FALSE;
354
355   host_ip = inet_ntoa(remote.sin_addr);
356   if (!host_ip)
357     return FALSE;
358
359   *ip = silc_memdup(host_ip, strlen(host_ip));
360   if (*ip == NULL)
361     return FALSE;
362 #endif
363
364   /* Do reverse lookup if we want hostname too. */
365   if (hostname) {
366     /* Get host by address */
367     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
368       return FALSE;
369
370     *hostname = silc_memdup(host, strlen(host));
371     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
372
373     /* Reverse */
374     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
375       return FALSE;
376
377     if (strcmp(*ip, host))
378       return FALSE;
379   }
380
381   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
382   return TRUE;
383 }
384
385 /* Performs lookups for local name and IP address. This peforms reverse
386    lookup as well to verify that the IP has FQDN. */
387
388 SilcBool silc_net_check_local_by_sock(SilcSocket sock, char **hostname,
389                                       char **ip)
390 {
391   char host[1024];
392   int rval, len;
393
394 #ifdef HAVE_IPV6
395   struct sockaddr_storage local;
396   char s[NI_MAXHOST];
397
398   if (hostname)
399     *hostname = NULL;
400   *ip = NULL;
401
402   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
403
404   memset(&local, 0, sizeof(local));
405   memset(&s, 0, sizeof(s));
406   len = sizeof(local);
407   rval = getsockname(sock, (struct sockaddr *)&local, &len);
408   if (rval < 0)
409     return FALSE;
410
411   if (getnameinfo((struct sockaddr *)&local, len, s, sizeof(s), NULL, 0,
412                   NI_NUMERICHOST))
413     return FALSE;
414
415   *ip = silc_memdup(s, strlen(s));
416   if (*ip == NULL)
417     return FALSE;
418 #else
419   struct sockaddr_in local;
420   char *host_ip;
421
422   if (hostname)
423     *hostname = NULL;
424   *ip = NULL;
425
426   SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
427
428   memset(&local, 0, sizeof(local));
429   len = sizeof(local);
430   rval = getsockname(sock, (struct sockaddr *)&local, &len);
431   if (rval < 0)
432     return FALSE;
433
434   host_ip = inet_ntoa(local.sin_addr);
435   if (!host_ip)
436     return FALSE;
437
438   *ip = silc_memdup(host_ip, strlen(host_ip));
439   if (*ip == NULL)
440     return FALSE;
441 #endif
442
443   /* Do reverse lookup if we want hostname too. */
444   if (hostname) {
445     /* Get host by address */
446     if (!silc_net_gethostbyaddr(*ip, host, sizeof(host)))
447       return FALSE;
448
449     *hostname = silc_memdup(host, strlen(host));
450     SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
451
452     /* Reverse */
453     if (!silc_net_gethostbyname(*hostname, TRUE, host, sizeof(host)))
454       return FALSE;
455
456     if (strcmp(*ip, host))
457       return FALSE;
458   }
459
460   SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
461   return TRUE;
462 }
463
464 /* Return remote port by socket. */
465
466 SilcUInt16 silc_net_get_remote_port(SilcSocket sock)
467 {
468 #ifdef HAVE_IPV6
469   struct sockaddr_storage remote;
470   int len;
471   char s[NI_MAXSERV];
472
473   memset(&remote, 0, sizeof(remote));
474   len = sizeof(remote);
475   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
476     return 0;
477
478   if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
479                   NI_NUMERICSERV))
480     return 0;
481
482   return atoi(s);
483 #else
484   struct sockaddr_in remote;
485   int len;
486
487   memset(&remote, 0, sizeof(remote));
488   len = sizeof(remote);
489   if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
490     return 0;
491
492   return ntohs(remote.sin_port);
493 #endif
494 }
495
496 /* Return local port by socket. */
497
498 SilcUInt16 silc_net_get_local_port(SilcSocket sock)
499 {
500 #ifdef HAVE_IPV6
501   struct sockaddr_storage local;
502   int len;
503   char s[NI_MAXSERV];
504
505   memset(&local, 0, sizeof(local));
506   len = sizeof(local);
507   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
508     return 0;
509
510   if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
511                   NI_NUMERICSERV))
512     return 0;
513
514   return atoi(s);
515 #else
516   struct sockaddr_in local;
517   int len;
518
519   memset(&local, 0, sizeof(local));
520   len = sizeof(local);
521   if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
522     return 0;
523
524   return ntohs(local.sin_port);
525 #endif
526 }
527 #endif /* !SILC_SYMBIAN */
528
529 /* Return name of localhost. */
530
531 char *silc_net_localhost(void)
532 {
533   char hostname[256], ip_addr[64];
534
535   if (gethostname(hostname, sizeof(hostname)))
536     return NULL;
537
538   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
539     return strdup(hostname);
540
541   silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
542   return strdup(hostname);
543 }
544
545 /* Returns local IP address */
546
547 char *silc_net_localip(void)
548 {
549   char hostname[256], ip_addr[64];
550
551   if (gethostname(hostname, sizeof(hostname)))
552     return NULL;
553
554   if (!silc_net_gethostbyname(hostname, TRUE, ip_addr, sizeof(ip_addr)))
555     return NULL;
556
557   return strdup(ip_addr);
558 }