summarylogtreecommitdiffstats
path: root/openssh_multiple_bindaddress.patch
blob: af8b90b31d45df517ed771a07101c4b5ac708fa5 (plain)
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
From e93d805c6b39fe733b6ff223ce655a5b71ccdbf4 Mon Sep 17 00:00:00 2001
Message-Id: <e93d805c6b39fe733b6ff223ce655a5b71ccdbf4.1445475563.git.mschiffer@universe-factory.net>
From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Thu, 22 Oct 2015 02:59:14 +0200
Subject: [PATCH] Allow specifying multiple bind addresses

---
 readconf.c   | 10 ++++---
 readconf.h   | 12 +++++++-
 ssh.c        |  3 +-
 ssh_config   |  5 ++++
 ssh_config.5 |  7 +++--
 sshconnect.c | 89 +++++++++++++++++++++++++++++++++++-------------------------
 6 files changed, 81 insertions(+), 45 deletions(-)

diff --git a/readconf.c b/readconf.c
index 1d03bdf..a78ff2f 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1031,8 +1031,10 @@ parse_char_array:
 		goto parse_string;
 
 	case oBindAddress:
-		charptr = &options->bind_address;
-		goto parse_string;
+		cpptr = (char**)&options->bind_addresses;
+		uintptr = &options->num_bind_addresses;
+		max_entries = SSH_MAX_BIND_ADDRESSES;
+		goto parse_char_array;
 
 	case oPKCS11Provider:
 		charptr = &options->pkcs11_provider;
@@ -1639,7 +1641,7 @@ initialize_options(Options * options)
 	options->clear_forwardings = -1;
 	options->log_level = SYSLOG_LEVEL_NOT_SET;
 	options->preferred_authentications = NULL;
-	options->bind_address = NULL;
+	options->num_bind_addresses = 0;
 	options->pkcs11_provider = NULL;
 	options->enable_ssh_keysign = - 1;
 	options->no_host_authentication_for_localhost = - 1;
@@ -2300,7 +2302,6 @@ dump_client_config(Options *o, const char *host)
 	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
 
 	/* String options */
-	dump_cfg_string(oBindAddress, o->bind_address);
 	dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT);
 	dump_cfg_string(oControlPath, o->control_path);
 	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms ? o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG);
@@ -2324,6 +2325,7 @@ dump_client_config(Options *o, const char *host)
 
 	/* String array options */
 	dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
+	dump_cfg_strarray_oneline(oBindAddress, o->num_bind_addresses, o->bind_addresses);
 	dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
 	dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
 	dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
diff --git a/readconf.h b/readconf.h
index bb2d552..9750fe5 100644
--- a/readconf.h
+++ b/readconf.h
@@ -27,6 +27,11 @@ struct allowed_cname {
 	char *source_list;
 	char *target_list;
 };
+#define SSH_MAX_BIND_ADDRESSES	8	/* 8 addresses should be enough */
+
+#define SSH_BIND_ADDRESS_ANY	"any"	/* any address mark, used in
+					 * configuration file */
+#define SSH_BIND_ADDRESS_ANYlen	strlen(SSH_BIND_ADDRESS_ANY)
 
 typedef struct {
 	int     forward_agent;	/* Forward authentication agent. */
@@ -86,7 +91,12 @@ typedef struct {
 	u_int	num_user_hostfiles;	/* Path for $HOME/.ssh/known_hosts */
 	char   *user_hostfiles[SSH_MAX_HOSTS_FILES];
 	char   *preferred_authentications;
-	char   *bind_address;	/* local socket address for connection to sshd */
+
+	char   *bind_addresses[SSH_MAX_BIND_ADDRESSES];	/* local socket
+							 * address list for connection to sshd, main reason for this is ipv4 and
+							 * ipv6 only hosts, when using global host match */
+	u_int   num_bind_addresses;			/* count of bind_addresses */
+
 	char   *pkcs11_provider; /* PKCS#11 provider */
 	int	verify_host_key_dns;	/* Verify host key using DNS */
 
diff --git a/ssh.c b/ssh.c
index 59c1f93..47e6fdb 100644
--- a/ssh.c
+++ b/ssh.c
@@ -902,7 +902,8 @@ main(int ac, char **av)
 			options.control_path = xstrdup(optarg);
 			break;
 		case 'b':
-			options.bind_address = optarg;
+			options.bind_addresses[0]  = optarg;
+			options.num_bind_addresses = 1;
 			break;
 		case 'F':
 			config = optarg;
diff --git a/ssh_config b/ssh_config
index 03a228f..c1b653b 100644
--- a/ssh_config
+++ b/ssh_config
@@ -46,3 +46,8 @@
 #   VisualHostKey no
 #   ProxyCommand ssh -q -W %h:%p gateway.example.com
 #   RekeyLimit 1G 1h
+
+#   --Example of BindAddress
+#   BindAddress 192.168.0.1 3004:aaaa::beef any
+#   This means, that ssh tries 192.168.0.1 if fail to bind, next address willbe 3004:aaaa::beef and if it fails,
+#    uses default bind strategy, bind on any address
diff --git a/ssh_config.5 b/ssh_config.5
index a47f3ca..b9aaf2f 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -242,8 +242,11 @@ or
 The default is
 .Dq no .
 .It Cm BindAddress
-Use the specified address on the local machine as the source address of
-the connection.
+Use the specified address (or addresses seperated by space ) on the
+local machine as the source address of
+the connection. This list can be interrupted with
+.Dq any
+address.
 Only useful on systems with more than one address.
 Note that this option does not work if
 .Cm UsePrivilegedPort
diff --git a/sshconnect.c b/sshconnect.c
index 17fbe39..777b715 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -283,49 +283,64 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
 	fcntl(sock, F_SETFD, FD_CLOEXEC);
 
 	/* Bind the socket to an alternative local IP address */
-	if (options.bind_address == NULL && !privileged)
+	if (options.num_bind_addresses == 0 && !privileged)
 		return sock;
 
-	if (options.bind_address) {
-		memset(&hints, 0, sizeof(hints));
-		hints.ai_family = ai->ai_family;
-		hints.ai_socktype = ai->ai_socktype;
-		hints.ai_protocol = ai->ai_protocol;
-		hints.ai_flags = AI_PASSIVE;
-		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
-		if (gaierr) {
-			error("getaddrinfo: %s: %s", options.bind_address,
-			    ssh_gai_strerror(gaierr));
-			close(sock);
-			return -1;
+	verbose("Trying %d addresses to connect", options.num_bind_addresses);
+	uint i;
+	for (i = 0; i < options.num_bind_addresses || i == 0; i++) {
+		if (options.num_bind_addresses > 0)
+		    verbose("Trying bind address: %s", options.bind_addresses[i]);
+
+		if (options.num_bind_addresses > 0 && strncmp(options.bind_addresses[i], SSH_BIND_ADDRESS_ANY, SSH_BIND_ADDRESS_ANYlen) != 0) {
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_family = ai->ai_family;
+			hints.ai_socktype = ai->ai_socktype;
+			hints.ai_protocol = ai->ai_protocol;
+			hints.ai_flags = AI_PASSIVE;
+			gaierr = getaddrinfo(options.bind_addresses[i], NULL, &hints, &res);
+			if (gaierr) {
+				error("getaddrinfo: %s: %s", options.bind_addresses[i],
+				      ssh_gai_strerror(gaierr));
+				continue;
+			}
 		}
-	}
-	/*
-	 * If we are running as root and want to connect to a privileged
-	 * port, bind our own socket to a privileged port.
-	 */
-	if (privileged) {
-		PRIV_START;
-		r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
-		PRIV_END;
-		if (r < 0) {
-			error("bindresvport_sa: af=%d %s", ai->ai_family,
-			    strerror(errno));
-			goto fail;
+		else if (!privileged) {
+			return sock;
 		}
-	} else {
-		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
-			error("bind: %s: %s", options.bind_address,
-			    strerror(errno));
- fail:
-			close(sock);
-			freeaddrinfo(res);
-			return -1;
+
+		/*
+		 * If we are running as root and want to connect to a privileged
+		 * port, bind our own socket to a privileged port.
+		 */
+		if (privileged) {
+			PRIV_START;
+			r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
+			PRIV_END;
+			if (r < 0) {
+				error("bindresvport_sa: af=%d %s", ai->ai_family,
+				      strerror(errno));
+				goto fail;
+			}
+		} else {
+			if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+				error("bind: %s: %s", options.bind_addresses[i],
+				      strerror(errno));
+			  fail:
+				freeaddrinfo(res);
+				res = NULL;
+				continue;
+			}
 		}
+
+		if (res != NULL)
+			freeaddrinfo(res);
+
+		return sock;
 	}
-	if (res != NULL)
-		freeaddrinfo(res);
-	return sock;
+
+	close(sock);
+	return -1;
 }
 
 static int
-- 
2.6.2