diff lib/rfc959.c @ 146:782f84694489

2003-4-25 Brian Masney <masneyb@gftp.org> * lib/rfc959.c - added IPV6 support (RFC2428). The only part of the project that isn't IPV6 ready is the proxy comparsion functions. * lib/protocols.c - change the hints.ai_family paramater from AF_INET to PF_UNSPEC * lib/gftp.h lib/misc.c lib/pty.c - moved the functionality of pty[ms]_open() to pty.c. Combined these 2 functions into open_ptys(), and there is one defined for each system type (lots of #define's in this file!) * lib/Makefile.am po/POTFILES.in - added pty.c * lib/sshv2.c - when searching for the password prompt, omit the first character in case it is capitalized. Also, use the new function open_ptys() * configure.in - search for openpty in -lutil. Also, define HAVE_OPENPTY if this function is present on the system
author masneyb
date Sat, 26 Apr 2003 15:26:42 +0000
parents c363b08e0118
children dfc22cead576
line wrap: on
line diff
--- a/lib/rfc959.c	Fri Apr 25 12:53:23 2003 +0000
+++ b/lib/rfc959.c	Sat Apr 26 15:26:42 2003 +0000
@@ -562,7 +562,7 @@
 
 
 static int
-rfc959_data_connection_new (gftp_request * request)
+rfc959_ipv4_data_connection_new (gftp_request * request)
 {
   char *pos, *pos1, resp, *command;
   struct sockaddr_in data_addr;
@@ -571,10 +571,6 @@
   unsigned int temp[6];
   unsigned char ad[6];
 
-  g_return_val_if_fail (request != NULL, GFTP_EFATAL);
-  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, GFTP_EFATAL);
-  g_return_val_if_fail (request->sockfd > 0, GFTP_EFATAL);
-
   if ((request->datafd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
     {
       request->logging_function (gftp_logging_error, request->user_data,
@@ -597,7 +593,7 @@
             return (resp);
 
           gftp_set_request_option (request, "passive_transfer", GINT_TO_POINTER(0));
-	  return (rfc959_data_connection_new (request));
+	  return (rfc959_ipv4_data_connection_new (request));
 	}
 
       pos = request->last_ftp_response + 4;
@@ -700,11 +696,182 @@
 }
 
 
+#ifdef HAVE_IPV6
+
+static int
+rfc959_ipv6_data_connection_new (gftp_request * request)
+{
+  char *pos, resp, buf[64], *command;
+  struct sockaddr_in6 data_addr;
+  int passive_transfer;
+  size_t data_addr_len;
+  unsigned int port;
+
+  if ((request->datafd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0)
+    {
+      request->logging_function (gftp_logging_error, request->user_data,
+				 _("Failed to create a socket: %s\n"),
+				 g_strerror (errno));
+      gftp_disconnect (request);
+      return (GFTP_ERETRYABLE);
+    }
+
+  data_addr_len = sizeof (data_addr);
+  /* This condition shouldn't happen. We better check anyway... */
+  if (data_addr_len != request->hostp->ai_addrlen) 
+    {
+      request->logging_function (gftp_logging_error, request->user_data,
+				 _("Error: It doesn't look like we are connected via IPv6. Aborting connection.\n"));
+      gftp_disconnect (request);
+      return (GFTP_EFATAL);
+    }
+
+  memset (&data_addr, 0, data_addr_len);
+  data_addr.sin6_family = AF_INET6;
+
+  gftp_lookup_request_option (request, "passive_transfer", &passive_transfer);
+  if (passive_transfer)
+    {
+      if ((resp = rfc959_send_command (request, "EPSV\r\n")) != '2')
+	{
+          if (request->sockfd < 0)
+            return (resp);
+
+          gftp_set_request_option (request, "passive_transfer", 
+                                   GINT_TO_POINTER(0));
+	  return (rfc959_ipv6_data_connection_new (request));
+	}
+
+      pos = request->last_ftp_response + 4;
+      while (*pos != '(' && *pos != '\0')
+        pos++;
+      pos++;
+
+      if (*pos == '\0')
+        {
+          request->logging_function (gftp_logging_error, request->user_data,
+                      _("Invalid EPSV response '%s'\n"),
+                      request->last_ftp_response);
+          gftp_disconnect (request);
+          return (GFTP_EFATAL);
+        }
+
+      if (sscanf (pos, "|||%d|", &port) != 1)
+        {
+          request->logging_function (gftp_logging_error, request->user_data,
+                      _("Invalid EPSV response '%s'\n"),
+                      request->last_ftp_response);
+          gftp_disconnect (request);
+          return (GFTP_EFATAL);
+        }
+
+      memcpy (&data_addr, request->hostp->ai_addr, data_addr_len);
+      data_addr.sin6_port = htons (port);
+
+      if (connect (request->datafd, (struct sockaddr *) &data_addr, 
+                   data_addr_len) == -1)
+        {
+          request->logging_function (gftp_logging_error, request->user_data,
+                                    _("Cannot create a data connection: %s\n"),
+                                    g_strerror (errno));
+          gftp_disconnect (request);
+          return (GFTP_ERETRYABLE);
+	}
+    }
+  else
+    {
+      memcpy (&data_addr, request->hostp->ai_addr, data_addr_len);
+      data_addr.sin6_port = 0;
+
+      if (bind (request->datafd, (struct sockaddr *) &data_addr, 
+                data_addr_len) == -1)
+	{
+	  request->logging_function (gftp_logging_error, request->user_data,
+				     _("Cannot bind a port: %s\n"),
+				     g_strerror (errno));
+          gftp_disconnect (request);
+	  return (GFTP_ERETRYABLE);
+	}
+
+      if (getsockname (request->datafd, (struct sockaddr *) &data_addr, 
+                       &data_addr_len) == -1)
+        {
+          request->logging_function (gftp_logging_error, request->user_data,
+				     _("Cannot get socket name: %s\n"),
+				     g_strerror (errno));
+          gftp_disconnect (request);
+	  return (GFTP_ERETRYABLE);
+        }
+
+      if (listen (request->datafd, 1) == -1)
+	{
+	  request->logging_function (gftp_logging_error, request->user_data,
+				     _("Cannot listen on port %d: %s\n"),
+				     ntohs (data_addr.sin6_port),
+				     g_strerror (errno));
+          gftp_disconnect (request);
+	  return (GFTP_ERETRYABLE);
+	}
+
+      if (inet_ntop (AF_INET6, &data_addr.sin6_addr, buf, sizeof (buf)) == NULL)
+        {
+          request->logging_function (gftp_logging_error, request->user_data,
+				     _("Cannot get address of local socket: %s\n"),
+				     g_strerror (errno));
+          gftp_disconnect (request);
+	  return (GFTP_ERETRYABLE);
+        }
+
+      command = g_strdup_printf ("EPRT |2|%s|%d|\n", buf,
+                                 ntohs (data_addr.sin6_port));
+
+      resp = rfc959_send_command (request, command);
+      g_free (command);
+      if (resp != '2')
+	{
+          gftp_disconnect (request);
+	  return (GFTP_ERETRYABLE);
+	}
+    }
+
+  return (0);
+}
+
+#endif /* HAVE_IPV6 */
+
+
+static int
+rfc959_data_connection_new (gftp_request * request)
+{
+  g_return_val_if_fail (request != NULL, GFTP_EFATAL);
+  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, GFTP_EFATAL);
+  g_return_val_if_fail (request->sockfd > 0, GFTP_EFATAL);
+
+  if (GFTP_GET_AI_FAMILY(request) == AF_INET)
+    return (rfc959_ipv4_data_connection_new (request));
+#ifdef HAVE_IPV6
+  else
+    return (rfc959_ipv6_data_connection_new (request));
+#else /* Shouldn't happen */
+  else
+    {
+      request->logging_function (gftp_logging_error, request->user_data,
+				 _("Error: IPV6 support was not completely compiled in\n"));
+      return (GFTP_EFATAL);
+    }
+#endif
+}
+
+
 static int
 rfc959_accept_active_connection (gftp_request * request)
 {
   int infd, ret, passive_transfer;
+#ifdef HAVE_IPV6
   struct sockaddr_in cli_addr;
+#else
+  struct sockaddr_in6 cli_addr;
+#endif
   size_t cli_addr_len;
 
   g_return_val_if_fail (request != NULL, GFTP_EFATAL);