[gimp/gimp-2-6] Bug 623045 - script-fu: make server IPv6 aware



commit f9e79c4429ded0be61ff9a042f3ff879c48ea8af
Author: Nils Philippsen <nils redhat com>
Date:   Tue Jun 29 15:43:31 2010 +0200

    Bug 623045 - script-fu: make server IPv6 aware
    
    use getnameinfo() instead of inet_ntoa()
    use two sockets for IPv4, IPv6
    (cherry picked from commit 04538534fc581de0507ef2ec1148853ff1e96dbe)

 configure.in                          |    8 +-
 plug-ins/script-fu/script-fu-server.c |  141 +++++++++++++++++++++++++-------
 plug-ins/script-fu/servertest.py      |   76 ++++++++++++------
 3 files changed, 166 insertions(+), 59 deletions(-)
---
diff --git a/configure.in b/configure.in
index c567873..516936b 100644
--- a/configure.in
+++ b/configure.in
@@ -605,14 +605,14 @@ AC_CHECK_FUNC(rint, AC_DEFINE(HAVE_RINT, 1,
     AC_DEFINE(HAVE_RINT)])])
 
 
-######################################################
-# Check for extra libs needed for inet_ntoa and socket
-######################################################
+########################################################
+# Check for extra libs needed for getnameinfo and socket
+########################################################
 
 gimp_save_LIBS=$LIBS
 LIBS=""
 
-AC_CHECK_FUNCS(inet_ntoa, , AC_CHECK_LIB(nsl, inet_ntoa))
+AC_CHECK_FUNCS(getnameinfo, , AC_CHECK_LIB(nsl, getnameinfo))
 AC_CHECK_LIB(socket, socket)
 
 SOCKET_LIBS="$LIBS"
diff --git a/plug-ins/script-fu/script-fu-server.c b/plug-ins/script-fu/script-fu-server.c
index db1de13..c4776a2 100644
--- a/plug-ins/script-fu/script-fu-server.c
+++ b/plug-ins/script-fu/script-fu-server.c
@@ -137,7 +137,8 @@ static void      server_start       (gint         port,
                                      const gchar *logfile);
 static gboolean  execute_command    (SFCommand   *cmd);
 static gint      read_from_client   (gint         filedes);
-static gint      make_socket        (guint        port);
+static gint      make_socket        (const struct addrinfo
+                                                 *ai);
 static void      server_log         (const gchar *format,
                                      ...) G_GNUC_PRINTF (1, 2);
 static void      server_quit        (void);
@@ -151,7 +152,10 @@ static void      print_socket_api_error (const gchar *api_name);
 /*
  *  Local variables
  */
-static gint         server_sock;
+static gint         server_socks[2],
+                    server_socks_used = 0;
+static const gint   server_socks_len = sizeof (server_socks) /
+                                       sizeof (server_socks[0]);
 static GList       *command_queue   = NULL;
 static gint         queue_length    = 0;
 static gint         request_no      = 0;
@@ -285,6 +289,7 @@ script_fu_server_listen (gint timeout)
   struct timeval  tv;
   struct timeval *tvp = NULL;
   SELECT_MASK     fds;
+  gint            sockno;
 
   /*  Set time struct  */
   if (timeout)
@@ -295,7 +300,10 @@ script_fu_server_listen (gint timeout)
     }
 
   FD_ZERO (&fds);
-  FD_SET (server_sock, &fds);
+  for (sockno = 0; sockno < server_socks_used; sockno++)
+    {
+      FD_SET (server_socks[sockno], &fds);
+    }
   g_hash_table_foreach (clients, script_fu_server_add_fd, &fds);
 
   /* Block until input arrives on one or more active sockets
@@ -307,15 +315,25 @@ script_fu_server_listen (gint timeout)
       return;
     }
 
-  /* Service the server socket if it has input pending. */
-  if (FD_ISSET (server_sock, &fds))
+  /* Service the server sockets if any has input pending. */
+  for (sockno = 0; sockno < server_socks_used; sockno++)
     {
-      struct sockaddr_in  clientname;
+      struct sockaddr_storage  client;
+      struct sockaddr_in      *client_in;
+      struct sockaddr_in6     *client_in6;
+      gchar                    clientname[NI_MAXHOST];
 
       /* Connection request on original socket. */
-      guint size = sizeof (clientname);
-      gint  new  = accept (server_sock,
-                           (struct sockaddr *) &clientname, &size);
+      guint                    size = sizeof (client);
+      gint                     new;
+      guint                    portno;
+
+      if (! FD_ISSET (server_socks[sockno], &fds))
+        {
+          continue;
+        }
+
+      new = accept (server_socks[sockno], (struct sockaddr *) &client, &size);
 
       if (new < 0)
         {
@@ -324,13 +342,34 @@ script_fu_server_listen (gint timeout)
         }
 
       /*  Associate the client address with the socket  */
-      g_hash_table_insert (clients,
-                           GINT_TO_POINTER (new),
-                           g_strdup (inet_ntoa (clientname.sin_addr)));
+
+      /* If all else fails ... */
+      strncpy (clientname, "(error during host address lookup)", NI_MAXHOST-1);
+
+      /* Lookup address */
+      (void) getnameinfo ((struct sockaddr *) &client, size, clientname,
+                          sizeof (clientname), NULL, 0, NI_NUMERICHOST);
+
+      g_hash_table_insert (clients, GINT_TO_POINTER (new),
+                           g_strdup (clientname));
+
+      /* Determine port number */
+      switch (client.ss_family)
+        {
+          case AF_INET:
+            client_in = (struct sockaddr_in *) &client;
+            portno = (guint) g_ntohs (client_in->sin_port);
+            break;
+          case AF_INET6:
+            client_in6 = (struct sockaddr_in6 *) &client;
+            portno = (guint) g_ntohs (client_in6->sin6_port);
+            break;
+          default:
+            portno = 0;
+        }
 
       server_log ("Server: connect from host %s, port %d.\n",
-                  inet_ntoa (clientname.sin_addr),
-                  (unsigned int) ntohs (clientname.sin_port));
+                  clientname, portno);
     }
 
   /* Service the client sockets. */
@@ -392,18 +431,46 @@ static void
 server_start (gint         port,
               const gchar *logfile)
 {
-  const gchar *progress;
+  struct addrinfo *ai,
+                  *ai_curr;
+  struct addrinfo  hints;
+  gint             e,
+                   sockno;
+  gchar           *port_s;
 
-  /* First of all, create the socket and set it up to accept connections. */
-  /* This may fail if there's a server running on this port already.      */
-  server_sock = make_socket (port);
+  const gchar     *progress;
 
-  if (listen (server_sock, 5) < 0)
+  memset (&hints, 0, sizeof (hints));
+  hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+  hints.ai_socktype = SOCK_STREAM;
+
+  port_s = g_strdup_printf ("%d", port);
+  e = getaddrinfo (NULL, port_s, &hints, &ai);
+  g_free (port_s);
+
+  if (e != 0)
     {
-      print_socket_api_error ("listen");
+      g_printerr ("getaddrinfo: %s", gai_strerror (e));
       return;
     }
 
+  for (ai_curr = ai, sockno = 0;
+       ai_curr != NULL && sockno < server_socks_len;
+       ai_curr = ai_curr->ai_next, sockno++)
+    {
+      /* Create the socket and set it up to accept connections.          */
+      /* This may fail if there's a server running on this port already. */
+      server_socks[sockno] = make_socket (ai_curr);
+
+      if (listen (server_socks[sockno], 5) < 0)
+        {
+          print_socket_api_error ("listen");
+          return;
+        }
+    }
+
+  server_socks_used = sockno;
+
   /*  Setup up the server log file  */
   if (logfile && *logfile)
     server_log_file = g_fopen (logfile, "a");
@@ -592,11 +659,10 @@ read_from_client (gint filedes)
 }
 
 static gint
-make_socket (guint port)
+make_socket (const struct addrinfo *ai)
 {
-  struct sockaddr_in name;
-  gint               sock;
-  gint               v = 1;
+  gint                    sock;
+  gint                    v = 1;
 
   /*  Win32 needs the winsock library initialized.  */
 #ifdef G_OS_WIN32
@@ -620,7 +686,7 @@ make_socket (guint port)
 #endif
 
   /* Create the socket. */
-  sock = socket (PF_INET, SOCK_STREAM, 0);
+  sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   if (sock < 0)
     {
       print_socket_api_error ("socket");
@@ -629,12 +695,20 @@ make_socket (guint port)
 
   setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
 
-  /* Give the socket a name. */
-  name.sin_family      = AF_INET;
-  name.sin_port        = htons (port);
-  name.sin_addr.s_addr = htonl (INADDR_ANY);
+#ifdef IPV6_V6ONLY
+  /* Only listen on IPv6 addresses, otherwise bind() will fail. */
+  if (ai->ai_family == AF_INET6)
+    {
+      v = 1;
+      if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v)) < 0)
+        {
+          print_socket_api_error ("setsockopt");
+          gimp_quit();
+        }
+    }
+#endif
 
-  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+  if (bind (sock, ai->ai_addr, ai->ai_addrlen) < 0)
     {
       print_socket_api_error ("bind");
       gimp_quit ();
@@ -672,7 +746,12 @@ script_fu_server_shutdown_fd (gpointer key,
 static void
 server_quit (void)
 {
-  CLOSESOCKET (server_sock);
+  gint sockno;
+
+  for (sockno = 0; sockno < server_socks_used; sockno++)
+    {
+      CLOSESOCKET (server_socks[sockno]);
+    }
 
   if (clients)
     {
diff --git a/plug-ins/script-fu/servertest.py b/plug-ins/script-fu/servertest.py
index b636557..5fb673a 100644
--- a/plug-ins/script-fu/servertest.py
+++ b/plug-ins/script-fu/servertest.py
@@ -2,38 +2,66 @@
 
 import readline, socket, sys
 
-if len (sys.argv) == 1:
-   HOST = 'localhost'
-   PORT = 10008
-elif len (sys.argv) == 3:
-   HOST = sys.argv[1]
-   PORT = int (sys.argv[2])
-else:
-   print >> sys.stderr, "Usage: %s <host> <port>" % sys.argv[0]
-   print >> sys.stderr, "       (if omitted connect to localhost, port 10008)"
-   sys.exit ()
+if len(sys.argv) < 1 or len(sys.argv) > 3:
+   print >>sys.stderr, "Usage: %s <host> <port>" % sys.argv[0]
+   print >>sys.stderr, "       (if omitted connect to localhost, port 10008)"
+   sys.exit(1)
 
+HOST = "localhost"
+PORT = 10008
 
-sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
-sock.connect ((HOST, PORT))
+try:
+    HOST = sys.argv[1]
+    try:
+        PORT = int(sys.argv[2])
+    except IndexError:
+        pass
+except IndexError:
+    pass
+
+addresses = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM)
+
+connected = False
+
+for addr in addresses:
+    (family, socktype, proto, canonname, sockaddr) = addr
+
+    numeric_addr = sockaddr[0]
+
+    if canonname:
+        print "Trying %s ('%s')." % (numeric_addr, canonname)
+    else:
+        print "Trying %s." % numeric_addr
+
+    try:
+        sock = socket.socket(family, socket.SOCK_STREAM)
+        sock.connect((HOST, PORT))
+        connected = True
+        break
+    except:
+        pass
+
+if not connected:
+    print "Failed."
+    sys.exit(1)
 
 try:
-   cmd = raw_input ("Script-Fu-Remote - Testclient\n> ")
+   cmd = raw_input("Script-Fu-Remote - Testclient\n> ")
 
-   while len (cmd) > 0:
-      sock.send ('G%c%c%s' % (len (cmd) / 256, len (cmd) % 256, cmd))
+   while len(cmd) > 0:
+      sock.send('G%c%c%s' % (len(cmd) / 256, len(cmd) % 256, cmd))
 
       data = ""
-      while len (data) < 4:
-         data += sock.recv (4 - len (data))
+      while len(data) < 4:
+         data += sock.recv(4 - len(data))
 
-      if len (data) >= 4:
+      if len(data) >= 4:
          if data[0] == 'G':
-            l = ord (data[2]) * 256 + ord (data[3])
+            l = ord(data[2]) * 256 + ord(data[3])
             msg = ""
-            while len (msg) < l:
-               msg += sock.recv (l - len (msg))
-            if ord (data[1]):
+            while len(msg) < l:
+               msg += sock.recv(l - len(msg))
+            if ord(data[1]):
                print "(ERR):", msg
             else:
                print " (OK):", msg
@@ -41,9 +69,9 @@ try:
             print "invalid magic: %s\n" % data
       else:
          print "short response: %s\n" % data
-      cmd = raw_input ("> ")
+      cmd = raw_input("> ")
 
 except EOFError:
    print
 
-sock.close
+sock.close()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]