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