From ea21df8782ed30cde71404a4233bf18f7b2a4bb6 Mon Sep 17 00:00:00 2001
From: Andrew Potter <agpotter@gmail.com>
Date: Wed, 27 Mar 2013 16:00:16 -0700
Subject: [PATCH 2/2] xfer: Support IPv6 DCC.

There are two primary changes to weechat_network_connect_to():
* Because a ulong obviously can't transfer an IPv6 address, struct
  addrinfo* is used instead, and the port parameter is unused as it
  would be redundant, ignored, or confusing.
* Because the proxy may be at an IPv4 address and the target IPv6--or
  vice versa--the socket() call must be made in network_connect_to()
  so that it is created in the correct family. So network_connect_to()
  now returns the socket fd.

struct t_xfer is modified: local/remote_address are no longer ulong,
but instead struct sockaddr_storage. local/remote_addrlen and
local/remote_address_str fields are also added.
---
 src/core/wee-network.c          |  80 +++++++++------
 src/core/wee-network.h          |   8 +-
 src/plugins/irc/irc-command.c   |  26 +++--
 src/plugins/irc/irc-server.c    |  37 ++++++-
 src/plugins/weechat-plugin.h    |  11 +-
 src/plugins/xfer/xfer-buffer.c  |  11 +-
 src/plugins/xfer/xfer-chat.c    |   8 +-
 src/plugins/xfer/xfer-command.c |   7 +-
 src/plugins/xfer/xfer-dcc.c     |   6 +-
 src/plugins/xfer/xfer-network.c |  59 +++++++----
 src/plugins/xfer/xfer.c         | 216 +++++++++++++++++++++++++---------------
 src/plugins/xfer/xfer.h         |  86 ++++++++--------
 12 files changed, 344 insertions(+), 211 deletions(-)

diff --git a/src/core/wee-network.c b/src/core/wee-network.c
index b1dcf77..82ad448 100644
--- a/src/core/wee-network.c
+++ b/src/core/wee-network.c
@@ -606,56 +606,76 @@ network_connect (int sock, const struct sockaddr *addr, socklen_t addrlen)
  * process.
  *
  * Returns:
- *   1: OK
- *   0: error
+ *   >=0: Connected socket fd
+ *    -1: error
  */
 
 int
-network_connect_to (const char *proxy, int sock,
-                    unsigned long address, int port)
+network_connect_to (const char *proxy, struct sockaddr* address,
+                    socklen_t addrlen)
 {
     struct t_proxy *ptr_proxy;
-    struct sockaddr_in addr;
-    struct hostent *hostent;
-    char *ip4;
+    struct addrinfo *proxy_addrinfo = NULL;
+    struct addrinfo hints;
+    char port_str[16];
+    char ip[NI_MAXHOST];
+    int rc, port, sock = -1;
 
     ptr_proxy = NULL;
     if (proxy && proxy[0])
     {
         ptr_proxy = proxy_search (proxy);
         if (!ptr_proxy)
-            return 0;
+            return -1;
     }
 
     if (ptr_proxy)
     {
-        memset (&addr, 0, sizeof (addr));
-        addr.sin_addr.s_addr = htonl (address);
-        ip4 = inet_ntoa(addr.sin_addr);
-
-        memset (&addr, 0, sizeof (addr));
-        addr.sin_port = htons (CONFIG_INTEGER(ptr_proxy->options[PROXY_OPTION_PORT]));
-        addr.sin_family = AF_INET;
-        hostent = gethostbyname (CONFIG_STRING(ptr_proxy->options[PROXY_OPTION_ADDRESS]));
-        if (!hostent)
-            return 0;
-        memcpy(&(addr.sin_addr), *(hostent->h_addr_list), sizeof(struct in_addr));
-        if (!network_connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
-            return 0;
-        if (!network_pass_proxy (proxy, sock, ip4, port))
-            return 0;
+        /* Record address's ip address and port*/
+        if (getnameinfo(address, addrlen, ip, sizeof(ip), port_str, sizeof(port_str),
+                        NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+            return -1;
+        port = atoi (port_str);
+
+        /* Get sockaddr for proxy */
+        memset (&hints, 0, sizeof(hints));
+        hints.ai_family = AF_UNSPEC;
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_flags = AI_NUMERICSERV;
+        snprintf (port_str, sizeof(port_str), "%d",
+                 CONFIG_INTEGER(ptr_proxy->options[PROXY_OPTION_PORT]));
+        if (getaddrinfo(CONFIG_STRING(ptr_proxy->options[PROXY_OPTION_ADDRESS]),
+                        port_str, &hints, &proxy_addrinfo) != 0)
+            return -1;
+
+        /* Connect and pass address to proxy */
+        sock = socket (proxy_addrinfo->ai_family, SOCK_STREAM, 0);
+        if (sock == -1)
+            goto error;
+        rc = network_connect (sock, proxy_addrinfo->ai_addr, proxy_addrinfo->ai_addrlen);
+        if (!rc)
+            goto error;
+        if (!network_pass_proxy(proxy, sock, ip, port))
+            goto error;
+        freeaddrinfo (proxy_addrinfo);
     }
     else
     {
-        memset (&addr, 0, sizeof (addr));
-        addr.sin_port = htons (port);
-        addr.sin_family = AF_INET;
-        addr.sin_addr.s_addr = htonl (address);
-        if (!network_connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
-            return 0;
+        sock = socket (address->sa_family, SOCK_STREAM, 0);
+        if (sock == -1)
+            return -1;
+        if (!network_connect (sock, address, addrlen))
+            goto error;
     }
 
-    return 1;
+    return sock;
+
+error:
+    if (sock != -1)
+        close (sock);
+    if (proxy_addrinfo)
+        freeaddrinfo (proxy_addrinfo);
+    return -1;
 }
 
 /*
diff --git a/src/core/wee-network.h b/src/core/wee-network.h
index e421db0..b2f9907 100644
--- a/src/core/wee-network.h
+++ b/src/core/wee-network.h
@@ -20,6 +20,10 @@
 #ifndef __WEECHAT_NETWORK_H
 #define __WEECHAT_NETWORK_H 1
 
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
 struct t_hook;
 
 struct t_network_socks4
@@ -47,8 +51,8 @@ extern void network_init ();
 extern void network_end ();
 extern int network_pass_proxy (const char *proxy, int sock,
                                const char *address, int port);
-extern int network_connect_to (const char *proxy, int sock,
-                               unsigned long address, int port);
+extern int network_connect_to (const char *proxy, struct sockaddr *address,
+                               socklen_t addrlen);
 extern void network_connect_with_fork (struct t_hook *hook_connect);
 
 #endif /* __WEECHAT_NETWORK_H */
diff --git a/src/plugins/irc/irc-command.c b/src/plugins/irc/irc-command.c
index d53794b..85efc2a 100644
--- a/src/plugins/irc/irc-command.c
+++ b/src/plugins/irc/irc-command.c
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netdb.h>
 
 #include "../weechat-plugin.h"
 #include "irc.h"
@@ -1158,12 +1159,12 @@ int
 irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc,
                  char **argv, char **argv_eol)
 {
-    struct sockaddr_in addr;
+    struct sockaddr_storage addr;
     socklen_t length;
-    unsigned long address;
     struct t_infolist *infolist;
     struct t_infolist_item *item;
-    char str_address[128], charset_modifier[256];
+    char str_address[NI_MAXHOST], charset_modifier[256];
+    int rc;
 
     IRC_BUFFER_GET_SERVER_CHANNEL(buffer);
     IRC_COMMAND_CHECK_SERVER("dcc", 1);
@@ -1174,11 +1175,20 @@ irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc,
     if (argc > 1)
     {
         /* use the local interface, from the server socket */
-        memset (&addr, 0, sizeof (struct sockaddr_in));
+        memset (&addr, 0, sizeof (addr));
         length = sizeof (addr);
         getsockname (ptr_server->sock, (struct sockaddr *) &addr, &length);
-        addr.sin_family = AF_INET;
-        address = ntohl (addr.sin_addr.s_addr);
+        rc = getnameinfo((struct sockaddr*)&addr, length, str_address,
+                         sizeof(str_address), NULL, 0, NI_NUMERICHOST);
+        if (rc != 0)
+        {
+            weechat_printf (ptr_server->buffer,
+                            _("%s%s: Bug! Can't resolve local address"
+                              " of server socket: %s"),
+                            weechat_prefix ("error"), IRC_PLUGIN_NAME,
+                            gai_strerror(rc));
+            return WEECHAT_RC_OK;
+        }
 
         /* DCC SEND file */
         if (weechat_strcasecmp (argv[1], "send") == 0)
@@ -1200,8 +1210,6 @@ irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc,
                     weechat_infolist_new_var_string (item, "remote_nick", argv[2]);
                     weechat_infolist_new_var_string (item, "local_nick", ptr_server->nick);
                     weechat_infolist_new_var_string (item, "filename", argv_eol[3]);
-                    snprintf (str_address, sizeof (str_address),
-                              "%lu", address);
                     weechat_infolist_new_var_string (item, "local_address", str_address);
                     weechat_infolist_new_var_integer (item, "socket", ptr_server->sock);
                     weechat_hook_signal_send ("xfer_add",
@@ -1232,8 +1240,6 @@ irc_command_dcc (void *data, struct t_gui_buffer *buffer, int argc,
                     snprintf (charset_modifier, sizeof (charset_modifier),
                               "irc.%s.%s", ptr_server->name, argv[2]);
                     weechat_infolist_new_var_string (item, "charset_modifier", charset_modifier);
-                    snprintf (str_address, sizeof (str_address),
-                              "%lu", address);
                     weechat_infolist_new_var_string (item, "local_address", str_address);
                     weechat_hook_signal_send ("xfer_add",
                                               WEECHAT_HOOK_SIGNAL_POINTER,
diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c
index 69aa527..d8b74fd 100644
--- a/src/plugins/irc/irc-server.c
+++ b/src/plugins/irc/irc-server.c
@@ -36,6 +36,9 @@
 #include <sys/socket.h>
 #include <sys/time.h>
 #endif
+#include <sys/types.h>
+#include <netdb.h>
+#include <arpa/inet.h>
 
 #ifdef HAVE_GNUTLS
 #include <gnutls/gnutls.h>
@@ -4319,8 +4322,12 @@ irc_server_xfer_send_ready_cb (void *data, const char *signal,
 {
     struct t_infolist *infolist;
     struct t_irc_server *ptr_server;
-    const char *plugin_name, *plugin_id, *type, *filename;
+    const char *plugin_name, *plugin_id, *type, *filename, *local_address;
+    char converted_addr[NI_MAXHOST] = { '\0' };
+    struct addrinfo *ainfo;
+    struct sockaddr_in *saddr;
     int spaces_in_name;
+    int rc;
 
     /* make C compiler happy */
     (void) data;
@@ -4338,9 +4345,31 @@ irc_server_xfer_send_ready_cb (void *data, const char *signal,
             ptr_server = irc_server_search (plugin_id);
             if (ptr_server)
             {
+                local_address = weechat_infolist_string (infolist, "local_address");
+                if (local_address)
+                {
+                    /* Transform dotted 4 IP address to ulong string */
+                    rc = getaddrinfo (local_address, NULL, NULL, &ainfo);
+                    if (rc == 0 && ainfo && ainfo->ai_addr)
+                    {
+                        if (ainfo->ai_family == AF_INET)
+                        {
+                            saddr = (struct sockaddr_in*)ainfo->ai_addr;
+                            snprintf(converted_addr, sizeof(converted_addr), "%lu",
+                                     (unsigned long)ntohl(saddr->sin_addr.s_addr));
+                        }
+                        else
+                        {
+                            snprintf(converted_addr, sizeof(converted_addr), "%s",
+                                     local_address);
+                        }
+                    }
+                }
+
                 type = weechat_infolist_string (infolist, "type_string");
-                if (type)
+                if (type && converted_addr[0])
                 {
+                    /* Send DCC PRIVMSG */
                     if (strcmp (type, "file_send") == 0)
                     {
                         filename = weechat_infolist_string (infolist, "filename");
@@ -4353,7 +4382,7 @@ irc_server_xfer_send_ready_cb (void *data, const char *signal,
                                           (spaces_in_name) ? "\"" : "",
                                           filename,
                                           (spaces_in_name) ? "\"" : "",
-                                          weechat_infolist_string (infolist, "local_address"),
+                                          converted_addr,
                                           weechat_infolist_integer (infolist, "port"),
                                           weechat_infolist_string (infolist, "size"));
                     }
@@ -4363,7 +4392,7 @@ irc_server_xfer_send_ready_cb (void *data, const char *signal,
                                           IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL,
                                           "PRIVMSG %s :\01DCC CHAT chat %s %d\01",
                                           weechat_infolist_string (infolist, "remote_nick"),
-                                          weechat_infolist_string (infolist, "local_address"),
+                                          converted_addr,
                                           weechat_infolist_integer (infolist, "port"));
                     }
                 }
diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h
index 429eeec..b2491d8 100644
--- a/src/plugins/weechat-plugin.h
+++ b/src/plugins/weechat-plugin.h
@@ -23,6 +23,7 @@
 #define __WEECHAT_WEECHAT_PLUGIN_H 1
 
 #include <sys/types.h>
+#include <sys/socket.h>
 
 /* some systems like GNU/Hurd do not define PATH_MAX */
 #ifndef PATH_MAX
@@ -795,8 +796,9 @@ struct t_weechat_plugin
     /* network */
     int (*network_pass_proxy) (const char *proxy, int sock,
                                const char *address, int port);
-    int (*network_connect_to) (const char *proxy, int sock,
-                               unsigned long address, int port);
+    int (*network_connect_to) (const char *proxy,
+                               struct sockaddr *address,
+                               socklen_t addrlen);
 
     /* infos */
     const char *(*info_get) (struct t_weechat_plugin *plugin,
@@ -1571,9 +1573,8 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin);
 #define weechat_network_pass_proxy(__proxy, __sock, __address, __port)  \
     weechat_plugin->network_pass_proxy(__proxy, __sock, __address,      \
                                        __port)
-#define weechat_network_connect_to(__proxy, __sock, __address, __port)  \
-    weechat_plugin->network_connect_to(__proxy, __sock, __address,      \
-                                       __port)
+#define weechat_network_connect_to(__proxy, __address, __addrlen)       \
+    weechat_plugin->network_connect_to(__proxy, __address, __addrlen)
 
 /* infos */
 #define weechat_info_get(__info_name, __arguments)                      \
diff --git a/src/plugins/xfer/xfer-buffer.c b/src/plugins/xfer/xfer-buffer.c
index 83967ef..c5cbd8a 100644
--- a/src/plugins/xfer/xfer-buffer.c
+++ b/src/plugins/xfer/xfer-buffer.c
@@ -44,7 +44,7 @@ xfer_buffer_refresh (const char *hotlist)
 {
     struct t_xfer *ptr_xfer, *xfer_selected;
     char str_color[256], suffix[32], status[64], date[128], eta[128];
-    char str_ip[32];
+    char str_ip[64];
     char *progress_bar, *str_pos, *str_total, *str_bytes_per_sec;
     int i, length, line, progress_bar_size, num_bars;
     unsigned long long pos, pct_complete;
@@ -93,14 +93,11 @@ xfer_buffer_refresh (const char *hotlist)
                       weechat_config_string (xfer_config_color_text_bg));
 
             str_ip[0] = '\0';
-            if (ptr_xfer->remote_address != 0)
+            if (ptr_xfer->remote_address_str)
             {
                 snprintf (str_ip, sizeof (str_ip),
-                          " (%ld.%ld.%ld.%ld)",
-                          ptr_xfer->remote_address >> 24,
-                          (ptr_xfer->remote_address >> 16) & 0xff,
-                          (ptr_xfer->remote_address >> 8) & 0xff,
-                          ptr_xfer->remote_address & 0xff);
+                          " (%s)",
+                          ptr_xfer->remote_address_str);
             }
 
             /* display first line with remote nick, filename and plugin name/id */
diff --git a/src/plugins/xfer/xfer-chat.c b/src/plugins/xfer/xfer-chat.c
index d658633..2d692da 100644
--- a/src/plugins/xfer/xfer-chat.c
+++ b/src/plugins/xfer/xfer-chat.c
@@ -365,13 +365,9 @@ xfer_chat_open_buffer (struct t_xfer *xfer)
         }
 
         weechat_printf (xfer->buffer,
-                        _("Connected to %s (%ld.%ld.%ld.%ld) via "
-                          "xfer chat"),
+                        _("Connected to %s (%s) via xfer chat"),
                         xfer->remote_nick,
-                        xfer->remote_address >> 24,
-                        (xfer->remote_address >> 16) & 0xff,
-                        (xfer->remote_address >> 8) & 0xff,
-                        xfer->remote_address & 0xff);
+                        xfer->remote_address_str);
 
         free (name);
     }
diff --git a/src/plugins/xfer/xfer-command.c b/src/plugins/xfer/xfer-command.c
index a341967..bcca5f0 100644
--- a/src/plugins/xfer/xfer-command.c
+++ b/src/plugins/xfer/xfer-command.c
@@ -157,15 +157,12 @@ xfer_command_xfer_list (int full)
                     weechat_printf (NULL,
                                     _("     plugin: %s (id: %s), file: %llu "
                                       "bytes (position: %llu), address: "
-                                      "%ld.%ld.%ld.%ld (port %d)"),
+                                      "%s (port %d)"),
                                     ptr_xfer->plugin_name,
                                     ptr_xfer->plugin_id,
                                     ptr_xfer->size,
                                     ptr_xfer->pos,
-                                    ptr_xfer->remote_address >> 24,
-                                    (ptr_xfer->remote_address >> 16) & 0xff,
-                                    (ptr_xfer->remote_address >> 8) & 0xff,
-                                    ptr_xfer->remote_address & 0xff,
+                                    ptr_xfer->remote_address_str,
                                     ptr_xfer->port);
                     date[0] = '\0';
                     date_tmp = localtime (&(ptr_xfer->start_transfer));
diff --git a/src/plugins/xfer/xfer-dcc.c b/src/plugins/xfer/xfer-dcc.c
index 0e0142a..72a2e69 100644
--- a/src/plugins/xfer/xfer-dcc.c
+++ b/src/plugins/xfer/xfer-dcc.c
@@ -231,8 +231,10 @@ xfer_dcc_recv_file_child (struct t_xfer *xfer)
     fd_set read_fds, write_fds, except_fds;
 
     /* first connect to sender (blocking) */
-    if (!weechat_network_connect_to (xfer->proxy, xfer->sock,
-                                     xfer->remote_address, xfer->port))
+    xfer->sock = weechat_network_connect_to (xfer->proxy,
+                                             (struct sockaddr*)&xfer->remote_address,
+                                             xfer->remote_addrlen);
+    if (xfer->sock == -1)
     {
         xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                  XFER_ERROR_CONNECT_SENDER);
diff --git a/src/plugins/xfer/xfer-network.c b/src/plugins/xfer/xfer-network.c
index 8aea101..f2dda5c 100644
--- a/src/plugins/xfer/xfer-network.c
+++ b/src/plugins/xfer/xfer-network.c
@@ -225,14 +225,11 @@ xfer_network_send_file_fork (struct t_xfer *xfer)
     }
 
     weechat_printf (NULL,
-                    _("%s: sending file to %s (%ld.%ld.%ld.%ld, %s.%s), "
+                    _("%s: sending file to %s (%s, %s.%s), "
                       "name: %s (local filename: %s), %llu bytes (protocol: %s)"),
                     XFER_PLUGIN_NAME,
                     xfer->remote_nick,
-                    xfer->remote_address >> 24,
-                    (xfer->remote_address >> 16) & 0xff,
-                    (xfer->remote_address >> 8) & 0xff,
-                    xfer->remote_address & 0xff,
+                    xfer->remote_address_str,
                     xfer->plugin_name,
                     xfer->plugin_id,
                     xfer->filename,
@@ -345,12 +342,16 @@ xfer_network_fd_cb (void *arg_xfer, int fd)
 {
     struct t_xfer *xfer;
     int sock, flags, error;
-    struct sockaddr_in addr;
+    struct sockaddr_storage addr;
     socklen_t length;
+    char hbuf[NI_MAXHOST];
+    int rc;
 
     /* make C compiler happy */
     (void) fd;
 
+    length = sizeof(addr);
+    memset(&addr, 0, length);
     xfer = (struct t_xfer *)arg_xfer;
 
     if (xfer->status == XFER_STATUS_CONNECTING)
@@ -358,7 +359,6 @@ xfer_network_fd_cb (void *arg_xfer, int fd)
         if (xfer->type == XFER_TYPE_FILE_SEND)
         {
             xfer->last_activity = time (NULL);
-            length = sizeof (addr);
             sock = accept (xfer->sock,
                            (struct sockaddr *) &addr, &length);
             error = errno;
@@ -392,7 +392,15 @@ xfer_network_fd_cb (void *arg_xfer, int fd)
                 xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
                 return WEECHAT_RC_OK;
             }
-            xfer->remote_address = ntohl (addr.sin_addr.s_addr);
+            rc = getnameinfo((struct sockaddr*)&addr, length, hbuf,
+                             sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
+            if (rc != 0)
+            {
+                snprintf(hbuf, sizeof(hbuf), "Error: %s", gai_strerror(rc));
+            }
+            memcpy(&xfer->remote_address, &addr, length);
+            xfer->remote_addrlen = length;
+            xfer->remote_address_str = strdup(hbuf);
             xfer->status = XFER_STATUS_ACTIVE;
             xfer->start_transfer = time (NULL);
             xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
@@ -437,7 +445,15 @@ xfer_network_fd_cb (void *arg_xfer, int fd)
                 xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
                 return WEECHAT_RC_OK;
             }
-            xfer->remote_address = ntohl (addr.sin_addr.s_addr);
+            rc = getnameinfo((struct sockaddr*)&addr, length, hbuf,
+                             sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
+            if (rc != 0)
+            {
+                snprintf(hbuf, sizeof(hbuf), "Error: %s", gai_strerror(rc));
+	        }
+            memcpy(&xfer->remote_address, &addr, length);
+            xfer->remote_addrlen = length;
+            xfer->remote_address_str = strdup(hbuf);
             xfer->status = XFER_STATUS_ACTIVE;
             xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
             xfer->hook_fd = weechat_hook_fd (xfer->sock,
@@ -498,15 +514,16 @@ xfer_network_connect (struct t_xfer *xfer)
     else
         xfer->status = XFER_STATUS_CONNECTING;
 
-    if (xfer->sock < 0)
+    if (XFER_IS_SEND(xfer->type))
     {
-        xfer->sock = socket (AF_INET, SOCK_STREAM, 0);
+        /* create socket */
         if (xfer->sock < 0)
-            return 0;
-    }
+        {
+            xfer->sock = socket (xfer->remote_address.ss_family, SOCK_STREAM, 0);
+            if (xfer->sock < 0)
+                return 0;
+        }
 
-    if (XFER_IS_SEND(xfer->type))
-    {
         /* listen to socket */
         flags = fcntl (xfer->sock, F_GETFL);
         if (flags == -1)
@@ -536,13 +553,21 @@ xfer_network_connect (struct t_xfer *xfer)
     /* for chat receiving, connect to listening host */
     if (xfer->type == XFER_TYPE_CHAT_RECV)
     {
+        xfer->sock = weechat_network_connect_to (xfer->proxy,
+                                                 (struct sockaddr*)&xfer->remote_address,
+                                                 xfer->remote_addrlen);
+        if (xfer->sock == -1)
+            return 0;
+
         flags = fcntl (xfer->sock, F_GETFL);
         if (flags == -1)
             flags = 0;
         if (fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK) == -1)
+        {
+            close(xfer->sock);
+            xfer->sock = -1;
             return 0;
-        weechat_network_connect_to (xfer->proxy, xfer->sock,
-                                    xfer->remote_address, xfer->port);
+        }
 
         xfer->hook_fd = weechat_hook_fd (xfer->sock,
                                          1, 0, 0,
diff --git a/src/plugins/xfer/xfer.c b/src/plugins/xfer/xfer.c
index 823c0a8..40e58b8 100644
--- a/src/plugins/xfer/xfer.c
+++ b/src/plugins/xfer/xfer.c
@@ -30,6 +30,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
+#include <arpa/inet.h>
 
 #include "../weechat-plugin.h"
 #include "xfer.h"
@@ -294,16 +295,13 @@ xfer_close (struct t_xfer *xfer, enum t_xfer_status status)
         if (XFER_IS_FILE(xfer->type))
         {
             weechat_printf (NULL,
-                            _("%s%s: file %s %s %s (%ld.%ld.%ld.%ld): %s"),
+                            _("%s%s: file %s %s %s (%s): %s"),
                             (xfer->status == XFER_STATUS_DONE) ? "" : weechat_prefix ("error"),
                             XFER_PLUGIN_NAME,
                             xfer->filename,
                             (xfer->type == XFER_TYPE_FILE_SEND) ? _("sent to") : _("received from"),
                             xfer->remote_nick,
-                            xfer->remote_address >> 24,
-                            (xfer->remote_address >> 16) & 0xff,
-                            (xfer->remote_address >> 8) & 0xff,
-                            xfer->remote_address & 0xff,
+                            xfer->remote_address_str,
                             (xfer->status == XFER_STATUS_DONE) ? _("OK") : _("FAILED"));
             xfer_network_child_kill (xfer);
         }
@@ -314,13 +312,10 @@ xfer_close (struct t_xfer *xfer, enum t_xfer_status status)
         {
             weechat_printf (xfer->buffer,
                             _("%s: chat closed with %s "
-                              "(%ld.%ld.%ld.%ld)"),
+                              "(%s)"),
                             XFER_PLUGIN_NAME,
                             xfer->remote_nick,
-                            xfer->remote_address >> 24,
-                            (xfer->remote_address >> 16) & 0xff,
-                            (xfer->remote_address >> 8) & 0xff,
-                            xfer->remote_address & 0xff);
+                            xfer->remote_address_str);
         }
     }
 
@@ -451,8 +446,12 @@ xfer_alloc ()
     /* default values */
     new_xfer->filename = NULL;
     new_xfer->size = 0;
-    new_xfer->local_address = 0;
-    new_xfer->remote_address = 0;
+    memset(&new_xfer->local_address, 0, sizeof(new_xfer->local_address));
+    new_xfer->local_addrlen = sizeof(new_xfer->local_address);
+    new_xfer->local_address_str = NULL;
+    memset(&new_xfer->remote_address, 0, sizeof(new_xfer->remote_address));
+    new_xfer->remote_addrlen = sizeof(new_xfer->remote_address);
+    new_xfer->remote_address_str = NULL;
     new_xfer->port = 0;
     new_xfer->remote_nick = NULL;
     new_xfer->local_nick = NULL;
@@ -557,11 +556,13 @@ xfer_new (const char *plugin_name, const char *plugin_id,
           enum t_xfer_type type, enum t_xfer_protocol protocol,
           const char *remote_nick, const char *local_nick,
           const char *charset_modifier, const char *filename,
-          unsigned long long size, const char *proxy, unsigned long address,
-          int port, int sock, const char *local_filename)
+          unsigned long long size, const char *proxy, struct sockaddr_storage *address,
+          socklen_t addrlen, int port, int sock, const char *local_filename)
 {
     struct t_xfer *new_xfer;
     const char *ptr_color;
+    char hbuf[NI_MAXHOST];
+    int rc;
 
     new_xfer = xfer_alloc ();
     if (!new_xfer)
@@ -594,17 +595,35 @@ xfer_new (const char *plugin_name, const char *plugin_id,
         new_xfer->filename = strdup (_("xfer chat"));
     new_xfer->size = size;
     new_xfer->proxy = (proxy) ? strdup (proxy) : NULL;
+    new_xfer->port = port;
+
+    rc = getnameinfo((struct sockaddr*)address, addrlen, hbuf,
+                     sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
+    if (rc != 0)
+    {
+	    weechat_printf (NULL,
+	                    _("%s%s: Error interpreting DCC address: %s"),
+	                    weechat_prefix ("error"), XFER_PLUGIN_NAME,
+	                    gai_strerror(rc));
+    }
     if (XFER_IS_RECV(type))
     {
-        new_xfer->local_address = 0;
-        new_xfer->remote_address = address;
+        new_xfer->local_addrlen = sizeof(new_xfer->local_address);
+        memset(&(new_xfer->local_address), 0, new_xfer->local_addrlen);
+        new_xfer->local_address_str = strdup("");
+        memcpy(&(new_xfer->remote_address), address, addrlen);
+        new_xfer->remote_address_str = strdup(hbuf);
+        new_xfer->remote_addrlen = addrlen;
     }
     else
     {
-        new_xfer->local_address = address;
-        new_xfer->remote_address = 0;
+        memcpy(&new_xfer->local_address, address, addrlen);
+        new_xfer->local_address_str = strdup(hbuf);
+        new_xfer->local_addrlen = addrlen;
+        new_xfer->remote_addrlen = sizeof(new_xfer->remote_address);
+        memset(&new_xfer->remote_address, 0, new_xfer->remote_addrlen);
+        new_xfer->remote_address_str = strdup("");
     }
-    new_xfer->port = port;
 
     new_xfer->status = XFER_STATUS_WAITING;
     new_xfer->sock = sock;
@@ -619,14 +638,11 @@ xfer_new (const char *plugin_name, const char *plugin_id,
         case XFER_TYPE_FILE_RECV:
             weechat_printf (NULL,
                             _("%s: incoming file from %s "
-                              "(%ld.%ld.%ld.%ld, %s.%s), name: %s, %llu bytes "
+                              "(%s, %s.%s), name: %s, %llu bytes "
                               "(protocol: %s)"),
                             XFER_PLUGIN_NAME,
                             remote_nick,
-                            address >> 24,
-                            (address >> 16) & 0xff,
-                            (address >> 8) & 0xff,
-                            address & 0xff,
+                            hbuf,
                             plugin_name,
                             plugin_id,
                             filename,
@@ -651,13 +667,10 @@ xfer_new (const char *plugin_name, const char *plugin_id,
         case XFER_TYPE_CHAT_RECV:
             weechat_printf (NULL,
                             _("%s: incoming chat request from %s "
-                              "(%ld.%ld.%ld.%ld, %s.%s)"),
+                              "(%s, %s.%s)"),
                             XFER_PLUGIN_NAME,
                             remote_nick,
-                            address >> 24,
-                            (address >> 16) & 0xff,
-                            (address >> 8) & 0xff,
-                            address & 0xff,
+                            hbuf,
                             plugin_name,
                             plugin_id);
             xfer_buffer_refresh (WEECHAT_HOTLIST_MESSAGE);
@@ -761,6 +774,10 @@ xfer_free (struct t_xfer *xfer)
         free (xfer->charset_modifier);
     if (xfer->filename)
         free (xfer->filename);
+    if (xfer->local_address_str)
+        free (xfer->local_address_str);
+    if (xfer->remote_address_str)
+        free (xfer->remote_address_str);
     if (xfer->remote_nick_color)
         free (xfer->remote_nick_color);
     if (xfer->unterminated_message)
@@ -788,15 +805,16 @@ xfer_add_cb (void *data, const char *signal, const char *type_data,
     struct t_infolist *infolist;
     const char *plugin_name, *plugin_id, *str_type, *str_protocol;
     const char *remote_nick, *local_nick, *charset_modifier, *filename, *proxy;
-    int type, protocol, args, port_start, port_end, sock, port;
+    const char *addr_str, *port_str;
+    int type, protocol, args, port_start, port_end, sock, port, rc;
     const char *weechat_dir;
     char *dir1, *dir2, *filename2, *short_filename, *pos;
+    char port_buf[16];
     struct stat st;
-    struct hostent *host;
-    struct sockaddr_in addr;
+    struct addrinfo *ainfo;
+    struct addrinfo hints;
+    struct sockaddr_storage addr;
     socklen_t length;
-    struct in_addr tmpaddr;
-    unsigned long local_addr;
     unsigned long long file_size;
     struct t_xfer *ptr_xfer;
 
@@ -952,52 +970,76 @@ xfer_add_cb (void *data, const char *signal, const char *type_data,
         }
         file_size = st.st_size;
     }
+    port = weechat_infolist_integer (infolist, "port");
+
+    /* Set addr */
+    memset (&hints, 0, sizeof (struct addrinfo));
+    hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+    hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = 0;
+    hints.ai_canonname = NULL;
+    hints.ai_addr = NULL;
+    hints.ai_next = NULL;
 
     if (XFER_IS_RECV(type))
     {
-        sscanf (weechat_infolist_string (infolist, "remote_address"), "%lu", &local_addr);
-        port = weechat_infolist_integer (infolist, "port");
+        addr_str = weechat_infolist_string (infolist, "remote_address");
+        snprintf(port_buf, sizeof(port_buf), "%d", port);
+        port_str = port_buf;
     }
     else
     {
-        /* get local IP address */
-        sscanf (weechat_infolist_string (infolist, "local_address"), "%lu", &local_addr);
-
-        memset (&addr, 0, sizeof (struct sockaddr_in));
-        addr.sin_family = AF_INET;
-        addr.sin_addr.s_addr = htonl (local_addr);
-
-        /* look up the IP address from network_own_ip, if set */
+        port_str = NULL;
         if (weechat_config_string(xfer_config_network_own_ip)
             && weechat_config_string(xfer_config_network_own_ip)[0])
         {
-            host = gethostbyname (weechat_config_string (xfer_config_network_own_ip));
-            if (host)
-            {
-                memcpy (&tmpaddr, host->h_addr_list[0], sizeof(struct in_addr));
-                local_addr = ntohl (tmpaddr.s_addr);
+            addr_str = weechat_config_string (xfer_config_network_own_ip);
+            hints.ai_flags = AI_NUMERICSERV;
+        }
+        else
+        {
+            addr_str = weechat_infolist_string (infolist, "local_address");
+        }
+    }
 
-                sock = weechat_infolist_integer (infolist, "socket");
-                if (sock > 0)
-                {
-                    memset (&addr, 0, sizeof (struct sockaddr_in));
-                    length = sizeof (addr);
-                    getsockname (sock, (struct sockaddr *) &addr, &length);
-                    addr.sin_family = AF_INET;
-                }
-            }
-            else
-            {
-                weechat_printf (NULL,
-                                _("%s%s: could not find address for \"%s\", "
-                                  "falling back to local IP"),
-                                weechat_prefix ("error"), XFER_PLUGIN_NAME,
-                                weechat_config_string (xfer_config_network_own_ip));
-            }
+    rc = getaddrinfo (addr_str, port_str, &hints, &ainfo);
+    if (rc == 0 && ainfo && ainfo->ai_addr)
+    {
+        memset (&addr,  0, sizeof (addr));
+        memcpy (&addr, ainfo->ai_addr, ainfo->ai_addrlen);
+        length = ainfo->ai_addrlen;
+        freeaddrinfo(ainfo);
+    }
+    else
+    {
+        /* Report appropriate error */
+        if (weechat_config_string(xfer_config_network_own_ip)
+            &&  weechat_config_string(xfer_config_network_own_ip)[0])
+        {
+            weechat_printf (NULL,
+                            _("%s%s: xfer.network.own_ip \"%s\" is invalid: %s."),
+                            weechat_prefix ("error"), XFER_PLUGIN_NAME,
+                            addr_str, gai_strerror(rc));
         }
+        else
+        {
+            weechat_printf (NULL,
+                            _("%s%s: could not find address for \"%s\": %s."),
+                            weechat_prefix ("error"), XFER_PLUGIN_NAME,
+                            addr_str,
+                            gai_strerror(rc));
+        }
+
+        if (filename2)
+            free (filename2);
+	    goto error;
+    }
 
+    if (XFER_IS_SEND(type))
+    {
         /* open socket for xfer */
-        sock = socket (AF_INET, SOCK_STREAM, 0);
+        sock = socket (addr.ss_family, SOCK_STREAM, 0);
         if (sock < 0)
         {
             weechat_printf (NULL,
@@ -1030,8 +1072,15 @@ xfer_add_cb (void *data, const char *signal, const char *type_data,
                     if (!xfer_port_in_use (port))
                     {
                         /* attempt to bind to the free port */
-                        addr.sin_port = htons (port);
-                        if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0)
+                        if (addr.ss_family == AF_INET)
+                        {
+	                        ((struct sockaddr_in*)&addr)->sin_port = htons (port);
+                        }
+                        else
+                        {
+	                        ((struct sockaddr_in6*)&addr)->sin6_port = htons (port);
+                        }
+                        if (bind (sock, (struct sockaddr *) &addr, length) == 0)
                             break;
                     }
                     port++;
@@ -1045,12 +1094,17 @@ xfer_add_cb (void *data, const char *signal, const char *type_data,
         if (port == 0)
         {
             /* find port automatically */
-            addr.sin_port = 0;
-            if (bind (sock, (struct sockaddr *) &addr, sizeof (addr)) == 0)
+            if (bind (sock, (struct sockaddr *) &addr, length) == 0)
             {
-                length = sizeof (addr);
                 getsockname (sock, (struct sockaddr *) &addr, &length);
-                port = ntohs (addr.sin_port);
+                if (addr.ss_family == AF_INET)
+                {
+	                port = ntohs (((struct sockaddr_in*)&addr)->sin_port);
+                }
+                else
+                {
+	                port = ntohs (((struct sockaddr_in6*)&addr)->sin6_port);
+                }
             }
             else
                 port = -1;
@@ -1104,12 +1158,12 @@ xfer_add_cb (void *data, const char *signal, const char *type_data,
     if (XFER_IS_FILE(type))
         ptr_xfer = xfer_new (plugin_name, plugin_id, type, protocol,
                              remote_nick, local_nick, charset_modifier,
-                             short_filename, file_size, proxy, local_addr,
-                             port, sock, filename2);
+                             short_filename, file_size, proxy, &addr,
+                             length, port, sock, filename2);
     else
         ptr_xfer = xfer_new (plugin_name, plugin_id, type, protocol,
                              remote_nick, local_nick, charset_modifier, NULL,
-                             0, proxy, local_addr, port, sock, NULL);
+                             0, proxy, &addr, length, port, sock, NULL);
 
     if (!ptr_xfer)
     {
@@ -1362,11 +1416,9 @@ xfer_add_to_infolist (struct t_infolist *infolist, struct t_xfer *xfer)
         return 0;
     if (!weechat_infolist_new_var_string (ptr_item, "proxy", xfer->proxy))
         return 0;
-    snprintf (value, sizeof (value), "%lu", xfer->local_address);
-    if (!weechat_infolist_new_var_string (ptr_item, "local_address", value))
+    if (!weechat_infolist_new_var_string (ptr_item, "local_address", xfer->local_address_str))
         return 0;
-    snprintf (value, sizeof (value), "%lu", xfer->remote_address);
-    if (!weechat_infolist_new_var_string (ptr_item, "remote_address", value))
+    if (!weechat_infolist_new_var_string (ptr_item, "remote_address", xfer->remote_address_str))
         return 0;
     if (!weechat_infolist_new_var_integer (ptr_item, "port", xfer->port))
         return 0;
@@ -1460,8 +1512,8 @@ xfer_print_log ()
         weechat_log_printf ("  filename. . . . . . : '%s'",  ptr_xfer->filename);
         weechat_log_printf ("  size. . . . . . . . : %llu",  ptr_xfer->size);
         weechat_log_printf ("  proxy . . . . . . . : '%s'",  ptr_xfer->proxy);
-        weechat_log_printf ("  local_address . . . : %lu",   ptr_xfer->local_address);
-        weechat_log_printf ("  remote_address. . . : %lu",   ptr_xfer->remote_address);
+        weechat_log_printf ("  local_address . . . : %s" ,   ptr_xfer->local_address_str);
+        weechat_log_printf ("  remote_address. . . : %s",    ptr_xfer->remote_address_str);
         weechat_log_printf ("  port. . . . . . . . : %d",    ptr_xfer->port);
 
         weechat_log_printf ("  status. . . . . . . : %d (%s)",
diff --git a/src/plugins/xfer/xfer.h b/src/plugins/xfer/xfer.h
index bef8938..cbb35fc 100644
--- a/src/plugins/xfer/xfer.h
+++ b/src/plugins/xfer/xfer.h
@@ -112,49 +112,53 @@ enum t_xfer_error
 struct t_xfer
 {
     /* data received by xfer to initiate a transfer */
-    char *plugin_name;                 /* plugin name                       */
-    char *plugin_id;                   /* id used by plugin                 */
-    enum t_xfer_type type;             /* xfer type (send/recv file)        */
-    enum t_xfer_protocol protocol;     /* xfer protocol (for file transfer) */
-    char *remote_nick;                 /* remote nick                       */
-    char *local_nick;                  /* local nick                        */
-    char *charset_modifier;            /* string for charset modifier_data  */
-    char *filename;                    /* filename                          */
-    unsigned long long size;           /* file size                         */
-    char *proxy;                       /* proxy to use (optional)           */
-    unsigned long local_address;       /* local IP address                  */
-    unsigned long remote_address;      /* remote IP address                 */
-    int port;                          /* remote port                       */
+    char *plugin_name;                      /* plugin name                       */
+    char *plugin_id;                        /* id used by plugin                 */
+    enum t_xfer_type type;                  /* xfer type (send/recv file)        */
+    enum t_xfer_protocol protocol;          /* xfer protocol (for file transfer) */
+    char *remote_nick;                      /* remote nick                       */
+    char *local_nick;                       /* local nick                        */
+    char *charset_modifier;                 /* string for charset modifier_data  */
+    char *filename;                         /* filename                          */
+    unsigned long long size;                /* file size                         */
+    char *proxy;                            /* proxy to use (optional)           */
+    struct sockaddr_storage local_address;  /* local IP address                  */
+    socklen_t local_addrlen;                /* local sockaddr length             */
+    char *local_address_str;                /* local IP address as string        */
+    struct sockaddr_storage remote_address; /* remote IP address                 */
+    socklen_t remote_addrlen;               /* remote sockaddr length            */
+    char *remote_address_str;               /* remote IP address as string       */
+    int port;                               /* remote port                       */
 
     /* internal data */
-    enum t_xfer_status status;         /* xfer status (waiting, sending,..) */
-    struct t_gui_buffer *buffer;       /* buffer (for chat only)            */
-    char *remote_nick_color;           /* color name for remote nick        */
-                                       /* (returned by IRC plugin)          */
-    int fast_send;                     /* fast send file: does not wait ACK */
-    int blocksize;                     /* block size for sending file       */
-    time_t start_time;                 /* time when xfer started            */
-    time_t start_transfer;             /* time when xfer transfer started   */
-    int sock;                          /* socket for connection             */
-    pid_t child_pid;                   /* pid of child process (send/recv)  */
-    int child_read;                    /* to read into child pipe           */
-    int child_write;                   /* to write into child pipe          */
-    struct t_hook *hook_fd;            /* hook for socket or child pipe     */
-    struct t_hook *hook_timer;         /* timeout for receiver accept       */
-    char *unterminated_message;        /* beginning of a message            */
-    int file;                          /* local file (read or write)        */
-    char *local_filename;              /* local filename (with path)        */
-    int filename_suffix;               /* suffix (like .1) if renaming file */
-    unsigned long long pos;            /* number of bytes received/sent     */
-    unsigned long long ack;            /* number of bytes received OK       */
-    unsigned long long start_resume;   /* start of resume (in bytes)        */
-    time_t last_check_time;            /* last time we checked bytes snt/rcv*/
-    unsigned long long last_check_pos; /* bytes sent/recv at last check     */
-    time_t last_activity;              /* time of last byte received/sent   */
-    unsigned long long bytes_per_sec;  /* bytes per second                  */
-    unsigned long long eta;            /* estimated time of arrival         */
-    struct t_xfer *prev_xfer;          /* link to previous xfer             */
-    struct t_xfer *next_xfer;          /* link to next xfer                 */
+    enum t_xfer_status status;              /* xfer status (waiting, sending,..) */
+    struct t_gui_buffer *buffer;            /* buffer (for chat only)            */
+    char *remote_nick_color;                /* color name for remote nick        */
+                                            /* (returned by IRC plugin)          */
+    int fast_send;                          /* fast send file: does not wait ACK */
+    int blocksize;                          /* block size for sending file       */
+    time_t start_time;                      /* time when xfer started            */
+    time_t start_transfer;                  /* time when xfer transfer started   */
+    int sock;                               /* socket for connection             */
+    pid_t child_pid;                        /* pid of child process (send/recv)  */
+    int child_read;                         /* to read into child pipe           */
+    int child_write;                        /* to write into child pipe          */
+    struct t_hook *hook_fd;                 /* hook for socket or child pipe     */
+    struct t_hook *hook_timer;              /* timeout for receiver accept       */
+    char *unterminated_message;             /* beginning of a message            */
+    int file;                               /* local file (read or write)        */
+    char *local_filename;                   /* local filename (with path)        */
+    int filename_suffix;                    /* suffix (like .1) if renaming file */
+    unsigned long long pos;                 /* number of bytes received/sent     */
+    unsigned long long ack;                 /* number of bytes received OK       */
+    unsigned long long start_resume;        /* start of resume (in bytes)        */
+    time_t last_check_time;                 /* last time we checked bytes snt/rcv*/
+    unsigned long long last_check_pos;      /* bytes sent/recv at last check     */
+    time_t last_activity;                   /* time of last byte received/sent   */
+    unsigned long long bytes_per_sec;       /* bytes per second                  */
+    unsigned long long eta;                 /* estimated time of arrival         */
+    struct t_xfer *prev_xfer;               /* link to previous xfer             */
+    struct t_xfer *next_xfer;               /* link to next xfer                 */
 };
 
 extern struct t_weechat_plugin *weechat_xfer_plugin;
-- 
1.8.1.4

