diff --git a/configure.ac b/configure.ac index 7dea7da0cd..dd73b9ca0d 100644 --- a/configure.ac +++ b/configure.ac @@ -622,14 +622,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 35585f6c5f..c158fa02a1 100644 --- a/plug-ins/script-fu/script-fu-server.c +++ b/plug-ins/script-fu/script-fu-server.c @@ -136,7 +136,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); @@ -150,7 +151,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; @@ -284,6 +288,7 @@ script_fu_server_listen (gint timeout) struct timeval tv; struct timeval *tvp = NULL; SELECT_MASK fds; + gint sockno; /* Set time struct */ if (timeout) @@ -294,7 +299,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 @@ -306,15 +314,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) { @@ -323,13 +341,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. */ @@ -391,18 +430,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"); @@ -591,11 +658,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 @@ -619,7 +685,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"); @@ -628,12 +694,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 (); @@ -671,7 +745,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 b6365577d5..5fb673ae56 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 " % 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 " % sys.argv[0] + print >>sys.stderr, " (if omitted connect to localhost, port 10008)" + sys.exit(1) - -sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) -sock.connect ((HOST, PORT)) +HOST = "localhost" +PORT = 10008 try: - cmd = raw_input ("Script-Fu-Remote - Testclient\n> ") + HOST = sys.argv[1] + try: + PORT = int(sys.argv[2]) + except IndexError: + pass +except IndexError: + pass - while len (cmd) > 0: - sock.send ('G%c%c%s' % (len (cmd) / 256, len (cmd) % 256, cmd)) +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> ") + + 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()