2005-09-14 [paul] 1.9.14cvs26
[claws.git] / src / common / socket.c
index 9b1b6208b5e607ec88898e0aaff812ef16b111d6..918ab1826457cb60f2b55d1d0409df4aa4ea74fc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <glib.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
+#ifdef G_OS_WIN32
+#  include <winsock2.h>
+#else
+#  if HAVE_SYS_WAIT_H
+#    include <sys/wait.h>
+#  endif
+#  include <sys/socket.h>
+#  include <sys/stat.h>
+#  include <sys/un.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  include <resolv.h>
+#  include <netdb.h>
+#endif /* G_OS_WIN32 */
 #include <unistd.h>
 #include <stdio.h>
 #include <string.h>
@@ -133,6 +141,7 @@ static gint sock_connect_by_getaddrinfo     (const gchar    *hostname,
 static SockInfo *sockinfo_from_fd(const gchar *hostname,
                                  gushort port,
                                  gint sock);
+#ifdef G_OS_UNIX
 static void sock_address_list_free             (GList          *addr_list);
 
 static gboolean sock_connect_async_cb          (GIOChannel     *source,
@@ -153,7 +162,31 @@ static SockLookupData *sock_get_address_info_async
                                                 SockAddrFunc    func,
                                                 gpointer        data);
 static gint sock_get_address_info_async_cancel (SockLookupData *lookup_data);
+#endif /* G_OS_UNIX */
+
+
+gint sock_init(void)
+{
+#ifdef G_OS_WIN32
+       WSADATA wsadata;
+       gint result;
+
+       result = WSAStartup(MAKEWORD(2, 2), &wsadata);
+       if (result != NO_ERROR) {
+               g_warning("WSAStartup() failed\n");
+               return -1;
+       }
+#endif
+       return 0;
+}
 
+gint sock_cleanup(void)
+{
+#ifdef G_OS_WIN32
+       WSACleanup();
+#endif
+       return 0;
+}
 
 gint sock_set_io_timeout(guint sec)
 {
@@ -161,8 +194,28 @@ gint sock_set_io_timeout(guint sec)
        return 0;
 }
 
+void refresh_resolvers(void)
+{
+       static time_t resolv_conf_changed = (time_t)NULL;
+       struct stat s;
+
+       /* This makes the glibc re-read resolv.conf, if it changed
+        * since our startup. Maybe that should be #ifdef'ed, I don't
+        * know if it'd work on BSDs.
+        * Why doesn't the glibc do it by itself?
+        */
+       if (stat("/etc/resolv.conf", &s) == 0) {
+               if (s.st_mtime > resolv_conf_changed) {
+                       resolv_conf_changed = s.st_mtime;
+                       res_init();
+               }
+       } /* else
+               we'll have bigger problems. */
+}
+
 gint fd_connect_unix(const gchar *path)
 {
+#ifdef G_OS_UNIX
        gint sock;
        struct sockaddr_un addr;
 
@@ -182,10 +235,14 @@ gint fd_connect_unix(const gchar *path)
        }
 
        return sock;
+#else
+       return -1;
+#endif
 }
 
 gint fd_open_unix(const gchar *path)
 {
+#ifdef G_OS_UNIX
        gint sock;
        struct sockaddr_un addr;
 
@@ -213,20 +270,28 @@ gint fd_open_unix(const gchar *path)
        }
 
        return sock;
+#else
+       return -1;
+#endif
 }
 
 gint fd_accept(gint sock)
 {
+#ifdef G_OS_UNIX
        struct sockaddr_in caddr;
-       gint caddr_len;
+       guint caddr_len;
 
        caddr_len = sizeof(caddr);
        return accept(sock, (struct sockaddr *)&caddr, &caddr_len);
+#else
+       return -1;
+#endif
 }
 
 
 static gint set_nonblocking_mode(gint fd, gboolean nonblock)
 {
+#ifdef G_OS_UNIX
        gint flags;
 
        flags = fcntl(fd, F_GETFL, 0);
@@ -241,6 +306,9 @@ static gint set_nonblocking_mode(gint fd, gboolean nonblock)
                flags &= ~O_NONBLOCK;
 
        return fcntl(fd, F_SETFL, flags);
+#else
+       return -1;
+#endif
 }
 
 gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock)
@@ -252,6 +320,7 @@ gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock)
 
 static gboolean is_nonblocking_mode(gint fd)
 {
+#ifdef G_OS_UNIX
        gint flags;
 
        flags = fcntl(fd, F_GETFL, 0);
@@ -261,6 +330,9 @@ static gboolean is_nonblocking_mode(gint fd)
        }
 
        return ((flags & O_NONBLOCK) != 0);
+#else
+       return FALSE;
+#endif
 }
 
 gboolean sock_is_nonblocking_mode(SockInfo *sock)
@@ -330,10 +402,10 @@ static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition,
 {
        SockInfo *sock = (SockInfo *)data;
 
-       if (!sock || !sock->callback || !sock->data) {
-               return FALSE;
-       }
-       return sock->callback(sock, condition, sock->data);
+       if ((condition & sock->condition) == 0)
+               return TRUE;
+
+       return sock->callback(sock, sock->condition, sock->data);
 }
 
 guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func,
@@ -352,7 +424,7 @@ guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func,
                g_source_set_priority(source, G_PRIORITY_DEFAULT);
                g_source_set_can_recurse(source, FALSE);
                sock->g_source = g_source_attach(source, NULL);
-               return g_source_attach(source, NULL);
+               return sock->g_source;
        }
 #endif
 
@@ -389,12 +461,14 @@ static gint fd_check_io(gint fd, GIOCondition cond)
        }
 }
 
+#ifdef G_OS_UNIX
 static sigjmp_buf jmpenv;
 
 static void timeout_handler(gint sig)
 {
        siglongjmp(jmpenv, 1);
 }
+#endif
 
 static gint sock_connect_with_timeout(gint sock,
                                      const struct sockaddr *serv_addr,
@@ -402,8 +476,9 @@ static gint sock_connect_with_timeout(gint sock,
                                      guint timeout_secs)
 {
        gint ret;
+#ifdef G_OS_UNIX
        void (*prev_handler)(gint);
-
+       
        alarm(0);
        prev_handler = signal(SIGALRM, timeout_handler);
        if (sigsetjmp(jmpenv, 1)) {
@@ -413,11 +488,14 @@ static gint sock_connect_with_timeout(gint sock,
                return -1;
        }
        alarm(timeout_secs);
+#endif
 
        ret = connect(sock, serv_addr, addrlen);
 
+#ifdef G_OS_UNIX
        alarm(0);
        signal(SIGALRM, prev_handler);
+#endif
 
        return ret;
 }
@@ -425,8 +503,9 @@ static gint sock_connect_with_timeout(gint sock,
 struct hostent *my_gethostbyname(const gchar *hostname)
 {
        struct hostent *hp;
+#ifdef G_OS_UNIX
        void (*prev_handler)(gint);
-
+       
        alarm(0);
        prev_handler = signal(SIGALRM, timeout_handler);
        if (sigsetjmp(jmpenv, 1)) {
@@ -437,17 +516,22 @@ struct hostent *my_gethostbyname(const gchar *hostname)
                return NULL;
        }
        alarm(io_timeout);
+#endif
 
        if ((hp = gethostbyname(hostname)) == NULL) {
+#ifdef G_OS_UNIX
                alarm(0);
                signal(SIGALRM, prev_handler);
+#endif
                fprintf(stderr, "%s: unknown host.\n", hostname);
                errno = 0;
                return NULL;
        }
 
+#ifdef G_OS_UNIX
        alarm(0);
        signal(SIGALRM, prev_handler);
+#endif
 
        return hp;
 }
@@ -483,6 +567,8 @@ static gint sock_connect_by_hostname(gint sock, const gchar *hostname,
        ad.sin_family = AF_INET;
        ad.sin_port = htons(port);
 
+       refresh_resolvers();
+
        if (!my_inet_aton(hostname, &ad.sin_addr)) {
                if ((hp = my_gethostbyname(hostname)) == NULL) {
                        fprintf(stderr, "%s: unknown host.\n", hostname);
@@ -510,6 +596,8 @@ static gint sock_connect_by_getaddrinfo(const gchar *hostname, gushort      port)
        struct addrinfo hints, *res, *ai;
        gchar port_str[6];
 
+       refresh_resolvers();
+
        memset(&hints, 0, sizeof(hints));
        /* hints.ai_flags = AI_CANONNAME; */
        hints.ai_family = AF_UNSPEC;
@@ -578,14 +666,23 @@ SockInfo *sock_connect_cmd(const gchar *hostname, const gchar *tunnelcmd)
 
 SockInfo *sock_connect(const gchar *hostname, gushort port)
 {
+#ifdef G_OS_WIN32
+       SOCKET sock;
+#else
        gint sock;
+#endif
 
 #ifdef INET6
        if ((sock = sock_connect_by_getaddrinfo(hostname, port)) < 0)
                return NULL;
+#else
+#ifdef G_OS_WIN32
+       if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+               g_warning("socket() failed: %ld\n", WSAGetLastError());
 #else
        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                perror("socket");
+#endif /* G_OS_WIN32 */
                return NULL;
        }
 
@@ -599,6 +696,7 @@ SockInfo *sock_connect(const gchar *hostname, gushort port)
        return sockinfo_from_fd(hostname, port, sock);
 }
 
+#ifdef G_OS_UNIX
 static void sock_address_list_free(GList *addr_list)
 {
        GList *cur;
@@ -620,9 +718,12 @@ static gboolean sock_connect_async_cb(GIOChannel *source,
        SockConnectData *conn_data = (SockConnectData *)data;
        gint fd;
        gint val;
-       gint len;
+       guint len;
        SockInfo *sockinfo;
 
+       if (conn_data->io_tag == 0 && conn_data->channel == NULL)
+               return FALSE;
+
        fd = g_io_channel_unix_get_fd(source);
 
        conn_data->io_tag = 0;
@@ -794,7 +895,7 @@ static gboolean sock_get_address_info_async_cb(GIOChannel *source,
        SockLookupData *lookup_data = (SockLookupData *)data;
        GList *addr_list = NULL;
        SockAddrData *addr_data;
-       guint bytes_read;
+       gsize bytes_read;
        gint ai_member[4];
        struct sockaddr *addr;
 
@@ -864,6 +965,8 @@ static SockLookupData *sock_get_address_info_async(const gchar *hostname,
        SockLookupData *lookup_data = NULL;
        gint pipe_fds[2];
        pid_t pid;
+       
+       refresh_resolvers();
 
        if (pipe(pipe_fds) < 0) {
                perror("pipe");
@@ -992,6 +1095,7 @@ static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data)
 
        return 0;
 }
+#endif /* G_OS_UNIX */
 
 
 static SockInfo *sockinfo_from_fd(const gchar *hostname,
@@ -1007,8 +1111,6 @@ static SockInfo *sockinfo_from_fd(const gchar *hostname,
        sockinfo->port = port;
        sockinfo->state = CONN_ESTABLISHED;
 
-       usleep(100000);
-
        return sockinfo;
 }
 
@@ -1029,24 +1131,38 @@ gint fd_read(gint fd, gchar *buf, gint len)
        if (fd_check_io(fd, G_IO_IN) < 0)
                return -1;
 
+#ifdef G_OS_WIN32
+       return recv(fd, buf, len, 0);
+#else
        return read(fd, buf, len);
+#endif
 }
 
 #if USE_OPENSSL
 gint ssl_read(SSL *ssl, gchar *buf, gint len)
 {
-       gint ret;
+       gint err, ret;
+
+       if (SSL_pending(ssl) == 0) {
+               if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0)
+                       return -1;
+       }
 
        ret = SSL_read(ssl, buf, len);
 
-       switch (SSL_get_error(ssl, ret)) {
+       switch ((err = SSL_get_error(ssl, ret))) {
        case SSL_ERROR_NONE:
                return ret;
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
                errno = EAGAIN;
                return -1;
+       case SSL_ERROR_ZERO_RETURN:
+               return 0;
        default:
+               g_warning("SSL_read() returned error %d, ret = %d\n", err, ret);
+               if (ret == 0)
+                       return 0;
                return -1;
        }
 }
@@ -1075,7 +1191,11 @@ gint fd_write(gint fd, const gchar *buf, gint len)
        if (fd_check_io(fd, G_IO_OUT) < 0)
                return -1;
 
+#ifdef G_OS_WIN32
+       return send(fd, buf, len, 0);
+#else
        return write(fd, buf, len);
+#endif
 }
 
 #if USE_OPENSSL
@@ -1325,18 +1445,31 @@ gint sock_puts(SockInfo *sock, const gchar *buf)
 #if USE_OPENSSL
 gint ssl_peek(SSL *ssl, gchar *buf, gint len)
 {
-       gint ret;
+       gint err, ret;
+
+       if (SSL_pending(ssl) == 0) {
+               if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0)
+                       return -1;
+       }
 
        ret = SSL_peek(ssl, buf, len);
 
-       switch (SSL_get_error(ssl, ret)) {
+       switch ((err = SSL_get_error(ssl, ret))) {
        case SSL_ERROR_NONE:
                return ret;
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
                errno = EAGAIN;
                return -1;
+       case SSL_ERROR_ZERO_RETURN:
+               return 0;
+       case SSL_ERROR_SYSCALL:
+               g_warning("SSL_peek() returned syscall error. errno=%d\n", errno);
+               return -1;
        default:
+               g_warning("SSL_peek() returned error %d, ret = %d\n", err, ret);
+               if (ret == 0)
+                       return 0;
                return -1;
        }
 }