* src/common/plugin.[ch]
[claws.git] / src / common / socket.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdarg.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <setjmp.h>
41 #if HAVE_SYS_SELECT_H
42 #  include <sys/select.h>
43 #endif
44
45 #include "socket.h"
46 #include "utils.h"
47 #include "log.h"
48 #if USE_OPENSSL
49 #  include "ssl.h"
50 #endif
51
52 #if USE_GIO
53 #error USE_GIO is currently not supported
54 #endif
55
56 #define BUFFSIZE        8192
57
58 typedef gint (*SockAddrFunc)    (GList          *addr_list,
59                                  gpointer        data);
60
61 typedef struct _SockConnectData SockConnectData;
62 typedef struct _SockLookupData  SockLookupData;
63 typedef struct _SockAddrData    SockAddrData;
64
65 struct _SockConnectData {
66         gint id;
67         gchar *hostname;
68         gushort port;
69         GList *addr_list;
70         GList *cur_addr;
71         SockLookupData *lookup_data;
72         GIOChannel *channel;
73         guint io_tag;
74         SockConnectFunc func;
75         gpointer data;
76 };
77
78 struct _SockLookupData {
79         gchar *hostname;
80         pid_t child_pid;
81         GIOChannel *channel;
82         guint io_tag;
83         SockAddrFunc func;
84         gpointer data;
85 };
86
87 struct _SockAddrData {
88         gint family;
89         gint socktype;
90         gint protocol;
91         gint addr_len;
92         struct sockaddr *addr;
93 };
94
95 static guint io_timeout = 60;
96
97 static GList *sock_connect_data_list = NULL;
98
99 static gboolean sock_prepare            (gpointer        source_data,
100                                          GTimeVal       *current_time,
101                                          gint           *timeout,
102                                          gpointer        data);
103 static gboolean sock_check              (gpointer        source_data,
104                                          GTimeVal       *current_time,
105                                          gpointer        user_data);
106 static gboolean sock_dispatch           (gpointer        source_data,
107                                          GTimeVal       *current_time,
108                                          gpointer        user_data);
109
110 GSourceFuncs sock_watch_funcs = {
111         sock_prepare,
112         sock_check,
113         sock_dispatch,
114         NULL
115 };
116
117 static gint sock_connect_with_timeout   (gint                    sock,
118                                          const struct sockaddr  *serv_addr,
119                                          gint                    addrlen,
120                                          guint                   timeout_secs);
121
122 #ifndef INET6
123 static gint sock_connect_by_hostname    (gint            sock,
124                                          const gchar    *hostname,
125                                          gushort         port);
126 #else
127 static gint sock_connect_by_getaddrinfo (const gchar    *hostname,
128                                          gushort         port);
129 #endif
130
131 static SockInfo *sockinfo_from_fd(const gchar *hostname,
132                                   gushort port,
133                                   gint sock);
134 static void sock_address_list_free              (GList          *addr_list);
135
136 static gboolean sock_connect_async_cb           (GIOChannel     *source,
137                                                  GIOCondition    condition,
138                                                  gpointer        data);
139 static gint sock_connect_async_get_address_info_cb
140                                                 (GList          *addr_list,
141                                                  gpointer        data);
142
143 static gint sock_connect_address_list_async     (SockConnectData *conn_data);
144
145 static gboolean sock_get_address_info_async_cb  (GIOChannel     *source,
146                                                  GIOCondition    condition,
147                                                  gpointer        data);
148 static SockLookupData *sock_get_address_info_async
149                                                 (const gchar    *hostname,
150                                                  gushort         port,
151                                                  SockAddrFunc    func,
152                                                  gpointer        data);
153 static gint sock_get_address_info_async_cancel  (SockLookupData *lookup_data);
154
155
156 gint sock_set_io_timeout(guint sec)
157 {
158         io_timeout = sec;
159         return 0;
160 }
161
162 gint fd_connect_unix(const gchar *path)
163 {
164         gint sock;
165         struct sockaddr_un addr;
166
167         sock = socket(PF_UNIX, SOCK_STREAM, 0);
168         if (sock < 0) {
169                 perror("sock_connect_unix(): socket");
170                 return -1;
171         }
172
173         memset(&addr, 0, sizeof(addr));
174         addr.sun_family = AF_UNIX;
175         strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
176
177         if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
178                 close(sock);
179                 return -1;
180         }
181
182         return sock;
183 }
184
185 gint fd_open_unix(const gchar *path)
186 {
187         gint sock;
188         struct sockaddr_un addr;
189
190         sock = socket(PF_UNIX, SOCK_STREAM, 0);
191
192         if (sock < 0) {
193                 perror("sock_open_unix(): socket");
194                 return -1;
195         }
196
197         memset(&addr, 0, sizeof(addr));
198         addr.sun_family = AF_UNIX;
199         strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
200
201         if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
202                 perror("bind");
203                 close(sock);
204                 return -1;
205         }
206
207         if (listen(sock, 1) < 0) {
208                 perror("listen");
209                 close(sock);
210                 return -1;              
211         }
212
213         return sock;
214 }
215
216 gint fd_accept(gint sock)
217 {
218         struct sockaddr_in caddr;
219         gint caddr_len;
220
221         caddr_len = sizeof(caddr);
222         return accept(sock, (struct sockaddr *)&caddr, &caddr_len);
223 }
224
225
226 static gint set_nonblocking_mode(gint fd, gboolean nonblock)
227 {
228         gint flags;
229
230         flags = fcntl(fd, F_GETFL, 0);
231         if (flags < 0) {
232                 perror("fcntl");
233                 return -1;
234         }
235
236         if (nonblock)
237                 flags |= O_NONBLOCK;
238         else
239                 flags &= ~O_NONBLOCK;
240
241         return fcntl(fd, F_SETFL, flags);
242 }
243
244 gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock)
245 {
246         g_return_val_if_fail(sock != NULL, -1);
247
248         return set_nonblocking_mode(sock->sock, nonblock);
249 }
250
251 static gboolean is_nonblocking_mode(gint fd)
252 {
253         gint flags;
254
255         flags = fcntl(fd, F_GETFL, 0);
256         if (flags < 0) {
257                 perror("fcntl");
258                 return FALSE;
259         }
260
261         return ((flags & O_NONBLOCK) != 0);
262 }
263
264 gboolean sock_is_nonblocking_mode(SockInfo *sock)
265 {
266         g_return_val_if_fail(sock != NULL, FALSE);
267
268         return is_nonblocking_mode(sock->sock);
269 }
270
271
272 static gboolean sock_prepare(gpointer source_data, GTimeVal *current_time,
273                              gint *timeout, gpointer data)
274 {
275         *timeout = 1;
276         return FALSE;
277 }
278
279 static gboolean sock_check(gpointer source_data, GTimeVal *current_time,
280                            gpointer user_data)
281 {
282         SockInfo *sock = (SockInfo *)source_data;
283         struct timeval timeout = {0, 0};
284         fd_set fds;
285         GIOCondition condition = sock->condition;
286
287 #if USE_OPENSSL
288         if (sock->ssl) {
289                 if (condition & G_IO_IN) {
290                         if (SSL_pending(sock->ssl) > 0)
291                                 return TRUE;
292                         if (SSL_want_write(sock->ssl))
293                                 condition |= G_IO_OUT;
294                 }
295
296                 if (condition & G_IO_OUT) {
297                         if (SSL_want_read(sock->ssl))
298                                 condition |= G_IO_IN;
299                 }
300         }
301 #endif
302
303         FD_ZERO(&fds);
304         FD_SET(sock->sock, &fds);
305
306         select(sock->sock + 1,
307                (condition & G_IO_IN)  ? &fds : NULL,
308                (condition & G_IO_OUT) ? &fds : NULL,
309                NULL, &timeout);
310
311         return FD_ISSET(sock->sock, &fds) != 0;
312 }
313
314 static gboolean sock_dispatch(gpointer source_data, GTimeVal *current_time,
315                               gpointer user_data)
316 {
317         SockInfo *sock = (SockInfo *)source_data;
318
319         return sock->callback(sock, sock->condition, user_data);
320 }
321
322 static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition,
323                               gpointer data)
324 {
325         SockInfo *sock = (SockInfo *)data;
326
327         return sock->callback(sock, condition, sock->data);
328 }
329
330 guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func,
331                      gpointer data)
332 {
333         sock->callback = func;
334         sock->condition = condition;
335         sock->data = data;
336
337 #if USE_OPENSSL
338         if (sock->ssl)
339                 return g_source_add(G_PRIORITY_DEFAULT, FALSE,
340                                     &sock_watch_funcs, sock, data, NULL);
341 #endif
342
343         return g_io_add_watch(sock->sock_ch, condition, sock_watch_cb, sock);
344 }
345
346 static gint fd_check_io(gint fd, GIOCondition cond)
347 {
348         struct timeval timeout;
349         fd_set fds;
350
351         if (is_nonblocking_mode(fd))
352                 return 0;
353
354         timeout.tv_sec  = io_timeout;
355         timeout.tv_usec = 0;
356
357         FD_ZERO(&fds);
358         FD_SET(fd, &fds);
359
360         if (cond == G_IO_IN) {
361                 select(fd + 1, &fds, NULL, NULL,
362                        io_timeout > 0 ? &timeout : NULL);
363         } else {
364                 select(fd + 1, NULL, &fds, NULL,
365                        io_timeout > 0 ? &timeout : NULL);
366         }
367
368         if (FD_ISSET(fd, &fds)) {
369                 return 0;
370         } else {
371                 g_warning("Socket IO timeout\n");
372                 return -1;
373         }
374 }
375
376 static sigjmp_buf jmpenv;
377
378 static void timeout_handler(gint sig)
379 {
380         siglongjmp(jmpenv, 1);
381 }
382
383 static gint sock_connect_with_timeout(gint sock,
384                                       const struct sockaddr *serv_addr,
385                                       gint addrlen,
386                                       guint timeout_secs)
387 {
388         gint ret;
389         void (*prev_handler)(gint);
390
391         alarm(0);
392         prev_handler = signal(SIGALRM, timeout_handler);
393         if (sigsetjmp(jmpenv, 1)) {
394                 alarm(0);
395                 signal(SIGALRM, prev_handler);
396                 errno = ETIMEDOUT;
397                 return -1;
398         }
399         alarm(timeout_secs);
400
401         ret = connect(sock, serv_addr, addrlen);
402
403         alarm(0);
404         signal(SIGALRM, prev_handler);
405
406         return ret;
407 }
408
409 struct hostent *my_gethostbyname(const gchar *hostname)
410 {
411         struct hostent *hp;
412         void (*prev_handler)(gint);
413
414         alarm(0);
415         prev_handler = signal(SIGALRM, timeout_handler);
416         if (sigsetjmp(jmpenv, 1)) {
417                 alarm(0);
418                 signal(SIGALRM, prev_handler);
419                 fprintf(stderr, "%s: host lookup timed out.\n", hostname);
420                 errno = 0;
421                 return NULL;
422         }
423         alarm(io_timeout);
424
425         if ((hp = gethostbyname(hostname)) == NULL) {
426                 alarm(0);
427                 signal(SIGALRM, prev_handler);
428                 fprintf(stderr, "%s: unknown host.\n", hostname);
429                 errno = 0;
430                 return NULL;
431         }
432
433         alarm(0);
434         signal(SIGALRM, prev_handler);
435
436         return hp;
437 }
438
439 #ifndef INET6
440 static gint my_inet_aton(const gchar *hostname, struct in_addr *inp)
441 {
442 #if HAVE_INET_ATON
443         return inet_aton(hostname, inp);
444 #else
445 #if HAVE_INET_ADDR
446         guint32 inaddr;
447
448         inaddr = inet_addr(hostname);
449         if (inaddr != -1) {
450                 memcpy(inp, &inaddr, sizeof(inaddr));
451                 return 1;
452         } else
453                 return 0;
454 #else
455         return 0;
456 #endif
457 #endif /* HAVE_INET_ATON */
458 }
459
460 static gint sock_connect_by_hostname(gint sock, const gchar *hostname,
461                                      gushort port)
462 {
463         struct hostent *hp;
464         struct sockaddr_in ad;
465
466         memset(&ad, 0, sizeof(ad));
467         ad.sin_family = AF_INET;
468         ad.sin_port = htons(port);
469
470         if (!my_inet_aton(hostname, &ad.sin_addr)) {
471                 if ((hp = my_gethostbyname(hostname)) == NULL) {
472                         fprintf(stderr, "%s: unknown host.\n", hostname);
473                         errno = 0;
474                         return -1;
475                 }
476
477                 if (hp->h_length != 4 && hp->h_length != 8) {
478                         fprintf(stderr, "illegal address length received for host %s\n", hostname);
479                         errno = 0;
480                         return -1;
481                 }
482
483                 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
484         }
485
486         return sock_connect_with_timeout(sock, (struct sockaddr *)&ad,
487                                          sizeof(ad), io_timeout);
488 }
489
490 #else /* INET6 */
491 static gint sock_connect_by_getaddrinfo(const gchar *hostname, gushort  port)
492 {
493         gint sock = -1, gai_error;
494         struct addrinfo hints, *res, *ai;
495         gchar port_str[6];
496
497         memset(&hints, 0, sizeof(hints));
498         /* hints.ai_flags = AI_CANONNAME; */
499         hints.ai_family = AF_UNSPEC;
500         hints.ai_socktype = SOCK_STREAM;
501         hints.ai_protocol = IPPROTO_TCP;
502
503         /* convert port from integer to string. */
504         g_snprintf(port_str, sizeof(port_str), "%d", port);
505
506         if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) {
507                 fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n",
508                         hostname, port_str, gai_strerror(gai_error));
509                 return -1;
510         }
511
512         for (ai = res; ai != NULL; ai = ai->ai_next) {
513                 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
514                 if (sock < 0)
515                         continue;
516
517                 if (sock_connect_with_timeout
518                         (sock, ai->ai_addr, ai->ai_addrlen, io_timeout) == 0)
519                         break;
520
521                 close(sock);
522         }
523
524         if (res != NULL)
525                 freeaddrinfo(res);
526
527         if (ai == NULL)
528                 return -1;
529
530         return sock;
531 }
532 #endif /* !INET6 */
533
534
535 /* Open a connection using an external program.  May be useful when
536  * you need to tunnel through a SOCKS or other firewall, or to
537  * establish an IMAP-over-SSH connection. */
538 /* TODO: Recreate this for sock_connect_thread() */
539 SockInfo *sock_connect_cmd(const gchar *hostname, const gchar *tunnelcmd)
540 {
541         gint fd[2];
542         int r;
543                      
544         if ((r = socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) == -1) {
545                 perror("socketpair");
546                 return NULL;
547         }
548         log_message("launching tunnel command \"%s\"\n", tunnelcmd);
549         if (fork() == 0) {
550                 close(fd[0]);
551                 close(0);
552                 close(1);
553                 dup(fd[1]);     /* set onto stdin */
554                 dup(fd[1]);
555                 execlp("/bin/sh", "/bin/sh", "-c", tunnelcmd, NULL);
556         }
557
558         close(fd[1]);
559         return sockinfo_from_fd(hostname, 0, fd[0]);
560 }
561
562
563 SockInfo *sock_connect(const gchar *hostname, gushort port)
564 {
565         gint sock;
566
567 #ifdef INET6
568         if ((sock = sock_connect_by_getaddrinfo(hostname, port)) < 0)
569                 return NULL;
570 #else
571         if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
572                 perror("socket");
573                 return NULL;
574         }
575
576         if (sock_connect_by_hostname(sock, hostname, port) < 0) {
577                 if (errno != 0) perror("connect");
578                 close(sock);
579                 return NULL;
580         }
581 #endif /* INET6 */
582
583         return sockinfo_from_fd(hostname, port, sock);
584 }
585
586 static void sock_address_list_free(GList *addr_list)
587 {
588         GList *cur;
589
590         for (cur = addr_list; cur != NULL; cur = cur->next) {
591                 SockAddrData *addr_data = (SockAddrData *)cur->data;
592                 g_free(addr_data->addr);
593                 g_free(addr_data);
594         }
595
596         g_list_free(addr_list);
597 }
598
599 /* asynchronous TCP connection */
600
601 static gboolean sock_connect_async_cb(GIOChannel *source,
602                                       GIOCondition condition, gpointer data)
603 {
604         SockConnectData *conn_data = (SockConnectData *)data;
605         gint fd;
606         gint val;
607         gint len;
608         SockInfo *sockinfo;
609
610         fd = g_io_channel_unix_get_fd(source);
611
612         conn_data->io_tag = 0;
613         conn_data->channel = NULL;
614         g_io_channel_unref(source);
615
616         len = sizeof(val);
617         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) < 0) {
618                 perror("getsockopt");
619                 close(fd);
620                 sock_connect_address_list_async(conn_data);
621                 return FALSE;
622         }
623
624         if (val != 0) {
625                 close(fd);
626                 sock_connect_address_list_async(conn_data);
627                 return FALSE;
628         }
629
630         sockinfo = g_new0(SockInfo, 1);
631         sockinfo->sock = fd;
632         sockinfo->sock_ch = g_io_channel_unix_new(fd);
633         sockinfo->hostname = g_strdup(conn_data->hostname);
634         sockinfo->port = conn_data->port;
635         sockinfo->state = CONN_ESTABLISHED;
636
637         conn_data->func(sockinfo, conn_data->data);
638
639         sock_connect_async_cancel(conn_data->id);
640
641         return FALSE;
642 }
643
644 static gint sock_connect_async_get_address_info_cb(GList *addr_list,
645                                                    gpointer data)
646 {
647         SockConnectData *conn_data = (SockConnectData *)data;
648
649         conn_data->addr_list = addr_list;
650         conn_data->cur_addr = addr_list;
651         conn_data->lookup_data = NULL;
652
653         return sock_connect_address_list_async(conn_data);
654 }
655
656 gint sock_connect_async(const gchar *hostname, gushort port,
657                         SockConnectFunc func, gpointer data)
658 {
659         static gint id = 1;
660         SockConnectData *conn_data;
661
662         conn_data = g_new0(SockConnectData, 1);
663         conn_data->id = id++;
664         conn_data->hostname = g_strdup(hostname);
665         conn_data->port = port;
666         conn_data->addr_list = NULL;
667         conn_data->cur_addr = NULL;
668         conn_data->io_tag = 0;
669         conn_data->func = func;
670         conn_data->data = data;
671
672         conn_data->lookup_data = sock_get_address_info_async
673                 (hostname, port, sock_connect_async_get_address_info_cb,
674                  conn_data);
675
676         if (conn_data->lookup_data == NULL) {
677                 g_free(conn_data->hostname);
678                 g_free(conn_data);
679                 return -1;
680         }
681
682         sock_connect_data_list = g_list_append(sock_connect_data_list,
683                                                conn_data);
684
685         return conn_data->id;
686 }
687
688 gint sock_connect_async_cancel(gint id)
689 {
690         SockConnectData *conn_data = NULL;
691         GList *cur;
692
693         for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) {
694                 if (((SockConnectData *)cur->data)->id == id) {
695                         conn_data = (SockConnectData *)cur->data;
696                         break;
697                 }
698         }
699
700         if (conn_data) {
701                 sock_connect_data_list = g_list_remove(sock_connect_data_list,
702                                                        conn_data);
703
704                 if (conn_data->lookup_data)
705                         sock_get_address_info_async_cancel
706                                 (conn_data->lookup_data);
707
708                 if (conn_data->io_tag > 0)
709                         g_source_remove(conn_data->io_tag);
710                 if (conn_data->channel) {
711                         g_io_channel_close(conn_data->channel);
712                         g_io_channel_unref(conn_data->channel);
713                 }
714
715                 sock_address_list_free(conn_data->addr_list);
716                 g_free(conn_data->hostname);
717                 g_free(conn_data);
718         } else {
719                 g_warning("sock_connect_async_cancel: id %d not found.\n", id);
720                 return -1;
721         }
722
723         return 0;
724 }
725
726 static gint sock_connect_address_list_async(SockConnectData *conn_data)
727 {
728         SockAddrData *addr_data;
729         gint sock = -1;
730
731         for (; conn_data->cur_addr != NULL;
732              conn_data->cur_addr = conn_data->cur_addr->next) {
733                 addr_data = (SockAddrData *)conn_data->cur_addr->data;
734
735                 if ((sock = socket(addr_data->family, addr_data->socktype,
736                                    addr_data->protocol)) < 0) {
737                         perror("socket");
738                         continue;
739                 }
740
741                 set_nonblocking_mode(sock, TRUE);
742
743                 if (connect(sock, addr_data->addr, addr_data->addr_len) < 0) {
744                         if (EINPROGRESS == errno) {
745                                 break;
746                         } else {
747                                 perror("connect");
748                                 close(sock);
749                         }
750                 } else
751                         break;
752         }
753
754         if (conn_data->cur_addr == NULL) {
755                 g_warning("sock_connect_address_list_async: "
756                           "connection to %s:%d failed\n",
757                           conn_data->hostname, conn_data->port);
758                 conn_data->func(NULL, conn_data->data);
759                 sock_connect_async_cancel(conn_data->id);
760                 return -1;
761         }
762
763         conn_data->cur_addr = conn_data->cur_addr->next;
764
765         conn_data->channel = g_io_channel_unix_new(sock);
766         conn_data->io_tag = g_io_add_watch(conn_data->channel, G_IO_IN|G_IO_OUT,
767                                            sock_connect_async_cb, conn_data);
768
769         return 0;
770 }
771
772 /* asynchronous DNS lookup */
773
774 static gboolean sock_get_address_info_async_cb(GIOChannel *source,
775                                                GIOCondition condition,
776                                                gpointer data)
777 {
778         SockLookupData *lookup_data = (SockLookupData *)data;
779         GList *addr_list = NULL;
780         SockAddrData *addr_data;
781         guint bytes_read;
782         gint ai_member[4];
783         struct sockaddr *addr;
784
785         for (;;) {
786                 if (g_io_channel_read(source, (gchar *)ai_member,
787                                       sizeof(ai_member), &bytes_read)
788                     != G_IO_ERROR_NONE) {
789                         g_warning("sock_get_address_info_async_cb: "
790                                   "address length read error\n");
791                         break;
792                 }
793
794                 if (bytes_read == 0 || bytes_read != sizeof(ai_member))
795                         break;
796
797                 if (ai_member[0] == AF_UNSPEC) {
798                         g_warning("DNS lookup failed\n");
799                         break;
800                 }
801
802                 addr = g_malloc(ai_member[3]);
803                 if (g_io_channel_read(source, (gchar *)addr, ai_member[3],
804                                       &bytes_read)
805                     != G_IO_ERROR_NONE) {
806                         g_warning("sock_get_address_info_async_cb: "
807                                   "address data read error\n");
808                         g_free(addr);
809                         break;
810                 }
811
812                 if (bytes_read != ai_member[3]) {
813                         g_warning("sock_get_address_info_async_cb: "
814                                   "incomplete address data\n");
815                         g_free(addr);
816                         break;
817                 }
818
819                 addr_data = g_new0(SockAddrData, 1);
820                 addr_data->family = ai_member[0];
821                 addr_data->socktype = ai_member[1];
822                 addr_data->protocol = ai_member[2];
823                 addr_data->addr_len = ai_member[3];
824                 addr_data->addr = addr;
825
826                 addr_list = g_list_append(addr_list, addr_data);
827         }
828
829         g_io_channel_close(source);
830         g_io_channel_unref(source);
831
832         kill(lookup_data->child_pid, SIGKILL);
833         waitpid(lookup_data->child_pid, NULL, 0);
834
835         lookup_data->func(addr_list, lookup_data->data);
836
837         g_free(lookup_data->hostname);
838         g_free(lookup_data);
839
840         return FALSE;
841 }
842
843 static SockLookupData *sock_get_address_info_async(const gchar *hostname,
844                                                    gushort port,
845                                                    SockAddrFunc func,
846                                                    gpointer data)
847 {
848         SockLookupData *lookup_data = NULL;
849         gint pipe_fds[2];
850         pid_t pid;
851
852         if (pipe(pipe_fds) < 0) {
853                 perror("pipe");
854                 func(NULL, data);
855                 return NULL;
856         }
857
858         if ((pid = fork()) < 0) {
859                 perror("fork");
860                 func(NULL, data);
861                 return NULL;
862         }
863
864         /* child process */
865         if (pid == 0) {
866 #ifdef INET6
867                 gint gai_err;
868                 struct addrinfo hints, *res, *ai;
869                 gchar port_str[6];
870 #else /* !INET6 */
871                 struct hostent *hp;
872                 gchar **addr_list_p;
873                 struct sockaddr_in ad;
874 #endif /* INET6 */
875                 gint ai_member[4] = {AF_UNSPEC, 0, 0, 0};
876
877                 close(pipe_fds[0]);
878
879 #ifdef INET6
880                 memset(&hints, 0, sizeof(hints));
881                 /* hints.ai_flags = AI_CANONNAME; */
882                 hints.ai_family = AF_UNSPEC;
883                 hints.ai_socktype = SOCK_STREAM;
884                 hints.ai_protocol = IPPROTO_TCP;
885
886                 g_snprintf(port_str, sizeof(port_str), "%d", port);
887
888                 gai_err = getaddrinfo(hostname, port_str, &hints, &res);
889                 if (gai_err != 0) {
890                         g_warning("getaddrinfo for %s:%s failed: %s\n",
891                                   hostname, port_str, gai_strerror(gai_err));
892                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
893                                      sizeof(ai_member));
894                         close(pipe_fds[1]);
895                         _exit(1);
896                 }
897
898                 for (ai = res; ai != NULL; ai = ai->ai_next) {
899                         ai_member[0] = ai->ai_family;
900                         ai_member[1] = ai->ai_socktype;
901                         ai_member[2] = ai->ai_protocol;
902                         ai_member[3] = ai->ai_addrlen;
903
904                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
905                                      sizeof(ai_member));
906                         fd_write_all(pipe_fds[1], (gchar *)ai->ai_addr,
907                                      ai->ai_addrlen);
908                 }
909
910                 if (res != NULL)
911                         freeaddrinfo(res);
912 #else /* !INET6 */
913                 hp = my_gethostbyname(hostname);
914                 if (hp == NULL || hp->h_addrtype != AF_INET) {
915                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
916                                      sizeof(ai_member));
917                         close(pipe_fds[1]);
918                         _exit(1);
919                 }
920
921                 ai_member[0] = AF_INET;
922                 ai_member[1] = SOCK_STREAM;
923                 ai_member[2] = IPPROTO_TCP;
924                 ai_member[3] = sizeof(ad);
925
926                 memset(&ad, 0, sizeof(ad));
927                 ad.sin_family = AF_INET;
928                 ad.sin_port = htons(port);
929
930                 for (addr_list_p = hp->h_addr_list; *addr_list_p != NULL;
931                      addr_list_p++) {
932                         memcpy(&ad.sin_addr, *addr_list_p, hp->h_length);
933                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
934                                      sizeof(ai_member));
935                         fd_write_all(pipe_fds[1], (gchar *)&ad, sizeof(ad));
936                 }
937 #endif /* INET6 */
938
939                 close(pipe_fds[1]);
940
941                 _exit(0);
942         } else {
943                 close(pipe_fds[1]);
944
945                 lookup_data = g_new0(SockLookupData, 1);
946                 lookup_data->hostname = g_strdup(hostname);
947                 lookup_data->child_pid = pid;
948                 lookup_data->func = func;
949                 lookup_data->data = data;
950
951                 lookup_data->channel = g_io_channel_unix_new(pipe_fds[0]);
952                 lookup_data->io_tag = g_io_add_watch
953                         (lookup_data->channel, G_IO_IN,
954                          sock_get_address_info_async_cb, lookup_data);
955         }
956
957         return lookup_data;
958 }
959
960 static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data)
961 {
962         if (lookup_data->io_tag > 0)
963                 g_source_remove(lookup_data->io_tag);
964         if (lookup_data->channel) {
965                 g_io_channel_close(lookup_data->channel);
966                 g_io_channel_unref(lookup_data->channel);
967         }
968
969         if (lookup_data->child_pid > 0) {
970                 kill(lookup_data->child_pid, SIGKILL);
971                 waitpid(lookup_data->child_pid, NULL, 0);
972         }
973
974         g_free(lookup_data->hostname);
975         g_free(lookup_data);
976
977         return 0;
978 }
979
980
981 static SockInfo *sockinfo_from_fd(const gchar *hostname,
982                                   gushort port,
983                                   gint sock)
984 {
985         SockInfo *sockinfo;
986
987         sockinfo = g_new0(SockInfo, 1);
988         sockinfo->sock = sock;
989         sockinfo->sock_ch = g_io_channel_unix_new(sock);
990         sockinfo->hostname = g_strdup(hostname);
991         sockinfo->port = port;
992         sockinfo->state = CONN_ESTABLISHED;
993
994         usleep(100000);
995
996         return sockinfo;
997 }
998
999 gint sock_printf(SockInfo *sock, const gchar *format, ...)
1000 {
1001         va_list args;
1002         gchar buf[BUFFSIZE];
1003
1004         va_start(args, format);
1005         g_vsnprintf(buf, sizeof(buf), format, args);
1006         va_end(args);
1007
1008         return sock_write_all(sock, buf, strlen(buf));
1009 }
1010
1011 gint fd_read(gint fd, gchar *buf, gint len)
1012 {
1013         if (fd_check_io(fd, G_IO_IN) < 0)
1014                 return -1;
1015
1016         return read(fd, buf, len);
1017 }
1018
1019 #if USE_OPENSSL
1020 gint ssl_read(SSL *ssl, gchar *buf, gint len)
1021 {
1022         gint ret;
1023
1024         ret = SSL_read(ssl, buf, len);
1025
1026         switch (SSL_get_error(ssl, ret)) {
1027         case SSL_ERROR_NONE:
1028                 return ret;
1029         case SSL_ERROR_WANT_READ:
1030         case SSL_ERROR_WANT_WRITE:
1031                 errno = EAGAIN;
1032                 return -1;
1033         default:
1034                 return -1;
1035         }
1036 }
1037 #endif
1038
1039 gint sock_read(SockInfo *sock, gchar *buf, gint len)
1040 {
1041         gint ret;
1042
1043         g_return_val_if_fail(sock != NULL, -1);
1044
1045 #if USE_OPENSSL
1046         if (sock->ssl)
1047                 ret = ssl_read(sock->ssl, buf, len);
1048         else
1049 #endif
1050                 ret = fd_read(sock->sock, buf, len);
1051         
1052         if (ret < 0)
1053                 sock->state = CONN_DISCONNECTED;
1054         return ret;
1055 }
1056
1057 gint fd_write(gint fd, const gchar *buf, gint len)
1058 {
1059         if (fd_check_io(fd, G_IO_OUT) < 0)
1060                 return -1;
1061
1062         return write(fd, buf, len);
1063 }
1064
1065 #if USE_OPENSSL
1066 gint ssl_write(SSL *ssl, const gchar *buf, gint len)
1067 {
1068         gint ret;
1069
1070         ret = SSL_write(ssl, buf, len);
1071
1072         switch (SSL_get_error(ssl, ret)) {
1073         case SSL_ERROR_NONE:
1074                 return ret;
1075         case SSL_ERROR_WANT_READ:
1076         case SSL_ERROR_WANT_WRITE:
1077                 errno = EAGAIN;
1078                 return -1;
1079         default:
1080                 return -1;
1081         }
1082 }
1083 #endif
1084
1085 gint sock_write(SockInfo *sock, const gchar *buf, gint len)
1086 {
1087         gint ret;
1088
1089         g_return_val_if_fail(sock != NULL, -1);
1090
1091 #if USE_OPENSSL
1092         if (sock->ssl)
1093                 ret = ssl_write(sock->ssl, buf, len);
1094         else
1095 #endif
1096                 ret = fd_write(sock->sock, buf, len);
1097
1098         if (ret < 0)
1099                 sock->state = CONN_DISCONNECTED;
1100         return ret;
1101 }
1102
1103 gint fd_write_all(gint fd, const gchar *buf, gint len)
1104 {
1105         gint n, wrlen = 0;
1106
1107         while (len) {
1108                 if (fd_check_io(fd, G_IO_OUT) < 0)
1109                         return -1;
1110                 signal(SIGPIPE, SIG_IGN);
1111                 n = write(fd, buf, len);
1112                 if (n <= 0) {
1113                         log_error("write on fd%d: %s\n", fd, strerror(errno));
1114                         return -1;
1115                 }
1116                 len -= n;
1117                 wrlen += n;
1118                 buf += n;
1119         }
1120
1121         return wrlen;
1122 }
1123
1124 #if USE_OPENSSL
1125 gint ssl_write_all(SSL *ssl, const gchar *buf, gint len)
1126 {
1127         gint n, wrlen = 0;
1128
1129         while (len) {
1130                 n = ssl_write(ssl, buf, len);
1131                 if (n <= 0)
1132                         return -1;
1133                 len -= n;
1134                 wrlen += n;
1135                 buf += n;
1136         }
1137
1138         return wrlen;
1139 }
1140 #endif
1141
1142 gint sock_write_all(SockInfo *sock, const gchar *buf, gint len)
1143 {
1144         gint ret;
1145
1146         g_return_val_if_fail(sock != NULL, -1);
1147
1148 #if USE_OPENSSL
1149         if (sock->ssl)
1150                 ret = ssl_write_all(sock->ssl, buf, len);
1151         else
1152 #endif
1153                 ret = fd_write_all(sock->sock, buf, len);
1154
1155         if (ret < 0)
1156                 sock->state = CONN_DISCONNECTED;
1157         return ret;
1158 }
1159
1160 gint fd_recv(gint fd, gchar *buf, gint len, gint flags)
1161 {
1162         if (fd_check_io(fd, G_IO_IN) < 0)
1163                 return -1;
1164
1165         return recv(fd, buf, len, flags);
1166 }
1167
1168 gint fd_gets(gint fd, gchar *buf, gint len)
1169 {
1170         gchar *newline, *bp = buf;
1171         gint n;
1172
1173         if (--len < 1)
1174                 return -1;
1175         do {
1176                 if ((n = fd_recv(fd, bp, len, MSG_PEEK)) <= 0)
1177                         return -1;
1178                 if ((newline = memchr(bp, '\n', n)) != NULL)
1179                         n = newline - bp + 1;
1180                 if ((n = fd_read(fd, bp, n)) < 0)
1181                         return -1;
1182                 bp += n;
1183                 len -= n;
1184         } while (!newline && len);
1185
1186         *bp = '\0';
1187         return bp - buf;
1188 }
1189
1190 #if USE_OPENSSL
1191 gint ssl_gets(SSL *ssl, gchar *buf, gint len)
1192 {
1193         gchar *newline, *bp = buf;
1194         gint n;
1195
1196         if (--len < 1)
1197                 return -1;
1198         do {
1199                 if ((n = ssl_peek(ssl, bp, len)) <= 0)
1200                         return -1;
1201                 if ((newline = memchr(bp, '\n', n)) != NULL)
1202                         n = newline - bp + 1;
1203                 if ((n = ssl_read(ssl, bp, n)) < 0)
1204                         return -1;
1205                 bp += n;
1206                 len -= n;
1207         } while (!newline && len);
1208
1209         *bp = '\0';
1210         return bp - buf;
1211 }
1212 #endif
1213
1214 gint sock_gets(SockInfo *sock, gchar *buf, gint len)
1215 {
1216         gint ret;
1217
1218         g_return_val_if_fail(sock != NULL, -1);
1219
1220 #if USE_OPENSSL
1221         if (sock->ssl)
1222                 return ssl_gets(sock->ssl, buf, len);
1223         else
1224 #endif
1225                 return fd_gets(sock->sock, buf, len);
1226
1227         if (ret < 0)
1228                 sock->state = CONN_DISCONNECTED;
1229         return ret;
1230 }
1231
1232 gint fd_getline(gint fd, gchar **str)
1233 {
1234         gchar buf[BUFFSIZE];
1235         gint len;
1236         gulong size = 1;
1237
1238         while ((len = fd_gets(fd, buf, sizeof(buf))) > 0) {
1239                 size += len;
1240                 if (!*str)
1241                         *str = g_strdup(buf);
1242                 else {
1243                         *str = g_realloc(*str, size);
1244                         strcat(*str, buf);
1245                 }
1246                 if (buf[len - 1] == '\n')
1247                         break;
1248         }
1249         if (len == -1 && *str)
1250                 g_free(*str);
1251
1252         return len;
1253 }
1254
1255 #if USE_OPENSSL
1256 gint ssl_getline(SSL *ssl, gchar **str)
1257 {
1258         gchar buf[BUFFSIZE];
1259         gint len;
1260         gulong size = 1;
1261
1262         while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) {
1263                 size += len;
1264                 if (!*str)
1265                         *str = g_strdup(buf);
1266                 else {
1267                         *str = g_realloc(*str, size);
1268                         strcat(*str, buf);
1269                 }
1270                 if (buf[len - 1] == '\n')
1271                         break;
1272         }
1273         if (len == -1 && *str)
1274                 g_free(*str);
1275
1276         return len;
1277 }
1278 #endif
1279
1280 gchar *sock_getline(SockInfo *sock)
1281 {
1282         gint ret;
1283         gchar *str = NULL;
1284
1285         g_return_val_if_fail(sock != NULL, NULL);
1286
1287 #if USE_OPENSSL
1288         if (sock->ssl)
1289                 ret = ssl_getline(sock->ssl, &str);
1290         else
1291 #endif
1292                 ret = fd_getline(sock->sock, &str);
1293
1294         if (ret < 0)
1295                 sock->state = CONN_DISCONNECTED;
1296         return str;
1297 }
1298
1299 gint sock_puts(SockInfo *sock, const gchar *buf)
1300 {
1301         gint ret;
1302
1303         if ((ret = sock_write_all(sock, buf, strlen(buf))) < 0)
1304                 return ret;
1305         return sock_write_all(sock, "\r\n", 2);
1306 }
1307
1308 /* peek at the socket data without actually reading it */
1309 #if USE_OPENSSL
1310 gint ssl_peek(SSL *ssl, gchar *buf, gint len)
1311 {
1312         gint ret;
1313
1314         ret = SSL_peek(ssl, buf, len);
1315
1316         switch (SSL_get_error(ssl, ret)) {
1317         case SSL_ERROR_NONE:
1318                 return ret;
1319         case SSL_ERROR_WANT_READ:
1320         case SSL_ERROR_WANT_WRITE:
1321                 errno = EAGAIN;
1322                 return -1;
1323         default:
1324                 return -1;
1325         }
1326 }
1327 #endif
1328
1329 gint sock_peek(SockInfo *sock, gchar *buf, gint len)
1330 {
1331         g_return_val_if_fail(sock != NULL, -1);
1332
1333 #if USE_OPENSSL
1334         if (sock->ssl)
1335                 return ssl_peek(sock->ssl, buf, len);
1336 #endif
1337         return fd_recv(sock->sock, buf, len, MSG_PEEK);
1338 }
1339
1340 gint sock_close(SockInfo *sock)
1341 {
1342         gint ret;
1343
1344         if (!sock)
1345                 return 0;
1346
1347         if (sock->sock_ch)
1348                 g_io_channel_unref(sock->sock_ch);
1349
1350 #if USE_OPENSSL
1351         if (sock->ssl)
1352                 ssl_done_socket(sock);
1353 #endif
1354         ret = fd_close(sock->sock); 
1355         g_free(sock->hostname);
1356         g_free(sock);
1357
1358         return ret;
1359 }
1360
1361 gint fd_close(gint fd)
1362 {
1363         return close(fd);
1364 }