2004-08-10 [leroyc] 0.9.12cvs57
[claws.git] / src / common / socket.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 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         if (conn_data->io_tag == 0 && conn_data->channel == NULL)
611                 return FALSE;
612         
613         fd = g_io_channel_unix_get_fd(source);
614
615         conn_data->io_tag = 0;
616         conn_data->channel = NULL;
617         g_io_channel_unref(source);
618
619         len = sizeof(val);
620         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) < 0) {
621                 perror("getsockopt");
622                 close(fd);
623                 sock_connect_address_list_async(conn_data);
624                 return FALSE;
625         }
626
627         if (val != 0) {
628                 close(fd);
629                 sock_connect_address_list_async(conn_data);
630                 return FALSE;
631         }
632
633         sockinfo = g_new0(SockInfo, 1);
634         sockinfo->sock = fd;
635         sockinfo->sock_ch = g_io_channel_unix_new(fd);
636         sockinfo->hostname = g_strdup(conn_data->hostname);
637         sockinfo->port = conn_data->port;
638         sockinfo->state = CONN_ESTABLISHED;
639
640         conn_data->func(sockinfo, conn_data->data);
641
642         sock_connect_async_cancel(conn_data->id);
643
644         return FALSE;
645 }
646
647 static gint sock_connect_async_get_address_info_cb(GList *addr_list,
648                                                    gpointer data)
649 {
650         SockConnectData *conn_data = (SockConnectData *)data;
651
652         conn_data->addr_list = addr_list;
653         conn_data->cur_addr = addr_list;
654         conn_data->lookup_data = NULL;
655
656         return sock_connect_address_list_async(conn_data);
657 }
658
659 gint sock_connect_async(const gchar *hostname, gushort port,
660                         SockConnectFunc func, gpointer data)
661 {
662         static gint id = 1;
663         SockConnectData *conn_data;
664
665         conn_data = g_new0(SockConnectData, 1);
666         conn_data->id = id++;
667         conn_data->hostname = g_strdup(hostname);
668         conn_data->port = port;
669         conn_data->addr_list = NULL;
670         conn_data->cur_addr = NULL;
671         conn_data->io_tag = 0;
672         conn_data->func = func;
673         conn_data->data = data;
674
675         conn_data->lookup_data = sock_get_address_info_async
676                 (hostname, port, sock_connect_async_get_address_info_cb,
677                  conn_data);
678
679         if (conn_data->lookup_data == NULL) {
680                 g_free(conn_data->hostname);
681                 g_free(conn_data);
682                 return -1;
683         }
684
685         sock_connect_data_list = g_list_append(sock_connect_data_list,
686                                                conn_data);
687
688         return conn_data->id;
689 }
690
691 gint sock_connect_async_cancel(gint id)
692 {
693         SockConnectData *conn_data = NULL;
694         GList *cur;
695
696         for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) {
697                 if (((SockConnectData *)cur->data)->id == id) {
698                         conn_data = (SockConnectData *)cur->data;
699                         break;
700                 }
701         }
702
703         if (conn_data) {
704                 sock_connect_data_list = g_list_remove(sock_connect_data_list,
705                                                        conn_data);
706
707                 if (conn_data->lookup_data)
708                         sock_get_address_info_async_cancel
709                                 (conn_data->lookup_data);
710
711                 if (conn_data->io_tag > 0)
712                         g_source_remove(conn_data->io_tag);
713                 if (conn_data->channel) {
714                         g_io_channel_close(conn_data->channel);
715                         g_io_channel_unref(conn_data->channel);
716                 }
717
718                 sock_address_list_free(conn_data->addr_list);
719                 g_free(conn_data->hostname);
720                 g_free(conn_data);
721         } else {
722                 g_warning("sock_connect_async_cancel: id %d not found.\n", id);
723                 return -1;
724         }
725
726         return 0;
727 }
728
729 static gint sock_connect_address_list_async(SockConnectData *conn_data)
730 {
731         SockAddrData *addr_data;
732         gint sock = -1;
733
734         for (; conn_data->cur_addr != NULL;
735              conn_data->cur_addr = conn_data->cur_addr->next) {
736                 addr_data = (SockAddrData *)conn_data->cur_addr->data;
737
738                 if ((sock = socket(addr_data->family, addr_data->socktype,
739                                    addr_data->protocol)) < 0) {
740                         perror("socket");
741                         continue;
742                 }
743
744                 set_nonblocking_mode(sock, TRUE);
745
746                 if (connect(sock, addr_data->addr, addr_data->addr_len) < 0) {
747                         if (EINPROGRESS == errno) {
748                                 break;
749                         } else {
750                                 perror("connect");
751                                 close(sock);
752                         }
753                 } else
754                         break;
755         }
756
757         if (conn_data->cur_addr == NULL) {
758                 g_warning("sock_connect_address_list_async: "
759                           "connection to %s:%d failed\n",
760                           conn_data->hostname, conn_data->port);
761                 conn_data->func(NULL, conn_data->data);
762                 sock_connect_async_cancel(conn_data->id);
763                 return -1;
764         }
765
766         conn_data->cur_addr = conn_data->cur_addr->next;
767
768         conn_data->channel = g_io_channel_unix_new(sock);
769         conn_data->io_tag = g_io_add_watch(conn_data->channel, G_IO_IN|G_IO_OUT,
770                                            sock_connect_async_cb, conn_data);
771
772         return 0;
773 }
774
775 /* asynchronous DNS lookup */
776
777 static gboolean sock_get_address_info_async_cb(GIOChannel *source,
778                                                GIOCondition condition,
779                                                gpointer data)
780 {
781         SockLookupData *lookup_data = (SockLookupData *)data;
782         GList *addr_list = NULL;
783         SockAddrData *addr_data;
784         guint bytes_read;
785         gint ai_member[4];
786         struct sockaddr *addr;
787
788         for (;;) {
789                 if (g_io_channel_read(source, (gchar *)ai_member,
790                                       sizeof(ai_member), &bytes_read)
791                     != G_IO_ERROR_NONE) {
792                         g_warning("sock_get_address_info_async_cb: "
793                                   "address length read error\n");
794                         break;
795                 }
796
797                 if (bytes_read == 0 || bytes_read != sizeof(ai_member))
798                         break;
799
800                 if (ai_member[0] == AF_UNSPEC) {
801                         g_warning("DNS lookup failed\n");
802                         break;
803                 }
804
805                 addr = g_malloc(ai_member[3]);
806                 if (g_io_channel_read(source, (gchar *)addr, ai_member[3],
807                                       &bytes_read)
808                     != G_IO_ERROR_NONE) {
809                         g_warning("sock_get_address_info_async_cb: "
810                                   "address data read error\n");
811                         g_free(addr);
812                         break;
813                 }
814
815                 if (bytes_read != ai_member[3]) {
816                         g_warning("sock_get_address_info_async_cb: "
817                                   "incomplete address data\n");
818                         g_free(addr);
819                         break;
820                 }
821
822                 addr_data = g_new0(SockAddrData, 1);
823                 addr_data->family = ai_member[0];
824                 addr_data->socktype = ai_member[1];
825                 addr_data->protocol = ai_member[2];
826                 addr_data->addr_len = ai_member[3];
827                 addr_data->addr = addr;
828
829                 addr_list = g_list_append(addr_list, addr_data);
830         }
831
832         g_io_channel_close(source);
833         g_io_channel_unref(source);
834
835         kill(lookup_data->child_pid, SIGKILL);
836         waitpid(lookup_data->child_pid, NULL, 0);
837
838         lookup_data->func(addr_list, lookup_data->data);
839
840         g_free(lookup_data->hostname);
841         g_free(lookup_data);
842
843         return FALSE;
844 }
845
846 static SockLookupData *sock_get_address_info_async(const gchar *hostname,
847                                                    gushort port,
848                                                    SockAddrFunc func,
849                                                    gpointer data)
850 {
851         SockLookupData *lookup_data = NULL;
852         gint pipe_fds[2];
853         pid_t pid;
854
855         if (pipe(pipe_fds) < 0) {
856                 perror("pipe");
857                 func(NULL, data);
858                 return NULL;
859         }
860
861         if ((pid = fork()) < 0) {
862                 perror("fork");
863                 func(NULL, data);
864                 return NULL;
865         }
866
867         /* child process */
868         if (pid == 0) {
869 #ifdef INET6
870                 gint gai_err;
871                 struct addrinfo hints, *res, *ai;
872                 gchar port_str[6];
873 #else /* !INET6 */
874                 struct hostent *hp;
875                 gchar **addr_list_p;
876                 struct sockaddr_in ad;
877 #endif /* INET6 */
878                 gint ai_member[4] = {AF_UNSPEC, 0, 0, 0};
879
880                 close(pipe_fds[0]);
881
882 #ifdef INET6
883                 memset(&hints, 0, sizeof(hints));
884                 /* hints.ai_flags = AI_CANONNAME; */
885                 hints.ai_family = AF_UNSPEC;
886                 hints.ai_socktype = SOCK_STREAM;
887                 hints.ai_protocol = IPPROTO_TCP;
888
889                 g_snprintf(port_str, sizeof(port_str), "%d", port);
890
891                 gai_err = getaddrinfo(hostname, port_str, &hints, &res);
892                 if (gai_err != 0) {
893                         g_warning("getaddrinfo for %s:%s failed: %s\n",
894                                   hostname, port_str, gai_strerror(gai_err));
895                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
896                                      sizeof(ai_member));
897                         close(pipe_fds[1]);
898                         _exit(1);
899                 }
900
901                 for (ai = res; ai != NULL; ai = ai->ai_next) {
902                         ai_member[0] = ai->ai_family;
903                         ai_member[1] = ai->ai_socktype;
904                         ai_member[2] = ai->ai_protocol;
905                         ai_member[3] = ai->ai_addrlen;
906
907                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
908                                      sizeof(ai_member));
909                         fd_write_all(pipe_fds[1], (gchar *)ai->ai_addr,
910                                      ai->ai_addrlen);
911                 }
912
913                 if (res != NULL)
914                         freeaddrinfo(res);
915 #else /* !INET6 */
916                 hp = my_gethostbyname(hostname);
917                 if (hp == NULL || hp->h_addrtype != AF_INET) {
918                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
919                                      sizeof(ai_member));
920                         close(pipe_fds[1]);
921                         _exit(1);
922                 }
923
924                 ai_member[0] = AF_INET;
925                 ai_member[1] = SOCK_STREAM;
926                 ai_member[2] = IPPROTO_TCP;
927                 ai_member[3] = sizeof(ad);
928
929                 memset(&ad, 0, sizeof(ad));
930                 ad.sin_family = AF_INET;
931                 ad.sin_port = htons(port);
932
933                 for (addr_list_p = hp->h_addr_list; *addr_list_p != NULL;
934                      addr_list_p++) {
935                         memcpy(&ad.sin_addr, *addr_list_p, hp->h_length);
936                         fd_write_all(pipe_fds[1], (gchar *)ai_member,
937                                      sizeof(ai_member));
938                         fd_write_all(pipe_fds[1], (gchar *)&ad, sizeof(ad));
939                 }
940 #endif /* INET6 */
941
942                 close(pipe_fds[1]);
943
944                 _exit(0);
945         } else {
946                 close(pipe_fds[1]);
947
948                 lookup_data = g_new0(SockLookupData, 1);
949                 lookup_data->hostname = g_strdup(hostname);
950                 lookup_data->child_pid = pid;
951                 lookup_data->func = func;
952                 lookup_data->data = data;
953
954                 lookup_data->channel = g_io_channel_unix_new(pipe_fds[0]);
955                 lookup_data->io_tag = g_io_add_watch
956                         (lookup_data->channel, G_IO_IN,
957                          sock_get_address_info_async_cb, lookup_data);
958         }
959
960         return lookup_data;
961 }
962
963 static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data)
964 {
965         if (lookup_data->io_tag > 0)
966                 g_source_remove(lookup_data->io_tag);
967         if (lookup_data->channel) {
968                 g_io_channel_close(lookup_data->channel);
969                 g_io_channel_unref(lookup_data->channel);
970         }
971
972         if (lookup_data->child_pid > 0) {
973                 kill(lookup_data->child_pid, SIGKILL);
974                 waitpid(lookup_data->child_pid, NULL, 0);
975         }
976
977         g_free(lookup_data->hostname);
978         g_free(lookup_data);
979
980         return 0;
981 }
982
983
984 static SockInfo *sockinfo_from_fd(const gchar *hostname,
985                                   gushort port,
986                                   gint sock)
987 {
988         SockInfo *sockinfo;
989
990         sockinfo = g_new0(SockInfo, 1);
991         sockinfo->sock = sock;
992         sockinfo->sock_ch = g_io_channel_unix_new(sock);
993         sockinfo->hostname = g_strdup(hostname);
994         sockinfo->port = port;
995         sockinfo->state = CONN_ESTABLISHED;
996
997         usleep(100000);
998
999         return sockinfo;
1000 }
1001
1002 gint sock_printf(SockInfo *sock, const gchar *format, ...)
1003 {
1004         va_list args;
1005         gchar buf[BUFFSIZE];
1006
1007         va_start(args, format);
1008         g_vsnprintf(buf, sizeof(buf), format, args);
1009         va_end(args);
1010
1011         return sock_write_all(sock, buf, strlen(buf));
1012 }
1013
1014 gint fd_read(gint fd, gchar *buf, gint len)
1015 {
1016         if (fd_check_io(fd, G_IO_IN) < 0)
1017                 return -1;
1018
1019         return read(fd, buf, len);
1020 }
1021
1022 #if USE_OPENSSL
1023 gint ssl_read(SSL *ssl, gchar *buf, gint len)
1024 {
1025         gint ret;
1026
1027         if (SSL_pending(ssl) == 0) {
1028                 if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0)
1029                         return -1;
1030         }
1031
1032         ret = SSL_read(ssl, buf, len);
1033
1034         switch (SSL_get_error(ssl, ret)) {
1035         case SSL_ERROR_NONE:
1036                 return ret;
1037         case SSL_ERROR_WANT_READ:
1038         case SSL_ERROR_WANT_WRITE:
1039                 errno = EAGAIN;
1040                 return -1;
1041         default:
1042                 return -1;
1043         }
1044 }
1045 #endif
1046
1047 gint sock_read(SockInfo *sock, gchar *buf, gint len)
1048 {
1049         gint ret;
1050
1051         g_return_val_if_fail(sock != NULL, -1);
1052
1053 #if USE_OPENSSL
1054         if (sock->ssl)
1055                 ret = ssl_read(sock->ssl, buf, len);
1056         else
1057 #endif
1058                 ret = fd_read(sock->sock, buf, len);
1059         
1060         if (ret < 0)
1061                 sock->state = CONN_DISCONNECTED;
1062         return ret;
1063 }
1064
1065 gint fd_write(gint fd, const gchar *buf, gint len)
1066 {
1067         if (fd_check_io(fd, G_IO_OUT) < 0)
1068                 return -1;
1069
1070         return write(fd, buf, len);
1071 }
1072
1073 #if USE_OPENSSL
1074 gint ssl_write(SSL *ssl, const gchar *buf, gint len)
1075 {
1076         gint ret;
1077
1078         ret = SSL_write(ssl, buf, len);
1079
1080         switch (SSL_get_error(ssl, ret)) {
1081         case SSL_ERROR_NONE:
1082                 return ret;
1083         case SSL_ERROR_WANT_READ:
1084         case SSL_ERROR_WANT_WRITE:
1085                 errno = EAGAIN;
1086                 return -1;
1087         default:
1088                 return -1;
1089         }
1090 }
1091 #endif
1092
1093 gint sock_write(SockInfo *sock, const gchar *buf, gint len)
1094 {
1095         gint ret;
1096
1097         g_return_val_if_fail(sock != NULL, -1);
1098
1099 #if USE_OPENSSL
1100         if (sock->ssl)
1101                 ret = ssl_write(sock->ssl, buf, len);
1102         else
1103 #endif
1104                 ret = fd_write(sock->sock, buf, len);
1105
1106         if (ret < 0)
1107                 sock->state = CONN_DISCONNECTED;
1108         return ret;
1109 }
1110
1111 gint fd_write_all(gint fd, const gchar *buf, gint len)
1112 {
1113         gint n, wrlen = 0;
1114
1115         while (len) {
1116                 if (fd_check_io(fd, G_IO_OUT) < 0)
1117                         return -1;
1118                 signal(SIGPIPE, SIG_IGN);
1119                 n = write(fd, buf, len);
1120                 if (n <= 0) {
1121                         log_error("write on fd%d: %s\n", fd, strerror(errno));
1122                         return -1;
1123                 }
1124                 len -= n;
1125                 wrlen += n;
1126                 buf += n;
1127         }
1128
1129         return wrlen;
1130 }
1131
1132 #if USE_OPENSSL
1133 gint ssl_write_all(SSL *ssl, const gchar *buf, gint len)
1134 {
1135         gint n, wrlen = 0;
1136
1137         while (len) {
1138                 n = ssl_write(ssl, buf, len);
1139                 if (n <= 0)
1140                         return -1;
1141                 len -= n;
1142                 wrlen += n;
1143                 buf += n;
1144         }
1145
1146         return wrlen;
1147 }
1148 #endif
1149
1150 gint sock_write_all(SockInfo *sock, const gchar *buf, gint len)
1151 {
1152         gint ret;
1153
1154         g_return_val_if_fail(sock != NULL, -1);
1155
1156 #if USE_OPENSSL
1157         if (sock->ssl)
1158                 ret = ssl_write_all(sock->ssl, buf, len);
1159         else
1160 #endif
1161                 ret = fd_write_all(sock->sock, buf, len);
1162
1163         if (ret < 0)
1164                 sock->state = CONN_DISCONNECTED;
1165         return ret;
1166 }
1167
1168 gint fd_recv(gint fd, gchar *buf, gint len, gint flags)
1169 {
1170         if (fd_check_io(fd, G_IO_IN) < 0)
1171                 return -1;
1172
1173         return recv(fd, buf, len, flags);
1174 }
1175
1176 gint fd_gets(gint fd, gchar *buf, gint len)
1177 {
1178         gchar *newline, *bp = buf;
1179         gint n;
1180
1181         if (--len < 1)
1182                 return -1;
1183         do {
1184                 if ((n = fd_recv(fd, bp, len, MSG_PEEK)) <= 0)
1185                         return -1;
1186                 if ((newline = memchr(bp, '\n', n)) != NULL)
1187                         n = newline - bp + 1;
1188                 if ((n = fd_read(fd, bp, n)) < 0)
1189                         return -1;
1190                 bp += n;
1191                 len -= n;
1192         } while (!newline && len);
1193
1194         *bp = '\0';
1195         return bp - buf;
1196 }
1197
1198 #if USE_OPENSSL
1199 gint ssl_gets(SSL *ssl, gchar *buf, gint len)
1200 {
1201         gchar *newline, *bp = buf;
1202         gint n;
1203
1204         if (--len < 1)
1205                 return -1;
1206         do {
1207                 if ((n = ssl_peek(ssl, bp, len)) <= 0)
1208                         return -1;
1209                 if ((newline = memchr(bp, '\n', n)) != NULL)
1210                         n = newline - bp + 1;
1211                 if ((n = ssl_read(ssl, bp, n)) < 0)
1212                         return -1;
1213                 bp += n;
1214                 len -= n;
1215         } while (!newline && len);
1216
1217         *bp = '\0';
1218         return bp - buf;
1219 }
1220 #endif
1221
1222 gint sock_gets(SockInfo *sock, gchar *buf, gint len)
1223 {
1224         gint ret;
1225
1226         g_return_val_if_fail(sock != NULL, -1);
1227
1228 #if USE_OPENSSL
1229         if (sock->ssl)
1230                 return ssl_gets(sock->ssl, buf, len);
1231         else
1232 #endif
1233                 return fd_gets(sock->sock, buf, len);
1234
1235         if (ret < 0)
1236                 sock->state = CONN_DISCONNECTED;
1237         return ret;
1238 }
1239
1240 gint fd_getline(gint fd, gchar **str)
1241 {
1242         gchar buf[BUFFSIZE];
1243         gint len;
1244         gulong size = 1;
1245
1246         while ((len = fd_gets(fd, buf, sizeof(buf))) > 0) {
1247                 size += len;
1248                 if (!*str)
1249                         *str = g_strdup(buf);
1250                 else {
1251                         *str = g_realloc(*str, size);
1252                         strcat(*str, buf);
1253                 }
1254                 if (buf[len - 1] == '\n')
1255                         break;
1256         }
1257         if (len == -1 && *str)
1258                 g_free(*str);
1259
1260         return len;
1261 }
1262
1263 #if USE_OPENSSL
1264 gint ssl_getline(SSL *ssl, gchar **str)
1265 {
1266         gchar buf[BUFFSIZE];
1267         gint len;
1268         gulong size = 1;
1269
1270         while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) {
1271                 size += len;
1272                 if (!*str)
1273                         *str = g_strdup(buf);
1274                 else {
1275                         *str = g_realloc(*str, size);
1276                         strcat(*str, buf);
1277                 }
1278                 if (buf[len - 1] == '\n')
1279                         break;
1280         }
1281         if (len == -1 && *str)
1282                 g_free(*str);
1283
1284         return len;
1285 }
1286 #endif
1287
1288 gchar *sock_getline(SockInfo *sock)
1289 {
1290         gint ret;
1291         gchar *str = NULL;
1292
1293         g_return_val_if_fail(sock != NULL, NULL);
1294
1295 #if USE_OPENSSL
1296         if (sock->ssl)
1297                 ret = ssl_getline(sock->ssl, &str);
1298         else
1299 #endif
1300                 ret = fd_getline(sock->sock, &str);
1301
1302         if (ret < 0)
1303                 sock->state = CONN_DISCONNECTED;
1304         return str;
1305 }
1306
1307 gint sock_puts(SockInfo *sock, const gchar *buf)
1308 {
1309         gint ret;
1310
1311         if ((ret = sock_write_all(sock, buf, strlen(buf))) < 0)
1312                 return ret;
1313         return sock_write_all(sock, "\r\n", 2);
1314 }
1315
1316 /* peek at the socket data without actually reading it */
1317 #if USE_OPENSSL
1318 gint ssl_peek(SSL *ssl, gchar *buf, gint len)
1319 {
1320         gint ret;
1321
1322         if (SSL_pending(ssl) == 0) {
1323                 if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0)
1324                         return -1;
1325         }
1326
1327         ret = SSL_peek(ssl, buf, len);
1328
1329         switch (SSL_get_error(ssl, ret)) {
1330         case SSL_ERROR_NONE:
1331                 return ret;
1332         case SSL_ERROR_WANT_READ:
1333         case SSL_ERROR_WANT_WRITE:
1334                 errno = EAGAIN;
1335                 return -1;
1336         default:
1337                 return -1;
1338         }
1339 }
1340 #endif
1341
1342 gint sock_peek(SockInfo *sock, gchar *buf, gint len)
1343 {
1344         g_return_val_if_fail(sock != NULL, -1);
1345
1346 #if USE_OPENSSL
1347         if (sock->ssl)
1348                 return ssl_peek(sock->ssl, buf, len);
1349 #endif
1350         return fd_recv(sock->sock, buf, len, MSG_PEEK);
1351 }
1352
1353 gint sock_close(SockInfo *sock)
1354 {
1355         gint ret;
1356
1357         if (!sock)
1358                 return 0;
1359
1360         if (sock->sock_ch)
1361                 g_io_channel_unref(sock->sock_ch);
1362
1363 #if USE_OPENSSL
1364         if (sock->ssl)
1365                 ssl_done_socket(sock);
1366 #endif
1367         ret = fd_close(sock->sock); 
1368         g_free(sock->hostname);
1369         g_free(sock);
1370
1371         return ret;
1372 }
1373
1374 gint fd_close(gint fd)
1375 {
1376         return close(fd);
1377 }