1 /* 2 * Created on Jun 6, 2004 3 * 4 * Paros and its related class files. 5 * 6 * Paros is an HTTP/HTTPS proxy for assessing web application security. 7 * Copyright (C) 2003-2004 Chinotec Technologies Company 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the Clarified Artistic License 11 * as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * Clarified Artistic License for more details. 17 * 18 * You should have received a copy of the Clarified Artistic License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 */ 22 // ZAP: 2011/08/03 Cope with unexpected values in config file 23 // ZAP: 2012/04/23 Added @Override annotation to the appropriate method and removed 24 // unnecessary cast. 25 // ZAP: 2012/11/15 Issue 416: Normalise how multiple related options are managed 26 // throughout ZAP and enhance the usability of some options. 27 // ZAP: 2013/01/04 Added portsForSslTunneling parameter with method 28 // isPortDemandingSslTunnel() to indicate HTTP CONNECT behavior. 29 // ZAP: 2013/01/30 Issue 478: Allow to choose to send ZAP's managed cookies on 30 // a single Cookie request header and set it as the default 31 // ZAP: 2013/12/13 Issue 939: ZAP should accept SSL connections on non-standard ports automatically 32 // ZAP: 2014/03/23 Issue 416: Normalise how multiple related options are managed throughout ZAP 33 // and enhance the usability of some options 34 // ZAP: 2014/03/23 Issue 968: Allow to choose the enabled SSL/TLS protocols 35 // ZAP: 2014/03/23 Issue 1100: Annotate option methods that shouldn't be exposed in the ZAP API 36 // ZAP: 2041/08/14 Issue 1305: Outgoing proxy is disabled when updating from old versions 37 // ZAP: 2016/08/08 Issue 2742: Allow for override/customization of Java's "networkaddress.cache.ttl" 38 // value 39 // ZAP: 2017/01/11 Exclude some options from the API (manually handled to return correct values). 40 // ZAP: 2017/04/14 Validate that the SSL/TLS versions persisted can be set/used. 41 // ZAP: 2017/05/02 Added option key to enable / disable HTTP State 42 // ZAP: 2017/05/15 Ensure HttpState is non-null when HTTP State is enabled. 43 // ZAP: 2017/06/19 Do not allow to set negative timeout values and expose the default value. 44 // ZAP: 2017/09/26 Use helper methods to read the configurations. 45 // ZAP: 2018/02/14 Remove unnecessary boxing / unboxing 46 // ZAP: 2018/08/10 Set the default user agent to HttpRequestHeader (Issue 4846). 47 // ZAP: 2019/06/01 Normalise line endings. 48 // ZAP: 2019/06/05 Normalise format/style. 49 // ZAP: 2020/01/02 Updated default user agent 50 // ZAP: 2020/04/20 Allow to configure the SOCKS proxy (Issue 29). 51 // ZAP: 2020/11/26 Use Log4j 2 classes for logging. 52 // ZAP: 2021/10/06 Updated default user agent 53 package org.parosproxy.paros.network; 54 55 import java.net.PasswordAuthentication; 56 import java.security.Security; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.List; 61 import java.util.Objects; 62 import java.util.regex.Pattern; 63 import org.apache.commons.configuration.HierarchicalConfiguration; 64 import org.apache.commons.httpclient.HttpState; 65 import org.apache.logging.log4j.LogManager; 66 import org.apache.logging.log4j.Logger; 67 import org.parosproxy.paros.common.AbstractParam; 68 import org.zaproxy.zap.extension.api.ZapApiIgnore; 69 import org.zaproxy.zap.network.DomainMatcher; 70 import org.zaproxy.zap.network.SocksProxy; 71 72 public class ConnectionParam extends AbstractParam { 73 74 // ZAP: Added logger 75 private static Logger log = LogManager.getLogger(ConnectionParam.class); 76 77 private static final String CONNECTION_BASE_KEY = "connection"; 78 79 private static final String USE_PROXY_CHAIN_KEY = CONNECTION_BASE_KEY + ".proxyChain.enabled"; 80 private static final String PROXY_CHAIN_NAME = CONNECTION_BASE_KEY + ".proxyChain.hostName"; 81 private static final String PROXY_CHAIN_PORT = CONNECTION_BASE_KEY + ".proxyChain.port"; 82 private static final String USE_PROXY_CHAIN_AUTH_KEY = 83 CONNECTION_BASE_KEY + ".proxyChain.authEnabled"; 84 private static final String PROXY_CHAIN_REALM = CONNECTION_BASE_KEY + ".proxyChain.realm"; 85 private static final String PROXY_CHAIN_USER_NAME = 86 CONNECTION_BASE_KEY + ".proxyChain.userName"; 87 private static final String PROXY_CHAIN_PASSWORD = CONNECTION_BASE_KEY + ".proxyChain.password"; 88 89 private static final String PROXY_EXCLUDED_DOMAIN_KEY = 90 CONNECTION_BASE_KEY + ".proxyChain.exclusions"; 91 private static final String ALL_PROXY_EXCLUDED_DOMAINS_KEY = 92 PROXY_EXCLUDED_DOMAIN_KEY + ".exclusion"; 93 private static final String PROXY_EXCLUDED_DOMAIN_VALUE_KEY = "name"; 94 private static final String PROXY_EXCLUDED_DOMAIN_REGEX_KEY = "regex"; 95 private static final String PROXY_EXCLUDED_DOMAIN_ENABLED_KEY = "enabled"; 96 private static final String CONFIRM_REMOVE_EXCLUDED_DOMAIN = 97 CONNECTION_BASE_KEY + ".proxyChain.confirmRemoveExcludedDomain"; 98 99 private static final String SECURITY_PROTOCOLS_ENABLED = 100 CONNECTION_BASE_KEY + ".securityProtocolsEnabled"; 101 private static final String SECURITY_PROTOCOL_ELEMENT_KEY = "protocol"; 102 private static final String ALL_SECURITY_PROTOCOLS_ENABLED_KEY = 103 SECURITY_PROTOCOLS_ENABLED + "." + SECURITY_PROTOCOL_ELEMENT_KEY; 104 105 // ZAP: Added prompt option and timeout 106 private static final String PROXY_CHAIN_PROMPT = CONNECTION_BASE_KEY + ".proxyChain.prompt"; 107 private static final String TIMEOUT_IN_SECS = CONNECTION_BASE_KEY + ".timeoutInSecs"; 108 private static final String SINGLE_COOKIE_REQUEST_HEADER = 109 CONNECTION_BASE_KEY + ".singleCookieRequestHeader"; 110 private static final String HTTP_STATE_ENABLED = CONNECTION_BASE_KEY + ".httpStateEnabled"; 111 public static final String DEFAULT_USER_AGENT = CONNECTION_BASE_KEY + ".defaultUserAgent"; 112 113 public static final String DEFAULT_DEFAULT_USER_AGENT = 114 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0"; 115 116 /** The security property for TTL of successful DNS queries. */ 117 private static final String DNS_TTL_SUCCESSFUL_QUERIES_SECURITY_PROPERTY = 118 "networkaddress.cache.ttl"; 119 120 /** 121 * The default TTL (in seconds) of successful DNS queries. 122 * 123 * @since 2.6.0 124 */ 125 public static final int DNS_DEFAULT_TTL_SUCCESSFUL_QUERIES = 30; 126 127 /** The configuration key for TTL of successful DNS queries. */ 128 private static final String DNS_TTL_SUCCESSFUL_QUERIES_KEY = 129 CONNECTION_BASE_KEY + ".dnsTtlSuccessfulQueries"; 130 131 /** 132 * The default connection timeout (in seconds). 133 * 134 * @since 2.7.0 135 */ 136 public static final int DEFAULT_TIMEOUT = 20; 137 138 private static final String SOCKS_PROXY_BASE_KEY = CONNECTION_BASE_KEY + ".socksProxy."; 139 private static final String USE_SOCKS_PROXY_KEY = SOCKS_PROXY_BASE_KEY + "enabled"; 140 private static final String SOCKS_PROXY_HOST_KEY = SOCKS_PROXY_BASE_KEY + "host"; 141 private static final String SOCKS_PROXY_PORT_KEY = SOCKS_PROXY_BASE_KEY + "port"; 142 private static final String SOCKS_PROXY_VERSION_KEY = SOCKS_PROXY_BASE_KEY + "version"; 143 private static final String SOCKS_PROXY_DNS_KEY = SOCKS_PROXY_BASE_KEY + "dns"; 144 private static final String SOCKS_PROXY_USERNAME_KEY = SOCKS_PROXY_BASE_KEY + "username"; 145 private static final String SOCKS_PROXY_PASSWORD_KEY = SOCKS_PROXY_BASE_KEY + "password"; 146 147 /** 148 * The default SOCKS proxy configuration. 149 * 150 * @since 2.10.0 151 */ 152 public static final SocksProxy DEFAULT_SOCKS_PROXY = new SocksProxy("localhost", 1080); 153 154 /** 155 * Pattern with loopback names and addresses that should be always resolved (when creating the 156 * {@link java.net.InetSocketAddress}). 157 * 158 * <p>Same pattern used by default proxy selector. 159 * 160 * @see #shouldResolveRemoteHostname(String) 161 */ 162 private static final Pattern LOOPBACK_PATTERN = 163 Pattern.compile("\\Qlocalhost\\E|\\Q127.\\E.*|\\Q[::1]\\E|\\Q0.0.0.0\\E|\\Q[::0]\\E"); 164 165 private boolean useProxyChain; 166 private String proxyChainName = ""; 167 private int proxyChainPort = 8080; 168 private boolean confirmRemoveProxyExcludeDomain = true; 169 private boolean useProxyChainAuth; 170 private String proxyChainRealm = ""; 171 private String proxyChainUserName = ""; 172 private String proxyChainPassword = ""; 173 174 private boolean useSocksProxy; 175 private SocksProxy socksProxy = DEFAULT_SOCKS_PROXY; 176 private PasswordAuthentication socksProxyPasswordAuth = 177 new PasswordAuthentication("", new char[0]); 178 179 private HttpState httpState = null; 180 private boolean httpStateEnabled = false; 181 private List<DomainMatcher> proxyExcludedDomains = new ArrayList<>(0); 182 private List<DomainMatcher> proxyExcludedDomainsEnabled = new ArrayList<>(0); 183 184 private String[] securityProtocolsEnabled; 185 186 // ZAP: Added prompt option and timeout 187 private boolean proxyChainPrompt = false; 188 private int timeoutInSecs = DEFAULT_TIMEOUT; 189 190 private boolean singleCookieRequestHeader = true; 191 private String defaultUserAgent = ""; 192 193 /** The TTL (in seconds) of successful DNS queries. */ 194 private int dnsTtlSuccessfulQueries = DNS_DEFAULT_TTL_SUCCESSFUL_QUERIES; 195 196 /** @return Returns the httpStateEnabled. */ isHttpStateEnabled()197 public boolean isHttpStateEnabled() { 198 return httpStateEnabled; 199 } 200 /** @param httpStateEnabled The httpStateEnabled to set. */ setHttpStateEnabled(boolean httpStateEnabled)201 public void setHttpStateEnabled(boolean httpStateEnabled) { 202 setHttpStateEnabledImpl(httpStateEnabled); 203 getConfig().setProperty(HTTP_STATE_ENABLED, this.httpStateEnabled); 204 } 205 setHttpStateEnabledImpl(boolean httpStateEnabled)206 private void setHttpStateEnabledImpl(boolean httpStateEnabled) { 207 this.httpStateEnabled = httpStateEnabled; 208 if (this.httpStateEnabled) { 209 httpState = new HttpState(); 210 } else { 211 httpState = null; 212 } 213 } 214 ConnectionParam()215 public ConnectionParam() {} 216 217 @Override parse()218 protected void parse() { 219 updateOptions(); 220 221 dnsTtlSuccessfulQueries = 222 getInt(DNS_TTL_SUCCESSFUL_QUERIES_KEY, DNS_DEFAULT_TTL_SUCCESSFUL_QUERIES); 223 Security.setProperty( 224 DNS_TTL_SUCCESSFUL_QUERIES_SECURITY_PROPERTY, 225 Integer.toString(dnsTtlSuccessfulQueries)); 226 227 useProxyChain = getBoolean(USE_PROXY_CHAIN_KEY, false); 228 useProxyChainAuth = getBoolean(USE_PROXY_CHAIN_AUTH_KEY, false); 229 230 setProxyChainName(getString(PROXY_CHAIN_NAME, "")); 231 setProxyChainPort(getInt(PROXY_CHAIN_PORT, 8080)); 232 233 loadProxyExcludedDomains(); 234 this.confirmRemoveProxyExcludeDomain = getBoolean(CONFIRM_REMOVE_EXCLUDED_DOMAIN, true); 235 236 setProxyChainRealm(getString(PROXY_CHAIN_REALM, "")); 237 setProxyChainUserName(getString(PROXY_CHAIN_USER_NAME, "")); 238 239 try { 240 // ZAP: Added prompt option 241 if (getConfig().getProperty(PROXY_CHAIN_PROMPT) instanceof String 242 && ((String) getConfig().getProperty(PROXY_CHAIN_PROMPT)).isEmpty()) { 243 // In 1.2.0 the default for this field was empty, which causes a crash in 1.3.* 244 setProxyChainPrompt(false); 245 } else if (getBoolean(PROXY_CHAIN_PROMPT, false)) { 246 setProxyChainPrompt(true); 247 } else { 248 setProxyChainPrompt(false); 249 setProxyChainPassword(getString(PROXY_CHAIN_PASSWORD, "")); 250 } 251 } catch (Exception e) { 252 // ZAP: Log exceptions 253 log.error(e.getMessage(), e); 254 } 255 256 setTimeoutInSecsImpl(getInt(TIMEOUT_IN_SECS, DEFAULT_TIMEOUT)); 257 258 this.singleCookieRequestHeader = getBoolean(SINGLE_COOKIE_REQUEST_HEADER, true); 259 260 setHttpStateEnabledImpl(getBoolean(HTTP_STATE_ENABLED, false)); 261 262 this.defaultUserAgent = getString(DEFAULT_USER_AGENT, DEFAULT_DEFAULT_USER_AGENT); 263 HttpRequestHeader.setDefaultUserAgent(defaultUserAgent); 264 265 loadSecurityProtocolsEnabled(); 266 267 parseSocksProxyOptions(); 268 } 269 updateOptions()270 private void updateOptions() { 271 final String oldKey = CONNECTION_BASE_KEY + "sslConnectPorts"; 272 if (getConfig().containsKey(oldKey)) { 273 getConfig().clearProperty(oldKey); 274 } 275 276 final String oldSkipNameKey = CONNECTION_BASE_KEY + ".proxyChain.skipName"; 277 if (getConfig().containsKey(oldSkipNameKey)) { 278 migrateOldSkipNameOption(getConfig().getString(oldSkipNameKey, "")); 279 getConfig().clearProperty(oldSkipNameKey); 280 } 281 282 if (!getConfig().containsKey(USE_PROXY_CHAIN_KEY)) { 283 String proxyName = getConfig().getString(PROXY_CHAIN_NAME, ""); 284 if (!proxyName.isEmpty()) { 285 getConfig().setProperty(USE_PROXY_CHAIN_KEY, Boolean.TRUE); 286 } 287 } 288 289 if (!getConfig().containsKey(USE_PROXY_CHAIN_AUTH_KEY)) { 290 String proxyUserName = getConfig().getString(PROXY_CHAIN_USER_NAME, ""); 291 if (!proxyUserName.isEmpty()) { 292 getConfig().setProperty(USE_PROXY_CHAIN_AUTH_KEY, Boolean.TRUE); 293 } 294 } 295 } 296 migrateOldSkipNameOption(String skipNames)297 private void migrateOldSkipNameOption(String skipNames) { 298 List<DomainMatcher> excludedDomains = convertOldSkipNameOption(skipNames); 299 300 if (!excludedDomains.isEmpty()) { 301 setProxyExcludedDomains(excludedDomains); 302 } 303 } 304 convertOldSkipNameOption(String skipNames)305 private static List<DomainMatcher> convertOldSkipNameOption(String skipNames) { 306 if (skipNames == null || skipNames.isEmpty()) { 307 return Collections.emptyList(); 308 } 309 310 ArrayList<DomainMatcher> excludedDomains = new ArrayList<>(); 311 String[] names = skipNames.split(";"); 312 for (String name : names) { 313 String excludedDomain = name.trim(); 314 if (!excludedDomain.isEmpty()) { 315 if (excludedDomain.contains("*")) { 316 excludedDomain = excludedDomain.replace(".", "\\.").replace("*", ".*?"); 317 try { 318 Pattern pattern = Pattern.compile(excludedDomain, Pattern.CASE_INSENSITIVE); 319 excludedDomains.add(new DomainMatcher(pattern)); 320 } catch (IllegalArgumentException e) { 321 log.error("Failed to migrate the excluded domain name: " + name, e); 322 } 323 } else { 324 excludedDomains.add(new DomainMatcher(excludedDomain)); 325 } 326 } 327 } 328 excludedDomains.trimToSize(); 329 return excludedDomains; 330 } 331 332 /** 333 * Tells whether or not the outgoing connections should use the proxy set. 334 * 335 * @return {@code true} if outgoing connections should use the proxy set, {@code false} 336 * otherwise. 337 * @since 2.3.0 338 * @see #setUseProxyChain(boolean) 339 */ isUseProxyChain()340 public boolean isUseProxyChain() { 341 return useProxyChain; 342 } 343 344 /** 345 * Sets whether or not the outgoing connections should use the proxy set. 346 * 347 * <p><strong>Note:</strong> The call to this method has no effect if set to use the proxy but 348 * the proxy was not previously configured. 349 * 350 * @param useProxyChain {@code true} if outgoing connections should use the proxy set, {@code 351 * false} otherwise. 352 * @since 2.3.0 353 * @see #isUseProxyChain() 354 * @see #setProxyChainName(String) 355 * @see #setProxyChainPort(int) 356 */ setUseProxyChain(boolean useProxyChain)357 public void setUseProxyChain(boolean useProxyChain) { 358 if (useProxyChain && (getProxyChainName() == null || getProxyChainName().isEmpty())) { 359 return; 360 } 361 362 this.useProxyChain = useProxyChain; 363 getConfig().setProperty(USE_PROXY_CHAIN_KEY, this.useProxyChain); 364 } 365 366 /** 367 * Returns the name of the outgoing proxy. The returned name is never {@code null}. 368 * 369 * @return the name of the outgoing proxy, never {@code null}. 370 * @see #isUseProxyChain() 371 * @see #setProxyChainName(String) 372 */ getProxyChainName()373 public String getProxyChainName() { 374 return proxyChainName; 375 } 376 377 /** 378 * Sets the name of the outgoing proxy. If empty the use of the outgoing proxy will be disabled. 379 * 380 * <p><strong>Note:</strong> The call to this method has no effect if the given {@code 381 * proxyChainName} is {@code null}. 382 * 383 * @param proxyChainName the name of the outgoing proxy 384 * @see #getProxyChainName() 385 * @see #setUseProxyChain(boolean) 386 */ setProxyChainName(String proxyChainName)387 public void setProxyChainName(String proxyChainName) { 388 if (proxyChainName == null) { 389 return; 390 } 391 this.proxyChainName = proxyChainName.trim(); 392 if (proxyChainName.isEmpty()) { 393 setUseProxyChain(false); 394 } 395 getConfig().setProperty(PROXY_CHAIN_NAME, this.proxyChainName); 396 } 397 getProxyChainPort()398 public int getProxyChainPort() { 399 return proxyChainPort; 400 } 401 setProxyChainPort(int proxyChainPort)402 public void setProxyChainPort(int proxyChainPort) { 403 this.proxyChainPort = proxyChainPort; 404 getConfig().setProperty(PROXY_CHAIN_PORT, Integer.toString(this.proxyChainPort)); 405 } 406 407 /** 408 * @deprecated (2.3.0) Replaced by {@link #getProxyExcludedDomains()} and {@link 409 * #getProxyExcludedDomainsEnabled()}. <strong>Note:</strong> Newer regular expression 410 * excluded domains will not be returned by this method. 411 */ 412 @Deprecated 413 @ZapApiIgnore 414 @SuppressWarnings({"javadoc"}) getProxyChainSkipName()415 public String getProxyChainSkipName() { 416 StringBuilder skipNamesStringBuilder = new StringBuilder(""); 417 for (DomainMatcher excludedDomain : proxyExcludedDomains) { 418 if (!excludedDomain.isRegex()) { 419 skipNamesStringBuilder.append(excludedDomain.getValue()).append(';'); 420 } 421 } 422 return skipNamesStringBuilder.toString(); 423 } 424 425 /** @deprecated (2.3.0) Replaced by {@link #setProxyExcludedDomains(List)}. */ 426 @Deprecated 427 @SuppressWarnings({"javadoc"}) setProxyChainSkipName(String proxyChainSkipName)428 public void setProxyChainSkipName(String proxyChainSkipName) { 429 setProxyExcludedDomains(convertOldSkipNameOption(proxyChainSkipName)); 430 } 431 432 /** 433 * Tells whether or not the outgoing connections should use the proxy authentication credentials 434 * set. 435 * 436 * @return {@code true} if outgoing connections should use the proxy authentication credentials 437 * set, {@code false} otherwise. 438 * @since 2.3.0 439 * @see #isUseProxyChain() 440 * @see #setUseProxyChainAuth(boolean) 441 */ isUseProxyChainAuth()442 public boolean isUseProxyChainAuth() { 443 return useProxyChainAuth; 444 } 445 446 /** 447 * Sets whether or not the outgoing connections should use the proxy authentication credentials 448 * set. 449 * 450 * <p><strong>Note:</strong> The call to this method has no effect if set to use the credentials 451 * but the credentials were not previously set. 452 * 453 * @param useProxyChainAuth {@code true} if outgoing connections should use the proxy 454 * authentication credentials set, {@code false} otherwise. 455 * @since 2.3.0 456 * @see #isUseProxyChainAuth() 457 * @see #setUseProxyChain(boolean) 458 * @see #setProxyChainUserName(String) 459 * @see #setProxyChainPassword(String) 460 * @see #setProxyChainRealm(String) 461 */ setUseProxyChainAuth(boolean useProxyChainAuth)462 public void setUseProxyChainAuth(boolean useProxyChainAuth) { 463 if (useProxyChainAuth 464 && (getProxyChainUserName() == null || getProxyChainUserName().isEmpty())) { 465 return; 466 } 467 468 this.useProxyChainAuth = useProxyChainAuth; 469 getConfig().setProperty(USE_PROXY_CHAIN_AUTH_KEY, this.useProxyChainAuth); 470 } 471 getProxyChainRealm()472 public String getProxyChainRealm() { 473 return proxyChainRealm; 474 } 475 setProxyChainRealm(String proxyChainRealm)476 public void setProxyChainRealm(String proxyChainRealm) { 477 this.proxyChainRealm = proxyChainRealm.trim(); 478 getConfig().setProperty(PROXY_CHAIN_REALM, this.proxyChainRealm); 479 } 480 getProxyChainUserName()481 public String getProxyChainUserName() { 482 return proxyChainUserName; 483 } 484 setProxyChainUserName(String proxyChainUserName)485 public void setProxyChainUserName(String proxyChainUserName) { 486 this.proxyChainUserName = proxyChainUserName.trim(); 487 getConfig().setProperty(PROXY_CHAIN_USER_NAME, this.proxyChainUserName); 488 } 489 getProxyChainPassword()490 public String getProxyChainPassword() { 491 return proxyChainPassword.trim(); 492 } 493 setProxyChainPassword(String proxyChainPassword)494 public void setProxyChainPassword(String proxyChainPassword) { 495 this.proxyChainPassword = proxyChainPassword; 496 getConfig().setProperty(PROXY_CHAIN_PASSWORD, this.proxyChainPassword); 497 } 498 499 // ZAP: Added setProxyChainPassword(String, boolean) method setProxyChainPassword(String proxyChainPassword, boolean save)500 public void setProxyChainPassword(String proxyChainPassword, boolean save) { 501 if (save) { 502 this.setProxyChainPassword(proxyChainPassword); 503 } else { 504 this.proxyChainPassword = proxyChainPassword; 505 } 506 } 507 508 // ZAP: Added prompt option setProxyChainPrompt(boolean proxyPrompt)509 public void setProxyChainPrompt(boolean proxyPrompt) { 510 this.proxyChainPrompt = proxyPrompt; 511 getConfig().setProperty(PROXY_CHAIN_PROMPT, this.proxyChainPrompt); 512 } 513 isProxyChainPrompt()514 public boolean isProxyChainPrompt() { 515 return this.proxyChainPrompt; 516 } 517 518 /** 519 * Tells whether or not the given {@code domainName} should be excluded from the outgoing proxy. 520 * 521 * @param domainName the domain to be checked 522 * @return {@code true} if the given {@code domainName} should be excluded, {@code false} 523 * otherwise. 524 * @since 2.3.0 525 */ isDomainExcludedFromProxy(String domainName)526 private boolean isDomainExcludedFromProxy(String domainName) { 527 if (domainName == null || domainName.isEmpty()) { 528 return false; 529 } 530 531 for (DomainMatcher excludedDomain : proxyExcludedDomainsEnabled) { 532 if (excludedDomain.matches(domainName)) { 533 return true; 534 } 535 } 536 return false; 537 } 538 539 /** 540 * Check if given host name need to send using proxy. 541 * 542 * @param hostName host name to be checked. 543 * @return true = need to send via proxy. 544 */ isUseProxy(String hostName)545 public boolean isUseProxy(String hostName) { 546 if (!isUseProxyChain() || isDomainExcludedFromProxy(hostName)) { 547 return false; 548 } else { 549 return true; 550 } 551 } 552 553 /** @return Returns the httpState. */ getHttpState()554 public HttpState getHttpState() { 555 return httpState; 556 } 557 /** @param httpState The httpState to set. */ setHttpState(HttpState httpState)558 public void setHttpState(HttpState httpState) { 559 this.httpState = httpState; 560 } 561 getTimeoutInSecs()562 public int getTimeoutInSecs() { 563 return timeoutInSecs; 564 } 565 setTimeoutInSecs(int timeoutInSecs)566 public void setTimeoutInSecs(int timeoutInSecs) { 567 setTimeoutInSecsImpl(timeoutInSecs); 568 getConfig().setProperty(TIMEOUT_IN_SECS, this.timeoutInSecs); 569 } 570 setTimeoutInSecsImpl(int timeoutInSecs)571 private void setTimeoutInSecsImpl(int timeoutInSecs) { 572 if (timeoutInSecs < 0) { 573 this.timeoutInSecs = 0; 574 return; 575 } 576 577 this.timeoutInSecs = timeoutInSecs; 578 } 579 580 /** 581 * Tells whether the cookies should be set on a single "Cookie" request header or multiple 582 * "Cookie" request headers, when sending an HTTP request to the server. 583 * 584 * @return {@code true} if the cookies should be set on a single request header, {@code false} 585 * otherwise 586 */ isSingleCookieRequestHeader()587 public boolean isSingleCookieRequestHeader() { 588 return this.singleCookieRequestHeader; 589 } 590 591 /** 592 * Sets whether the cookies should be set on a single "Cookie" request header or multiple 593 * "Cookie" request headers, when sending an HTTP request to the server. 594 * 595 * @param singleCookieRequestHeader {@code true} if the cookies should be set on a single 596 * request header, {@code false} otherwise 597 */ setSingleCookieRequestHeader(boolean singleCookieRequestHeader)598 public void setSingleCookieRequestHeader(boolean singleCookieRequestHeader) { 599 this.singleCookieRequestHeader = singleCookieRequestHeader; 600 getConfig().setProperty(SINGLE_COOKIE_REQUEST_HEADER, singleCookieRequestHeader); 601 } 602 603 /** 604 * Returns the domains excluded from the outgoing proxy. 605 * 606 * @return the domains excluded from the outgoing proxy. 607 * @since 2.3.0 608 * @see #isUseProxy(String) 609 * @see #getProxyExcludedDomainsEnabled() 610 * @see #setProxyExcludedDomains(List) 611 */ 612 @ZapApiIgnore getProxyExcludedDomains()613 public List<DomainMatcher> getProxyExcludedDomains() { 614 return proxyExcludedDomains; 615 } 616 617 /** 618 * Returns the, enabled, domains excluded from the outgoing proxy. 619 * 620 * @return the enabled domains excluded from the outgoing proxy. 621 * @since 2.3.0 622 * @see #isUseProxy(String) 623 * @see #getProxyExcludedDomains() 624 * @see #setProxyExcludedDomains(List) 625 */ 626 @ZapApiIgnore getProxyExcludedDomainsEnabled()627 public List<DomainMatcher> getProxyExcludedDomainsEnabled() { 628 return proxyExcludedDomainsEnabled; 629 } 630 631 /** 632 * Sets the domains that will be excluded from the outgoing proxy. 633 * 634 * @param proxyExcludedDomains the domains that will be excluded. 635 * @since 2.3.0 636 * @see #getProxyExcludedDomains() 637 * @see #getProxyExcludedDomainsEnabled() 638 */ setProxyExcludedDomains(List<DomainMatcher> proxyExcludedDomains)639 public void setProxyExcludedDomains(List<DomainMatcher> proxyExcludedDomains) { 640 if (proxyExcludedDomains == null || proxyExcludedDomains.isEmpty()) { 641 ((HierarchicalConfiguration) getConfig()).clearTree(ALL_PROXY_EXCLUDED_DOMAINS_KEY); 642 643 this.proxyExcludedDomains = Collections.emptyList(); 644 this.proxyExcludedDomainsEnabled = Collections.emptyList(); 645 return; 646 } 647 648 this.proxyExcludedDomains = new ArrayList<>(proxyExcludedDomains); 649 650 ((HierarchicalConfiguration) getConfig()).clearTree(ALL_PROXY_EXCLUDED_DOMAINS_KEY); 651 652 int size = proxyExcludedDomains.size(); 653 ArrayList<DomainMatcher> enabledExcludedDomains = new ArrayList<>(size); 654 for (int i = 0; i < size; ++i) { 655 String elementBaseKey = ALL_PROXY_EXCLUDED_DOMAINS_KEY + "(" + i + ")."; 656 DomainMatcher excludedDomain = proxyExcludedDomains.get(i); 657 658 getConfig() 659 .setProperty( 660 elementBaseKey + PROXY_EXCLUDED_DOMAIN_VALUE_KEY, 661 excludedDomain.getValue()); 662 getConfig() 663 .setProperty( 664 elementBaseKey + PROXY_EXCLUDED_DOMAIN_REGEX_KEY, 665 excludedDomain.isRegex()); 666 getConfig() 667 .setProperty( 668 elementBaseKey + PROXY_EXCLUDED_DOMAIN_ENABLED_KEY, 669 excludedDomain.isEnabled()); 670 671 if (excludedDomain.isEnabled()) { 672 enabledExcludedDomains.add(excludedDomain); 673 } 674 } 675 676 enabledExcludedDomains.trimToSize(); 677 this.proxyExcludedDomainsEnabled = enabledExcludedDomains; 678 } 679 loadProxyExcludedDomains()680 private void loadProxyExcludedDomains() { 681 List<HierarchicalConfiguration> fields = 682 ((HierarchicalConfiguration) getConfig()) 683 .configurationsAt(ALL_PROXY_EXCLUDED_DOMAINS_KEY); 684 this.proxyExcludedDomains = new ArrayList<>(fields.size()); 685 ArrayList<DomainMatcher> excludedDomainsEnabled = new ArrayList<>(fields.size()); 686 for (HierarchicalConfiguration sub : fields) { 687 String value = sub.getString(PROXY_EXCLUDED_DOMAIN_VALUE_KEY, ""); 688 if (value.isEmpty()) { 689 log.warn( 690 "Failed to read an outgoing proxy excluded domain entry, required value is empty."); 691 continue; 692 } 693 694 DomainMatcher excludedDomain = null; 695 boolean regex = sub.getBoolean(PROXY_EXCLUDED_DOMAIN_REGEX_KEY, false); 696 if (regex) { 697 try { 698 Pattern pattern = DomainMatcher.createPattern(value); 699 excludedDomain = new DomainMatcher(pattern); 700 } catch (IllegalArgumentException e) { 701 log.error( 702 "Failed to read an outgoing proxy excluded domain entry with regex: " 703 + value, 704 e); 705 } 706 } else { 707 excludedDomain = new DomainMatcher(value); 708 } 709 710 if (excludedDomain != null) { 711 excludedDomain.setEnabled(sub.getBoolean(PROXY_EXCLUDED_DOMAIN_ENABLED_KEY, true)); 712 713 proxyExcludedDomains.add(excludedDomain); 714 715 if (excludedDomain.isEnabled()) { 716 excludedDomainsEnabled.add(excludedDomain); 717 } 718 } 719 } 720 721 excludedDomainsEnabled.trimToSize(); 722 this.proxyExcludedDomainsEnabled = excludedDomainsEnabled; 723 } 724 725 /** 726 * Tells whether or not the remotion of a proxy exclusion needs confirmation. 727 * 728 * @return {@code true} if the remotion needs confirmation, {@code false} otherwise. 729 * @since 2.3.0 730 */ 731 @ZapApiIgnore isConfirmRemoveProxyExcludedDomain()732 public boolean isConfirmRemoveProxyExcludedDomain() { 733 return this.confirmRemoveProxyExcludeDomain; 734 } 735 736 /** 737 * Sets whether or not the remotion of a proxy exclusion needs confirmation. 738 * 739 * @param confirmRemove {@code true} if the remotion needs confirmation, {@code false} 740 * otherwise. 741 * @since 2.3.0 742 */ 743 @ZapApiIgnore setConfirmRemoveProxyExcludedDomain(boolean confirmRemove)744 public void setConfirmRemoveProxyExcludedDomain(boolean confirmRemove) { 745 this.confirmRemoveProxyExcludeDomain = confirmRemove; 746 getConfig().setProperty(CONFIRM_REMOVE_EXCLUDED_DOMAIN, confirmRemoveProxyExcludeDomain); 747 } 748 749 /** 750 * Returns the security protocols enabled (SSL/TLS) for outgoing connections. 751 * 752 * @return the security protocols enabled for outgoing connections. 753 * @since 2.3.0 754 */ 755 @ZapApiIgnore getSecurityProtocolsEnabled()756 public String[] getSecurityProtocolsEnabled() { 757 return Arrays.copyOf(securityProtocolsEnabled, securityProtocolsEnabled.length); 758 } 759 760 /** 761 * Sets the security protocols enabled (SSL/TLS) for outgoing connections. 762 * 763 * <p>The call has no effect if the given array is null or empty. 764 * 765 * @param enabledProtocols the security protocols enabled (SSL/TLS) for outgoing connections. 766 * @throws IllegalArgumentException if at least one of the {@code enabledProtocols} is {@code 767 * null} or empty. 768 * @since 2.3.0 769 */ setSecurityProtocolsEnabled(String[] enabledProtocols)770 public void setSecurityProtocolsEnabled(String[] enabledProtocols) { 771 if (enabledProtocols == null || enabledProtocols.length == 0) { 772 return; 773 } 774 for (int i = 0; i < enabledProtocols.length; i++) { 775 if (enabledProtocols[i] == null || enabledProtocols[i].isEmpty()) { 776 throw new IllegalArgumentException( 777 "The parameter enabledProtocols must not contain null or empty elements."); 778 } 779 } 780 781 ((HierarchicalConfiguration) getConfig()).clearTree(ALL_SECURITY_PROTOCOLS_ENABLED_KEY); 782 783 for (int i = 0; i < enabledProtocols.length; ++i) { 784 String elementBaseKey = ALL_SECURITY_PROTOCOLS_ENABLED_KEY + "(" + i + ")"; 785 getConfig().setProperty(elementBaseKey, enabledProtocols[i]); 786 } 787 788 this.securityProtocolsEnabled = Arrays.copyOf(enabledProtocols, enabledProtocols.length); 789 setClientEnabledProtocols(); 790 } 791 loadSecurityProtocolsEnabled()792 private void loadSecurityProtocolsEnabled() { 793 List<Object> protocols = getConfig().getList(ALL_SECURITY_PROTOCOLS_ENABLED_KEY); 794 if (protocols.size() != 0) { 795 securityProtocolsEnabled = new String[protocols.size()]; 796 securityProtocolsEnabled = protocols.toArray(securityProtocolsEnabled); 797 setClientEnabledProtocols(); 798 } else { 799 setSecurityProtocolsEnabled(SSLConnector.getClientEnabledProtocols()); 800 } 801 } 802 setClientEnabledProtocols()803 private void setClientEnabledProtocols() { 804 try { 805 SSLConnector.setClientEnabledProtocols(securityProtocolsEnabled); 806 } catch (IllegalArgumentException e) { 807 log.warn( 808 "Failed to set persisted protocols " 809 + Arrays.toString(securityProtocolsEnabled) 810 + " falling back to " 811 + Arrays.toString(SSLConnector.getFailSafeProtocols()) 812 + " caused by: " 813 + e.getMessage()); 814 securityProtocolsEnabled = SSLConnector.getFailSafeProtocols(); 815 SSLConnector.setClientEnabledProtocols(securityProtocolsEnabled); 816 } 817 } 818 getDefaultUserAgent()819 public String getDefaultUserAgent() { 820 return this.defaultUserAgent; 821 } 822 setDefaultUserAgent(String defaultUserAgent)823 public void setDefaultUserAgent(String defaultUserAgent) { 824 this.defaultUserAgent = defaultUserAgent; 825 HttpRequestHeader.setDefaultUserAgent(defaultUserAgent); 826 getConfig().setProperty(DEFAULT_USER_AGENT, defaultUserAgent); 827 } 828 829 /** 830 * Gets the TTL (in seconds) of successful DNS queries. 831 * 832 * @return the TTL in seconds 833 * @since 2.6.0 834 * @see #setDnsTtlSuccessfulQueries(int) 835 */ getDnsTtlSuccessfulQueries()836 public int getDnsTtlSuccessfulQueries() { 837 return dnsTtlSuccessfulQueries; 838 } 839 840 /** 841 * Sets the TTL (in seconds) of successful DNS queries. 842 * 843 * <p>Some values have special meaning: 844 * 845 * <ul> 846 * <li>Negative number, cache forever; 847 * <li>Zero, disables caching; 848 * <li>Positive number, the number of seconds the successful DNS queries will be cached. 849 * </ul> 850 * 851 * @param ttl the TTL in seconds 852 * @since 2.6.0 853 * @see #getDnsTtlSuccessfulQueries() 854 */ setDnsTtlSuccessfulQueries(int ttl)855 public void setDnsTtlSuccessfulQueries(int ttl) { 856 if (dnsTtlSuccessfulQueries == ttl) { 857 return; 858 } 859 860 dnsTtlSuccessfulQueries = ttl; 861 getConfig().setProperty(DNS_TTL_SUCCESSFUL_QUERIES_KEY, ttl); 862 } 863 parseSocksProxyOptions()864 private void parseSocksProxyOptions() { 865 String host = System.getProperty("socksProxyHost"); 866 int port; 867 String version; 868 boolean useDns = getBoolean(SOCKS_PROXY_DNS_KEY, DEFAULT_SOCKS_PROXY.isUseDns()); 869 870 if (host != null && !host.isEmpty()) { 871 port = parseSocksPort(System.getProperty("socksProxyPort")); 872 version = System.getProperty("socksProxyVersion"); 873 874 useSocksProxy = true; 875 } else { 876 host = getString(SOCKS_PROXY_HOST_KEY, DEFAULT_SOCKS_PROXY.getHost()); 877 port = parseSocksPort(getConfig().getString(SOCKS_PROXY_PORT_KEY)); 878 version = 879 getString( 880 SOCKS_PROXY_VERSION_KEY, 881 String.valueOf(DEFAULT_SOCKS_PROXY.getVersion().number())); 882 883 useSocksProxy = getBoolean(USE_SOCKS_PROXY_KEY, false); 884 } 885 886 socksProxy = new SocksProxy(host, port, SocksProxy.Version.from(version), useDns); 887 if (useSocksProxy) { 888 apply(socksProxy); 889 } 890 891 socksProxyPasswordAuth = 892 new PasswordAuthentication( 893 getString(SOCKS_PROXY_USERNAME_KEY, ""), 894 getString(SOCKS_PROXY_PASSWORD_KEY, "").toCharArray()); 895 } 896 parseSocksPort(String value)897 private static int parseSocksPort(String value) { 898 if (value == null || value.isEmpty()) { 899 return DEFAULT_SOCKS_PROXY.getPort(); 900 } 901 902 int port; 903 try { 904 port = Integer.parseInt(value); 905 } catch (NumberFormatException e) { 906 log.warn("Failed to parse the SOCKS port: " + value, e); 907 return DEFAULT_SOCKS_PROXY.getPort(); 908 } 909 910 if (port > 0 && port <= 65535) { 911 return port; 912 } 913 914 log.warn("Invalid SOCKS port: " + value); 915 return DEFAULT_SOCKS_PROXY.getPort(); 916 } 917 918 /** 919 * Applies the given SOCKS proxy configuration to the SOCKS system properties. 920 * 921 * <p>If the SOCKS proxy is not in use (i.e. {@link #useSocksProxy} is {@code false}) the system 922 * properties are cleared. 923 * 924 * @param socksProxy the SOCKS proxy to apply. 925 */ apply(SocksProxy socksProxy)926 private void apply(SocksProxy socksProxy) { 927 String host = ""; 928 String port = ""; 929 String version = ""; 930 if (useSocksProxy) { 931 host = socksProxy.getHost(); 932 port = Integer.toString(socksProxy.getPort()); 933 version = Integer.toString(socksProxy.getVersion().number()); 934 } 935 System.setProperty("socksProxyHost", host); 936 System.setProperty("socksProxyPort", port); 937 System.setProperty("socksProxyVersion", version); 938 } 939 940 /** 941 * Tells whether or not the given hostname should be resolved. 942 * 943 * <p>The names should not be resolved when ZAP is configured to use a SOCKSv5 proxy and rely on 944 * it for resolution. 945 * 946 * <p><strong>Note:</strong> Not part of the public API. 947 * 948 * @param hostname the name to check. 949 * @return {@code true} if the given {@code hostname} should be resolved, {@code false} 950 * otherwise. 951 */ 952 @ZapApiIgnore shouldResolveRemoteHostname(String hostname)953 public boolean shouldResolveRemoteHostname(String hostname) { 954 if (!useSocksProxy 955 || !socksProxy.isUseDns() 956 || socksProxy.getVersion() != SocksProxy.Version.SOCKS5) { 957 return true; 958 } 959 return LOOPBACK_PATTERN.matcher(hostname).matches(); 960 } 961 962 /** 963 * Tells whether or not the outgoing connections should use the SOCKS proxy. 964 * 965 * @return {@code true} if outgoing connections should use the SOCKS proxy, {@code false} 966 * otherwise. 967 * @since 2.10.0 968 * @see #setUseSocksProxy(boolean) 969 */ isUseSocksProxy()970 public boolean isUseSocksProxy() { 971 return useSocksProxy; 972 } 973 974 /** 975 * Sets whether or not the outgoing connections should use the SOCKS proxy. 976 * 977 * @param useSocksProxy {@code true} if outgoing connections should use the SOCKS proxy, {@code 978 * false} otherwise. 979 * @since 2.10.0 980 * @see #isUseSocksProxy() 981 * @see #setSocksProxy(SocksProxy) 982 */ setUseSocksProxy(boolean useSocksProxy)983 public void setUseSocksProxy(boolean useSocksProxy) { 984 if (this.useSocksProxy == useSocksProxy) { 985 return; 986 } 987 988 this.useSocksProxy = useSocksProxy; 989 990 getConfig().setProperty(USE_SOCKS_PROXY_KEY, useSocksProxy); 991 992 apply(socksProxy); 993 } 994 995 /** 996 * Gets the SOCKS proxy for outgoing connections. 997 * 998 * @return the SOCKS proxy, never {@code null}. 999 * @since 2.10.0 1000 * @see #isUseSocksProxy() 1001 * @see #setSocksProxy(SocksProxy) 1002 */ 1003 @ZapApiIgnore getSocksProxy()1004 public SocksProxy getSocksProxy() { 1005 return socksProxy; 1006 } 1007 1008 /** 1009 * Sets the SOCKS proxy for outgoing connections. 1010 * 1011 * @param socksProxy the SOCKS proxy. 1012 * @throws NullPointerException if the given {@code socksProxy} is {@code null}. 1013 * @since 2.10.0 1014 * @see #getSocksProxy() 1015 * @see #setUseSocksProxy(boolean) 1016 */ setSocksProxy(SocksProxy socksProxy)1017 public void setSocksProxy(SocksProxy socksProxy) { 1018 if (this.socksProxy.equals(socksProxy)) { 1019 return; 1020 } 1021 1022 this.socksProxy = Objects.requireNonNull(socksProxy); 1023 1024 getConfig().setProperty(SOCKS_PROXY_HOST_KEY, socksProxy.getHost()); 1025 getConfig().setProperty(SOCKS_PROXY_PORT_KEY, socksProxy.getPort()); 1026 getConfig().setProperty(SOCKS_PROXY_VERSION_KEY, socksProxy.getVersion().number()); 1027 getConfig().setProperty(SOCKS_PROXY_DNS_KEY, socksProxy.isUseDns()); 1028 1029 if (useSocksProxy) { 1030 apply(socksProxy); 1031 } 1032 } 1033 1034 /** 1035 * Gets the SOCKS proxy password authentication. 1036 * 1037 * @return the SOCKS proxy password authentication, never {@code null}. 1038 * @since 2.10.0 1039 * @see #isUseSocksProxy() 1040 * @see #setSocksProxyPasswordAuth(PasswordAuthentication) 1041 */ 1042 @ZapApiIgnore getSocksProxyPasswordAuth()1043 public PasswordAuthentication getSocksProxyPasswordAuth() { 1044 return socksProxyPasswordAuth; 1045 } 1046 1047 /** 1048 * Sets the SOCKS proxy password authentication. 1049 * 1050 * @param passwordAuth the password authentication. 1051 * @throws NullPointerException if the given {@code passwordAuth} is {@code null}. 1052 * @since 2.10.0 1053 * @see #getSocksProxyPasswordAuth() 1054 */ setSocksProxyPasswordAuth(PasswordAuthentication passwordAuth)1055 public void setSocksProxyPasswordAuth(PasswordAuthentication passwordAuth) { 1056 this.socksProxyPasswordAuth = Objects.requireNonNull(passwordAuth); 1057 1058 getConfig().setProperty(SOCKS_PROXY_USERNAME_KEY, passwordAuth.getUserName()); 1059 getConfig().setProperty(SOCKS_PROXY_PASSWORD_KEY, new String(passwordAuth.getPassword())); 1060 } 1061 } 1062