Merge Irssi 0.8.16-rc1
[silc.git] / apps / irssi / src / core / network.c
1 /*
2  network.c : Network stuff
3
4     Copyright (C) 1999-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "network.h"
23
24 #include <sys/un.h>
25
26 #ifndef INADDR_NONE
27 #  define INADDR_NONE INADDR_BROADCAST
28 #endif
29
30 union sockaddr_union {
31         struct sockaddr sa;
32         struct sockaddr_in sin;
33 #ifdef HAVE_IPV6
34         struct sockaddr_in6 sin6;
35 #endif
36 };
37
38 #ifdef HAVE_IPV6
39 #  define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
40         sizeof(so.sin6) : sizeof(so.sin))
41 #else
42 #  define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
43 #endif
44
45 GIOChannel *g_io_channel_new(int handle)
46 {
47         GIOChannel *chan;
48 #ifdef WIN32
49         chan = g_io_channel_win32_new_socket(handle);
50 #else
51         chan = g_io_channel_unix_new(handle);
52 #endif
53         g_io_channel_set_encoding(chan, NULL, NULL);
54         g_io_channel_set_buffered(chan, FALSE);
55         return chan;
56 }
57
58 /* Cygwin need this, don't know others.. */
59 /*#define BLOCKING_SOCKETS 1*/
60
61 IPADDR ip4_any = {
62         AF_INET,
63         { INADDR_ANY }
64 };
65
66 int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
67 {
68         if (ip1->family != ip2->family)
69                 return 0;
70
71 #ifdef HAVE_IPV6
72         if (ip1->family == AF_INET6)
73                 return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
74 #endif
75
76         return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
77 }
78
79
80 static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
81 {
82         if (ip == NULL) {
83 #ifdef HAVE_IPV6
84                 so->sin6.sin6_family = AF_INET6;
85                 so->sin6.sin6_addr = in6addr_any;
86 #else
87                 so->sin.sin_family = AF_INET;
88                 so->sin.sin_addr.s_addr = INADDR_ANY;
89 #endif
90                 return;
91         }
92
93         so->sin.sin_family = ip->family;
94 #ifdef HAVE_IPV6
95         if (ip->family == AF_INET6)
96                 memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
97         else
98 #endif
99                 memcpy(&so->sin.sin_addr, &ip->ip, 4);
100 }
101
102 void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
103 {
104         ip->family = so->sin.sin_family;
105
106 #ifdef HAVE_IPV6
107         if (ip->family == AF_INET6)
108                 memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
109         else
110 #endif
111                 memcpy(&ip->ip, &so->sin.sin_addr, 4);
112 }
113
114 static void sin_set_port(union sockaddr_union *so, int port)
115 {
116 #ifdef HAVE_IPV6
117         if (so->sin.sin_family == AF_INET6)
118                 so->sin6.sin6_port = htons((unsigned short)port);
119         else
120 #endif
121                 so->sin.sin_port = htons((unsigned short)port);
122 }
123
124 static int sin_get_port(union sockaddr_union *so)
125 {
126 #ifdef HAVE_IPV6
127         if (so->sin.sin_family == AF_INET6)
128                 return ntohs(so->sin6.sin6_port);
129 #endif
130         return ntohs(so->sin.sin_port);
131 }
132
133 /* Connect to socket */
134 GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip)
135 {
136         IPADDR ip4, ip6, *ip;
137
138         g_return_val_if_fail(addr != NULL, NULL);
139
140         if (net_gethostbyname(addr, &ip4, &ip6) == -1)
141                 return NULL;
142
143         if (my_ip == NULL) {
144                 /* prefer IPv4 addresses */
145                 ip = ip4.family != 0 ? &ip4 : &ip6;
146         } else if (IPADDR_IS_V6(my_ip)) {
147                 /* my_ip is IPv6 address, use it if possible */
148                 if (ip6.family != 0)
149                         ip = &ip6;
150                 else {
151                         my_ip = NULL;
152                         ip = &ip4;
153                 }
154         } else {
155                 /* my_ip is IPv4 address, use it if possible */
156                 if (ip4.family != 0)
157                         ip = &ip4;
158                 else {
159                         my_ip = NULL;
160                         ip = &ip6;
161                 }
162         }
163
164         return net_connect_ip(ip, port, my_ip);
165 }
166
167 /* Connect to socket with ip address */
168 GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
169 {
170         union sockaddr_union so;
171         int handle, ret, opt = 1;
172
173         if (my_ip != NULL && ip->family != my_ip->family) {
174                 g_warning("net_connect_ip(): ip->family != my_ip->family");
175                 my_ip = NULL;
176         }
177
178         /* create the socket */
179         memset(&so, 0, sizeof(so));
180         so.sin.sin_family = ip->family;
181         handle = socket(ip->family, SOCK_STREAM, 0);
182
183         if (handle == -1)
184                 return NULL;
185
186         /* set socket options */
187 #ifndef WIN32
188         fcntl(handle, F_SETFL, O_NONBLOCK);
189 #endif
190         setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
191         setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
192
193         /* set our own address */
194         if (my_ip != NULL) {
195                 sin_set_ip(&so, my_ip);
196                 if (bind(handle, &so.sa, SIZEOF_SOCKADDR(so)) < 0) {
197                         int old_errno = errno;
198
199                         close(handle);
200                         errno = old_errno;
201                         return NULL;
202                 }
203         }
204
205         /* connect */
206         sin_set_ip(&so, ip);
207         sin_set_port(&so, port);
208         ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so));
209
210 #ifndef WIN32
211         if (ret < 0 && errno != EINPROGRESS)
212 #else
213         if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
214 #endif
215         {
216                 int old_errno = errno;
217                 close(handle);
218                 errno = old_errno;
219                 return NULL;
220         }
221
222         return g_io_channel_new(handle);
223 }
224
225 /* Connect to named UNIX socket */
226 GIOChannel *net_connect_unix(const char *path)
227 {
228         struct sockaddr_un sa;
229         int handle, ret;
230
231         /* create the socket */
232         handle = socket(PF_UNIX, SOCK_STREAM, 0);
233         if (handle == -1)
234                 return NULL;
235
236         /* set socket options */
237 #ifndef WIN32
238         fcntl(handle, F_SETFL, O_NONBLOCK);
239 #endif
240
241         /* connect */
242         memset(&sa, 0, sizeof(sa));
243         sa.sun_family = AF_UNIX;
244         strncpy(sa.sun_path, path, sizeof(sa.sun_path)-1);
245         sa.sun_path[sizeof(sa.sun_path)-1] = '\0';
246
247         ret = connect(handle, (struct sockaddr *) &sa, sizeof(sa));
248         if (ret < 0 && errno != EINPROGRESS) {
249                 int old_errno = errno;
250                 close(handle);
251                 errno = old_errno;
252                 return NULL;
253         }
254
255         return g_io_channel_new(handle);
256 }
257
258 /* Disconnect socket */
259 void net_disconnect(GIOChannel *handle)
260 {
261         g_return_if_fail(handle != NULL);
262
263         g_io_channel_close(handle);
264         g_io_channel_unref(handle);
265 }
266
267 /* Listen for connections on a socket. if `my_ip' is NULL, listen in any
268    address. */
269 GIOChannel *net_listen(IPADDR *my_ip, int *port)
270 {
271         union sockaddr_union so;
272         int ret, handle, opt = 1;
273         socklen_t len;
274
275         g_return_val_if_fail(port != NULL, NULL);
276
277         memset(&so, 0, sizeof(so));
278         sin_set_ip(&so, my_ip);
279         sin_set_port(&so, *port);
280
281         /* create the socket */
282         handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
283 #ifdef HAVE_IPV6
284         if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
285                 /* IPv6 is not supported by OS */
286                 so.sin.sin_family = AF_INET;
287                 so.sin.sin_addr.s_addr = INADDR_ANY;
288
289                 handle = socket(AF_INET, SOCK_STREAM, 0);
290         }
291 #endif
292         if (handle == -1)
293                 return NULL;
294
295         /* set socket options */
296 #ifndef WIN32
297         fcntl(handle, F_SETFL, O_NONBLOCK);
298 #endif
299         setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
300         setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
301
302         /* specify the address/port we want to listen in */
303         ret = bind(handle, &so.sa, SIZEOF_SOCKADDR(so));
304         if (ret >= 0) {
305                 /* get the actual port we started listen */
306                 len = SIZEOF_SOCKADDR(so);
307                 ret = getsockname(handle, &so.sa, &len);
308                 if (ret >= 0) {
309                         *port = sin_get_port(&so);
310
311                         /* start listening */
312                         if (listen(handle, 1) >= 0)
313                                 return g_io_channel_new(handle);
314                 }
315
316         }
317
318         /* error */
319         close(handle);
320         return NULL;
321 }
322
323 /* Accept a connection on a socket */
324 GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port)
325 {
326         union sockaddr_union so;
327         int ret;
328         socklen_t addrlen;
329
330         g_return_val_if_fail(handle != NULL, NULL);
331
332         addrlen = sizeof(so);
333         ret = accept(g_io_channel_unix_get_fd(handle), &so.sa, &addrlen);
334
335         if (ret < 0)
336                 return NULL;
337
338         if (addr != NULL) sin_get_ip(&so, addr);
339         if (port != NULL) *port = sin_get_port(&so);
340
341 #ifndef WIN32
342         fcntl(ret, F_SETFL, O_NONBLOCK);
343 #endif
344         return g_io_channel_new(ret);
345 }
346
347 /* Read data from socket, return number of bytes read, -1 = error */
348 int net_receive(GIOChannel *handle, char *buf, int len)
349 {
350         gsize ret;
351         GIOStatus status;
352         GError *err = NULL;
353
354         g_return_val_if_fail(handle != NULL, -1);
355         g_return_val_if_fail(buf != NULL, -1);
356
357         status = g_io_channel_read_chars(handle, buf, len, &ret, &err);
358         if (err != NULL) {
359                 g_warning(err->message);
360                 g_error_free(err);
361         }
362         if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF)
363                 return -1; /* disconnected */
364
365         return ret;
366 }
367
368 /* Transmit data, return number of bytes sent, -1 = error */
369 int net_transmit(GIOChannel *handle, const char *data, int len)
370 {
371         gsize ret;
372         GIOStatus status;
373         GError *err = NULL;
374
375         g_return_val_if_fail(handle != NULL, -1);
376         g_return_val_if_fail(data != NULL, -1);
377
378         status = g_io_channel_write_chars(handle, (char *) data, len, &ret, &err);
379         if (err != NULL) {
380                 g_warning(err->message);
381                 g_error_free(err);
382         }
383         if (status == G_IO_STATUS_ERROR)
384                 return -1;
385
386         return ret;
387 }
388
389 /* Get socket address/port */
390 int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
391 {
392         union sockaddr_union so;
393         socklen_t addrlen;
394
395         g_return_val_if_fail(handle != NULL, -1);
396         g_return_val_if_fail(addr != NULL, -1);
397
398         addrlen = sizeof(so);
399         if (getsockname(g_io_channel_unix_get_fd(handle),
400                         (struct sockaddr *) &so, &addrlen) == -1)
401                 return -1;
402
403         sin_get_ip(&so, addr);
404         if (port) *port = sin_get_port(&so);
405
406         return 0;
407 }
408
409 /* Get IP addresses for host, both IPv4 and IPv6 if possible.
410    If ip->family is 0, the address wasn't found.
411    Returns 0 = ok, others = error code for net_gethosterror() */
412 int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
413 {
414 #ifdef HAVE_IPV6
415         union sockaddr_union *so;
416         struct addrinfo hints, *ai, *ailist;
417         int ret, count_v4, count_v6, use_v4, use_v6;
418 #else
419         struct hostent *hp;
420         int count;
421 #endif
422
423         g_return_val_if_fail(addr != NULL, -1);
424
425         memset(ip4, 0, sizeof(IPADDR));
426         memset(ip6, 0, sizeof(IPADDR));
427
428 #ifdef HAVE_IPV6
429         memset(&hints, 0, sizeof(struct addrinfo));
430         hints.ai_socktype = SOCK_STREAM;
431
432         /* save error to host_error for later use */
433         ret = getaddrinfo(addr, NULL, &hints, &ailist);
434         if (ret != 0)
435                 return ret;
436
437         /* count IPs */
438         count_v4 = count_v6 = 0;
439         for (ai = ailist; ai != NULL; ai = ai->ai_next) {
440                 if (ai->ai_family == AF_INET)
441                         count_v4++;
442                 else if (ai->ai_family == AF_INET6)
443                         count_v6++;
444         }
445
446         if (count_v4 == 0 && count_v6 == 0)
447                 return HOST_NOT_FOUND; /* shouldn't happen? */
448
449         /* if there are multiple addresses, return random one */
450         use_v4 = count_v4 <= 1 ? 0 : rand() % count_v4;
451         use_v6 = count_v6 <= 1 ? 0 : rand() % count_v6;
452
453         count_v4 = count_v6 = 0;
454         for (ai = ailist; ai != NULL; ai = ai->ai_next) {
455                 so = (union sockaddr_union *) ai->ai_addr;
456
457                 if (ai->ai_family == AF_INET) {
458                         if (use_v4 == count_v4)
459                                 sin_get_ip(so, ip4);
460                         count_v4++;
461                 } else if (ai->ai_family == AF_INET6) {
462                         if (use_v6 == count_v6)
463                                 sin_get_ip(so, ip6);
464                         count_v6++;
465                 }
466         }
467         freeaddrinfo(ailist);
468         return 0;
469 #else
470         hp = gethostbyname(addr);
471         if (hp == NULL)
472                 return h_errno;
473
474         /* count IPs */
475         count = 0;
476         while (hp->h_addr_list[count] != NULL)
477                 count++;
478
479         if (count == 0)
480                 return HOST_NOT_FOUND; /* shouldn't happen? */
481
482         /* if there are multiple addresses, return random one */
483         ip4->family = AF_INET;
484         memcpy(&ip4->ip, hp->h_addr_list[rand() % count], 4);
485
486         return 0;
487 #endif
488 }
489
490 /* Get name for host, *name should be g_free()'d unless it's NULL.
491    Return values are the same as with net_gethostbyname() */
492 int net_gethostbyaddr(IPADDR *ip, char **name)
493 {
494 #ifdef HAVE_IPV6
495         union sockaddr_union so;
496         int host_error;
497         char hostname[NI_MAXHOST];
498 #else
499         struct hostent *hp;
500 #endif
501
502         g_return_val_if_fail(ip != NULL, -1);
503         g_return_val_if_fail(name != NULL, -1);
504
505         *name = NULL;
506 #ifdef HAVE_IPV6
507         memset(&so, 0, sizeof(so));
508         sin_set_ip(&so, ip);
509
510         /* save error to host_error for later use */
511         host_error = getnameinfo((struct sockaddr *) &so, sizeof(so),
512                                  hostname, sizeof(hostname), NULL, 0, 0);
513         if (host_error != 0)
514                 return host_error;
515
516         *name = g_strdup(hostname);
517 #else
518         if (ip->family != AF_INET) return -1;
519         hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET);
520         if (hp == NULL) return -1;
521
522         *name = g_strdup(hp->h_name);
523 #endif
524
525         return 0;
526 }
527
528 int net_ip2host(IPADDR *ip, char *host)
529 {
530 #ifdef HAVE_IPV6
531         if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
532                 return -1;
533 #else
534         unsigned long ip4;
535
536         if (ip->family != AF_INET) {
537                 strcpy(host, "0.0.0.0");
538         } else {
539                 ip4 = ntohl(ip->ip.s_addr);
540                 g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
541                            (ip4 & 0xff000000UL) >> 24,
542                            (ip4 & 0x00ff0000) >> 16,
543                            (ip4 & 0x0000ff00) >> 8,
544                            (ip4 & 0x000000ff));
545         }
546 #endif
547         return 0;
548 }
549
550 int net_host2ip(const char *host, IPADDR *ip)
551 {
552         unsigned long addr;
553
554         if (strchr(host, ':') != NULL) {
555                 /* IPv6 */
556                 ip->family = AF_INET6;
557 #ifdef HAVE_IPV6
558                 if (inet_pton(AF_INET6, host, &ip->ip) == 0)
559                         return -1;
560 #else
561                 ip->ip.s_addr = 0;
562 #endif
563         } else {
564                 /* IPv4 */
565                 ip->family = AF_INET;
566 #ifdef HAVE_INET_ATON
567                 if (inet_aton(host, &ip->ip.s_addr) == 0)
568                         return -1;
569 #else
570                 addr = inet_addr(host);
571                 if (addr == INADDR_NONE)
572                         return -1;
573
574                 memcpy(&ip->ip, &addr, 4);
575 #endif
576         }
577
578         return 0;
579 }
580
581 /* Get socket error */
582 int net_geterror(GIOChannel *handle)
583 {
584         int data;
585         socklen_t len = sizeof(data);
586
587         if (getsockopt(g_io_channel_unix_get_fd(handle),
588                        SOL_SOCKET, SO_ERROR, (void *) &data, &len) == -1)
589                 return -1;
590
591         return data;
592 }
593
594 /* get error of net_gethostname() */
595 const char *net_gethosterror(int error)
596 {
597 #ifdef HAVE_IPV6
598         g_return_val_if_fail(error != 0, NULL);
599
600         return gai_strerror(error);
601 #else
602         switch (error) {
603         case HOST_NOT_FOUND:
604                 return "Host not found";
605         case NO_ADDRESS:
606                 return "No IP address found for name";
607         case NO_RECOVERY:
608                 return "A non-recovable name server error occurred";
609         case TRY_AGAIN:
610                 return "A temporary error on an authoritative name server";
611         }
612
613         /* unknown error */
614         return NULL;
615 #endif
616 }
617
618 /* return TRUE if host lookup failed because it didn't exist (ie. not
619    some error with name server) */
620 int net_hosterror_notfound(int error)
621 {
622 #ifdef HAVE_IPV6
623 #ifdef EAI_NODATA /* NODATA is depricated */
624         return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
625 #else
626         return error != 1 && (error == EAI_NONAME);
627 #endif
628 #else
629         return error == HOST_NOT_FOUND || error == NO_ADDRESS;
630 #endif
631 }
632
633 /* Get name of TCP service */
634 char *net_getservbyport(int port)
635 {
636         struct servent *entry;
637
638         entry = getservbyport(htons((unsigned short) port), "tcp");
639         return entry == NULL ? NULL : entry->s_name;
640 }
641
642 int is_ipv4_address(const char *host)
643 {
644         while (*host != '\0') {
645                 if (*host != '.' && !i_isdigit(*host))
646                         return 0;
647                 host++;
648         }
649
650         return 1;
651 }
652
653 int is_ipv6_address(const char *host)
654 {
655         while (*host != '\0') {
656                 if (*host != ':' && !i_isxdigit(*host))
657                         return 0;
658                 host++;
659         }
660
661         return 1;
662 }