PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/platform/com.subgraph.sgmail.javamail/src/com/sun/mail/util/SocketFetcher.java

https://github.com/subgraph/sgmail
Java | 726 lines | 442 code | 49 blank | 235 comment | 112 complexity | 3ac9a73219653b37380d54a2b1bb5473 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * The contents of this file are subject to the terms of either the GNU
  7. * General Public License Version 2 only ("GPL") or the Common Development
  8. * and Distribution License("CDDL") (collectively, the "License"). You
  9. * may not use this file except in compliance with the License. You can
  10. * obtain a copy of the License at
  11. * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
  12. * or packager/legal/LICENSE.txt. See the License for the specific
  13. * language governing permissions and limitations under the License.
  14. *
  15. * When distributing the software, include this License Header Notice in each
  16. * file and include the License file at packager/legal/LICENSE.txt.
  17. *
  18. * GPL Classpath Exception:
  19. * Oracle designates this particular file as subject to the "Classpath"
  20. * exception as provided by Oracle in the GPL Version 2 section of the License
  21. * file that accompanied this code.
  22. *
  23. * Modifications:
  24. * If applicable, add the following below the License Header, with the fields
  25. * enclosed by brackets [] replaced by your own identifying information:
  26. * "Portions Copyright [year] [name of copyright owner]"
  27. *
  28. * Contributor(s):
  29. * If you wish your version of this file to be governed by only the CDDL or
  30. * only the GPL Version 2, indicate your decision by adding "[Contributor]
  31. * elects to include this software in this distribution under the [CDDL or GPL
  32. * Version 2] license." If you don't indicate a single choice of license, a
  33. * recipient has the option to distribute your version of this file under
  34. * either the CDDL, the GPL Version 2 or to extend the choice of license to
  35. * its licensees as provided above. However, if you add GPL Version 2 code
  36. * and therefore, elected the GPL Version 2 license, then the option applies
  37. * only if the new code is made subject to such option by the copyright
  38. * holder.
  39. */
  40. package com.sun.mail.util;
  41. import java.security.*;
  42. import java.net.*;
  43. import java.io.*;
  44. import java.lang.reflect.*;
  45. import java.util.*;
  46. import java.util.regex.*;
  47. import java.util.logging.Level;
  48. import java.security.cert.*;
  49. import javax.net.*;
  50. import javax.net.ssl.*;
  51. import javax.security.auth.x500.X500Principal;
  52. /**
  53. * This class is used to get Sockets. Depending on the arguments passed
  54. * it will either return a plain java.net.Socket or dynamically load
  55. * the SocketFactory class specified in the classname param and return
  56. * a socket created by that SocketFactory.
  57. *
  58. * @author Max Spivak
  59. * @author Bill Shannon
  60. */
  61. public class SocketFetcher {
  62. private static MailLogger logger = new MailLogger(
  63. SocketFetcher.class,
  64. "socket",
  65. "DEBUG SocketFetcher",
  66. PropUtil.getBooleanSystemProperty("mail.socket.debug", false),
  67. System.out);
  68. // No one should instantiate this class.
  69. private SocketFetcher() {
  70. }
  71. /**
  72. * This method returns a Socket. Properties control the use of
  73. * socket factories and other socket characteristics. The properties
  74. * used are: <p>
  75. * <ul>
  76. * <li> <i>prefix</i>.socketFactory
  77. * <li> <i>prefix</i>.socketFactory.class
  78. * <li> <i>prefix</i>.socketFactory.fallback
  79. * <li> <i>prefix</i>.socketFactory.port
  80. * <li> <i>prefix</i>.ssl.socketFactory
  81. * <li> <i>prefix</i>.ssl.socketFactory.class
  82. * <li> <i>prefix</i>.ssl.socketFactory.port
  83. * <li> <i>prefix</i>.timeout
  84. * <li> <i>prefix</i>.connectiontimeout
  85. * <li> <i>prefix</i>.localaddress
  86. * <li> <i>prefix</i>.localport
  87. * </ul> <p>
  88. * If we're making an SSL connection, the ssl.socketFactory
  89. * properties are used first, if set. <p>
  90. *
  91. * If the socketFactory property is set, the value is an
  92. * instance of a SocketFactory class, not a string. The
  93. * instance is used directly. If the socketFactory property
  94. * is not set, the socketFactory.class property is considered.
  95. * (Note that the SocketFactory property must be set using the
  96. * <code>put</code> method, not the <code>setProperty</code>
  97. * method.) <p>
  98. *
  99. * If the socketFactory.class property isn't set, the socket
  100. * returned is an instance of java.net.Socket connected to the
  101. * given host and port. If the socketFactory.class property is set,
  102. * it is expected to contain a fully qualified classname of a
  103. * javax.net.SocketFactory subclass. In this case, the class is
  104. * dynamically instantiated and a socket created by that
  105. * SocketFactory is returned. <p>
  106. *
  107. * If the socketFactory.fallback property is set to false, don't
  108. * fall back to using regular sockets if the socket factory fails. <p>
  109. *
  110. * The socketFactory.port specifies a port to use when connecting
  111. * through the socket factory. If unset, the port argument will be
  112. * used. <p>
  113. *
  114. * If the connectiontimeout property is set, the timeout is passed
  115. * to the socket connect method. <p>
  116. *
  117. * If the timeout property is set, it is used to set the socket timeout.
  118. * <p>
  119. *
  120. * If the localaddress property is set, it's used as the local address
  121. * to bind to. If the localport property is also set, it's used as the
  122. * local port number to bind to.
  123. *
  124. * @param host The host to connect to
  125. * @param port The port to connect to at the host
  126. * @param props Properties object containing socket properties
  127. * @param prefix Property name prefix, e.g., "mail.imap"
  128. * @param useSSL use the SSL socket factory as the default
  129. */
  130. public static Socket getSocket(String host, int port, Properties props,
  131. String prefix, boolean useSSL)
  132. throws IOException {
  133. if (logger.isLoggable(Level.FINER))
  134. logger.finer("getSocket" + ", host " + host + ", port " + port +
  135. ", prefix " + prefix + ", useSSL " + useSSL);
  136. if (prefix == null)
  137. prefix = "socket";
  138. if (props == null)
  139. props = new Properties(); // empty
  140. int cto = PropUtil.getIntProperty(props,
  141. prefix + ".connectiontimeout", -1);
  142. Socket socket = null;
  143. String localaddrstr = props.getProperty(prefix + ".localaddress", null);
  144. InetAddress localaddr = null;
  145. if (localaddrstr != null)
  146. localaddr = InetAddress.getByName(localaddrstr);
  147. int localport = PropUtil.getIntProperty(props,
  148. prefix + ".localport", 0);
  149. boolean fb = PropUtil.getBooleanProperty(props,
  150. prefix + ".socketFactory.fallback", true);
  151. int sfPort = -1;
  152. String sfErr = "unknown socket factory";
  153. int to = PropUtil.getIntProperty(props, prefix + ".timeout", -1);
  154. try {
  155. /*
  156. * If using SSL, first look for SSL-specific class name or
  157. * factory instance.
  158. */
  159. SocketFactory sf = null;
  160. String sfPortName = null;
  161. if (useSSL) {
  162. Object sfo = props.get(prefix + ".ssl.socketFactory");
  163. if (sfo instanceof SocketFactory) {
  164. sf = (SocketFactory)sfo;
  165. sfErr = "SSL socket factory instance " + sf;
  166. }
  167. if (sf == null) {
  168. String sfClass =
  169. props.getProperty(prefix + ".ssl.socketFactory.class");
  170. sf = getSocketFactory(sfClass);
  171. sfErr = "SSL socket factory class " + sfClass;
  172. }
  173. sfPortName = ".ssl.socketFactory.port";
  174. }
  175. if (sf == null) {
  176. Object sfo = props.get(prefix + ".socketFactory");
  177. if (sfo instanceof SocketFactory) {
  178. sf = (SocketFactory)sfo;
  179. sfErr = "socket factory instance " + sf;
  180. }
  181. if (sf == null) {
  182. String sfClass =
  183. props.getProperty(prefix + ".socketFactory.class");
  184. sf = getSocketFactory(sfClass);
  185. sfErr = "socket factory class " + sfClass;
  186. }
  187. sfPortName = ".socketFactory.port";
  188. }
  189. // if we now have a socket factory, use it
  190. if (sf != null) {
  191. sfPort = PropUtil.getIntProperty(props,
  192. prefix + sfPortName, -1);
  193. // if port passed in via property isn't valid, use param
  194. if (sfPort == -1)
  195. sfPort = port;
  196. socket = createSocket(localaddr, localport,
  197. host, sfPort, cto, to, props, prefix, sf, useSSL);
  198. }
  199. } catch (SocketTimeoutException sex) {
  200. throw sex;
  201. } catch (Exception ex) {
  202. if (!fb) {
  203. if (ex instanceof InvocationTargetException) {
  204. Throwable t =
  205. ((InvocationTargetException)ex).getTargetException();
  206. if (t instanceof Exception)
  207. ex = (Exception)t;
  208. }
  209. if (ex instanceof IOException)
  210. throw (IOException)ex;
  211. throw new SocketConnectException("Using " + sfErr, ex,
  212. host, sfPort, cto);
  213. }
  214. }
  215. if (socket == null) {
  216. socket = createSocket(localaddr, localport,
  217. host, port, cto, to, props, prefix, null, useSSL);
  218. } else {
  219. if (to >= 0)
  220. socket.setSoTimeout(to);
  221. }
  222. return socket;
  223. }
  224. public static Socket getSocket(String host, int port, Properties props,
  225. String prefix) throws IOException {
  226. return getSocket(host, port, props, prefix, false);
  227. }
  228. /**
  229. * Create a socket with the given local address and connected to
  230. * the given host and port. Use the specified connection timeout
  231. * and read timeout.
  232. * If a socket factory is specified, use it. Otherwise, use the
  233. * SSLSocketFactory if useSSL is true.
  234. */
  235. private static Socket createSocket(InetAddress localaddr, int localport,
  236. String host, int port, int cto, int to,
  237. Properties props, String prefix,
  238. SocketFactory sf, boolean useSSL)
  239. throws IOException {
  240. Socket socket = null;
  241. String socksHost = props.getProperty(prefix + ".socks.host", null);
  242. int socksPort = 1080;
  243. String err = null;
  244. if (socksHost != null) {
  245. int i = socksHost.indexOf(':');
  246. if (i >= 0) {
  247. socksHost = socksHost.substring(0, i);
  248. try {
  249. socksPort = Integer.parseInt(socksHost.substring(i + 1));
  250. } catch (NumberFormatException ex) {
  251. // ignore it
  252. }
  253. }
  254. socksPort = PropUtil.getIntProperty(props,
  255. prefix + ".socks.port", socksPort);
  256. err = "Using SOCKS host, port: " + socksHost + ", " + socksPort;
  257. if (logger.isLoggable(Level.FINER))
  258. logger.finer("socks host " + socksHost + ", port " + socksPort);
  259. }
  260. if (sf != null)
  261. socket = sf.createSocket();
  262. if (socket == null) {
  263. if (socksHost != null)
  264. socket = new Socket(
  265. new java.net.Proxy(java.net.Proxy.Type.SOCKS,
  266. new InetSocketAddress(socksHost, socksPort)));
  267. else
  268. socket = new Socket();
  269. }
  270. if (to >= 0)
  271. socket.setSoTimeout(to);
  272. int writeTimeout = PropUtil.getIntProperty(props,
  273. prefix + ".writetimeout", -1);
  274. if (writeTimeout != -1) // wrap original
  275. socket = new WriteTimeoutSocket(socket, writeTimeout);
  276. if (localaddr != null)
  277. socket.bind(new InetSocketAddress(localaddr, localport));
  278. try {
  279. if (cto >= 0)
  280. socket.connect(new InetSocketAddress(host, port), cto);
  281. else
  282. socket.connect(new InetSocketAddress(host, port));
  283. } catch (IOException ex) {
  284. throw new SocketConnectException(err, ex, host, port, cto);
  285. }
  286. /*
  287. * If we want an SSL connection and we didn't get an SSLSocket,
  288. * wrap our plain Socket with an SSLSocket.
  289. */
  290. if (useSSL && !(socket instanceof SSLSocket)) {
  291. String trusted;
  292. SSLSocketFactory ssf;
  293. if ((trusted = props.getProperty(prefix + ".ssl.trust")) != null) {
  294. try {
  295. MailSSLSocketFactory msf = new MailSSLSocketFactory();
  296. if (trusted.equals("*"))
  297. msf.setTrustAllHosts(true);
  298. else
  299. msf.setTrustedHosts(trusted.split("\\s+"));
  300. ssf = msf;
  301. } catch (GeneralSecurityException gex) {
  302. IOException ioex = new IOException(
  303. "Can't create MailSSLSocketFactory");
  304. ioex.initCause(gex);
  305. throw ioex;
  306. }
  307. } else
  308. ssf = (SSLSocketFactory)SSLSocketFactory.getDefault();
  309. socket = ssf.createSocket(socket, host, port, true);
  310. sf = ssf;
  311. }
  312. /*
  313. * No matter how we created the socket, if it turns out to be an
  314. * SSLSocket, configure it.
  315. */
  316. configureSSLSocket(socket, host, props, prefix, sf);
  317. return socket;
  318. }
  319. /**
  320. * Return a socket factory of the specified class.
  321. */
  322. private static SocketFactory getSocketFactory(String sfClass)
  323. throws ClassNotFoundException,
  324. NoSuchMethodException,
  325. IllegalAccessException,
  326. InvocationTargetException {
  327. if (sfClass == null || sfClass.length() == 0)
  328. return null;
  329. // dynamically load the class
  330. ClassLoader cl = getContextClassLoader();
  331. Class clsSockFact = null;
  332. if (cl != null) {
  333. try {
  334. clsSockFact = Class.forName(sfClass, false, cl);
  335. } catch (ClassNotFoundException cex) { }
  336. }
  337. if (clsSockFact == null)
  338. clsSockFact = Class.forName(sfClass);
  339. // get & invoke the getDefault() method
  340. Method mthGetDefault = clsSockFact.getMethod("getDefault",
  341. new Class[]{});
  342. SocketFactory sf = (SocketFactory)
  343. mthGetDefault.invoke(new Object(), new Object[]{});
  344. return sf;
  345. }
  346. /**
  347. * Start TLS on an existing socket.
  348. * Supports the "STARTTLS" command in many protocols.
  349. * This version for compatibility with possible third party code
  350. * that might've used this API even though it shouldn't.
  351. */
  352. public static Socket startTLS(Socket socket) throws IOException {
  353. return startTLS(socket, new Properties(), "socket");
  354. }
  355. /**
  356. * Start TLS on an existing socket.
  357. * Supports the "STARTTLS" command in many protocols.
  358. * This version for compatibility with possible third party code
  359. * that might've used this API even though it shouldn't.
  360. */
  361. public static Socket startTLS(Socket socket, Properties props,
  362. String prefix) throws IOException {
  363. InetAddress a = socket.getInetAddress();
  364. String host = a.getHostName();
  365. return startTLS(socket, host, props, prefix);
  366. }
  367. /**
  368. * Start TLS on an existing socket.
  369. * Supports the "STARTTLS" command in many protocols.
  370. */
  371. public static Socket startTLS(Socket socket, String host, Properties props,
  372. String prefix) throws IOException {
  373. int port = socket.getPort();
  374. if (logger.isLoggable(Level.FINER))
  375. logger.finer("startTLS host " + host + ", port " + port);
  376. String sfErr = "unknown socket factory";
  377. try {
  378. SSLSocketFactory ssf = null;
  379. SocketFactory sf = null;
  380. // first, look for an SSL socket factory
  381. Object sfo = props.get(prefix + ".ssl.socketFactory");
  382. if (sfo instanceof SocketFactory) {
  383. sf = (SocketFactory)sfo;
  384. sfErr = "SSL socket factory instance " + sf;
  385. }
  386. if (sf == null) {
  387. String sfClass =
  388. props.getProperty(prefix + ".ssl.socketFactory.class");
  389. sf = getSocketFactory(sfClass);
  390. sfErr = "SSL socket factory class " + sfClass;
  391. }
  392. if (sf != null && sf instanceof SSLSocketFactory)
  393. ssf = (SSLSocketFactory)sf;
  394. // next, look for a regular socket factory that happens to be
  395. // an SSL socket factory
  396. if (ssf == null) {
  397. sfo = props.get(prefix + ".socketFactory");
  398. if (sfo instanceof SocketFactory) {
  399. sf = (SocketFactory)sfo;
  400. sfErr = "socket factory instance " + sf;
  401. }
  402. if (sf == null) {
  403. String sfClass =
  404. props.getProperty(prefix + ".socketFactory.class");
  405. sf = getSocketFactory(sfClass);
  406. sfErr = "socket factory class " + sfClass;
  407. }
  408. if (sf != null && sf instanceof SSLSocketFactory)
  409. ssf = (SSLSocketFactory)sf;
  410. }
  411. // finally, use the default SSL socket factory
  412. if (ssf == null) {
  413. String trusted;
  414. if ((trusted = props.getProperty(prefix + ".ssl.trust")) !=
  415. null) {
  416. try {
  417. MailSSLSocketFactory msf = new MailSSLSocketFactory();
  418. if (trusted.equals("*"))
  419. msf.setTrustAllHosts(true);
  420. else
  421. msf.setTrustedHosts(trusted.split("\\s+"));
  422. ssf = msf;
  423. sfErr = "mail SSL socket factory";
  424. } catch (GeneralSecurityException gex) {
  425. IOException ioex = new IOException(
  426. "Can't create MailSSLSocketFactory");
  427. ioex.initCause(gex);
  428. throw ioex;
  429. }
  430. } else {
  431. ssf = (SSLSocketFactory)SSLSocketFactory.getDefault();
  432. sfErr = "default SSL socket factory";
  433. }
  434. }
  435. socket = ssf.createSocket(socket, host, port, true);
  436. configureSSLSocket(socket, host, props, prefix, ssf);
  437. } catch (Exception ex) {
  438. if (ex instanceof InvocationTargetException) {
  439. Throwable t =
  440. ((InvocationTargetException)ex).getTargetException();
  441. if (t instanceof Exception)
  442. ex = (Exception)t;
  443. }
  444. if (ex instanceof IOException)
  445. throw (IOException)ex;
  446. // wrap anything else before sending it on
  447. IOException ioex = new IOException(
  448. "Exception in startTLS using " + sfErr +
  449. ": host, port: " +
  450. host + ", " + port +
  451. "; Exception: " + ex);
  452. ioex.initCause(ex);
  453. throw ioex;
  454. }
  455. return socket;
  456. }
  457. /**
  458. * Configure the SSL options for the socket (if it's an SSL socket),
  459. * based on the mail.<protocol>.ssl.protocols and
  460. * mail.<protocol>.ssl.ciphersuites properties.
  461. * Check the identity of the server as specified by the
  462. * mail.<protocol>.ssl.checkserveridentity property.
  463. */
  464. private static void configureSSLSocket(Socket socket, String host,
  465. Properties props, String prefix, SocketFactory sf)
  466. throws IOException {
  467. if (!(socket instanceof SSLSocket))
  468. return;
  469. SSLSocket sslsocket = (SSLSocket)socket;
  470. String protocols = props.getProperty(prefix + ".ssl.protocols", null);
  471. if (protocols != null)
  472. sslsocket.setEnabledProtocols(stringArray(protocols));
  473. else {
  474. /*
  475. * At least the UW IMAP server insists on only the TLSv1
  476. * protocol for STARTTLS, and won't accept the old SSLv2
  477. * or SSLv3 protocols. Here we enable only the TLSv1
  478. * protocol. XXX - this should probably be parameterized.
  479. */
  480. sslsocket.setEnabledProtocols(new String[] {"TLSv1"});
  481. }
  482. String ciphers = props.getProperty(prefix + ".ssl.ciphersuites", null);
  483. if (ciphers != null)
  484. sslsocket.setEnabledCipherSuites(stringArray(ciphers));
  485. if (logger.isLoggable(Level.FINER)) {
  486. logger.finer("SSL protocols after " +
  487. Arrays.asList(sslsocket.getEnabledProtocols()));
  488. logger.finer("SSL ciphers after " +
  489. Arrays.asList(sslsocket.getEnabledCipherSuites()));
  490. }
  491. /*
  492. * Force the handshake to be done now so that we can report any
  493. * errors (e.g., certificate errors) to the caller of the startTLS
  494. * method.
  495. */
  496. sslsocket.startHandshake();
  497. /*
  498. * Check server identity and trust.
  499. */
  500. boolean idCheck = PropUtil.getBooleanProperty(props,
  501. prefix + ".ssl.checkserveridentity", false);
  502. if (idCheck)
  503. checkServerIdentity(host, sslsocket);
  504. if (sf instanceof MailSSLSocketFactory) {
  505. MailSSLSocketFactory msf = (MailSSLSocketFactory)sf;
  506. if (!msf.isServerTrusted(host, sslsocket)) {
  507. try {
  508. sslsocket.close();
  509. } finally {
  510. throw new IOException("Server is not trusted: " + host);
  511. }
  512. }
  513. }
  514. }
  515. /**
  516. * Check the server from the Socket connection against the server name(s)
  517. * as expressed in the server certificate (RFC 2595 check).
  518. *
  519. * @param server name of the server expected
  520. * @param sslSocket SSLSocket connected to the server
  521. * @return true if the RFC 2595 check passes
  522. */
  523. private static void checkServerIdentity(String server, SSLSocket sslSocket)
  524. throws IOException {
  525. // Check against the server name(s) as expressed in server certificate
  526. try {
  527. java.security.cert.Certificate[] certChain =
  528. sslSocket.getSession().getPeerCertificates();
  529. if (certChain != null && certChain.length > 0 &&
  530. certChain[0] instanceof X509Certificate &&
  531. matchCert(server, (X509Certificate)certChain[0]))
  532. return;
  533. } catch (SSLPeerUnverifiedException e) {
  534. sslSocket.close();
  535. IOException ioex = new IOException(
  536. "Can't verify identity of server: " + server);
  537. ioex.initCause(e);
  538. throw ioex;
  539. }
  540. // If we get here, there is nothing to consider the server as trusted.
  541. sslSocket.close();
  542. throw new IOException("Can't verify identity of server: " + server);
  543. }
  544. /**
  545. * Do any of the names in the cert match the server name?
  546. *
  547. * @param server name of the server expected
  548. * @param cert X509Certificate to get the subject's name from
  549. * @return true if it matches
  550. */
  551. private static boolean matchCert(String server, X509Certificate cert) {
  552. if (logger.isLoggable(Level.FINER))
  553. logger.finer("matchCert server " +
  554. server + ", cert " + cert);
  555. /*
  556. * First, try to use sun.security.util.HostnameChecker,
  557. * which exists in Sun's JDK starting with 1.4.1.
  558. * We use reflection to access it in case it's not available
  559. * in the JDK we're running on.
  560. */
  561. try {
  562. Class hnc = Class.forName("sun.security.util.HostnameChecker");
  563. // invoke HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP)
  564. // HostnameChecker.TYPE_LDAP == 2
  565. // LDAP requires the same regex handling as we need
  566. Method getInstance = hnc.getMethod("getInstance",
  567. new Class[] { byte.class });
  568. Object hostnameChecker = getInstance.invoke(new Object(),
  569. new Object[] { Byte.valueOf((byte)2) });
  570. // invoke hostnameChecker.match( server, cert)
  571. if (logger.isLoggable(Level.FINER))
  572. logger.finer("using sun.security.util.HostnameChecker");
  573. Method match = hnc.getMethod("match",
  574. new Class[] { String.class, X509Certificate.class });
  575. try {
  576. match.invoke(hostnameChecker, new Object[] { server, cert });
  577. return true;
  578. } catch (InvocationTargetException cex) {
  579. logger.log(Level.FINER, "FAIL", cex);
  580. return false;
  581. }
  582. } catch (Exception ex) {
  583. logger.log(Level.FINER, "NO sun.security.util.HostnameChecker", ex);
  584. // ignore failure and continue below
  585. }
  586. /*
  587. * Lacking HostnameChecker, we implement a crude version of
  588. * the same checks ourselves.
  589. */
  590. try {
  591. /*
  592. * Check each of the subjectAltNames.
  593. * XXX - only checks DNS names, should also handle
  594. * case where server name is a literal IP address
  595. */
  596. Collection names = cert.getSubjectAlternativeNames();
  597. if (names != null) {
  598. boolean foundName = false;
  599. for (Iterator it = names.iterator(); it.hasNext(); ) {
  600. List nameEnt = (List)it.next();
  601. Integer type = (Integer)nameEnt.get(0);
  602. if (type.intValue() == 2) { // 2 == dNSName
  603. foundName = true;
  604. String name = (String)nameEnt.get(1);
  605. if (logger.isLoggable(Level.FINER))
  606. logger.finer("found name: " + name);
  607. if (matchServer(server, name))
  608. return true;
  609. }
  610. }
  611. if (foundName) // found a name, but no match
  612. return false;
  613. }
  614. } catch (CertificateParsingException ex) {
  615. // ignore it
  616. }
  617. // XXX - following is a *very* crude parse of the name and ignores
  618. // all sorts of important issues such as quoting
  619. Pattern p = Pattern.compile("CN=([^,]*)");
  620. Matcher m = p.matcher(cert.getSubjectX500Principal().getName());
  621. if (m.find() && matchServer(server, m.group(1).trim()))
  622. return true;
  623. return false;
  624. }
  625. /**
  626. * Does the server we're expecting to connect to match the
  627. * given name from the server's certificate?
  628. *
  629. * @param server name of the server expected
  630. * @param name name from the server's certificate
  631. */
  632. private static boolean matchServer(String server, String name) {
  633. if (logger.isLoggable(Level.FINER))
  634. logger.finer("match server " + server + " with " + name);
  635. if (name.startsWith("*.")) {
  636. // match "foo.example.com" with "*.example.com"
  637. String tail = name.substring(2);
  638. if (tail.length() == 0)
  639. return false;
  640. int off = server.length() - tail.length();
  641. if (off < 1)
  642. return false;
  643. // if tail matches and is preceeded by "."
  644. return server.charAt(off - 1) == '.' &&
  645. server.regionMatches(true, off, tail, 0, tail.length());
  646. } else
  647. return server.equalsIgnoreCase(name);
  648. }
  649. /**
  650. * Parse a string into whitespace separated tokens
  651. * and return the tokens in an array.
  652. */
  653. private static String[] stringArray(String s) {
  654. StringTokenizer st = new StringTokenizer(s);
  655. List tokens = new ArrayList();
  656. while (st.hasMoreTokens())
  657. tokens.add(st.nextToken());
  658. return (String[])tokens.toArray(new String[tokens.size()]);
  659. }
  660. /**
  661. * Convenience method to get our context class loader.
  662. * Assert any privileges we might have and then call the
  663. * Thread.getContextClassLoader method.
  664. */
  665. private static ClassLoader getContextClassLoader() {
  666. return (ClassLoader)
  667. AccessController.doPrivileged(new PrivilegedAction() {
  668. public Object run() {
  669. ClassLoader cl = null;
  670. try {
  671. cl = Thread.currentThread().getContextClassLoader();
  672. } catch (SecurityException ex) { }
  673. return cl;
  674. }
  675. });
  676. }
  677. }