View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2014 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.utils;
19  
20  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21  import org.apache.commons.lang3.StringUtils;
22  
23  import java.io.IOException;
24  import java.net.Authenticator;
25  import java.net.HttpURLConnection;
26  import java.net.InetSocketAddress;
27  import java.net.PasswordAuthentication;
28  import java.net.Proxy;
29  import java.net.SocketAddress;
30  import java.net.URL;
31  import java.net.URLConnection;
32  import java.security.KeyManagementException;
33  import java.security.NoSuchAlgorithmException;
34  import javax.net.ssl.HttpsURLConnection;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * A URLConnection Factory to create new connections. This encapsulates several
40   * configuration checks to ensure that the connection uses the correct proxy
41   * settings.
42   *
43   * @author Jeremy Long
44   */
45  public final class URLConnectionFactory {
46  
47      /**
48       * The logger.
49       */
50      private static final Logger LOGGER = LoggerFactory.getLogger(URLConnectionFactory.class);
51  
52      /**
53       * Private constructor for this factory.
54       */
55      private URLConnectionFactory() {
56      }
57  
58      /**
59       * Utility method to create an HttpURLConnection. If the application is
60       * configured to use a proxy this method will retrieve the proxy settings
61       * and use them when setting up the connection.
62       *
63       * @param url the url to connect to
64       * @return an HttpURLConnection
65       * @throws URLConnectionFailureException thrown if there is an exception
66       */
67      @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", justification = "Just being extra safe")
68      public static HttpURLConnection createHttpURLConnection(URL url) throws URLConnectionFailureException {
69          HttpURLConnection conn = null;
70          final String proxyUrl = Settings.getString(Settings.KEYS.PROXY_SERVER);
71  
72          try {
73              if (proxyUrl != null && !matchNonProxy(url)) {
74                  final int proxyPort = Settings.getInt(Settings.KEYS.PROXY_PORT);
75                  final SocketAddress address = new InetSocketAddress(proxyUrl, proxyPort);
76  
77                  final String username = Settings.getString(Settings.KEYS.PROXY_USERNAME);
78                  final String password = Settings.getString(Settings.KEYS.PROXY_PASSWORD);
79  
80                  if (username != null && password != null) {
81                      final Authenticator auth = new Authenticator() {
82                          @Override
83                          public PasswordAuthentication getPasswordAuthentication() {
84                              if (getRequestorType().equals(Authenticator.RequestorType.PROXY)) {
85                                  return new PasswordAuthentication(username, password.toCharArray());
86                              }
87                              return super.getPasswordAuthentication();
88                          }
89                      };
90                      Authenticator.setDefault(auth);
91                  }
92  
93                  final Proxy proxy = new Proxy(Proxy.Type.HTTP, address);
94                  conn = (HttpURLConnection) url.openConnection(proxy);
95              } else {
96                  conn = (HttpURLConnection) url.openConnection();
97              }
98              final int timeout = Settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000);
99              conn.setConnectTimeout(timeout);
100             conn.setInstanceFollowRedirects(true);
101         } catch (IOException ex) {
102             if (conn != null) {
103                 try {
104                     conn.disconnect();
105                 } finally {
106                     conn = null;
107                 }
108             }
109             throw new URLConnectionFailureException("Error getting connection.", ex);
110         }
111         configureTLS(url, conn);
112         return conn;
113     }
114 
115     /**
116      * Check if hostname matches nonProxy settings
117      *
118      * @param url the url to connect to
119      * @return matching result. true: match nonProxy
120      */
121     private static boolean matchNonProxy(final URL url) {
122         final String host = url.getHost();
123 
124         // code partially from org.apache.maven.plugins.site.AbstractDeployMojo#getProxyInfo
125         final String nonProxyHosts = Settings.getString(Settings.KEYS.PROXY_NON_PROXY_HOSTS);
126         if (null != nonProxyHosts) {
127             final String[] nonProxies = nonProxyHosts.split("(,)|(;)|(\\|)");
128             for (final String nonProxyHost : nonProxies) {
129                 //if ( StringUtils.contains( nonProxyHost, "*" ) )
130                 if (null != nonProxyHost && nonProxyHost.contains("*")) {
131                     // Handle wildcard at the end, beginning or middle of the nonProxyHost
132                     final int pos = nonProxyHost.indexOf('*');
133                     final String nonProxyHostPrefix = nonProxyHost.substring(0, pos);
134                     final String nonProxyHostSuffix = nonProxyHost.substring(pos + 1);
135                     // prefix*
136                     if (!StringUtils.isEmpty(nonProxyHostPrefix) && host.startsWith(nonProxyHostPrefix) && StringUtils.isEmpty(nonProxyHostSuffix)) {
137                         return true;
138                     }
139                     // *suffix
140                     if (StringUtils.isEmpty(nonProxyHostPrefix) && !StringUtils.isEmpty(nonProxyHostSuffix) && host.endsWith(nonProxyHostSuffix)) {
141                         return true;
142                     }
143                     // prefix*suffix
144                     if (!StringUtils.isEmpty(nonProxyHostPrefix) && host.startsWith(nonProxyHostPrefix) && !StringUtils.isEmpty(nonProxyHostSuffix)
145                             && host.endsWith(nonProxyHostSuffix)) {
146                         return true;
147                     }
148                 } else if (host.equals(nonProxyHost)) {
149                     return true;
150                 }
151             }
152         }
153         return false;
154     }
155 
156     /**
157      * Utility method to create an HttpURLConnection. The use of a proxy here is
158      * optional as there may be cases where a proxy is configured but we don't
159      * want to use it (for example, if there's an internal repository
160      * configured)
161      *
162      * @param url the URL to connect to
163      * @param proxy whether to use the proxy (if configured)
164      * @return a newly constructed HttpURLConnection
165      * @throws URLConnectionFailureException thrown if there is an exception
166      */
167     public static HttpURLConnection createHttpURLConnection(URL url, boolean proxy) throws URLConnectionFailureException {
168         if (proxy) {
169             return createHttpURLConnection(url);
170         }
171         HttpURLConnection conn = null;
172         try {
173             conn = (HttpURLConnection) url.openConnection();
174             final int timeout = Settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000);
175             conn.setConnectTimeout(timeout);
176             conn.setInstanceFollowRedirects(true);
177         } catch (IOException ioe) {
178             throw new URLConnectionFailureException("Error getting connection.", ioe);
179         }
180         configureTLS(url, conn);
181         return conn;
182     }
183 
184     /**
185      * If the protocol is HTTPS, this will configure the cipher suites so that
186      * connections can be made to the NVD, and others, using older versions of
187      * Java.
188      *
189      * @param url the URL
190      * @param conn the connection
191      */
192     private static void configureTLS(URL url, URLConnection conn) {
193         if ("https".equals(url.getProtocol())) {
194             try {
195                 final HttpsURLConnection secCon = (HttpsURLConnection) conn;
196                 final SSLSocketFactoryEx factory = new SSLSocketFactoryEx();
197                 secCon.setSSLSocketFactory(factory);
198             } catch (NoSuchAlgorithmException ex) {
199                 LOGGER.debug("Unsupported algorithm in SSLSocketFactoryEx", ex);
200             } catch (KeyManagementException ex) {
201                 LOGGER.debug("Key management exception in SSLSocketFactoryEx", ex);
202             }
203         }
204     }
205 }