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