Skip to content
This repository was archived by the owner on Feb 20, 2020. It is now read-only.

Commit b498233

Browse files
committed
Squashed commit of the following:
commit cf314a3a8b03157d419df359a3c73d033ac149b4 Author: Bill Shannon <bill.shannon@oracle.com> Date: Mon Aug 20 15:22:35 2018 -0700 Clean up proxy authentication support, improve test - fix #331 commit 3ab2135828f3f2d23652c5297d749ec5c9948075 Merge: de2627a 6e54257 Author: Bill Shannon <bill.shannon@oracle.com> Date: Mon Aug 20 14:18:46 2018 -0700 Merge branch 'proxy-auth-merge' of https://github.com/FWiesner/javamail into FWiesner-proxy-auth-merge commit 6e54257 Author: Florian Wiesner <florian.wiesner@oracle.com> Date: Mon Aug 20 11:28:58 2018 +0200 Added support for HTTP Proxy Authentication commit 773ec3b Merge: c4e604d 32b74ae Author: Florian Wiesner <fwiesner@gmail.com> Date: Mon Aug 20 08:48:35 2018 +0200 Merge pull request #1 from javaee/master Pull from upstream
1 parent de2627a commit b498233

File tree

3 files changed

+94
-18
lines changed

3 files changed

+94
-18
lines changed

doc/release/CHANGES.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ GH 317 use System.lineSeparator() instead of System.getProperty(...)
3030
GH 321 URLName.getURL() returns incorrect url.
3131
GH 322 Dots in local part of emails not handled properly
3232
GH 323 Support loading protocol providers using ServiceLoader
33-
GH 326 Apply relaxed Content-Disposition parsing to Content-Disposition parameters
33+
GH 326 Apply relaxed Content-Disposition parsing to Content-Disposition params
34+
GH 332 http proxy support should support authenticating to the proxy server
3435

3536

3637
CHANGES IN THE 1.6.1 RELEASE

mail/src/main/java/com/sun/mail/util/SocketFetcher.java

+32-11
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import java.security.cert.*;
5353
import javax.net.*;
5454
import javax.net.ssl.*;
55-
import javax.security.auth.x500.X500Principal;
5655

5756
/**
5857
* This class is used to get Sockets. Depending on the arguments passed
@@ -275,8 +274,10 @@ private static Socket createSocket(InetAddress localaddr, int localport,
275274
", host " + host + ", port " + port +
276275
", connection timeout " + cto + ", timeout " + to +
277276
", socket factory " + sf + ", useSSL " + useSSL);
278-
277+
279278
String proxyHost = props.getProperty(prefix + ".proxy.host", null);
279+
String proxyUser = props.getProperty(prefix + ".proxy.user", null);
280+
String proxyPassword = props.getProperty(prefix + ".proxy.password", null);
280281
int proxyPort = 80;
281282
String socksHost = null;
282283
int socksPort = 1080;
@@ -295,8 +296,12 @@ private static Socket createSocket(InetAddress localaddr, int localport,
295296
proxyPort = PropUtil.getIntProperty(props,
296297
prefix + ".proxy.port", proxyPort);
297298
err = "Using web proxy host, port: " + proxyHost + ", " + proxyPort;
298-
if (logger.isLoggable(Level.FINER))
299+
if (logger.isLoggable(Level.FINER)) {
299300
logger.finer("web proxy host " + proxyHost + ", port " + proxyPort);
301+
if (proxyUser != null)
302+
logger.finer("web proxy user " + proxyUser + ", password " +
303+
(proxyPassword == null ? "<null>" : "<non-null>"));
304+
}
300305
} else if ((socksHost =
301306
props.getProperty(prefix + ".socks.host", null)) != null) {
302307
int i = socksHost.indexOf(':');
@@ -346,7 +351,8 @@ private static Socket createSocket(InetAddress localaddr, int localport,
346351
try {
347352
logger.finest("connecting...");
348353
if (proxyHost != null)
349-
proxyConnect(socket, proxyHost, proxyPort, host, port, cto);
354+
proxyConnect(socket, proxyHost, proxyPort,
355+
proxyUser, proxyPassword, host, port, cto);
350356
else if (cto >= 0)
351357
socket.connect(new InetSocketAddress(host, port), cto);
352358
else
@@ -407,7 +413,7 @@ private static SocketFactory getSocketFactory(String sfClass)
407413
if (sfClass == null || sfClass.length() == 0)
408414
return null;
409415

410-
// dynamically load the class
416+
// dynamically load the class
411417

412418
ClassLoader cl = getContextClassLoader();
413419
Class<?> clsSockFact = null;
@@ -419,7 +425,7 @@ private static SocketFactory getSocketFactory(String sfClass)
419425
if (clsSockFact == null)
420426
clsSockFact = Class.forName(sfClass);
421427
// get & invoke the getDefault() method
422-
Method mthGetDefault = clsSockFact.getMethod("getDefault",
428+
Method mthGetDefault = clsSockFact.getMethod("getDefault",
423429
new Class<?>[]{});
424430
SocketFactory sf = (SocketFactory)
425431
mthGetDefault.invoke(new Object(), new Object[]{});
@@ -662,7 +668,7 @@ private static boolean isRecoverable(Throwable t) {
662668
/**
663669
* Check the server from the Socket connection against the server name(s)
664670
* as expressed in the server certificate (RFC 2595 check).
665-
*
671+
*
666672
* @param server name of the server expected
667673
* @param sslSocket SSLSocket connected to the server
668674
* @exception IOException if we can't verify identity of server
@@ -693,7 +699,7 @@ private static void checkServerIdentity(String server, SSLSocket sslSocket)
693699

694700
/**
695701
* Do any of the names in the cert match the server name?
696-
*
702+
*
697703
* @param server name of the server expected
698704
* @param cert X509Certificate to get the subject's name from
699705
* @return true if it matches
@@ -714,7 +720,7 @@ private static boolean matchCert(String server, X509Certificate cert) {
714720
// invoke HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP)
715721
// HostnameChecker.TYPE_LDAP == 2
716722
// LDAP requires the same regex handling as we need
717-
Method getInstance = hnc.getMethod("getInstance",
723+
Method getInstance = hnc.getMethod("getInstance",
718724
new Class<?>[] { byte.class });
719725
Object hostnameChecker = getInstance.invoke(new Object(),
720726
new Object[] { Byte.valueOf((byte)2) });
@@ -820,6 +826,7 @@ private static boolean matchServer(String server, String name) {
820826
*/
821827
private static void proxyConnect(Socket socket,
822828
String proxyHost, int proxyPort,
829+
String proxyUser, String proxyPassword,
823830
String host, int port, int cto)
824831
throws IOException {
825832
if (logger.isLoggable(Level.FINE))
@@ -832,8 +839,22 @@ private static void proxyConnect(Socket socket,
832839
socket.connect(new InetSocketAddress(proxyHost, proxyPort));
833840
PrintStream os = new PrintStream(socket.getOutputStream(), false,
834841
StandardCharsets.UTF_8.name());
835-
os.print("CONNECT " + host + ":" + port + " HTTP/1.1\r\n");
836-
os.print("Host: " + host + ":" + port + "\r\n\r\n");
842+
StringBuilder requestBuilder = new StringBuilder();
843+
requestBuilder.append("CONNECT ").append(host).append(":").append(port).
844+
append(" HTTP/1.1\r\n");
845+
requestBuilder.append("Host: ").append(host).append(":").append(port).
846+
append("\r\n");
847+
if (proxyUser != null && proxyPassword != null) {
848+
byte[] upbytes = (proxyUser + ':' + proxyPassword).
849+
getBytes(StandardCharsets.UTF_8);
850+
String proxyHeaderValue = new String(
851+
BASE64EncoderStream.encode(upbytes),
852+
StandardCharsets.US_ASCII);
853+
requestBuilder.append("Proxy-Authorization: Basic ").
854+
append(proxyHeaderValue).append("\r\n");
855+
}
856+
requestBuilder.append("Proxy-Connection: keep-alive\r\n\r\n");
857+
os.print(requestBuilder.toString());
837858
os.flush();
838859
BufferedReader r = new BufferedReader(new InputStreamReader(
839860
socket.getInputStream(), StandardCharsets.UTF_8));

mail/src/test/java/com/sun/mail/util/SocketFetcherTest.java

+60-6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.io.IOException;
4444
import java.net.Socket;
4545
import java.util.Properties;
46+
import java.nio.charset.StandardCharsets;
4647

4748
import com.sun.mail.test.TestServer;
4849
import com.sun.mail.test.ProtocolHandler;
@@ -71,6 +72,15 @@ public void testProxyHostPort() {
7172
assertTrue("proxy host, port", testProxy("proxy", "localhost", "PPPP"));
7273
}
7374

75+
/**
76+
* Test connecting with proxy host and port and user name and password.
77+
*/
78+
@Test
79+
public void testProxyHostPortUserPassword() {
80+
assertTrue("proxy host, port, user, password",
81+
testProxyUserPassword("proxy", "localhost", "PPPP", "user", "pwd"));
82+
}
83+
7484
/**
7585
* Test connecting with proxy host:port.
7686
*/
@@ -106,9 +116,16 @@ public void testNoProxy() {
106116
/**
107117
*/
108118
public boolean testProxy(String type, String host, String port) {
119+
return testProxyUserPassword(type, host, port, null, null);
120+
}
121+
122+
/**
123+
*/
124+
public boolean testProxyUserPassword(String type, String host, String port,
125+
String user, String pwd) {
109126
TestServer server = null;
110127
try {
111-
ProxyHandler handler = new ProxyHandler();
128+
ProxyHandler handler = new ProxyHandler(type.equals("proxy"));
112129
server = new TestServer(handler);
113130
server.start();
114131
String sport = "" + server.getPort();
@@ -122,6 +139,10 @@ public boolean testProxy(String type, String host, String port) {
122139
if (port != null)
123140
properties.setProperty("mail.test." + type + ".port",
124141
port.replace("PPPP", sport));
142+
if (user != null)
143+
properties.setProperty("mail.test." + type + ".user", user);
144+
if (pwd != null)
145+
properties.setProperty("mail.test." + type + ".password", pwd);
125146

126147
Socket s = null;
127148
try {
@@ -135,7 +156,12 @@ public boolean testProxy(String type, String host, String port) {
135156
if (s != null)
136157
s.close();
137158
}
138-
return handler.getConnected();
159+
if (!handler.getConnected())
160+
return false;
161+
if (user != null && pwd != null)
162+
return (user + ":" + pwd).equals(handler.getUserPassword());
163+
else
164+
return true;
139165

140166
} catch (final Exception e) {
141167
//e.printStackTrace();
@@ -149,28 +175,56 @@ public boolean testProxy(String type, String host, String port) {
149175
}
150176

151177
/**
152-
* Custom handler. Remember whether any data was sent.
178+
* Custom handler. Remember whether any data was sent
179+
* and save user/password string;
153180
*/
154181
private static class ProxyHandler extends ProtocolHandler {
182+
private boolean http;
183+
155184
// must be static because handler is cloned for each connection
156185
private static volatile boolean connected;
186+
private static volatile String userPassword;
157187

158-
public ProxyHandler() {
188+
public ProxyHandler(boolean http) {
189+
this.http = http;
159190
connected = false;
160191
}
161192

162193
@Override
163194
public void handleCommand() throws IOException {
164-
int c = in.read();
165-
if (c >= 0) {
195+
if (!http) {
196+
int c = in.read();
197+
if (c >= 0) {
198+
// any data means a real client connected
199+
connected = true;
200+
}
201+
exit();
202+
}
203+
204+
// else, http...
205+
String line;
206+
while ((line = readLine()) != null) {
166207
// any data means a real client connected
167208
connected = true;
209+
if (line.length() == 0)
210+
break;
211+
if (line.startsWith("Proxy-Authorization:")) {
212+
int i = line.indexOf("Basic ") + 6;
213+
String up = line.substring(i);
214+
userPassword = new String(BASE64DecoderStream.decode(
215+
up.getBytes(StandardCharsets.US_ASCII)),
216+
StandardCharsets.UTF_8);
217+
}
168218
}
169219
exit();
170220
}
171221

172222
public boolean getConnected() {
173223
return connected;
174224
}
225+
226+
public String getUserPassword() {
227+
return userPassword;
228+
}
175229
}
176230
}

0 commit comments

Comments
 (0)