Package: freeciv / 2.4.3-3

BindAll.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
From: Markus Koschany <apo@gambaru.de>
Date: Sun, 19 Oct 2014 22:22:23 +0200
Subject: BindAll

Fix socket binding and ensure that the client is always able to
connect to local servers.
See also http://svn.gna.org/viewcvs/freeciv?view=revision&revision=25969
Thanks to Jacob Nevins.

Origin: upstream http://gna.org/bugs/download.php?file_id=21743
Forwarded: not-needed
Debian-Bug: https://bugs.debian.org/757876

Thanks to Xavier Cartron for the report.
---
 client/connectdlg_common.c |  9 ++-------
 server/sernet.c            |  2 ++
 utility/netintf.c          | 32 +++++++++++++++++++++++---------
 utility/netintf.h          |  2 +-
 4 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/client/connectdlg_common.c b/client/connectdlg_common.c
index 9ad90e6..60196db 100644
--- a/client/connectdlg_common.c
+++ b/client/connectdlg_common.c
@@ -202,12 +202,7 @@ bool client_start_server(void)
 # endif /* WIN32_NATIVE */
 
 #ifdef IPV6_SUPPORT
-  /* We want port that is free in IPv4 even if we (the client) have
-   * IPv6 support. In the unlikely case that local server is IPv4-only
-   * (meaning that it has to be from different build than client) we
-   * have to give port that it can use. IPv6-enabled client would first
-   * try same port in IPv6 and if that fails, fallback to IPv4 too. */
-  enum fc_addr_family family = FC_ADDR_IPV4;
+  enum fc_addr_family family = FC_ADDR_ANY;
 #else
   enum fc_addr_family family = FC_ADDR_IPV4;
 #endif /* IPV6_SUPPORT */
@@ -223,7 +218,7 @@ bool client_start_server(void)
    * used by standalone server on Windows where this is known to be buggy
    * by not starting from DEFAULT_SOCK_PORT but from one higher. */
   internal_server_port = find_next_free_port(DEFAULT_SOCK_PORT + 1,
-                                             family, "localhost");
+                                             family, "localhost", TRUE);
 
   if (internal_server_port < 0) {
     output_window_append(ftc_client, _("Couldn't start the server."));
diff --git a/server/sernet.c b/server/sernet.c
index da3b8cc..5c0db94 100644
--- a/server/sernet.c
+++ b/server/sernet.c
@@ -1131,6 +1131,8 @@ int server_open_socket(void)
         /* Close only this socket. This address is not available.
          * This can happen with the IPv6 wildcard address if this
          * machine has no IPv6 interfaces. */
+        /* If you change this logic, be sure to make clientside checking
+         * of acceptable port to match. */
         fc_closesocket(s);
         continue;
       } else {
diff --git a/utility/netintf.c b/utility/netintf.c
index dfa0531..49881a4 100644
--- a/utility/netintf.c
+++ b/utility/netintf.c
@@ -621,7 +621,7 @@ const char *fc_url_encode(const char *txt)
   Finds the next (lowest) free port.
 **************************************************************************/ 
 int find_next_free_port(int starting_port, enum fc_addr_family family,
-                        char *net_interface)
+                        char *net_interface, bool not_avail_ok)
 {
   int port;
   int s;
@@ -650,8 +650,6 @@ int find_next_free_port(int starting_port, enum fc_addr_family family,
      return -1;
   }
 
-  s = socket(gafamily, SOCK_STREAM, 0);
-
   for (port = starting_port; !found ; port++) {
     /* HAVE_GETADDRINFO implies IPv6 support */
 #ifdef HAVE_GETADDRINFO
@@ -670,20 +668,36 @@ int find_next_free_port(int starting_port, enum fc_addr_family family,
     err = getaddrinfo(net_interface, servname, &hints, &res);
     if (!err) {
       struct addrinfo *current = res;
-
-      while (current != NULL && !found) {
-        if (bind(s, current->ai_addr, current->ai_addrlen) == 0) {
-          found = TRUE;
+      bool unusable = FALSE;
+
+      while (current != NULL && !unusable) {
+        s = socket(current->ai_family, SOCK_STREAM, 0);
+
+        if (s == -1) {
+          log_error("socket(): %s", fc_strerror(fc_get_errno()));
+        } else {
+          if (bind(s, current->ai_addr, current->ai_addrlen) != 0) {
+            if (!not_avail_ok || fc_get_errno() != EADDRNOTAVAIL) {
+              unusable = TRUE;
+            }
+          }
         }
         current = current->ai_next;
+        fc_closesocket(s);
       }
 
       freeaddrinfo(res);
+
+      if (!unusable && res != NULL) {
+        found = TRUE;
+      }
     }
 #else /* HAVE_GETADDRINFO */
     union fc_sockaddr tmp;
     struct sockaddr_in *sock4;
 
+    s = socket(gafamily, SOCK_STREAM, 0);
+
     sock4 = &tmp.saddr_in4;
     memset(&tmp, 0, sizeof(tmp));
     sock4->sin_family = AF_INET;
@@ -711,14 +725,14 @@ int find_next_free_port(int starting_port, enum fc_addr_family family,
     if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) {
       found = TRUE;
     }
+
+    fc_closesocket(s);
 #endif /* HAVE_GETADDRINFO */
   }
 
   /* Rollback the last increment from the loop, back to port
    * number found to be free. */
   port--;
-
-  fc_closesocket(s);
   
   return port;
 }
diff --git a/utility/netintf.h b/utility/netintf.h
index 77468c2..090322d 100644
--- a/utility/netintf.h
+++ b/utility/netintf.h
@@ -127,7 +127,7 @@ struct fc_sockaddr_list *net_lookup_service(const char *name, int port,
 					    enum fc_addr_family family);
 fz_FILE *fc_querysocket(int sock, void *buf, size_t size);
 int find_next_free_port(int starting_port, enum fc_addr_family family,
-                        char *net_interface);
+                        char *net_interface, bool not_avail_ok);
 
 const char *fc_lookup_httpd(char *server, int *port, const char *url);
 const char *fc_url_encode(const char *txt);