diff --git a/.gitignore b/.gitignore index 3b625ee7b..5c3213123 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,18 @@ target /.classpath /.settings /.project +*/.classpath +*/.settings +*/.project /.idea atlassian-ide-plugin.xml -.DS_Store \ No newline at end of file +.DS_Store +dependency-reduced-pom.xml +littleproxy_cert +littleproxy_keystore.jks + +# mockserver-related files +ClientCertificate.pem +ClientPrivateKey.pem +ClientPublicKey.pem +mockserver_keystore.jks \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..016ceb70b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +sudo: false + +language: java +jdk: + # Not running tests against openjdk7, since the SunEC is not included in travis-ci's version of openjdk7. + # Not running tests against oraclejdk7, since travis-ci no longer provides it. + # - openjdk7 + - oraclejdk8 + +cache: + directories: + - $HOME/.m2 diff --git a/README.md b/README.md index 5502aacbe..e932b282c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,96 @@ -BrowserMob Proxy -================ +# BrowserMob Proxy -BrowserMob Proxy is a simple utility that makes it easy to capture performance data from browsers, typically written using automation toolkits such as Selenium and Watir. +BrowserMob Proxy allows you to manipulate HTTP requests and responses, capture HTTP content, and export performance data as a [HAR file](http://www.softwareishard.com/blog/har-12-spec/). +BMP works well as a standalone proxy server, but it is especially useful when embedded in Selenium tests. -Features --------- +The latest version of BrowserMob Proxy is 2.1.5, powered by [LittleProxy](https://github.com/adamfisk/LittleProxy). -The proxy is programmatically controlled via a REST interface or by being embedded directly inside Java-based programs and unit tests. It captures performance data the [HAR format](http://groups.google.com/group/http-archive-specification). It addition it also can actually control HTTP traffic, such as: +If you're running BrowserMob Proxy within a Java application or Selenium test, get started with [Embedded Mode](#getting-started-embedded-mode). If you want to run BMP from the +command line as a standalone proxy, start with [Standalone](#getting-started-standalone). + +### Getting started: Embedded Mode +To use BrowserMob Proxy in your tests or application, add the `browsermob-core` dependency to your pom: +```xml + + net.lightbody.bmp + browsermob-core + 2.1.5 + test + +``` + +Start the proxy: +```java + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.start(0); + int port = proxy.getPort(); // get the JVM-assigned port + // Selenium or HTTP client configuration goes here +``` + +Then configure your HTTP client to use a proxy running at the specified port. + +**Using with Selenium?** See the [Using with Selenium](#using-with-selenium) section. + +### Getting started: Standalone +To run in standalone mode from the command line, first download the latest release from the [releases page](https://github.com/lightbody/browsermob-proxy/releases), or [build the latest from source](#building-the-latest-from-source). + +Start the REST API: +```sh + ./browsermob-proxy -port 8080 +``` + +Then create a proxy server instance: +```sh + curl -X POST http://localhost:8080/proxy + {"port":8081} +``` + +The "port" is the port of the newly-created proxy instance, so configure your HTTP client or web browser to use a proxy on the returned port. +For more information on the features available in the REST API, see [the REST API documentation](#rest-api). + +## Changes since 2.0.0 + +The new [BrowserMobProxyServer class](browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java) has replaced the legacy ProxyServer implementation. The legacy implementation is no longer actively supported; all new code should use `BrowserMobProxyServer`. We highly recommend that existing code migrate to the new implementation. + +The most important changes from 2.0 are: + +- [Separate REST API and Embedded Mode modules](#embedded-mode). Include only the functionality you need. +- [New BrowserMobProxy interface](browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java). The new interface will completely replace the legacy 2.0 ProxyServer contract in version 3.0 and higher. +- [LittleProxy support](#littleproxy-support). More stable and more powerful than the legacy Jetty back-end. + +### New BrowserMobProxy API + +BrowserMob Proxy 2.1 includes a [new BrowserMobProxy interface](browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java) to interact with BrowserMob Proxy programmatically. The new interface defines the functionality that BrowserMob Proxy will support in future releases (including 3.0+). To ease migration, both the legacy (Jetty-based) ProxyServer class and the new, LittleProxy-powered BrowserMobProxy class support the new BrowserMobProxy interface. + +We _highly_ recommend migrating existing code to the BrowserMobProxy interface using the `BrowserMobProxyServer` class. + +### Using the LittleProxy implementation with 2.0.0 code + +The legacy interface, implicitly defined by the ProxyServer class, has been extracted into `net.lightbody.bmp.proxy.LegacyProxyServer` and is now officially deprecated. The new LittleProxy-based implementation will implement LegacyProxyServer for all 2.1.x releases. This means you can switch to the LittleProxy-powered implementation with minimal change to existing code ([with the exception of interceptors](#http-request-manipulation)): + +```java + // With the Jetty-based 2.0.0 release, BMP was created like this: + ProxyServer proxyServer = new ProxyServer(); + proxyServer.start(); + // [...] + + // To use the LittleProxy-powered 2.1.5 release, simply change to + // the LegacyProxyServer interface and the adapter for the new + // LittleProxy-based implementation: + LegacyProxyServer proxyServer = new BrowserMobProxyServerLegacyAdapter(); + proxyServer.start(); + // Almost all deprecated 2.0.0 methods are supported by the + // new BrowserMobProxyServerLegacyAdapter implementation, so in most cases, + // no further code changes are necessary +``` + +LegacyProxyServer will not be supported after 3.0 is released, so we recommend migrating to the `BrowserMobProxy` interface as soon as possible. The new interface provides additional functionality and is compatible with both the legacy Jetty-based ProxyServer implementation [(with some exceptions)](new-interface-compatibility.md) and the new LittleProxy implementation. + +If you must continue using the legacy Jetty-based implementation, include the `browsermob-core-legacy` artifact instead of `browsermob-core`. + +## Features and Usage + +The proxy is programmatically controlled via a REST interface or by being embedded directly inside Java-based programs and unit tests. It captures performance data in the [HAR format](http://groups.google.com/group/http-archive-specification). In addition it can actually control HTTP traffic, such as: - blacklisting and whitelisting certain URL patterns - simulating various bandwidth and latency @@ -15,183 +99,303 @@ The proxy is programmatically controlled via a REST interface or by being embedd - controlling DNS and request timeouts - automatic BASIC authorization -REST API --------- +### REST API + +**New in 2.1:** LittleProxy is the default implementation of the REST API. You may specify `--use-littleproxy false` to disable LittleProxy in favor of the legacy Jetty 5-based implementation. To get started, first start the proxy by running `browsermob-proxy` or `browsermob-proxy.bat` in the bin directory: - $ sh browsermob-proxy -port 9090 + $ sh browsermob-proxy -port 8080 INFO 05/31 03:12:48 o.b.p.Main - Starting up... 2011-05-30 20:12:49.517:INFO::jetty-7.3.0.v20110203 2011-05-30 20:12:49.689:INFO::started o.e.j.s.ServletContextHandler{/,null} - 2011-05-30 20:12:49.820:INFO::Started SelectChannelConnector@0.0.0.0:9090 + 2011-05-30 20:12:49.820:INFO::Started SelectChannelConnector@0.0.0.0:8080 Once started, there won't be an actual proxy running until you create a new proxy. You can do this by POSTing to /proxy: - [~]$ curl -X POST http://localhost:9090/proxy - {"port":9091} + [~]$ curl -X POST http://localhost:8080/proxy + {"port":8081} or optionally specify your own port: - [~]$ curl -X POST -d 'port=9099' http://localhost:9090/proxy - {"port":9099} - -Once that is done, a new proxy will be available on the port returned. All you have to do is point a browser to that proxy on that port and you should be able to browser the internet. The following additional APIs will then be available: - - - PUT /proxy/[port]/har - creates a new HAR attached to the proxy and returns the HAR content if there was a previous HAR. Supports the following parameters: - - initialPageRef - the string name of the first page ref that should be used in the HAR. Defaults to "Page 1". - - captureHeaders - Boolean, capture headers - - captureContent - Boolean, capture content bodies - - captureBinaryContent - Boolean, capture binary content - - PUT /proxy/[port]/har/pageRef - starts a new page on the existing HAR. Supports the following parameters: - - pageRef - the string name of the first page ref that should be used in the HAR. Defaults to "Page N" where N is the next page number. - - PUT /proxy/[port]/har/pageRef - creates a new HAR attached to the proxy and returns the HAR content if there was a previous HAR - - DELETE /proxy/[port] - shuts down the proxy and closes the port - - GET /proxy/[port]/har - returns the JSON/HAR content representing all the HTTP traffic passed through the proxy - - PUT /proxy/[port]/whitelist - Sets a list of URL patterns to whitelist. Takes the following parameters: - - regex - a comma separated list of regular expressions - - status - the HTTP status code to return for URLs that do not match the whitelist - - PUT /proxy/[port]/blacklist - Set a URL to blacklist. Takes the following parameters: - - regex - the blacklist regular expression - - status - the HTTP status code to return for URLs that are blacklisted - - PUT /proxy/[port]/limit - Limit the bandwidth through the proxy. Takes the following parameters: - - downstreamKbps - Sets the downstream kbps - - upstreamKbps - Sets the upstream kbps - - latency - Add the given latency to each HTTP request - - enable - (true/false) a boolean that enable bandwidth limiter. By default the limit is disabled, although setting any of the properties above will implicitly enable throttling - - payloadPercentage - a number ]0, 100] specifying what percentage of data sent is payload. e.g. use this to take into account overhead due to tcp/ip. - - maxBitsPerSecond - The max bits per seconds you want this instance of StreamManager to respect. - - POST /proxy/[port]/headers - Set and override HTTP Request headers. For example setting a custom User-Agent. - - Payload data should be json encoded set of headers (not url-encoded) - - POST /proxy/[port]/hosts - Overrides normal DNS lookups and remaps the given hosts with the associated IP address - - Payload data should be json encoded set of name/value pairs (ex: {"example.com": "1.2.3.4"}) - - POST /proxy/[port]/auth/basic/[domain] - Sets automatic basic authentication for the specified domain - - Payload data should be json encoded username and password name/value pairs (ex: {"username": "myUsername", "password": "myPassword"} - - PUT /proxy/[port]/wait - wait till all request are being made - - quietPeriodInMs - Sets quiet period in milliseconds - - timeoutInMs - Sets timeout in milliseconds - - PUT /proxy/[port]/timeout - Handles different proxy timeouts. Takes the following parameters: - - requestTimeout - request timeout in milliseconds - - readTimeout - read timeout in milliseconds. Which is the timeout for waiting for data or, put differently, a maximum period inactivity between two consecutive data packets). A timeout value of zero is interpreted as an infinite timeout. - - connectionTimeout - Determines the timeout in milliseconds until a connection is established. A timeout value of zero is interpreted as an infinite timeout. - - dnsCacheTimeout - Sets the maximum length of time that records will be stored in this Cache. A negative value disables this feature (that is, sets no limit). - - PUT /proxy/[port]/rewrite - Redirecting URL's - - matchRegex - a matching URL regular expression - - replace - replacement URL - - PUT /proxy/[port]/retry - Setting the retry count - - retrycount - the number of times a method will be retried - - DELETE /proxy/[port]/dns/cache - Empties the Cache. + [~]$ curl -X POST -d 'port=8089' http://localhost:8080/proxy + {"port":8089} + +or if running BrowserMob Proxy in a multi-homed environment, specify a desired bind address (default is `0.0.0.0`): + + [~]$ curl -X POST -d 'bindAddress=192.168.1.222' http://localhost:8080/proxy + {"port":8086} + +Once that is done, a new proxy will be available on the port returned. All you have to do is point a browser to that proxy on that port and you should be able to browse the internet. The following additional APIs will then be available: + +Description | HTTP method | Request path | Request parameters +--- | :---: | :---: | --- +Get a list of ports attached to `ProxyServer` instances managed by `ProxyManager` | GET | */proxy* || +Creates a new proxy to run requests off of | POST | */proxy* |

*port* - Integer, The specific port to start the proxy service on. Optional, default is generated and returned in response.

*proxyUsername* - String, The username to use to authenticate with the chained proxy. Optional, default to null.

*proxyPassword* - String, The password to use to authenticate with the chained proxy. Optional, default to null.

*bindAddress* - String, If running BrowserMob Proxy in a multi-homed environment, specify a desired bind address. Optional, default to "0.0.0.0".

*serverBindAddress* - String, If running BrowserMob Proxy in a multi-homed environment, specify a desired server bind address. Optional, default to "0.0.0.0".

*useEcc* - Boolean. True, Uses Elliptic Curve Cryptography for certificate impersonation. Optional, default to "false".

*trustAllServers* - Boolean. True, Disables verification of all upstream servers' SSL certificates. All upstream servers will be trusted, even if they do not present valid certificates signed by certification authorities in the JDK's trust store. Optional, default to "false".

| +Creates a new HAR attached to the proxy and returns the HAR content if there was a previous HAR. *[port]* in request path it is port where your proxy was started | PUT |*/proxy/[port]/har* |

*captureHeaders* - Boolean, capture headers or not. Optional, default to "false".

*captureCookies* - Boolean, capture cookies or not. Optional, default to "false".

*captureContent* - Boolean, capture content bodies or not. Optional, default to "false".

*captureBinaryContent* - Boolean, capture binary content or not. Optional, default to "false".

*initialPageRef* - The string name of The first page ref that should be used in the HAR. Optional, default to "Page 1".

*initialPageTitle* - The title of first HAR page. Optional, default to *initialPageRef*.

+Starts a new page on the existing HAR. *[port]* in request path it is port where your proxy was started | PUT | */proxy/[port]/har/pageRef* |

*pageRef* - The string name of the first page ref that should be used in the HAR. Optional, default to "Page N" where N is the next page number.

*pageTitle* - The title of new HAR page. Optional, default to `pageRef`.

+Shuts down the proxy and closes the port. *[port]* in request path it is port where your proxy was started | DELETE | */proxy/[port]* || +Returns the JSON/HAR content representing all the HTTP traffic passed through the proxy (provided you have already created the HAR with [this method](#harcreate)) | GET | */proxy/[port]/har* || +Displays whitelisted items | GET | */proxy/[port]/whitelist* || +Sets a list of URL patterns to whitelist | PUT | */proxy/[port]/whitelist* |

*regex* - A comma separated list of regular expressions.

*status* - The HTTP status code to return for URLs that do not match the whitelist.

| +Clears all URL patterns from the whitelist | DELETE | */proxy/[port]/whitelist* || +Displays blacklisted items | GET | */proxy/[port]/blacklist* || +Set a URL to blacklist | PUT | */proxy/[port]/blacklist* |

*regex* - The blacklist regular expression.

*status* - The HTTP status code to return for URLs that are blacklisted.

*method* - The regular expression for matching HTTP method (GET, POST, PUT, etc). Optional, by default processing all HTTP method.

| +Clears all URL patterns from the blacklist | DELETE | */proxy/[port]/blacklist* || +Limit the bandwidth through the proxy on the *[port]* | PUT | */proxy/[port]/limit* |

*downstreamKbps* - Sets the downstream bandwidth limit in kbps. Optional.

*upstreamKbps* - Sets the upstream bandwidth limit kbps. Optional, by default unlimited.

*downstreamMaxKB* - Specifies how many kilobytes in total the client is allowed to download through the proxy. Optional, by default unlimited.

*upstreamMaxKB* - Specifies how many kilobytes in total the client is allowed to upload through the proxy. Optional, by default unlimited.

*latency* - Add the given latency to each HTTP request. Optional, by default all requests are invoked without latency.

*enable* - A boolean that enable bandwidth limiter. Optional, by default to "false", but setting any of the properties above will implicitly enable throttling

*payloadPercentage* - Specifying what percentage of data sent is payload, e.g. use this to take into account overhead due to tcp/ip. Optional.

*maxBitsPerSecond* - The max bits per seconds you want this instance of StreamManager to respect. Optional.

+Displays the amount of data remaining to be uploaded/downloaded until the limit is reached | GET | */proxy/[port]/limit* || +Set and override HTTP Request headers | POST | */proxy/[port]/headers* | Payload data should be **JSON** encoded set of headers. Where key is a header name (such as "User-Agent") and value is a value of HTTP header to setup (such as "BrowserMob-Agent"). Example: `{"User-Agent": "BrowserMob-Agent"}`| +Overrides normal DNS lookups and remaps the given hosts with the associated IP address | POST | */proxy/[port]/hosts* | Payload data should be **JSON** encoded set of hosts. Where key is a host name (such as "example.com") and value is a IP address which associatied with host hame (such as "1.2.3.4"'). Example: `{"example.com": "1.2.3.4"}`| +Sets automatic basic authentication for the specified domain | POST | */proxy/[port]/auth/basic/[domain]* | Payload data should be **JSON** encoded username and password name/value pairs. Example: `{"username": "myUsername", "password": "myPassword"}`| +Wait till all request are being made | PUT | */proxy/[port]/wait* |

*quietPeriodInMs* - Wait till all request are being made. Optional.

*timeoutInMs* - Sets quiet period in milliseconds. Optional.

| +Handles different proxy timeouts | PUT | *proxy/[port]/timeout* |

Payload data should be **JSON** encoded set of parameters. Where key is a parameters name (such as "connectionTimeout") and value is a value of parameter to setup (such as "500")

*requestTimeout* - Request timeout in milliseconds. A timeout value of -1 is interpreted as infinite timeout. Optional, default to "-1".

*readTimeout* - Read timeout in milliseconds. Which is the timeout for waiting for data or, put differently, a maximum period inactivity between two consecutive data packets). A timeout value of zero is interpreted as an infinite timeout. Optional, default to "60000".

*connectionTimeout* - Determines the timeout in milliseconds until a connection is established. A timeout value of zero is interpreted as an infinite timeout. Optional, default to "60000".

*dnsCacheTimeout* - Sets the maximum length of time that records will be stored in this Cache. A nonpositive value disables this feature (that is, sets no limit). Optional, default to "0".

Example: `{"connectionTimeout" : "500", "readTimeout" : "200"}`| +Redirecting URL's | PUT | */proxy/[port]/rewrite* |

*matchRegex* - A matching URL regular expression.

*replace* - replacement URL.

| +Removes all URL redirection rules currently in effect | DELETE | */proxy/[port]/rewrite* || +Setting the retry count | PUT | */proxy/[port]/retry* |

*retrycount* - The number of times a method will be retried.

| +Empties the DNS cache | DELETE | */proxy/[port]/dns/cache* || +| [REST API interceptors with LittleProxy](#interceptorsRESTapiLP) ||| +|Describe your own request interception | POST | */proxy/[port]/filter/request* | A string which determinates interceptor rules. See more [here](#interceptorsRESTapiLPRequestFilter) | +|Describe your own response interception | POST | */proxy/[port]/filter/response* | A string which determinates interceptor rules. See more [here](#interceptorsRESTapiLPResponseFilter) | +| [REST API with Legacy interceptors](#interceptorsRESTapiLegacy) |||| +|Describe your own request interception | POST | */proxy/[port]/interceptor/request* | A string which determinates interceptor rules. See more [here](#interceptorsRESTapiLegacy) | +|Describe your own response interception | POST | */proxy/[port]/interceptor/response* | A string which determinates interceptor rules. See more [here](#interceptorsRESTapiLegacy) | For example, once you've started the proxy you can create a new HAR to start recording data like so: - [~]$ curl -X PUT -d 'initialPageRef=Foo' http://localhost:8080/proxy/9091/har + [~]$ curl -X PUT -d 'initialPageRef=Foo' http://localhost:8080/proxy/8081/har Now when traffic goes through port 9091 it will be attached to a page reference named "Foo". Consult the HAR specification for more info on what a "pageRef" is. You can also start a new pageRef like so: - [~]$ curl -X PUT -d 'pageRef=Bar' http://localhost:8080/proxy/9091/har/pageRef + [~]$ curl -X PUT -d 'pageRef=Bar' http://localhost:8080/proxy/8081/har/pageRef -That will ensure no more HTTP requests get attached to the old pageRef (Foo) and start getting attached to the new pageRef (Bar). You can also get the HAR content at any time like so: +That will ensure no more HTTP requests get attached to the old pageRef (Foo) and start getting attached to the new pageRef (Bar). After creating the HAR, you can get its content at any time like so: - [~]$ curl http://localhost:8080/proxy/9091/har + [~]$ curl http://localhost:8080/proxy/8081/har Sometimes you will want to route requests through an upstream proxy server. In this case specify your proxy server by adding the httpProxy parameter to your create proxy request: - [~]$ curl -X POST http://localhost:9090/proxy?httpProxy=yourproxyserver.com:8080 - {"port":9091} + [~]$ curl -X POST http://localhost:8080/proxy?httpProxy=yourproxyserver.com:8080 + {"port":8081} Alternatively, you can specify the upstream proxy config for all proxies created using the standard JVM [system properties for HTTP proxies](http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html). Note that you can still override the default upstream proxy via the POST payload, but if you omit the payload the JVM system properties will be used to specify the upstream proxy. -*TODO*: Other REST APIs supporting all the BrowserMob Proxy features will be added soon. - -Embedded Mode -------------- +### Command-line Arguments -If you're using Java and Selenium, the easiest way to get started is to embed the project directly in your test. First, you'll need to make sure that all the dependencies are imported in to the project. You can find them in the *lib* directory. Or, if you're using Maven, you can add this to your pom: - - - biz.neustar - browsermob-proxy - LATEST_VERSION (ex: 2.0-beta-7) - test - + - -port \ + - Port on which the API listens. Default value is 8080. + - -address
+ - Address to which the API is bound. Default value is 0.0.0.0. + - -proxyPortRange \-\ + - Range of ports reserved for proxies. Only applies if *port* parameter is not supplied in the POST request. Default values are \+1 to \+500+1. + - -ttl \ + - Proxy will be automatically deleted after a specified time period. Off by default. -Once done, you can start a proxy using `org.browsermob.proxy.ProxyServer`: +### Embedded Mode - ProxyServer server = new ProxyServer(9090); - server.start(); +**New in 2.1:** New Embedded Mode module -This class supports every feature that the proxy supports. In fact, the REST API is a subset of the methods exposed here, so new features will show up here before they show up in the REST API. Consult the Javadocs for the full API. +**New in 2.1:** New [BrowserMobProxy interface](#new-browsermobproxy-api) for Embedded Mode -If your project already defines a Selenium dependency then you may want to exclude the version that browsermob-proxy pulls in, like so: +BrowserMob Proxy 2.1 separates the Embedded Mode and REST API into two modules. If you only need Embedded Mode functionality, add the `browsermob-core` artifact as a dependency. The REST API artifact is `browsermob-rest`. +If you're using Java and Selenium, the easiest way to get started is to embed the project directly in your test. First, you'll need to make sure that all the dependencies are imported in to the project. You can find them in the *lib* directory. Or, if you're using Maven, you can add this to your pom: +```xml - biz.neustar - browsermob-proxy - LATEST_VERSION (ex: 2.0-beta-7) + net.lightbody.bmp + browsermob-core + 2.1.5 test - - - org.seleniumhq.selenium - selenium-api - - +``` + +Once done, you can start a proxy using `net.lightbody.bmp.BrowserMobProxy`: +```java + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.start(0); + // get the JVM-assigned port and get to work! + int port = proxy.getPort(); + //... +``` +Consult the Javadocs on the `net.lightbody.bmp.BrowserMobProxy` class for the full API. -Using With Selenium -------------------- +### Using With Selenium -You can use the REST API with Selenium however you want. But if you're writing your tests in Java and using Selenium 2, this is the easiest way to use it: +**Selenium 3 users**: Due to a [geckodriver issue](https://github.com/mozilla/geckodriver/issues/97), Firefox 51 and lower do not properly support proxies with WebDriver's DesiredCapabilities. See [this answer](http://stackoverflow.com/a/41373808/4256475) for a suitable work-around. +BrowserMob Proxy makes it easy to use a proxy in Selenium tests: +```java // start the proxy - ProxyServer server = new ProxyServer(4444); - server.start(); + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.start(0); // get the Selenium proxy object - Proxy proxy = server.seleniumProxy(); + Proxy seleniumProxy = ClientUtil.createSeleniumProxy(proxy); // configure it as a desired capability DesiredCapabilities capabilities = new DesiredCapabilities(); - capabilities.setCapability(CapabilityType.PROXY, proxy); + capabilities.setCapability(CapabilityType.PROXY, seleniumProxy); // start the browser up WebDriver driver = new FirefoxDriver(capabilities); + // enable more detailed HAR capture, if desired (see CaptureType for the complete list) + proxy.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT); + // create a new HAR with the label "yahoo.com" - server.newHar("yahoo.com"); + proxy.newHar("yahoo.com"); // open yahoo.com driver.get("http://yahoo.com"); // get the HAR data - Har har = server.getHar(); + Har har = proxy.getHar(); +``` + +**Note**: If you're running running tests on a Selenium grid, you will need to customize the Selenium Proxy object +created by `createSeleniumProxy()` to point to the hostname of the machine that your test is running on. You can also run a standalone +BrowserMob Proxy instance on a separate machine and configure the Selenium Proxy object to use that proxy. +### HTTP Request Manipulation -HTTP Request Manipulation -------------------- +**HTTP request manipulation has changed in 2.1.0+ with LittleProxy.** The LittleProxy-based interceptors are easier to use and more reliable. The legacy ProxyServer implementation **will not** support the new interceptor methods. -While not yet available via the REST interface, you can manipulate the requests like so: +#### 2.1.0+ (LittleProxy) interceptors - server.addRequestInterceptor(new RequestInterceptor() { +There are four new methods to support request and response interception in LittleProxy: + + - `addRequestFilter` + - `addResponseFilter` + - `addFirstHttpFilterFactory` + - `addLastHttpFilterFactory` + +For most use cases, including inspecting and modifying requests/responses, `addRequestFilter` and `addResponseFilter` will be sufficient. The request and response filters are easy to use: +```java + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (messageInfo.getOriginalUri().endsWith("/some-endpoint-to-intercept")) { + // retrieve the existing message contents as a String or, for binary contents, as a byte[] + String messageContents = contents.getTextContents(); + + // do some manipulation of the contents + String newContents = messageContents.replaceAll("original-string", "my-modified-string"); + //[...] + + // replace the existing content by calling setTextContents() or setBinaryContents() + contents.setTextContents(newContents); + } + + // in the request filter, you can return an HttpResponse object to "short-circuit" the request + return null; + } + }); + + // responses are equally as simple: + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (/*...some filtering criteria...*/) { + contents.setTextContents("This message body will appear in all responses!"); + } + } + }); +``` + +With Java 8, the syntax is even more concise: +```java + proxy.addResponseFilter((response, contents, messageInfo) -> { + if (/*...some filtering criteria...*/) { + contents.setTextContents("This message body will appear in all responses!"); + } + }); +``` + +See the javadoc for the `RequestFilter` and `ResponseFilter` classes for more information. + +For fine-grained control over the request and response lifecycle, you can add "filter factories" directly using `addFirstHttpFilterFactory` and `addLastHttpFilterFactory` (see the examples in the InterceptorTest unit tests). + +#### REST API interceptors with LittleProxy + +When running the REST API with LittleProxy enabled, you cannot use the legacy `/:port/interceptor/` endpoints. Instead, POST the javascript payload to the new `/:port/filter/request` and `/:port/filter/response` endpoints. + +##### Request filters + +Javascript request filters have access to the variables `request` (type `io.netty.handler.codec.http.HttpRequest`), `contents` (type `net.lightbody.bmp.util.HttpMessageContents`), and `messageInfo` (type `net.lightbody.bmp.util.HttpMessageInfo`). `messageInfo` contains additional information about the message, including whether the message is sent over HTTP or HTTPS, as well as the original request received from the client before any changes made by previous filters. If the javascript returns an object of type `io.netty.handler.codec.http.HttpResponse`, the HTTP request will "short-circuit" and return the response immediately. + +**Example: Modify User-Agent header** + +```sh +curl -i -X POST -H 'Content-Type: text/plain' -d "request.headers().remove('User-Agent'); request.headers().add('User-Agent', 'My-Custom-User-Agent-String 1.0');" http://localhost:8080/proxy/8081/filter/request +``` + +##### Response filters + +Javascript response filters have access to the variables `response` (type `io.netty.handler.codec.http.HttpResponse`), `contents` (type `net.lightbody.bmp.util.HttpMessageContents`), and `messageInfo` (type `net.lightbody.bmp.util.HttpMessageInfo`). As in the request filter, `messageInfo` contains additional information about the message. + +**Example: Modify response body** + +```sh +curl -i -X POST -H 'Content-Type: text/plain' -d "contents.setTextContents('Response successfully intercepted');" http://localhost:8080/proxy/8081/filter/response +``` + +#### Legacy interceptors + +If you are using the legacy ProxyServer implementation, you can manipulate the requests like so: +```java + BrowserMobProxy server = new ProxyServer(); + ((LegacyProxyServer)server).addRequestInterceptor(new RequestInterceptor() { @Override - public void process(BrowserMobHttpRequest request) { + public void process(BrowserMobHttpRequest request, Har har) { request.getMethod().removeHeaders("User-Agent"); request.getMethod().addHeader("User-Agent", "Bananabot/1.0"); } }); +``` +You can also POST a JavaScript payload to `/:port/interceptor/request` and `/:port/interceptor/response` using the REST interface. The functions will have a `request`/`response` variable, respectively, and a `har` variable (which may be null if a HAR isn't set up yet). The JavaScript code will be run by [Rhino](https://github.com/mozilla/rhino) and have access to the same Java API in the example above: + + [~]$ curl -X POST -H 'Content-Type: text/plain' -d 'request.getMethod().removeHeaders("User-Agent");' http://localhost:8080/proxy/8081/interceptor/request + +Consult the Java API docs for more info. + +### SSL Support + +**BrowserMob Proxy 2.1.0+ now supports full MITM:** For most users, MITM will work out-of-the-box with default settings. Install the [ca-certificate-rsa.cer](/browsermob-core/src/main/resources/sslSupport/ca-certificate-rsa.cer) file in your browser or HTTP client to avoid untrusted certificate warnings. Generally, it is safer to generate your own private key, rather than using the .cer files distributed with BrowserMob Proxy. See the [README file in the `mitm` module](/mitm/README.md) for instructions on generating or using your own root certificate and private key with MITM. + +**Legacy Jetty-based ProxyServer support for MITM:** The legacy `ProxyServer` implementation uses the same `ca-certificate-rsa.cer` root certificate as the default BrowserMobProxyServer implementation. The previous cybervillainsCA.cer certificate has been removed. + +**Note: DO NOT** permanently install the .cer files distributed with BrowserMob Proxy in users' browsers. They should be used for testing only and must not be used with general web browsing. -We will soon be adding support for this advanced capability in the REST interface as well, using JavaScript snippets that can be posted as the interceptor code. +If you're doing testing with Selenium, you'll want to make sure that the browser profile that gets set up by Selenium not only has the proxy configured, but also has the CA installed. Unfortunately, there is no API for doing this in Selenium; it must be done manually for each browser and environment. -SSL Support ------------ +### NodeJS Support -While the proxy supports SSL, it requires that a Certificate Authority be installed in to the browser. This allows the browser to trust all the SSL traffic coming from the proxy, which will be proxied using a classic man-in-the-middle technique. IT IS CRITICAL THAT YOU NOT INSTALL THIS CERTIFICATE AUTHORITY ON A BROWSER THAT IS USED FOR ANYTHING OTHER THAN TESTING. +NodeJS bindings for browswermob-proxy are available [here](https://github.com/zzo/browsermob-node). Built-in support for [Selenium](http://seleniumhq.org) or use [CapserJS-on-PhantomJS](http://casperjs.org) or anything else to drive traffic for HAR generation. -If you're doing testing with Selenium, you'll want to make sure that the browser profile that gets set up by Selenium not only has the proxy configured, but also has the CA installed. Unfortuantely, there is no API for doing this in Selenium, so you'll have to solve it uniquely for each browser type. We hope to make this easier in upcoming releases. +### Logging -NodeJS Support --------------- +When running in stand-alone mode, the proxy loads the default logging configuration from the conf/bmp-logging.yaml file. To increase/decrease the logging level, change the logging entry for net.lightbody.bmp. -NodeJS bindings for browswermob-proxy are available [here](https://github.com/zzo/browsermob-node). Built-in support for [Selenium][http://seleniumhq.com] or use [CapserJS-on-PhantomJS](http://casperjs.org) or anything else to drive traffic for HAR generation. +### DNS Resolution + +The BrowserMobProxyServer implementation uses native DNS resolution by default, but supports custom DNS resolution and advanced DNS manipulation. See the [ClientUtil](browsermob-proxy/browsermob-core/src/main/java/net/lightbody/bmp/client/ClientUtil.java) class for information on DNS manipulation using the dnsjava resolver. + +## Building the latest from source + +You'll need maven (`brew install maven` if you're on OS X): + + [~]$ mvn -DskipTests + +You'll find the standalone BrowserMob Proxy distributable zip at `browsermob-dist/target/browsermob-proxy-2.1.5-SNAPSHOT-bin.zip`. Unzip the contents and run the `browsermob-proxy` or `browsermob-proxy.bat` files in the `bin` directory. + +When you build the latest code from source, you'll have access to the latest snapshot release. To use the SNAPSHOT version in your code, modify the version in your pom: +```xml + + net.lightbody.bmp + browsermob-core + 2.1.6-SNAPSHOT + test + +``` diff --git a/browsermob-core/pom.xml b/browsermob-core/pom.xml new file mode 100644 index 000000000..2071434ee --- /dev/null +++ b/browsermob-core/pom.xml @@ -0,0 +1,249 @@ + + + jar + + + browsermob-proxy + net.lightbody.bmp + 2.1.6-SNAPSHOT + + 4.0.0 + + browsermob-core + BrowserMob Proxy Core (LittleProxy) Module + + + 7.6.16.v20140903 + + + + + + src/main/resources + true + + net/lightbody/bmp/version + + + + src/main/resources + false + + **/** + + + net/lightbody/bmp/version + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Xmx1g -XX:MaxPermSize=256m + + + + + + + + net.lightbody.bmp + littleproxy + + + com.barchart.udt + barchart-udt-bundle + + + commons-cli + commons-cli + + + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.google.guava + guava + + + + dnsjava + dnsjava + 2.1.8 + + + + org.seleniumhq.selenium + selenium-api + true + + + + org.slf4j + slf4j-api + + + + org.slf4j + jcl-over-slf4j + + + + + com.jcraft + jzlib + + + io.netty + netty + + + + + + + io.netty + netty-all + + + + org.bouncycastle + bcprov-jdk15on + + + + org.bouncycastle + bcpkix-jdk15on + + + + net.lightbody.bmp + mitm + ${project.version} + + + + + org.javassist + javassist + true + + + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.seleniumhq.selenium + selenium-firefox-driver + test + + + junit + junit + test + + + + org.mockito + mockito-core + test + + + + org.mock-server + mockserver-netty + test + + + ch.qos.logback + logback-classic + + + + io.netty + netty-codec-socks + + + io.netty + netty-buffer + + + io.netty + netty-codec + + + io.netty + netty-codec-http + + + io.netty + netty-common + + + io.netty + netty-handler + + + io.netty + netty-transport + + + + + + org.eclipse.jetty + jetty-server + ${unit-test-jetty.version} + test + + + + org.eclipse.jetty + jetty-servlet + ${unit-test-jetty.version} + test + + + + org.eclipse.jetty + jetty-servlets + ${unit-test-jetty.version} + test + + + + org.hamcrest + hamcrest-library + test + + + \ No newline at end of file diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java new file mode 100644 index 000000000..a04ffe7af --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxy.java @@ -0,0 +1,643 @@ +package net.lightbody.bmp; + +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.filters.RequestFilter; +import net.lightbody.bmp.filters.ResponseFilter; +import net.lightbody.bmp.mitm.TrustSource; +import net.lightbody.bmp.proxy.BlacklistEntry; +import net.lightbody.bmp.proxy.CaptureType; +import net.lightbody.bmp.proxy.auth.AuthType; +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import org.littleshoot.proxy.HttpFiltersSource; +import org.littleshoot.proxy.MitmManager; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +public interface BrowserMobProxy { + /** + * Starts the proxy on port 0 (a JVM-selected open port). The proxy will bind the listener to the wildcard address (0:0:0:0 - all network interfaces). + * + * @throws java.lang.IllegalStateException if the proxy has already been started + */ + void start(); + + /** + * Starts the proxy on the specified port. The proxy will bind the listener to the wildcard address (0:0:0:0 - all network interfaces). + * + * @param port port to listen on + * @throws java.lang.IllegalStateException if the proxy has already been started + */ + void start(int port); + + /** + * Starts the proxy on the specified port. The proxy will listen for connections on the network interface specified by the bindAddress, and will + * also initiate connections to upstream servers on the same network interface. + * + * @param port port to listen on + * @param bindAddress address of the network interface on which the proxy will listen for connections and also attempt to connect to upstream servers. + * @throws java.lang.IllegalStateException if the proxy has already been started + */ + void start(int port, InetAddress bindAddress); + + /** + * Starts the proxy on the specified port. The proxy will listen for connections on the network interface specified by the clientBindAddress, and will + * initiate connections to upstream servers from the network interface specified by the serverBindAddress. + * + * @param port port to listen on + * @param clientBindAddress address of the network interface on which the proxy will listen for connections + * @param serverBindAddress address of the network interface on which the proxy will connect to upstream servers + * @throws java.lang.IllegalStateException if the proxy has already been started + */ + void start(int port, InetAddress clientBindAddress, InetAddress serverBindAddress); + + /** + * Returns true if the proxy is started and listening for connections, otherwise false. + */ + boolean isStarted(); + + /** + * Stops accepting new client connections and initiates a graceful shutdown of the proxy server, waiting up to 5 seconds for network + * traffic to stop. If the proxy was previously stopped or aborted, this method has no effect. + * + * @throws java.lang.IllegalStateException if the proxy has not been started. + */ + void stop(); + + /** + * Like {@link #stop()}, shuts down the proxy server and no longer accepts incoming connections, but does not wait for any existing + * network traffic to cease. Any existing connections to clients or to servers may be force-killed immediately. + * If the proxy was previously stopped or aborted, this method has no effect. + * + * @throws java.lang.IllegalStateException if the proxy has not been started + */ + void abort(); + + /** + * Returns the address of the network interface on which the proxy is listening for client connections. + * + * @return the client bind address, or null if the proxy has not been started + */ + InetAddress getClientBindAddress(); + + /** + * Returns the actual port on which the proxy is listening for client connections. + * + * @throws java.lang.IllegalStateException if the proxy has not been started + */ + int getPort(); + + /** + * Returns the address address of the network interface the proxy will use to initiate upstream connections. If no server bind address + * has been set, this method returns null, even if the proxy has been started. + * + * @return server bind address if one has been set, otherwise null + */ + InetAddress getServerBindAddress(); + + /** + * Retrieves the current HAR. + * + * @return current HAR, or null if HAR capture is not enabled + */ + Har getHar(); + + /** + * Starts a new HAR file with the default page name (see {@link #newPage()}. Enables HAR capture if it was not previously enabled. + * + * @return existing HAR file, or null if none exists or HAR capture was disabled + */ + Har newHar(); + + /** + * Starts a new HAR file with the specified initialPageRef as the page name and page title. Enables HAR capture if it was not previously enabled. + * + * @param initialPageRef initial page name of the new HAR file + * @return existing HAR file, or null if none exists or HAR capture was disabled + */ + Har newHar(String initialPageRef); + + /** + * Starts a new HAR file with the specified page name and page title. Enables HAR capture if it was not previously enabled. + * + * @param initialPageRef initial page name of the new HAR file + * @param initialPageTitle initial page title of the new HAR file + * @return existing HAR file, or null if none exists or HAR capture was disabled + */ + Har newHar(String initialPageRef, String initialPageTitle); + + /** + * Sets the data types that will be captured in the HAR file for future requests. Replaces any existing capture types with the specified + * capture types. A null or empty set will not disable HAR capture, but will disable collection of + * additional {@link net.lightbody.bmp.proxy.CaptureType} data types. {@link net.lightbody.bmp.proxy.CaptureType} provides several + * convenience methods to retrieve commonly-used capture settings. + *

+ * Note: HAR capture must still be explicitly enabled via {@link #newHar()} or {@link #newHar(String)} to begin capturing + * any request and response contents. + * + * @param captureTypes HAR data types to capture + */ + void setHarCaptureTypes(Set captureTypes); + + /** + * Sets the data types that will be captured in the HAR file for future requests. Replaces any existing capture types with the specified + * capture types. A null or empty set will not disable HAR capture, but will disable collection of + * additional {@link net.lightbody.bmp.proxy.CaptureType} data types. {@link net.lightbody.bmp.proxy.CaptureType} provides several + * convenience methods to retrieve commonly-used capture settings. + *

+ * Note: HAR capture must still be explicitly enabled via {@link #newHar()} or {@link #newHar(String)} to begin capturing + * any request and response contents. + * + * @param captureTypes HAR data types to capture + */ + void setHarCaptureTypes(CaptureType... captureTypes); + + /** + * @return A copy of HAR capture types currently in effect. The EnumSet cannot be used to modify the HAR capture types currently in effect. + */ + EnumSet getHarCaptureTypes(); + + /** + * Enables the specified HAR capture types. Does not replace or disable any other capture types that may already be enabled. + * + * @param captureTypes capture types to enable + */ + void enableHarCaptureTypes(Set captureTypes); + + /** + * Enables the specified HAR capture types. Does not replace or disable any other capture types that may already be enabled. + * + * @param captureTypes capture types to enable + */ + void enableHarCaptureTypes(CaptureType... captureTypes); + + /** + * Disables the specified HAR capture types. Does not replace or disable any other capture types that may already be enabled. + * + * @param captureTypes capture types to disable + */ + void disableHarCaptureTypes(Set captureTypes); + + /** + * Disables the specified HAR capture types. Does not replace or disable any other capture types that may already be enabled. + * + * @param captureTypes capture types to disable + */ + void disableHarCaptureTypes(CaptureType... captureTypes); + + /** + * Starts a new HAR page using the default page naming convention. The default page naming convention is "Page #", where "#" resets to 1 + * every time {@link #newHar()} or {@link #newHar(String)} is called, and increments on every subsequent call to {@link #newPage()} or + * {@link #newHar(String)}. Populates the {@link net.lightbody.bmp.core.har.HarPageTimings#onLoad} value based on the amount of time + * the current page has been captured. + * + * @return the HAR as it existed immediately after ending the current page + * @throws java.lang.IllegalStateException if HAR capture has not been enabled via {@link #newHar()} or {@link #newHar(String)} + */ + Har newPage(); + + /** + * Starts a new HAR page using the specified pageRef as the page name and the page title. Populates the + * {@link net.lightbody.bmp.core.har.HarPageTimings#onLoad} value based on the amount of time the current page has been captured. + * + * @param pageRef name of the new page + * @return the HAR as it existed immediately after ending the current page + * @throws java.lang.IllegalStateException if HAR capture has not been enabled via {@link #newHar()} or {@link #newHar(String)} + */ + Har newPage(String pageRef); + + /** + * Starts a new HAR page using the specified pageRef as the page name and the pageTitle as the page title. Populates the + * {@link net.lightbody.bmp.core.har.HarPageTimings#onLoad} value based on the amount of time the current page has been captured. + * + * @param pageRef name of the new page + * @param pageTitle title of the new page + * @return the HAR as it existed immediately after ending the current page + * @throws java.lang.IllegalStateException if HAR capture has not been enabled via {@link #newHar()} or {@link #newHar(String)} + */ + Har newPage(String pageRef, String pageTitle); + + /** + * Stops capturing traffic in the HAR. Populates the {@link net.lightbody.bmp.core.har.HarPageTimings#onLoad} value for the current page + * based on the amount of time it has been captured. + * + * @return the existing HAR + */ + Har endHar(); + + /** + * Sets the maximum bandwidth to consume when reading server responses. + * + * @param bytesPerSecond maximum bandwidth, in bytes per second + */ + void setReadBandwidthLimit(long bytesPerSecond); + + /** + * Returns the current bandwidth limit for reading, in bytes per second. + */ + long getReadBandwidthLimit(); + + /** + * Sets the maximum bandwidth to consume when sending requests to servers. + * + * @param bytesPerSecond maximum bandwidth, in bytes per second + */ + void setWriteBandwidthLimit(long bytesPerSecond); + + /** + * Returns the current bandwidth limit for writing, in bytes per second. + */ + long getWriteBandwidthLimit(); + + /** + * The minimum amount of time that will elapse between the time the proxy begins receiving a response from the server and the time the + * proxy begins sending the response to the client. + * + * @param latency minimum latency, or 0 for no minimum + * @param timeUnit TimeUnit for the latency + */ + void setLatency(long latency, TimeUnit timeUnit); + + /** + * Maximum amount of time to wait to establish a connection to a remote server. If the connection has not been established within the + * specified time, the proxy will respond with an HTTP 502 Bad Gateway. The default value is 60 seconds. + * + * @param connectionTimeout maximum time to wait to establish a connection to a server, or 0 to wait indefinitely + * @param timeUnit TimeUnit for the connectionTimeout + */ + void setConnectTimeout(int connectionTimeout, TimeUnit timeUnit); + + /** + * Maximum amount of time to allow a connection to remain idle. A connection becomes idle when it has not received data from a server + * within the the specified timeout. If the proxy has not yet begun to forward the response to the client, the proxy + * will respond with an HTTP 504 Gateway Timeout. If the proxy has already started forwarding the response to the client, the + * connection to the client may be closed abruptly. The default value is 60 seconds. + * + * @param idleConnectionTimeout maximum time to allow a connection to remain idle, or 0 to wait indefinitely. + * @param timeUnit TimeUnit for the idleConnectionTimeout + */ + void setIdleConnectionTimeout(int idleConnectionTimeout, TimeUnit timeUnit); + + /** + * Maximum amount of time to wait for an HTTP response from the remote server after the request has been sent in its entirety. The HTTP + * request must complete within the specified time. If the proxy has not yet begun to forward the response to the client, the proxy + * will respond with an HTTP 504 Gateway Timeout. If the proxy has already started forwarding the response to the client, the + * connection to the client may be closed abruptly. The default value is 0 (wait indefinitely). + * + * @param requestTimeout maximum time to wait for an HTTP response, or 0 to wait indefinitely + * @param timeUnit TimeUnit for the requestTimeout + */ + void setRequestTimeout(int requestTimeout, TimeUnit timeUnit); + + /** + * Enables automatic authorization for the specified domain and auth type. Every request sent to the specified domain will contain the + * specified authorization information. + * + * @param domain domain automatically send authorization information to + * @param username authorization username + * @param password authorization password + * @param authType authorization type + */ + void autoAuthorization(String domain, String username, String password, AuthType authType); + + /** + * Stops automatic authorization for the specified domain. + * + * @param domain domain to stop automatically sending authorization information to + */ + void stopAutoAuthorization(String domain); + + /** + * Enables chained proxy authorization using the Proxy-Authorization header described in RFC 7235, section 4.4 (https://tools.ietf.org/html/rfc7235#section-4.4). + * Currently, only {@link AuthType#BASIC} authentication is supported. + * + * @param username the username to use to authenticate with the chained proxy + * @param password the password to use to authenticate with the chained proxy + * @param authType the auth type to use (currently, must be BASIC) + */ + void chainedProxyAuthorization(String username, String password, AuthType authType); + + /** + * Adds a rewrite rule for the specified URL-matching regular expression. If there are any existing rewrite rules, the new rewrite + * rule will be applied last, after all other rewrite rules are applied. The specified urlPattern will be replaced with the specified + * replacement expression. The urlPattern is treated as a Java regular expression and must be properly escaped (see {@link java.util.regex.Pattern}). + * The replacementExpression may consist of capture groups specified in the urlPattern, denoted + * by a $ (see {@link java.util.regex.Matcher#appendReplacement(StringBuffer, String)}. + *

+ * For HTTP requests (not HTTPS), if the hostname and/or port is changed as a result of a rewrite rule, the Host header of the request will be modified + * to reflect the updated hostname/port. For HTTPS requests, the host and port cannot be changed by rewrite rules + * (use {@link #getHostNameResolver()} and {@link AdvancedHostResolver#remapHost(String, String)} to direct HTTPS requests + * to a different host). + *

+ * Note: The rewriting applies to the entire URL, including scheme (http:// or https://), hostname/address, port, and query string. Note that this means + * a urlPattern of {@code "http://www\.website\.com/page"} will NOT match {@code http://www.website.com:80/page}. + *

+ * For example, the following rewrite rule: + * + *

   {@code proxy.rewriteUrl("http://www\\.(yahoo|bing)\\.com/\\?(\\w+)=(\\w+)", "http://www.google.com/?originalDomain=$1&$2=$3");}
+ * + * will match an HTTP request (but not HTTPS!) to www.yahoo.com or www.bing.com with exactly 1 query parameter, + * and replace it with a call to www.google.com with an 'originalDomain' query parameter, as well as the original query parameter. + *

+ * When applied to the URL: + *

   {@code http://www.yahoo.com?theFirstParam=someValue}
+ * will result in the proxy making a request to: + *
   {@code http://www.google.com?originalDomain=yahoo&theFirstParam=someValue}
+ * When applied to the URL: + *
   {@code http://www.bing.com?anotherParam=anotherValue}
+ * will result in the proxy making a request to: + *
   {@code http://www.google.com?originalDomain=bing&anotherParam=anotherValue}
+ * + * @param urlPattern URL-matching regular expression + * @param replacementExpression an expression, which may optionally contain capture groups, which will replace any URL which matches urlPattern + */ + void rewriteUrl(String urlPattern, String replacementExpression); + + /** + * Replaces existing rewrite rules with the specified patterns and replacement expressions. The rules will be applied in the order + * specified by the Map's iterator. + *

+ * See {@link #rewriteUrl(String, String)} for details on the format of the rewrite rules. + * + * @param rewriteRules {@code Map} + */ + void rewriteUrls(Map rewriteRules); + + /** + * Returns all rewrite rules currently in effect. Iterating over the returned Map is guaranteed to return rewrite rules + * in the order in which the rules are actually applied. + * + * @return {@code Map} + */ + Map getRewriteRules(); + + /** + * Removes an existing rewrite rule whose urlPattern matches the specified pattern. + * + * @param urlPattern rewrite rule pattern to remove + */ + void removeRewriteRule(String urlPattern); + + /** + * Clears all existing rewrite rules. + */ + void clearRewriteRules(); + + /** + * Adds a URL-matching regular expression to the blacklist. Requests that match a blacklisted URL will return the specified HTTP + * statusCode for all HTTP methods. If there are existing patterns on the blacklist, the urlPattern will be evaluated last, + * after the URL is checked against all other blacklist entries. + *

+ * The urlPattern matches the full URL of the request, including scheme, host, and port, path, and query parameters + * for both HTTP and HTTPS requests. For example, to blacklist both HTTP and HTTPS requests to www.google.com, + * use a urlPattern of "https?://www\\.google\\.com/.*". + * + * @param urlPattern URL-matching regular expression to blacklist + * @param statusCode HTTP status code to return + */ + void blacklistRequests(String urlPattern, int statusCode); + + /** + * Adds a URL-matching regular expression to the blacklist. Requests that match a blacklisted URL will return the specified HTTP + * statusCode only when the request's HTTP method (GET, POST, PUT, etc.) matches the specified httpMethodPattern regular expression. + * If there are existing patterns on the blacklist, the urlPattern will be evaluated last, after the URL is checked against all + * other blacklist entries. + *

+ * See {@link #blacklistRequests(String, int)} for details on the URL the urlPattern will match. + * + * @param urlPattern URL-matching regular expression to blacklist + * @param statusCode HTTP status code to return + * @param httpMethodPattern regular expression matching a request's HTTP method + */ + void blacklistRequests(String urlPattern, int statusCode, String httpMethodPattern); + + /** + * Replaces any existing blacklist with the specified blacklist. URLs will be evaluated against the blacklist in the order + * specified by the Collection's iterator. + * + * @param blacklist new blacklist entries + */ + void setBlacklist(Collection blacklist); + + /** + * Returns all blacklist entries currently in effect. Iterating over the returned Collection is guaranteed to return + * blacklist entries in the order in which URLs are actually evaluated against the blacklist. + * + * @return blacklist entries, or an empty collection if none exist + */ + Collection getBlacklist(); + + /** + * Clears any existing blacklist. + */ + void clearBlacklist(); + + /** + * Whitelists URLs matching the specified regular expression patterns. Replaces any existing whitelist. + * The urlPattern matches the full URL of the request, including scheme, host, and port, path, and query parameters + * for both HTTP and HTTPS requests. For example, to whitelist both HTTP and HTTPS requests to www.google.com, use a urlPattern + * of "https?://www\\.google\\.com/.*". + *

+ * Note: All HTTP CONNECT requests are automatically whitelisted and cannot be short-circuited using the + * whitelist response code. + * + * @param urlPatterns URL-matching regular expressions to whitelist; null or an empty collection will enable an empty whitelist + * @param statusCode HTTP status code to return to clients when a URL matches a pattern + */ + void whitelistRequests(Collection urlPatterns, int statusCode); + + /** + * Adds a URL-matching regular expression to an existing whitelist. + * + * @param urlPattern URL-matching regular expressions to whitelist + * @throws java.lang.IllegalStateException if the whitelist is not enabled + */ + void addWhitelistPattern(String urlPattern); + + /** + * Enables the whitelist, but with no matching URLs. All requests will generated the specified HTTP statusCode. + * + * @param statusCode HTTP status code to return to clients on all requests + */ + void enableEmptyWhitelist(int statusCode); + + /** + * Clears any existing whitelist and disables whitelisting. + */ + void disableWhitelist(); + + /** + * Returns the URL-matching regular expressions currently in effect. If the whitelist is disabled, this method always returns an empty collection. + * If the whitelist is enabled but empty, this method return an empty collection. + * + * @return whitelist currently in effect, or an empty collection if the whitelist is disabled or empty + */ + Collection getWhitelistUrls(); + + /** + * Returns the status code returned for all URLs that do not match the whitelist. If the whitelist is not currently enabled, returns -1. + * + * @return HTTP status code returned for non-whitelisted URLs, or -1 if the whitelist is disabled. + */ + int getWhitelistStatusCode(); + + /** + * Returns true if the whitelist is enabled, otherwise false. + */ + boolean isWhitelistEnabled(); + + /** + * Adds the specified HTTP headers to every request. Replaces any existing additional headers with the specified headers. + * + * @param headers {@code Map

} to append to every request. + */ + void addHeaders(Map headers); + + /** + * Adds a new HTTP header to every request. If the header already exists on the request, it will be replaced with the specified header. + * + * @param name name of the header to add + * @param value new header's value + */ + void addHeader(String name, String value); + + /** + * Removes a header previously added with {@link #addHeader(String name, String value)}. + * + * @param name previously-added header's name + */ + void removeHeader(String name); + + /** + * Removes all headers previously added with {@link #addHeader(String name, String value)}. + */ + void removeAllHeaders(); + + /** + * Returns all headers previously added with {@link #addHeader(String name, String value)}. + * + * @return {@code Map
} + */ + Map getAllHeaders(); + + /** + * Sets the resolver that will be used to look up host names. To chain multiple resolvers, wrap a list + * of resolvers in a {@link net.lightbody.bmp.proxy.dns.ChainedHostResolver}. + * + * @param resolver host name resolver + */ + void setHostNameResolver(AdvancedHostResolver resolver); + + /** + * Returns the current host name resolver. + * + * @return current host name resolver + */ + AdvancedHostResolver getHostNameResolver(); + + /** + * Waits for existing network traffic to stop, and for the specified quietPeriod to elapse. Returns true if there is no network traffic + * for the quiet period within the specified timeout, otherwise returns false. + * + * @param quietPeriod amount of time after which network traffic will be considered "stopped" + * @param timeout maximum amount of time to wait for network traffic to stop + * @param timeUnit TimeUnit for the quietPeriod and timeout + * @return true if network traffic is stopped, otherwise false + */ + boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit); + + /** + * Instructs this proxy to route traffic through an upstream proxy. + * + * Note: A chained proxy must be set before the proxy is started, though it can be changed after the proxy is started. + * + * @param chainedProxyAddress address of the upstream proxy + */ + void setChainedProxy(InetSocketAddress chainedProxyAddress); + + /** + * Returns the address and port of the upstream proxy. + * + * @return address and port of the upstream proxy, or null of there is none. + */ + InetSocketAddress getChainedProxy(); + + /** + * Adds a new filter factory (request/response interceptor) to the beginning of the HttpFilters chain. + *

+ * Usage note: The actual filter (interceptor) instance is created on every request by implementing the + * {@link HttpFiltersSource#filterRequest(io.netty.handler.codec.http.HttpRequest, io.netty.channel.ChannelHandlerContext)} method and returning an + * {@link org.littleshoot.proxy.HttpFilters} instance (typically, a subclass of {@link org.littleshoot.proxy.HttpFiltersAdapter}). + * To disable or bypass a filter on a per-request basis, the filterRequest() method may return null. + * + * @param filterFactory factory to generate HttpFilters + */ + void addFirstHttpFilterFactory(HttpFiltersSource filterFactory); + + /** + * Adds a new filter factory (request/response interceptor) to the end of the HttpFilters chain. + *

+ * Usage note: The actual filter (interceptor) instance is created on every request by implementing the + * {@link HttpFiltersSource#filterRequest(io.netty.handler.codec.http.HttpRequest, io.netty.channel.ChannelHandlerContext)} method and returning an + * {@link org.littleshoot.proxy.HttpFilters} instance (typically, a subclass of {@link org.littleshoot.proxy.HttpFiltersAdapter}). + * To disable or bypass a filter on a per-request basis, the filterRequest() method may return null. + * + * @param filterFactory factory to generate HttpFilters + */ + void addLastHttpFilterFactory(HttpFiltersSource filterFactory); + + /** + * Adds a new ResponseFilter that can be used to examine and manipulate the response before sending it to the client. + * + * @param filter filter instance + */ + void addResponseFilter(ResponseFilter filter); + + /** + * Adds a new RequestFilter that can be used to examine and manipulate the request before sending it to the server. + * + * @param filter filter instance + */ + void addRequestFilter(RequestFilter filter); + + /** + * Completely disables MITM for this proxy server. The proxy will no longer intercept HTTPS requests, but they will + * still be pass-through proxied. This option must be set before the proxy is started; otherwise an IllegalStateException will be thrown. + * + * @param mitmDisabled when true, MITM capture will be disabled + * @throws java.lang.IllegalStateException if the proxy is already started + */ + void setMitmDisabled(boolean mitmDisabled); + + /** + * Sets the MITM manager, which is responsible for generating forged SSL certificates to present to clients. By default, + * BrowserMob Proxy uses the ca-certificate-rsa.cer root certificate for impersonation. See the documentation at + * {@link net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager} and {@link net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager.Builder} + * for details on customizing the root and server certificate generation. + * + * @param mitmManager MITM manager to use + */ + void setMitmManager(MitmManager mitmManager); + + /** + * Disables verification of all upstream servers' SSL certificates. All upstream servers will be trusted, even if they + * do not present valid certificates signed by certification authorities in the JDK's trust store. This option + * exposes the proxy to MITM attacks and should only be used when testing in trusted environments. + * + * @param trustAllServers when true, disables upstream server certificate verification + */ + void setTrustAllServers(boolean trustAllServers); + + /** + * Sets the {@link TrustSource} that contains trusted root certificate authorities that will be used to validate + * upstream servers' certificates. When null, disables certificate validation (see warning at {@link #setTrustAllServers(boolean)}). + * + * @param trustSource TrustSource containing root CAs, or null to disable upstream server validation + */ + void setTrustSource(TrustSource trustSource); +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java new file mode 100644 index 000000000..d4ddc5543 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java @@ -0,0 +1,1175 @@ +package net.lightbody.bmp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.MapMaker; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import net.lightbody.bmp.client.ClientUtil; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.core.har.HarLog; +import net.lightbody.bmp.core.har.HarNameVersion; +import net.lightbody.bmp.core.har.HarPage; +import net.lightbody.bmp.filters.AddHeadersFilter; +import net.lightbody.bmp.filters.AutoBasicAuthFilter; +import net.lightbody.bmp.filters.BlacklistFilter; +import net.lightbody.bmp.filters.BrowserMobHttpFilterChain; +import net.lightbody.bmp.filters.HarCaptureFilter; +import net.lightbody.bmp.filters.HttpConnectHarCaptureFilter; +import net.lightbody.bmp.filters.HttpsHostCaptureFilter; +import net.lightbody.bmp.filters.HttpsOriginalHostCaptureFilter; +import net.lightbody.bmp.filters.LatencyFilter; +import net.lightbody.bmp.filters.RegisterRequestFilter; +import net.lightbody.bmp.filters.RequestFilter; +import net.lightbody.bmp.filters.RequestFilterAdapter; +import net.lightbody.bmp.filters.ResolvedHostnameCacheFilter; +import net.lightbody.bmp.filters.ResponseFilter; +import net.lightbody.bmp.filters.ResponseFilterAdapter; +import net.lightbody.bmp.filters.RewriteUrlFilter; +import net.lightbody.bmp.filters.UnregisterRequestFilter; +import net.lightbody.bmp.filters.WhitelistFilter; +import net.lightbody.bmp.mitm.KeyStoreFileCertificateSource; +import net.lightbody.bmp.mitm.TrustSource; +import net.lightbody.bmp.mitm.keys.ECKeyGenerator; +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import net.lightbody.bmp.proxy.ActivityMonitor; +import net.lightbody.bmp.proxy.BlacklistEntry; +import net.lightbody.bmp.proxy.CaptureType; +import net.lightbody.bmp.proxy.RewriteRule; +import net.lightbody.bmp.proxy.Whitelist; +import net.lightbody.bmp.proxy.auth.AuthType; +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import net.lightbody.bmp.proxy.dns.DelegatingHostResolver; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import net.lightbody.bmp.util.BrowserMobProxyUtil; +import org.littleshoot.proxy.ChainedProxy; +import org.littleshoot.proxy.ChainedProxyAdapter; +import org.littleshoot.proxy.ChainedProxyManager; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersSource; +import org.littleshoot.proxy.HttpFiltersSourceAdapter; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.HttpProxyServerBootstrap; +import org.littleshoot.proxy.MitmManager; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; +import org.littleshoot.proxy.impl.ProxyUtils; +import org.littleshoot.proxy.impl.ThreadPoolConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; + +/** + * A LittleProxy-based implementation of {@link net.lightbody.bmp.BrowserMobProxy}. + */ +public class BrowserMobProxyServer implements BrowserMobProxy { + private static final Logger log = LoggerFactory.getLogger(BrowserMobProxyServer.class); + + private static final HarNameVersion HAR_CREATOR_VERSION = new HarNameVersion("BrowserMob Proxy", BrowserMobProxyUtil.getVersionString()); + + /* Default MITM resources */ + private static final String RSA_KEYSTORE_RESOURCE = "/sslSupport/ca-keystore-rsa.p12"; + private static final String EC_KEYSTORE_RESOURCE = "/sslSupport/ca-keystore-ec.p12"; + private static final String KEYSTORE_TYPE = "PKCS12"; + private static final String KEYSTORE_PRIVATE_KEY_ALIAS = "key"; + private static final String KEYSTORE_PASSWORD = "password"; + + /** + * The default pseudonym to use when adding the Via header to proxied requests. + */ + public static final String VIA_HEADER_ALIAS = "browsermobproxy"; + + /** + * True only after the proxy has been successfully started. + */ + private final AtomicBoolean started = new AtomicBoolean(false); + + /** + * True only after the proxy has been successfully started, then successfully stopped or aborted. + */ + private final AtomicBoolean stopped = new AtomicBoolean(false); + + /** + * Tracks the current page count, for use when auto-generating HAR page names. + */ + private final AtomicInteger harPageCount = new AtomicInteger(0); + + /** + * When true, MITM will be disabled. The proxy will no longer intercept HTTPS requests, but they will still be proxied. + */ + private volatile boolean mitmDisabled = false; + + /** + * The MITM manager that will be used for HTTPS requests. + */ + private volatile MitmManager mitmManager; + + /** + * The list of filterFactories that will generate the filters that implement browsermob-proxy behavior. + */ + private final List filterFactories = new CopyOnWriteArrayList<>(); + + /** + * List of rejected URL patterns + */ + private volatile Collection blacklistEntries = new CopyOnWriteArrayList<>(); + + /** + * List of URLs to rewrite + */ + private volatile CopyOnWriteArrayList rewriteRules = new CopyOnWriteArrayList<>(); + + /** + * The LittleProxy instance that performs all proxy operations. + */ + private volatile HttpProxyServer proxyServer; + + /** + * No capture types are enabled by default. + */ + private volatile EnumSet harCaptureTypes = EnumSet.noneOf(CaptureType.class); + + /** + * The current HAR being captured. + */ + private volatile Har har; + /** + * The current HarPage to which new requests will be associated. + */ + private volatile HarPage currentHarPage; + /** + * Maximum bandwidth to consume when reading responses from servers. + */ + private volatile long readBandwidthLimitBps; + /** + * Maximum bandwidth to consume when writing requests to servers. + */ + private volatile long writeBandwidthLimitBps; + /** + * List of accepted URL patterns. Unlisted URL patterns will be rejected with the response code contained in the Whitelist. + */ + private final AtomicReference whitelist = new AtomicReference<>(Whitelist.WHITELIST_DISABLED); + + /** + * Additional headers that will be sent with every request. The map is declared as a ConcurrentMap to indicate that writes may be performed + * by other threads concurrently (e.g. due to an incoming REST call), but the concurrencyLevel is set to 1 because modifications to the + * additionalHeaders are rare, and in most cases happen only once, at start-up. + */ + private volatile ConcurrentMap additionalHeaders = new MapMaker().concurrencyLevel(1).makeMap(); + + /** + * The amount of time to wait while connecting to a server. + */ + private volatile int connectTimeoutMs; + + /** + * The amount of time a connection to a server can remain idle while receiving data from the server. + */ + private volatile int idleConnectionTimeoutSec; + + /** + * The amount of time to wait before forwarding the response to the client. + */ + private volatile int latencyMs; + + /** + * Set to true once the HAR capture filter has been added to the filter chain. + */ + private final AtomicBoolean harCaptureFilterEnabled = new AtomicBoolean(false); + + /** + * Set to true when LittleProxy has been bootstrapped with the default chained proxy. This allows modifying the chained proxy + * after the proxy has been started. + */ + private final AtomicBoolean bootstrappedWithDefaultChainedProxy = new AtomicBoolean(false); + + /** + * The address of an upstream chained proxy to route traffic through. + */ + private volatile InetSocketAddress upstreamProxyAddress; + + /** + * The chained proxy manager that manages upstream proxies. + */ + private volatile ChainedProxyManager chainedProxyManager; + + /** + * The address of the network interface from which the proxy will initiate connections. + */ + private volatile InetAddress serverBindAddress; + + /** + * The TrustSource that will be used to validate servers' certificates. If null, will not validate server certificates. + */ + private volatile TrustSource trustSource = TrustSource.defaultTrustSource(); + + /** + * When true, use Elliptic Curve keys and certificates when impersonating upstream servers. + */ + private volatile boolean useEcc = false; + + /** + * Resolver to use when resolving hostnames to IP addresses. This is a bridge between {@link org.littleshoot.proxy.HostResolver} and + * {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver}. It allows the resolvers to be changed on-the-fly without re-bootstrapping the + * littleproxy server. The default resolver (native JDK resolver) can be changed using {@link #setHostNameResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)} and + * supplying one of the pre-defined resolvers in {@link ClientUtil}, such as {@link ClientUtil#createDnsJavaWithNativeFallbackResolver()} + * or {@link ClientUtil#createDnsJavaResolver()}. You can also build your own resolver, or use {@link net.lightbody.bmp.proxy.dns.ChainedHostResolver} + * to chain together multiple DNS resolvers. + */ + private final DelegatingHostResolver delegatingResolver = new DelegatingHostResolver(ClientUtil.createNativeCacheManipulatingResolver()); + + private final ActivityMonitor activityMonitor = new ActivityMonitor(); + + /** + * The acceptor and worker thread configuration for the Netty thread pools. + */ + private volatile ThreadPoolConfiguration threadPoolConfiguration; + + /** + * A mapping of hostnames to base64-encoded Basic auth credentials that will be added to the Authorization header for + * matching requests. + */ + private final ConcurrentMap basicAuthCredentials = new MapMaker() + .concurrencyLevel(1) + .makeMap(); + + /** + * Base64-encoded credentials to use to authenticate with the upstream proxy. + */ + private volatile String chainedProxyCredentials; + + public BrowserMobProxyServer() { + } + + @Override + public void start(int port, InetAddress clientBindAddress, InetAddress serverBindAddress) { + boolean notStarted = started.compareAndSet(false, true); + if (!notStarted) { + throw new IllegalStateException("Proxy server is already started. Not restarting."); + } + + InetSocketAddress clientBindSocket; + if (clientBindAddress == null) { + // if no client bind address was specified, bind to the wildcard address + clientBindSocket = new InetSocketAddress(port); + } else { + clientBindSocket = new InetSocketAddress(clientBindAddress, port); + } + + this.serverBindAddress = serverBindAddress; + + // initialize all the default BrowserMob filter factories that provide core BMP functionality + addBrowserMobFilters(); + + HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap() + .withFiltersSource(new HttpFiltersSource() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext channelHandlerContext) { + return new BrowserMobHttpFilterChain(BrowserMobProxyServer.this, originalRequest, channelHandlerContext); + } + + @Override + public int getMaximumRequestBufferSizeInBytes() { + return getMaximumRequestBufferSize(); + } + + @Override + public int getMaximumResponseBufferSizeInBytes() { + return getMaximumResponseBufferSize(); + } + }) + .withServerResolver(delegatingResolver) + .withAddress(clientBindSocket) + .withConnectTimeout(connectTimeoutMs) + .withIdleConnectionTimeout(idleConnectionTimeoutSec) + .withProxyAlias(VIA_HEADER_ALIAS); + + if (serverBindAddress != null) { + bootstrap.withNetworkInterface(new InetSocketAddress(serverBindAddress, 0)); + } + + + if (!mitmDisabled) { + if (mitmManager == null) { + mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(new KeyStoreFileCertificateSource( + KEYSTORE_TYPE, + useEcc ? EC_KEYSTORE_RESOURCE : RSA_KEYSTORE_RESOURCE, + KEYSTORE_PRIVATE_KEY_ALIAS, + KEYSTORE_PASSWORD)) + .serverKeyGenerator(useEcc ? new ECKeyGenerator() : new RSAKeyGenerator()) + .trustSource(trustSource) + .build(); + } + + bootstrap.withManInTheMiddle(mitmManager); + } + + if (readBandwidthLimitBps > 0 || writeBandwidthLimitBps > 0) { + bootstrap.withThrottling(readBandwidthLimitBps, writeBandwidthLimitBps); + } + + if (chainedProxyManager != null) { + bootstrap.withChainProxyManager(chainedProxyManager); + } else if (upstreamProxyAddress != null) { + // indicate that the proxy was bootstrapped with the default chained proxy manager, which allows changing the + // chained proxy after the proxy is started. + bootstrappedWithDefaultChainedProxy.set(true); + + bootstrap.withChainProxyManager(new ChainedProxyManager() { + @Override + public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies) { + final InetSocketAddress upstreamProxy = upstreamProxyAddress; + if (upstreamProxy != null) { + chainedProxies.add(new ChainedProxyAdapter() { + @Override + public InetSocketAddress getChainedProxyAddress() { + return upstreamProxy; + } + + @Override + public void filterRequest(HttpObject httpObject) { + String chainedProxyAuth = chainedProxyCredentials; + if (chainedProxyAuth != null) { + if (httpObject instanceof HttpRequest) { + HttpHeaders.addHeader((HttpRequest)httpObject, HttpHeaders.Names.PROXY_AUTHORIZATION, "Basic " + chainedProxyAuth); + } + } + } + }); + } + } + }); + } + + if (threadPoolConfiguration != null) { + bootstrap.withThreadPoolConfiguration(threadPoolConfiguration); + } + + proxyServer = bootstrap.start(); + } + + @Override + public boolean isStarted() { + return started.get(); + } + + @Override + public void start(int port) { + this.start(port, null, null); + } + + @Override + public void start(int port, InetAddress bindAddress) { + this.start(port, bindAddress, null); + + } + + @Override + public void start() { + this.start(0); + } + + @Override + public void stop() { + stop(true); + } + + @Override + public void abort() { + stop(false); + } + + protected void stop(boolean graceful) { + if (isStarted()) { + if (stopped.compareAndSet(false, true)) { + if (proxyServer != null) { + if (graceful) { + proxyServer.stop(); + } else { + proxyServer.abort(); + } + } else { + log.warn("Attempted to stop proxy server, but proxy was never successfully started."); + } + } else { + throw new IllegalStateException("Proxy server is already stopped. Cannot re-stop."); + } + } else { + throw new IllegalStateException("Proxy server has not been started"); + } + } + + @Override + public InetAddress getClientBindAddress() { + if (started.get()) { + return proxyServer.getListenAddress().getAddress(); + } else { + return null; + } + } + + @Override + public int getPort() { + if (started.get()) { + return proxyServer.getListenAddress().getPort(); + } else { + return 0; + } + } + + @Override + public InetAddress getServerBindAddress() { + return serverBindAddress; + } + + @Override + public Har getHar() { + return har; + } + + @Override + public Har newHar() { + return newHar(null); + } + + @Override + public Har newHar(String initialPageRef) { + return newHar(initialPageRef, null); + } + + @Override + public Har newHar(String initialPageRef, String initialPageTitle) { + Har oldHar = getHar(); + + addHarCaptureFilter(); + + harPageCount.set(0); + + this.har = new Har(new HarLog(HAR_CREATOR_VERSION)); + + newPage(initialPageRef, initialPageTitle); + + return oldHar; + } + + @Override + public void setHarCaptureTypes(Set harCaptureSettings) { + if (harCaptureSettings == null || harCaptureSettings.isEmpty()) { + harCaptureTypes = EnumSet.noneOf(CaptureType.class); + } else { + harCaptureTypes = EnumSet.copyOf(harCaptureSettings); + } + } + + @Override + public void setHarCaptureTypes(CaptureType... captureTypes) { + if (captureTypes == null) { + setHarCaptureTypes(EnumSet.noneOf(CaptureType.class)); + } else { + setHarCaptureTypes(EnumSet.copyOf(Arrays.asList(captureTypes))); + } + } + + @Override + public EnumSet getHarCaptureTypes() { + return EnumSet.copyOf(harCaptureTypes); + } + + @Override + public void enableHarCaptureTypes(Set captureTypes) { + harCaptureTypes.addAll(captureTypes); + } + + @Override + public void enableHarCaptureTypes(CaptureType... captureTypes) { + if (captureTypes == null) { + enableHarCaptureTypes(EnumSet.noneOf(CaptureType.class)); + } else { + enableHarCaptureTypes(EnumSet.copyOf(Arrays.asList(captureTypes))); + } + } + + @Override + public void disableHarCaptureTypes(Set captureTypes) { + harCaptureTypes.removeAll(captureTypes); + + } + + @Override + public void disableHarCaptureTypes(CaptureType... captureTypes) { + if (captureTypes == null) { + disableHarCaptureTypes(EnumSet.noneOf(CaptureType.class)); + } else { + disableHarCaptureTypes(EnumSet.copyOf(Arrays.asList(captureTypes))); + } + } + + @Override + public Har newPage() { + return newPage(null); + } + + @Override + public Har newPage(String pageRef) { + return newPage(pageRef, null); + } + + @Override + public Har newPage(String pageRef, String pageTitle) { + if (har == null) { + throw new IllegalStateException("No HAR exists for this proxy. Use newHar() to create a new HAR before calling newPage()."); + } + + Har endOfPageHar = null; + + if (currentHarPage != null) { + String currentPageRef = currentHarPage.getId(); + + // end the previous page, so that page-wide timings are populated + endPage(); + + // the interface requires newPage() to return the Har as it was immediately after the previous page was ended. + endOfPageHar = BrowserMobProxyUtil.copyHarThroughPageRef(har, currentPageRef); + } + + if (pageRef == null) { + pageRef = "Page " + harPageCount.getAndIncrement(); + } + + if (pageTitle == null) { + pageTitle = pageRef; + } + + HarPage newPage = new HarPage(pageRef, pageTitle); + har.getLog().addPage(newPage); + + currentHarPage = newPage; + + return endOfPageHar; + } + + @Override + public Har endHar() { + Har oldHar = getHar(); + + // end the page and populate timings + endPage(); + + this.har = null; + + return oldHar; + } + + @Override + public void setReadBandwidthLimit(long bytesPerSecond) { + this.readBandwidthLimitBps = bytesPerSecond; + + if (isStarted()) { + proxyServer.setThrottle(this.readBandwidthLimitBps, this.writeBandwidthLimitBps); + } + } + + @Override + public long getReadBandwidthLimit() { + return readBandwidthLimitBps; + } + + @Override + public void setWriteBandwidthLimit(long bytesPerSecond) { + this.writeBandwidthLimitBps = bytesPerSecond; + + if (isStarted()) { + proxyServer.setThrottle(this.readBandwidthLimitBps, this.writeBandwidthLimitBps); + } + } + + @Override + public long getWriteBandwidthLimit() { + return writeBandwidthLimitBps; + } + + public void endPage() { + if (har == null) { + throw new IllegalStateException("No HAR exists for this proxy. Use newHar() to create a new HAR."); + } + + HarPage previousPage = this.currentHarPage; + this.currentHarPage = null; + + if (previousPage == null) { + return; + } + + previousPage.getPageTimings().setOnLoad(new Date().getTime() - previousPage.getStartedDateTime().getTime()); + } + + @Override + public void addHeaders(Map headers) { + ConcurrentMap newHeaders = new MapMaker().concurrencyLevel(1).makeMap(); + newHeaders.putAll(headers); + + this.additionalHeaders = newHeaders; + } + + @Override + public void setLatency(long latency, TimeUnit timeUnit) { + this.latencyMs = (int) TimeUnit.MILLISECONDS.convert(latency, timeUnit); + } + + @Override + public void autoAuthorization(String domain, String username, String password, AuthType authType) { + switch (authType) { + case BASIC: + // base64 encode the "username:password" string + String base64EncodedCredentials = BrowserMobHttpUtil.base64EncodeBasicCredentials(username, password); + + basicAuthCredentials.put(domain, base64EncodedCredentials); + break; + + default: + throw new UnsupportedOperationException("AuthType " + authType + " is not supported for HTTP Authorization"); + } + } + + @Override + public void stopAutoAuthorization(String domain) { + basicAuthCredentials.remove(domain); + } + + @Override + public void chainedProxyAuthorization(String username, String password, AuthType authType) { + switch (authType) { + case BASIC: + chainedProxyCredentials = BrowserMobHttpUtil.base64EncodeBasicCredentials(username, password); + break; + + default: + throw new UnsupportedOperationException("AuthType " + authType + " is not supported for Proxy Authorization"); + } + } + + @Override + public void setConnectTimeout(int connectTimeout, TimeUnit timeUnit) { + this.connectTimeoutMs = (int) TimeUnit.MILLISECONDS.convert(connectTimeout, timeUnit); + + if (isStarted()) { + proxyServer.setConnectTimeout((int) TimeUnit.MILLISECONDS.convert(connectTimeout, timeUnit)); + } + } + + /** + * The LittleProxy implementation only allows idle connection timeouts to be specified in seconds. idleConnectionTimeouts greater than + * 0 but less than 1 second will be set to 1 second; otherwise, values will be truncated (i.e. 1500ms will become 1s). + */ + @Override + public void setIdleConnectionTimeout(int idleConnectionTimeout, TimeUnit timeUnit) { + long timeout = TimeUnit.SECONDS.convert(idleConnectionTimeout, timeUnit); + if (timeout == 0 && idleConnectionTimeout > 0) { + this.idleConnectionTimeoutSec = 1; + } else { + this.idleConnectionTimeoutSec = (int) timeout; + } + + if (isStarted()) { + proxyServer.setIdleConnectionTimeout(idleConnectionTimeoutSec); + } + } + + @Override + public void setRequestTimeout(int requestTimeout, TimeUnit timeUnit) { + //TODO: implement Request Timeouts using LittleProxy. currently this only sets an idle connection timeout, if the idle connection + // timeout is higher than the specified requestTimeout. + if (idleConnectionTimeoutSec == 0 || idleConnectionTimeoutSec > TimeUnit.SECONDS.convert(requestTimeout, timeUnit)) { + setIdleConnectionTimeout(requestTimeout, timeUnit); + } + } + + @Override + public void rewriteUrl(String pattern, String replace) { + rewriteRules.add(new RewriteRule(pattern, replace)); + } + + @Override + public void rewriteUrls(Map rewriteRules) { + List newRules = new ArrayList<>(rewriteRules.size()); + for (Map.Entry rewriteRule : rewriteRules.entrySet()) { + RewriteRule newRule = new RewriteRule(rewriteRule.getKey(), rewriteRule.getValue()); + newRules.add(newRule); + } + + this.rewriteRules = new CopyOnWriteArrayList<>(newRules); + } + + @Override + public void clearRewriteRules() { + rewriteRules.clear(); + } + + @Override + public void blacklistRequests(String pattern, int responseCode) { + blacklistEntries.add(new BlacklistEntry(pattern, responseCode)); + } + + @Override + public void blacklistRequests(String pattern, int responseCode, String method) { + blacklistEntries.add(new BlacklistEntry(pattern, responseCode, method)); + } + + @Override + public void setBlacklist(Collection blacklist) { + this.blacklistEntries = new CopyOnWriteArrayList<>(blacklist); + } + + @Override + public Collection getBlacklist() { + return Collections.unmodifiableCollection(blacklistEntries); + } + + @Override + public boolean isWhitelistEnabled() { + return whitelist.get().isEnabled(); + } + + @Override + public Collection getWhitelistUrls() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (Pattern pattern : whitelist.get().getPatterns()) { + builder.add(pattern.pattern()); + } + + return builder.build(); + } + + @Override + public int getWhitelistStatusCode() { + return whitelist.get().getStatusCode(); + } + + @Override + public void clearBlacklist() { + blacklistEntries.clear(); + } + + @Override + public void whitelistRequests(Collection urlPatterns, int statusCode) { + this.whitelist.set(new Whitelist(urlPatterns, statusCode)); + } + + @Override + public void addWhitelistPattern(String urlPattern) { + // to make sure this method is threadsafe, we need to guarantee that the "snapshot" of the whitelist taken at the beginning + // of the method has not been replaced by the time we have constructed a new whitelist at the end of the method + boolean whitelistUpdated = false; + while (!whitelistUpdated) { + Whitelist currentWhitelist = this.whitelist.get(); + if (!currentWhitelist.isEnabled()) { + throw new IllegalStateException("Whitelist is disabled. Cannot add patterns to a disabled whitelist."); + } + + // retrieve the response code and list of patterns from the current whitelist, the construct a new list of patterns that contains + // all of the old whitelist's patterns + this new pattern + int statusCode = currentWhitelist.getStatusCode(); + List newPatterns = new ArrayList<>(currentWhitelist.getPatterns().size() + 1); + for (Pattern pattern : currentWhitelist.getPatterns()) { + newPatterns.add(pattern.pattern()); + } + newPatterns.add(urlPattern); + + // create a new (immutable) Whitelist object with the new pattern list and status code + Whitelist newWhitelist = new Whitelist(newPatterns, statusCode); + + // replace the current whitelist with the new whitelist only if the current whitelist has not changed since we started + whitelistUpdated = this.whitelist.compareAndSet(currentWhitelist, newWhitelist); + } + } + + /** + * Whitelist the specified request patterns, returning the specified responseCode for non-whitelisted + * requests. + * + * @param patterns regular expression strings matching URL patterns to whitelist. if empty or null, + * the whitelist will be enabled but will not match any URLs. + * @param responseCode the HTTP response code to return for non-whitelisted requests + */ + public void whitelistRequests(String[] patterns, int responseCode) { + if (patterns == null || patterns.length == 0) { + this.enableEmptyWhitelist(responseCode); + } else { + this.whitelistRequests(Arrays.asList(patterns), responseCode); + } + } + + @Override + public void enableEmptyWhitelist(int statusCode) { + whitelist.set(new Whitelist(statusCode)); + } + + @Override + public void disableWhitelist() { + whitelist.set(Whitelist.WHITELIST_DISABLED); + } + + @Override + public void addHeader(String name, String value) { + additionalHeaders.put(name, value); + } + + @Override + public void removeHeader(String name) { + additionalHeaders.remove(name); + } + + @Override + public void removeAllHeaders() { + additionalHeaders.clear(); + } + + @Override + public Map getAllHeaders() { + return ImmutableMap.copyOf(additionalHeaders); + } + + @Override + public void setHostNameResolver(AdvancedHostResolver resolver) { + delegatingResolver.setResolver(resolver); + } + + @Override + public AdvancedHostResolver getHostNameResolver() { + return delegatingResolver.getResolver(); + } + + @Override + public boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit) { + return activityMonitor.waitForQuiescence(quietPeriod, timeout, timeUnit); + } + + /** + * Instructs this proxy to route traffic through an upstream proxy. + * + * Note: Using {@link #setChainedProxyManager(ChainedProxyManager)} will supersede any value set by this method. A chained + * proxy must be set before the proxy is started, though it can be changed after the proxy is started. + * + * @param chainedProxyAddress address of the upstream proxy + */ + @Override + public void setChainedProxy(InetSocketAddress chainedProxyAddress) { + if (isStarted() && !bootstrappedWithDefaultChainedProxy.get()) { + throw new IllegalStateException("Cannot set a chained proxy after the proxy is started if the proxy was started without a chained proxy."); + } + + upstreamProxyAddress = chainedProxyAddress; + } + + @Override + public InetSocketAddress getChainedProxy() { + return upstreamProxyAddress; + } + + /** + * Allows access to the LittleProxy {@link ChainedProxyManager} for fine-grained control of the chained proxies. To enable a single + * chained proxy, {@link BrowserMobProxy#setChainedProxy(InetSocketAddress)} is generally more convenient. + * + * Note: The chained proxy manager must be enabled before calling {@link #start()}. + * + * @param chainedProxyManager chained proxy manager to enable + */ + public void setChainedProxyManager(ChainedProxyManager chainedProxyManager) { + if (isStarted()) { + throw new IllegalStateException("Cannot configure chained proxy manager after proxy has started."); + } + + this.chainedProxyManager = chainedProxyManager; + } + + /** + * Configures the Netty thread pool used by the LittleProxy back-end. See {@link ThreadPoolConfiguration} for details. + * + * @param threadPoolConfiguration thread pool configuration to use + */ + public void setThreadPoolConfiguration(ThreadPoolConfiguration threadPoolConfiguration) { + if (isStarted()) { + throw new IllegalStateException("Cannot configure thread pool after proxy has started."); + } + + this.threadPoolConfiguration = threadPoolConfiguration; + } + + @Override + public void addFirstHttpFilterFactory(HttpFiltersSource filterFactory) { + filterFactories.add(0, filterFactory); + } + + @Override + public void addLastHttpFilterFactory(HttpFiltersSource filterFactory) { + filterFactories.add(filterFactory); + } + + /** + * Note: The current implementation of this method forces a maximum response size of 2 MiB. To adjust the maximum response size, or + * to disable aggregation (which disallows access to the {@link net.lightbody.bmp.util.HttpMessageContents}), you may add the filter source + * directly: addFirstHttpFilterFactory(new ResponseFilterAdapter.FilterSource(filter, bufferSizeInBytes)); + */ + @Override + public void addResponseFilter(ResponseFilter filter) { + addLastHttpFilterFactory(new ResponseFilterAdapter.FilterSource(filter)); + } + + /** + * Note: The current implementation of this method forces a maximum request size of 2 MiB. To adjust the maximum request size, or + * to disable aggregation (which disallows access to the {@link net.lightbody.bmp.util.HttpMessageContents}), you may add the filter source + * directly: addFirstHttpFilterFactory(new RequestFilterAdapter.FilterSource(filter, bufferSizeInBytes)); + */ + @Override + public void addRequestFilter(RequestFilter filter) { + addFirstHttpFilterFactory(new RequestFilterAdapter.FilterSource(filter)); + } + + @Override + public Map getRewriteRules() { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (RewriteRule rewriteRule : rewriteRules) { + builder.put(rewriteRule.getPattern().pattern(), rewriteRule.getReplace()); + } + + return builder.build(); + } + + @Override + public void removeRewriteRule(String urlPattern) { + // normally removing elements from the list we are iterating over would not be possible, but since this is a CopyOnWriteArrayList + // the iterator it returns is a "snapshot" of the list that will not be affected by removal (and that does not support removal, either) + for (RewriteRule rewriteRule : rewriteRules) { + if (rewriteRule.getPattern().pattern().equals(urlPattern)) { + rewriteRules.remove(rewriteRule); + } + } + } + + public boolean isStopped() { + return stopped.get(); + } + + public HarPage getCurrentHarPage() { + return currentHarPage; + } + + public void addHttpFilterFactory(HttpFiltersSource filterFactory) { + filterFactories.add(filterFactory); + } + + public List getFilterFactories() { + return filterFactories; + } + + @Override + public void setMitmDisabled(boolean mitmDisabled) throws IllegalStateException { + if (isStarted()) { + throw new IllegalStateException("Cannot disable MITM after the proxy has been started"); + } + + this.mitmDisabled = mitmDisabled; + } + + @Override + public void setMitmManager(MitmManager mitmManager) { + this.mitmManager = mitmManager; + } + + @Override + public void setTrustAllServers(boolean trustAllServers) { + if (isStarted()) { + throw new IllegalStateException("Cannot disable upstream server verification after the proxy has been started"); + } + + if (trustAllServers) { + trustSource = null; + } else { + if (trustSource == null) { + trustSource = TrustSource.defaultTrustSource(); + } + } + } + + @Override + public void setTrustSource(TrustSource trustSource) { + if (isStarted()) { + throw new IllegalStateException("Cannot change TrustSource after proxy has been started"); + } + + this.trustSource = trustSource; + } + + public boolean isMitmDisabled() { + return this.mitmDisabled; + } + + public void setUseEcc(boolean useEcc) { + this.useEcc = useEcc; + } + + /** + * Adds the basic browsermob-proxy filters, except for the relatively-expensive HAR capture filter. + */ + protected void addBrowserMobFilters() { + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new ResolvedHostnameCacheFilter(originalRequest, ctx); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new RegisterRequestFilter(originalRequest, ctx, activityMonitor); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new HttpsOriginalHostCaptureFilter(originalRequest, ctx); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new BlacklistFilter(originalRequest, ctx, getBlacklist()); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + Whitelist currentWhitelist = whitelist.get(); + return new WhitelistFilter(originalRequest, ctx, isWhitelistEnabled(), currentWhitelist.getStatusCode(), currentWhitelist.getPatterns()); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new AutoBasicAuthFilter(originalRequest, ctx, basicAuthCredentials); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new RewriteUrlFilter(originalRequest, ctx, rewriteRules); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new HttpsHostCaptureFilter(originalRequest, ctx); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new AddHeadersFilter(originalRequest, additionalHeaders); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new LatencyFilter(originalRequest, latencyMs); + } + }); + + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new UnregisterRequestFilter(originalRequest, ctx, activityMonitor); + } + }); + } + + private int getMaximumRequestBufferSize() { + int maxBufferSize = 0; + for (HttpFiltersSource source : filterFactories) { + int requestBufferSize = source.getMaximumRequestBufferSizeInBytes(); + if (requestBufferSize > maxBufferSize) { + maxBufferSize = requestBufferSize; + } + } + + return maxBufferSize; + } + + private int getMaximumResponseBufferSize() { + int maxBufferSize = 0; + for (HttpFiltersSource source : filterFactories) { + int requestBufferSize = source.getMaximumResponseBufferSizeInBytes(); + if (requestBufferSize > maxBufferSize) { + maxBufferSize = requestBufferSize; + } + } + + return maxBufferSize; + } + + /** + * Enables the HAR capture filter if it has not already been enabled. The filter will be added to the end of the filter chain. + * The HAR capture filter is relatively expensive, so this method is only called when a HAR is requested. + */ + protected void addHarCaptureFilter() { + if (harCaptureFilterEnabled.compareAndSet(false, true)) { + // the HAR capture filter is (relatively) expensive, so only enable it when a HAR is being captured. furthermore, + // restricting the HAR capture filter to requests where the HAR exists, as well as excluding HTTP CONNECTs + // from the HAR capture filter, greatly simplifies the filter code. + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + Har har = getHar(); + if (har != null && !ProxyUtils.isCONNECT(originalRequest)) { + return new HarCaptureFilter(originalRequest, ctx, har, getCurrentHarPage() == null ? null : getCurrentHarPage().getId(), getHarCaptureTypes()); + } else { + return null; + } + } + }); + + // HTTP CONNECTs are a special case, since they require special timing and error handling + addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + Har har = getHar(); + if (har != null && ProxyUtils.isCONNECT(originalRequest)) { + return new HttpConnectHarCaptureFilter(originalRequest, ctx, har, getCurrentHarPage() == null ? null : getCurrentHarPage().getId()); + } else { + return null; + } + } + }); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/client/ClientUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/client/ClientUtil.java new file mode 100644 index 000000000..9c8a151fd --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/client/ClientUtil.java @@ -0,0 +1,120 @@ +package net.lightbody.bmp.client; + +import com.google.common.collect.ImmutableList; +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import net.lightbody.bmp.proxy.dns.ChainedHostResolver; +import net.lightbody.bmp.proxy.dns.DnsJavaResolver; +import net.lightbody.bmp.proxy.dns.NativeCacheManipulatingResolver; +import net.lightbody.bmp.proxy.dns.NativeResolver; +import org.openqa.selenium.Proxy; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; + +/** + * A utility class with convenience methods for clients using BrowserMob Proxy in embedded mode. + */ +public class ClientUtil { + /** + * Creates a {@link net.lightbody.bmp.proxy.dns.NativeCacheManipulatingResolver} instance that can be used when + * calling {@link net.lightbody.bmp.BrowserMobProxy#setHostNameResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. + * + * @return a new NativeCacheManipulatingResolver + */ + public static AdvancedHostResolver createNativeCacheManipulatingResolver() { + return new NativeCacheManipulatingResolver(); + } + + /** + * Creates a {@link net.lightbody.bmp.proxy.dns.NativeResolver} instance that does not support cache manipulation that can be used when + * calling {@link net.lightbody.bmp.BrowserMobProxy#setHostNameResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. + * + * @return a new NativeResolver + */ + public static AdvancedHostResolver createNativeResolver() { + return new NativeResolver(); + } + + /** + * Creates a {@link net.lightbody.bmp.proxy.dns.DnsJavaResolver} instance that can be used when + * calling {@link net.lightbody.bmp.BrowserMobProxy#setHostNameResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. + * + * @return a new DnsJavaResolver + * @deprecated The dnsjava resolver has been deprecated in favor of the standard JVM resolver and will be removed in BMP >2.1. + */ + public static AdvancedHostResolver createDnsJavaResolver() { + return new DnsJavaResolver(); + } + + /** + * Creates a {@link net.lightbody.bmp.proxy.dns.ChainedHostResolver} instance that first attempts to resolve a hostname using a + * {@link net.lightbody.bmp.proxy.dns.DnsJavaResolver}, then uses {@link net.lightbody.bmp.proxy.dns.NativeCacheManipulatingResolver}. + * Can be used when calling {@link net.lightbody.bmp.BrowserMobProxy#setHostNameResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. + * + * @return a new ChainedHostResolver that resolves addresses first using a DnsJavaResolver, then using a NativeCacheManipulatingResolver + * @deprecated The dnsjava resolver has been deprecated in favor of the standard JVM resolver and will be removed in BMP >2.1. + */ + public static AdvancedHostResolver createDnsJavaWithNativeFallbackResolver() { + return new ChainedHostResolver(ImmutableList.of(new DnsJavaResolver(), new NativeCacheManipulatingResolver())); + } + + /** + * Creates a Selenium Proxy object from the BrowserMobProxy instance. The BrowserMobProxy must be started. Retrieves the address + * of the Proxy using {@link #getConnectableAddress()}. + * + * @param browserMobProxy started BrowserMobProxy instance to read connection information from + * @return a Selenium Proxy instance, configured to use the BrowserMobProxy instance as its proxy server + * @throws java.lang.IllegalStateException if the proxy has not been started. + */ + public static org.openqa.selenium.Proxy createSeleniumProxy(BrowserMobProxy browserMobProxy) { + return createSeleniumProxy(browserMobProxy, getConnectableAddress()); + } + + /** + * Creates a Selenium Proxy object from the BrowserMobProxy instance, using the specified connectableAddress as the Selenium Proxy object's + * proxy address. Determines the port using {@link net.lightbody.bmp.BrowserMobProxy#getPort()}. The BrowserMobProxy must be started. + * + * @param browserMobProxy started BrowserMobProxy instance to read the port from + * @param connectableAddress the network address the Selenium Proxy will use to reach this BrowserMobProxy instance + * @return a Selenium Proxy instance, configured to use the BrowserMobProxy instance as its proxy server + * @throws java.lang.IllegalStateException if the proxy has not been started. + */ + public static org.openqa.selenium.Proxy createSeleniumProxy(BrowserMobProxy browserMobProxy, InetAddress connectableAddress) { + return createSeleniumProxy(new InetSocketAddress(connectableAddress, browserMobProxy.getPort())); + } + + /** + * Creates a Selenium Proxy object using the specified connectableAddressAndPort as the HTTP proxy server. + * + * @param connectableAddressAndPort the network address (or hostname) and port the Selenium Proxy will use to reach its + * proxy server (the InetSocketAddress may be unresolved). + * @return a Selenium Proxy instance, configured to use the specified address and port as its proxy server + */ + public static org.openqa.selenium.Proxy createSeleniumProxy(InetSocketAddress connectableAddressAndPort) { + Proxy proxy = new Proxy(); + proxy.setProxyType(Proxy.ProxyType.MANUAL); + + String proxyStr = String.format("%s:%d", connectableAddressAndPort.getHostString(), connectableAddressAndPort.getPort()); + proxy.setHttpProxy(proxyStr); + proxy.setSslProxy(proxyStr); + + return proxy; + } + + /** + * Attempts to retrieve a "connectable" address for this device that other devices on the network can use to connect to a local proxy. + * This is a "reasonable guess" that is suitable in many (but not all) common scenarios. + * TODO: define the algorithm used to discover a "connectable" local host + * + * @return a "reasonable guess" at an address that can be used by other machines on the network to reach this host + */ + public static InetAddress getConnectableAddress() { + try { + return InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + throw new RuntimeException("Could not resolve localhost", e); + } + } +} diff --git a/src/main/java/org/browsermob/core/har/Har.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/Har.java similarity index 60% rename from src/main/java/org/browsermob/core/har/Har.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/Har.java index b87d88f03..40e1987ad 100644 --- a/src/main/java/org/browsermob/core/har/Har.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/Har.java @@ -1,6 +1,6 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.map.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; @@ -8,7 +8,10 @@ import java.io.Writer; public class Har { - private HarLog log; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private volatile HarLog log; public Har() { } @@ -26,17 +29,14 @@ public void setLog(HarLog log) { } public void writeTo(Writer writer) throws IOException { - ObjectMapper om = new ObjectMapper(); - om.writeValue(writer, this); + OBJECT_MAPPER.writeValue(writer, this); } public void writeTo(OutputStream os) throws IOException { - ObjectMapper om = new ObjectMapper(); - om.writeValue(os, this); + OBJECT_MAPPER.writeValue(os, this); } public void writeTo(File file) throws IOException { - ObjectMapper om = new ObjectMapper(); - om.writeValue(file, this); + OBJECT_MAPPER.writeValue(file, this); } } diff --git a/src/main/java/org/browsermob/core/har/HarCache.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCache.java similarity index 64% rename from src/main/java/org/browsermob/core/har/HarCache.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCache.java index ca9114e72..0a3594a33 100644 --- a/src/main/java/org/browsermob/core/har/HarCache.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCache.java @@ -1,11 +1,11 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonInclude; -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class HarCache { - private HarCacheStatus beforeRequest; - private HarCacheStatus afterRequest; + private volatile HarCacheStatus beforeRequest; + private volatile HarCacheStatus afterRequest; public HarCacheStatus getBeforeRequest() { return beforeRequest; diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java new file mode 100644 index 000000000..7b8f5f347 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java @@ -0,0 +1,57 @@ +package net.lightbody.bmp.core.har; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Date; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HarCacheStatus { + private volatile Date expires; + private volatile Date lastAccess; + private volatile String eTag; + private volatile int hitCount; + private volatile String comment = ""; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public Date getExpires() { + return expires; + } + + public void setExpires(Date expires) { + this.expires = expires; + } + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + public Date getLastAccess() { + return lastAccess; + } + + public void setLastAccess(Date lastAccess) { + this.lastAccess = lastAccess; + } + + public String geteTag() { + return eTag; + } + + public void seteTag(String eTag) { + this.eTag = eTag; + } + + public int getHitCount() { + return hitCount; + } + + public void setHitCount(int hitCount) { + this.hitCount = hitCount; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarContent.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarContent.java new file mode 100644 index 000000000..48b67583e --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarContent.java @@ -0,0 +1,65 @@ +package net.lightbody.bmp.core.har; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HarContent { + private volatile long size; + private volatile Long compression; + + // mimeType is required; though it shouldn't be set to null, if it is, it still needs to be included to comply with the HAR spec + @JsonInclude(JsonInclude.Include.ALWAYS) + private volatile String mimeType = ""; + + private volatile String text; + private volatile String encoding; + private volatile String comment = ""; + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public Long getCompression() { + return compression; + } + + public void setCompression(Long compression) { + this.compression = compression; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCookie.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCookie.java new file mode 100644 index 000000000..b21b7cb34 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarCookie.java @@ -0,0 +1,97 @@ +package net.lightbody.bmp.core.har; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Date; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HarCookie { + private volatile String name; + private volatile String value; + private volatile String path; + private volatile String domain; + private volatile Date expires; + private volatile Boolean httpOnly; + private volatile Boolean secure; + private volatile String comment = ""; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") + public Date getExpires() { + return expires; + } + + public void setExpires(Date expires) { + this.expires = expires; + } + + public Boolean getHttpOnly() { + return httpOnly; + } + + public void setHttpOnly(Boolean httpOnly) { + this.httpOnly = httpOnly; + } + + public Boolean getSecure() { + return secure; + } + + public void setSecure(Boolean secure) { + this.secure = secure; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + @Override + public String toString() { + return "HarCookie{" + + "name='" + name + '\'' + + ", value='" + value + '\'' + + ", path='" + path + '\'' + + ", domain='" + domain + '\'' + + ", expires=" + expires + + ", httpOnly=" + httpOnly + + ", secure=" + secure + + ", comment='" + comment + '\'' + + '}'; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java new file mode 100644 index 000000000..1864a78c7 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarEntry.java @@ -0,0 +1,160 @@ +package net.lightbody.bmp.core.har; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonAutoDetect +public class HarEntry { + private volatile String pageref; + private volatile Date startedDateTime; + private volatile HarRequest request; + private volatile HarResponse response; + private volatile HarCache cache = new HarCache(); + private volatile HarTimings timings = new HarTimings(); + private volatile String serverIPAddress; + private volatile String connection; + private volatile String comment = ""; + + public HarEntry() { + } + + public HarEntry(String pageref) { + this.pageref = pageref; + } + + public String getPageref() { + return pageref; + } + + public void setPageref(String pageref) { + this.pageref = pageref; + } + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") + public Date getStartedDateTime() { + return startedDateTime; + } + + public void setStartedDateTime(Date startedDateTime) { + this.startedDateTime = startedDateTime; + } + + /** + * Retrieves the time for this HarEntry in milliseconds. To retrieve the time in another time unit, use {@link #getTime(java.util.concurrent.TimeUnit)}. + * Rather than storing the time directly, calculate the time from the HarTimings as required in the HAR spec. + * From https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html, + * section 4.2.16 timings: +

+     Following must be true in case there are no -1 values (entry is an object in log.entries) :
+
+     entry.time == entry.timings.blocked + entry.timings.dns +
+     entry.timings.connect + entry.timings.send + entry.timings.wait +
+     entry.timings.receive;
+     
+ * @return time for this HAR entry, in milliseconds + */ + public long getTime() { + return getTime(TimeUnit.MILLISECONDS); + } + + /** + * Retrieve the time for this HarEntry in the specified timeUnit. See {@link #getTime()} for details. + * + * @param timeUnit units of time to return + * @return time for this har entry + */ + public long getTime(TimeUnit timeUnit) { + HarTimings timings = getTimings(); + if (timings == null) { + return -1; + } + + long timeNanos = 0; + if (timings.getBlocked(TimeUnit.NANOSECONDS) > 0) { + timeNanos += timings.getBlocked(TimeUnit.NANOSECONDS); + } + + if (timings.getDns(TimeUnit.NANOSECONDS) > 0) { + timeNanos += timings.getDns(TimeUnit.NANOSECONDS); + } + + if (timings.getConnect(TimeUnit.NANOSECONDS) > 0) { + timeNanos += timings.getConnect(TimeUnit.NANOSECONDS); + } + + if (timings.getSend(TimeUnit.NANOSECONDS) > 0) { + timeNanos += timings.getSend(TimeUnit.NANOSECONDS); + } + + if (timings.getWait(TimeUnit.NANOSECONDS) > 0) { + timeNanos += timings.getWait(TimeUnit.NANOSECONDS); + } + + if (timings.getReceive(TimeUnit.NANOSECONDS) > 0) { + timeNanos += timings.getReceive(TimeUnit.NANOSECONDS); + } + + return timeUnit.convert(timeNanos, TimeUnit.NANOSECONDS); + } + + public HarRequest getRequest() { + return request; + } + + public void setRequest(HarRequest request) { + this.request = request; + } + + public HarResponse getResponse() { + return response; + } + + public void setResponse(HarResponse response) { + this.response = response; + } + + public HarCache getCache() { + return cache; + } + + public void setCache(HarCache cache) { + this.cache = cache; + } + + public HarTimings getTimings() { + return timings; + } + + public void setTimings(HarTimings timings) { + this.timings = timings; + } + + public String getServerIPAddress() { + return serverIPAddress; + } + + public void setServerIPAddress(String serverIPAddress) { + this.serverIPAddress = serverIPAddress; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getConnection() { + return connection; + } + + public void setConnection(String connection) { + this.connection = connection; + } +} diff --git a/src/main/java/org/browsermob/core/har/HarLog.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java similarity index 51% rename from src/main/java/org/browsermob/core/har/HarLog.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java index e39352075..879e071f3 100644 --- a/src/main/java/org/browsermob/core/har/HarLog.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarLog.java @@ -1,17 +1,18 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class HarLog { - private String version = "1.2"; - private HarNameVersion creator; - private HarNameVersion browser; - private List pages = new CopyOnWriteArrayList(); - private List entries = new CopyOnWriteArrayList(); + private final String version = "1.2"; + private volatile HarNameVersion creator; + private volatile HarNameVersion browser; + private final List pages = new CopyOnWriteArrayList(); + private final List entries = new CopyOnWriteArrayList(); + private volatile String comment = ""; public HarLog() { } @@ -21,18 +22,10 @@ public HarLog(HarNameVersion creator) { } public void addPage(HarPage page) { - if (pages == null) { - pages = new CopyOnWriteArrayList(); - } - pages.add(page); } public void addEntry(HarEntry entry) { - if (entries == null) { - entries = new CopyOnWriteArrayList(); - } - entries.add(entry); } @@ -40,10 +33,6 @@ public String getVersion() { return version; } - public void setVersion(String version) { - this.version = version; - } - public HarNameVersion getCreator() { return creator; } @@ -64,15 +53,15 @@ public List getPages() { return pages; } - public void setPages(List pages) { - this.pages = pages; - } - public List getEntries() { return entries; } - public void setEntries(List entries) { - this.entries = entries; + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarNameValuePair.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarNameValuePair.java new file mode 100644 index 000000000..b1723380c --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarNameValuePair.java @@ -0,0 +1,43 @@ +package net.lightbody.bmp.core.har; + +public final class HarNameValuePair { + private final String name; + private final String value; + + public HarNameValuePair(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + public String toString() { + return name + "=" + value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HarNameValuePair that = (HarNameValuePair) o; + + if (name != null ? !name.equals(that.name) : that.name != null) return false; + if (value != null ? !value.equals(that.value) : that.value != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } +} diff --git a/src/main/java/org/browsermob/core/har/HarNameVersion.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarNameVersion.java similarity index 51% rename from src/main/java/org/browsermob/core/har/HarNameVersion.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarNameVersion.java index 6b7ae0752..b3d222bf7 100644 --- a/src/main/java/org/browsermob/core/har/HarNameVersion.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarNameVersion.java @@ -1,11 +1,9 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; public class HarNameVersion { - private String name; - private String version; - - public HarNameVersion() { - } + private final String name; + private final String version; + private volatile String comment = ""; public HarNameVersion(String name, String version) { this.name = name; @@ -16,15 +14,15 @@ public String getName() { return name; } - public void setName(String name) { - this.name = name; - } - public String getVersion() { return version; } - public void setVersion(String version) { - this.version = version; + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; } } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPage.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPage.java new file mode 100644 index 000000000..7b63dd42e --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPage.java @@ -0,0 +1,66 @@ +package net.lightbody.bmp.core.har; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Date; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HarPage { + private volatile String id; + private volatile Date startedDateTime; + private volatile String title = ""; + private final HarPageTimings pageTimings = new HarPageTimings(); + private volatile String comment = ""; + + public HarPage() { + } + + public HarPage(String id) { + this(id, ""); + } + + public HarPage(String id, String title) { + this.id = id; + this.title = title; + startedDateTime = new Date(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") + public Date getStartedDateTime() { + return startedDateTime; + } + + public void setStartedDateTime(Date startedDateTime) { + this.startedDateTime = startedDateTime; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public HarPageTimings getPageTimings() { + return pageTimings; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + +} diff --git a/src/main/java/org/browsermob/core/har/HarPageTimings.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPageTimings.java similarity index 57% rename from src/main/java/org/browsermob/core/har/HarPageTimings.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPageTimings.java index d87aad1c9..56d415e83 100644 --- a/src/main/java/org/browsermob/core/har/HarPageTimings.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPageTimings.java @@ -1,11 +1,12 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.annotate.JsonWriteNullProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -@JsonWriteNullProperties(value=false) +@JsonInclude(JsonInclude.Include.NON_NULL) public class HarPageTimings { - private Long onContentLoad; - private Long onLoad; + private volatile Long onContentLoad; + private volatile Long onLoad; + private volatile String comment = ""; public HarPageTimings() { } @@ -30,4 +31,13 @@ public Long getOnLoad() { public void setOnLoad(Long onLoad) { this.onLoad = onLoad; } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + } diff --git a/src/main/java/org/browsermob/core/har/HarPostData.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPostData.java similarity index 53% rename from src/main/java/org/browsermob/core/har/HarPostData.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPostData.java index 3d902ec28..a9f10d1a4 100644 --- a/src/main/java/org/browsermob/core/har/HarPostData.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPostData.java @@ -1,14 +1,15 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.annotate.JsonWriteNullProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; -@JsonWriteNullProperties(value=false) +@JsonInclude(JsonInclude.Include.NON_NULL) public class HarPostData { - private String mimeType; - private List params; - private String text; + private volatile String mimeType; + private volatile List params; + private volatile String text; + private volatile String comment = ""; public String getMimeType() { return mimeType; @@ -33,4 +34,12 @@ public String getText() { public void setText(String text) { this.text = text; } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } } diff --git a/src/main/java/org/browsermob/core/har/HarPostDataParam.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPostDataParam.java similarity index 62% rename from src/main/java/org/browsermob/core/har/HarPostDataParam.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPostDataParam.java index fca534749..38c5f5980 100644 --- a/src/main/java/org/browsermob/core/har/HarPostDataParam.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarPostDataParam.java @@ -1,13 +1,14 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.annotate.JsonWriteNullProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -@JsonWriteNullProperties(value=false) +@JsonInclude(JsonInclude.Include.NON_NULL) public class HarPostDataParam { - private String name; - private String value; - private String fileName; - private String contentType; + private volatile String name; + private volatile String value; + private volatile String fileName; + private volatile String contentType; + private volatile String comment = ""; public HarPostDataParam() { } @@ -48,4 +49,12 @@ public String getContentType() { public void setContentType(String contentType) { this.contentType = contentType; } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } } diff --git a/src/main/java/org/browsermob/core/har/HarRequest.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java similarity index 62% rename from src/main/java/org/browsermob/core/har/HarRequest.java rename to browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java index ff9d9a159..2cfe68957 100644 --- a/src/main/java/org/browsermob/core/har/HarRequest.java +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarRequest.java @@ -1,21 +1,22 @@ -package org.browsermob.core.har; +package net.lightbody.bmp.core.har; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class HarRequest { - private String method; - private String url; - private String httpVersion; - private List cookies = new CopyOnWriteArrayList(); - private List headers = new CopyOnWriteArrayList(); - private List queryString = new CopyOnWriteArrayList(); - private HarPostData postData; - private long headersSize; // Odd grammar in spec - private long bodySize; + private volatile String method; + private volatile String url; + private volatile String httpVersion; + private final List cookies = new CopyOnWriteArrayList(); + private final List headers = new CopyOnWriteArrayList(); + private final List queryString = new CopyOnWriteArrayList(); + private volatile HarPostData postData; + private volatile long headersSize; // Odd grammar in spec + private volatile long bodySize; + private volatile String comment = ""; public HarRequest() { } @@ -54,26 +55,14 @@ public List getCookies() { return cookies; } - public void setCookies(List cookies) { - this.cookies = cookies; - } - public List getHeaders() { return headers; } - public void setHeaders(List headers) { - this.headers = headers; - } - public List getQueryString() { return queryString; } - public void setQueryString(List queryString) { - this.queryString = queryString; - } - public HarPostData getPostData() { return postData; } @@ -97,4 +86,13 @@ public long getBodySize() { public void setBodySize(long bodySize) { this.bodySize = bodySize; } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + } diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java new file mode 100644 index 000000000..f82f248dd --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarResponse.java @@ -0,0 +1,117 @@ +package net.lightbody.bmp.core.har; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HarResponse { + private volatile int status; + private volatile String statusText; + private volatile String httpVersion; + private final List cookies = new CopyOnWriteArrayList(); + private final List headers = new CopyOnWriteArrayList(); + private final HarContent content = new HarContent(); + private volatile String redirectURL = ""; + + /* the values of headersSize and bodySize are set to -1 by default, in accordance with the HAR spec: + headersSize [number] - Total number of bytes from the start of the HTTP request message until (and including) the double CRLF before the body. Set to -1 if the info is not available. + bodySize [number] - Size of the request body (POST data payload) in bytes. Set to -1 if the info is not available. + */ + private volatile long headersSize = -1; + private volatile long bodySize = -1; + private volatile String comment = ""; + + /** + * A custom field indicating that an error occurred, such as DNS resolution failure. + */ + @JsonProperty("_error") + private volatile String error; + + public HarResponse() { + } + + public HarResponse(int status, String statusText, String httpVersion) { + this.status = status; + this.statusText = statusText; + this.httpVersion = httpVersion; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getStatusText() { + return statusText; + } + + public void setStatusText(String statusText) { + this.statusText = statusText; + } + + public String getHttpVersion() { + return httpVersion; + } + + public void setHttpVersion(String httpVersion) { + this.httpVersion = httpVersion; + } + + public List getCookies() { + return cookies; + } + + public List getHeaders() { + return headers; + } + + public HarContent getContent() { + return content; + } + + public String getRedirectURL() { + return redirectURL; + } + + public void setRedirectURL(String redirectURL) { + this.redirectURL = redirectURL; + } + + public long getHeadersSize() { + return headersSize; + } + + public void setHeadersSize(long headersSize) { + this.headersSize = headersSize; + } + + public long getBodySize() { + return bodySize; + } + + public void setBodySize(long bodySize) { + this.bodySize = bodySize; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java new file mode 100644 index 000000000..efb8d8612 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/core/har/HarTimings.java @@ -0,0 +1,177 @@ +package net.lightbody.bmp.core.har; + +import java.util.concurrent.TimeUnit; + +public class HarTimings { + // optional values are initialized to -1, which indicates they do not apply to the current request, according to the HAR spec + private volatile long blockedNanos = -1; + private volatile long dnsNanos = -1; + private volatile long connectNanos = -1; + private volatile long sendNanos; + private volatile long waitNanos; + private volatile long receiveNanos; + private volatile long sslNanos = -1; + private volatile String comment = ""; + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + // the following getters and setters take a TimeUnit parameter, to allow finer precision control when no marshalling to JSON + public long getBlocked(TimeUnit timeUnit) { + if (blockedNanos == -1) { + return -1; + } else { + return timeUnit.convert(blockedNanos, TimeUnit.NANOSECONDS); + } + } + + public void setBlocked(long blocked, TimeUnit timeUnit) { + if (blocked == -1) { + this.blockedNanos = -1; + } else { + this.blockedNanos = TimeUnit.NANOSECONDS.convert(blocked, timeUnit); + } + } + + public long getDns(TimeUnit timeUnit) { + if (dnsNanos == -1) { + return -1; + } else { + return timeUnit.convert(dnsNanos, TimeUnit.NANOSECONDS); + } + } + + public void setDns(long dns, TimeUnit timeUnit) { + if (dns == -1) { + this.dnsNanos = -1; + } else{ + this.dnsNanos = TimeUnit.NANOSECONDS.convert(dns, timeUnit); + } + } + + public long getConnect(TimeUnit timeUnit) { + if (connectNanos == -1) { + return -1; + } else { + return timeUnit.convert(connectNanos, TimeUnit.NANOSECONDS); + } + } + + public void setConnect(long connect, TimeUnit timeUnit) { + if (connect == -1) { + this.connectNanos = -1; + } else { + this.connectNanos = TimeUnit.NANOSECONDS.convert(connect, timeUnit); + } + } + + /* + According to the HAR spec: + The send, wait and receive timings are not optional and must have non-negative values. + */ + public long getSend(TimeUnit timeUnit) { + return timeUnit.convert(sendNanos, TimeUnit.NANOSECONDS); + } + + public void setSend(long send, TimeUnit timeUnit) { + this.sendNanos = TimeUnit.NANOSECONDS.convert(send, timeUnit); + } + + public long getWait(TimeUnit timeUnit) { + return timeUnit.convert(waitNanos, TimeUnit.NANOSECONDS); + } + + public void setWait(long wait, TimeUnit timeUnit) { + this.waitNanos = TimeUnit.NANOSECONDS.convert(wait, timeUnit); + } + + public long getReceive(TimeUnit timeUnit) { + return timeUnit.convert(receiveNanos, TimeUnit.NANOSECONDS); + } + + public void setReceive(long receive, TimeUnit timeUnit) { + this.receiveNanos = TimeUnit.NANOSECONDS.convert(receive, timeUnit); + } + + public long getSsl(TimeUnit timeUnit) { + if (sslNanos == -1) { + return -1; + } else { + return timeUnit.convert(sslNanos, TimeUnit.NANOSECONDS); + } + } + + public void setSsl(long ssl, TimeUnit timeUnit) { + if (ssl == -1) { + this.sslNanos = -1; + } else { + this.sslNanos = TimeUnit.NANOSECONDS.convert(ssl, timeUnit); + } + } + + // the following getters and setters assume TimeUnit.MILLISECOND precision. this allows jackson to generate ms values (in accordance + // with the HAR spec), and also preserves compatibility with the legacy methods. optional methods are also declared as Long instead of + // long (even though they always have values), to preserve compatibility. in general, the getters/setters which take TimeUnits + // should always be preferred. + public Long getBlocked() { + return getBlocked(TimeUnit.MILLISECONDS); + } + + public void setBlocked(long blocked) { + setBlocked(blocked, TimeUnit.MILLISECONDS); + } + + public Long getDns() { + return getDns(TimeUnit.MILLISECONDS); + } + + public void setDns(long dns) { + setDns(dns, TimeUnit.MILLISECONDS); + } + + public Long getConnect() { + return getConnect(TimeUnit.MILLISECONDS); + } + + public void setConnect(long connect) { + setConnect(connect, TimeUnit.MILLISECONDS); + } + + public long getSend() { + return getSend(TimeUnit.MILLISECONDS); + } + + public void setSend(long send) { + setSend(send, TimeUnit.MILLISECONDS); + } + + public long getWait() { + return getWait(TimeUnit.MILLISECONDS); + } + + public void setWait(long wait) { + setWait(wait, TimeUnit.MILLISECONDS); + } + + public long getReceive() { + return getReceive(TimeUnit.MILLISECONDS); + } + + public void setReceive(long receive) { + setReceive(receive, TimeUnit.MILLISECONDS); + } + + public Long getSsl() { + return getSsl(TimeUnit.MILLISECONDS); + } + + public void setSsl(long ssl) { + setSsl(ssl, TimeUnit.MILLISECONDS); + } + +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/exception/DecompressionException.java b/browsermob-core/src/main/java/net/lightbody/bmp/exception/DecompressionException.java new file mode 100644 index 000000000..a77e1a68b --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/exception/DecompressionException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.exception; + +/** + * Indicates that an error occurred while decompressing content. + */ +public class DecompressionException extends RuntimeException { + private static final long serialVersionUID = 8666473793514307564L; + + public DecompressionException() { + } + + public DecompressionException(String message) { + super(message); + } + + public DecompressionException(String message, Throwable cause) { + super(message, cause); + } + + public DecompressionException(Throwable cause) { + super(cause); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/exception/UnsupportedCharsetException.java b/browsermob-core/src/main/java/net/lightbody/bmp/exception/UnsupportedCharsetException.java new file mode 100644 index 000000000..2336dbbf8 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/exception/UnsupportedCharsetException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.exception; + +/** + * A checked exception wrapper for {@link java.nio.charset.UnsupportedCharsetException}. This exception is checked to prevent + * situations where an unsupported character set in e.g. a Content-Type header causes the proxy to fail completely, rather + * than fallback to some suitable default behavior, such as not parsing the text contents of a message. + */ +public class UnsupportedCharsetException extends Exception { + public UnsupportedCharsetException(java.nio.charset.UnsupportedCharsetException e) { + super(e); + + if (e == null) { + throw new IllegalArgumentException("net.lightbody.bmp.exception.UnsupportedCharsetException must be initialized with a non-null instance of java.nio.charset.UnsupportedCharsetException"); + } + } + + /** + * @return the underlying {@link java.nio.charset.UnsupportedCharsetException} that this exception wraps. + */ + public java.nio.charset.UnsupportedCharsetException getUnsupportedCharsetExceptionCause() { + return (java.nio.charset.UnsupportedCharsetException) this.getCause(); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java new file mode 100644 index 000000000..5b584ecfd --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java @@ -0,0 +1,40 @@ +package net.lightbody.bmp.filters; + +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import org.littleshoot.proxy.HttpFiltersAdapter; + +import java.util.Collections; +import java.util.Map; + +/** + * Adds the headers specified in the constructor to this request. The filter does not make a defensive copy of the map, so there is no guarantee + * that the map at the time of construction will contain the same values when the filter is actually invoked, if the map is modified concurrently. + */ +public class AddHeadersFilter extends HttpFiltersAdapter { + private final Map additionalHeaders; + + public AddHeadersFilter(HttpRequest originalRequest, Map additionalHeaders) { + super(originalRequest); + + if (additionalHeaders != null) { + this.additionalHeaders = additionalHeaders; + } else { + this.additionalHeaders = Collections.emptyMap(); + } + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + for (Map.Entry header : additionalHeaders.entrySet()) { + httpRequest.headers().add(header.getKey(), header.getValue()); + } + } + + return null; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java new file mode 100644 index 000000000..758f9bfaa --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java @@ -0,0 +1,54 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import org.littleshoot.proxy.impl.ProxyUtils; + +import java.util.Map; + +/** + * A filter that adds Basic authentication information to non-CONNECT requests. Takes a map of domain names to base64-encoded + * Basic auth credentials as a constructor parameter. If a key in the map matches the hostname of a filtered request, an Authorization + * header will be added to the request. + *

+ * The Authorization header itself is specified in RFC 7235, section 4.2: https://tools.ietf.org/html/rfc7235#section-4.2 + * The Basic authentication scheme is specified in RFC 2617, section 2: https://tools.ietf.org/html/rfc2617#section-2 + */ +public class AutoBasicAuthFilter extends HttpsAwareFiltersAdapter { + private final Map credentialsByHostname; + + public AutoBasicAuthFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Map credentialsByHostname) { + super(originalRequest, ctx); + + this.credentialsByHostname = credentialsByHostname; + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (credentialsByHostname.isEmpty()) { + return null; + } + + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + // providing authorization during a CONNECT is generally not useful + if (ProxyUtils.isCONNECT(httpRequest)) { + return null; + } + + String hostname = getHost(httpRequest); + + // if there is an entry in the credentials map matching this hostname, add the credentials to the request + String base64CredentialsForHostname = credentialsByHostname.get(hostname); + if (base64CredentialsForHostname != null) { + httpRequest.headers().add(HttpHeaders.Names.AUTHORIZATION, "Basic " + base64CredentialsForHostname); + } + } + + return null; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java new file mode 100644 index 000000000..b267673e1 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java @@ -0,0 +1,58 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import net.lightbody.bmp.proxy.BlacklistEntry; + +import java.util.Collection; +import java.util.Collections; + +/** + * Applies blacklist entries to this request. The filter does not make a defensive copy of the blacklist entries, so there is no guarantee + * that the blacklist at the time of construction will contain the same values when the filter is actually invoked, if the entries are modified concurrently. + */ +public class BlacklistFilter extends HttpsAwareFiltersAdapter { + private final Collection blacklistedUrls; + + public BlacklistFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Collection blacklistedUrls) { + super(originalRequest, ctx); + + if (blacklistedUrls != null) { + this.blacklistedUrls = blacklistedUrls; + } else { + this.blacklistedUrls = Collections.emptyList(); + } + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + String url = getFullUrl(httpRequest); + + for (BlacklistEntry entry : blacklistedUrls) { + if (HttpMethod.CONNECT.equals(httpRequest.getMethod()) && entry.getHttpMethodPattern() == null) { + // do not allow CONNECTs to be blacklisted unless a method pattern is explicitly specified + continue; + } + + if (entry.matches(url, httpRequest.getMethod().name())) { + HttpResponseStatus status = HttpResponseStatus.valueOf(entry.getStatusCode()); + HttpResponse resp = new DefaultFullHttpResponse(httpRequest.getProtocolVersion(), status); + HttpHeaders.setContentLength(resp, 0L); + + return resp; + } + } + } + + return null; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java new file mode 100644 index 000000000..6ddf1331f --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java @@ -0,0 +1,313 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import net.lightbody.bmp.BrowserMobProxyServer; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.littleshoot.proxy.HttpFiltersSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * The filter "driver" that delegates to all chained filters specified by the proxy server. + */ +public class BrowserMobHttpFilterChain extends HttpFiltersAdapter { + private static final Logger log = LoggerFactory.getLogger(BrowserMobHttpFilterChain.class); + + private final BrowserMobProxyServer proxyServer; + + private final List filters; + + public BrowserMobHttpFilterChain(BrowserMobProxyServer proxyServer, HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + + this.proxyServer = proxyServer; + + if (proxyServer.getFilterFactories() != null) { + filters = new ArrayList<>(proxyServer.getFilterFactories().size()); + + // instantiate all HttpFilters using the proxy's filter factories + for (HttpFiltersSource filterFactory : proxyServer.getFilterFactories()) { + HttpFilters filter = filterFactory.filterRequest(originalRequest, ctx); + // allow filter factories to avoid adding a filter on a per-request basis by returning a null + // HttpFilters instance + if (filter != null) { + filters.add(filter); + } + } + } else { + filters = Collections.emptyList(); + } + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (proxyServer.isStopped()) { + log.warn("Aborting request to {} because proxy is stopped", originalRequest.getUri()); + HttpResponse abortedResponse = new DefaultFullHttpResponse(originalRequest.getProtocolVersion(), HttpResponseStatus.SERVICE_UNAVAILABLE); + HttpHeaders.setContentLength(abortedResponse, 0L); + return abortedResponse; + } + + for (HttpFilters filter : filters) { + try { + HttpResponse filterResponse = filter.clientToProxyRequest(httpObject); + if (filterResponse != null) { + // if we are short-circuiting the response to an HttpRequest, update ModifiedRequestAwareFilter instances + // with this (possibly) modified HttpRequest before returning the short-circuit response + if (httpObject instanceof HttpRequest) { + updateFiltersWithModifiedResponse((HttpRequest) httpObject); + } + + return filterResponse; + } + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + + // if this httpObject is the HTTP request, set the modified request object on all ModifiedRequestAwareFilter + // instances, so they have access to all modifications the request filters made while filtering + if (httpObject instanceof HttpRequest) { + updateFiltersWithModifiedResponse((HttpRequest) httpObject); + } + + return null; + } + + @Override + public HttpResponse proxyToServerRequest(HttpObject httpObject) { + for (HttpFilters filter : filters) { + try { + HttpResponse filterResponse = filter.proxyToServerRequest(httpObject); + if (filterResponse != null) { + return filterResponse; + } + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + + return null; + } + + @Override + public void proxyToServerRequestSending() { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerRequestSending(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + + @Override + public HttpObject serverToProxyResponse(HttpObject httpObject) { + HttpObject processedHttpObject = httpObject; + + for (HttpFilters filter : filters) { + try { + processedHttpObject = filter.serverToProxyResponse(processedHttpObject); + if (processedHttpObject == null) { + return null; + } + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + + return processedHttpObject; + } + + @Override + public void serverToProxyResponseTimedOut() { + for (HttpFilters filter : filters) { + try { + filter.serverToProxyResponseTimedOut(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void serverToProxyResponseReceiving() { + for (HttpFilters filter : filters) { + try { + filter.serverToProxyResponseReceiving(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public InetSocketAddress proxyToServerResolutionStarted(String resolvingServerHostAndPort) { + InetSocketAddress overrideAddress = null; + String newServerHostAndPort = resolvingServerHostAndPort; + + for (HttpFilters filter : filters) { + try { + InetSocketAddress filterResult = filter.proxyToServerResolutionStarted(newServerHostAndPort); + if (filterResult != null) { + overrideAddress = filterResult; + newServerHostAndPort = filterResult.getHostString() + ":" + filterResult.getPort(); + } + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + + return overrideAddress; + } + + @Override + public void proxyToServerResolutionFailed(String hostAndPort) { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerResolutionFailed(hostAndPort); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void proxyToServerResolutionSucceeded(String serverHostAndPort, InetSocketAddress resolvedRemoteAddress) { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerResolutionSucceeded(serverHostAndPort, resolvedRemoteAddress); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + + super.proxyToServerResolutionSucceeded(serverHostAndPort, resolvedRemoteAddress); + } + + @Override + public void proxyToServerConnectionStarted() { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerConnectionStarted(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void proxyToServerConnectionSSLHandshakeStarted() { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerConnectionSSLHandshakeStarted(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void proxyToServerConnectionFailed() { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerConnectionFailed(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void proxyToServerConnectionSucceeded(ChannelHandlerContext serverCtx) { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerConnectionSucceeded(serverCtx); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void proxyToServerRequestSent() { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerRequestSent(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public void serverToProxyResponseReceived() { + for (HttpFilters filter : filters) { + try { + filter.serverToProxyResponseReceived(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + @Override + public HttpObject proxyToClientResponse(HttpObject httpObject) { + HttpObject processedHttpObject = httpObject; + for (HttpFilters filter : filters) { + try { + processedHttpObject = filter.proxyToClientResponse(processedHttpObject); + if (processedHttpObject == null) { + return null; + } + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + + return processedHttpObject; + } + + @Override + public void proxyToServerConnectionQueued() { + for (HttpFilters filter : filters) { + try { + filter.proxyToServerConnectionQueued(); + } catch (RuntimeException e) { + log.warn("Filter in filter chain threw exception. Filter method may have been aborted.", e); + } + } + } + + /** + * Updates {@link ModifiedRequestAwareFilter} filters with the final, modified request after all request filters have + * processed the request. + * + * @param modifiedRequest the modified HttpRequest after all filters have finished processing it + */ + private void updateFiltersWithModifiedResponse(HttpRequest modifiedRequest) { + for (HttpFilters filter : filters) { + if (filter instanceof ModifiedRequestAwareFilter) { + ModifiedRequestAwareFilter requestCaptureFilter = (ModifiedRequestAwareFilter) filter; + try { + requestCaptureFilter.setModifiedHttpRequest(modifiedRequest); + } catch (RuntimeException e) { + log.warn("ModifiedRequestAwareFilter in filter chain threw exception while setting modified HTTP request.", e); + } + } + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java new file mode 100644 index 000000000..d6bd1b58c --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java @@ -0,0 +1,93 @@ +package net.lightbody.bmp.filters; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.littleshoot.proxy.HttpFiltersAdapter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * This filter captures requests from the client (headers and content). + *

+ * The filter can be used in one of three ways: (1) directly, by adding the filter to the filter chain; (2) by subclassing + * the filter and overriding its filter methods; or (3) by invoking the filter directly from within another filter (see + * {@link net.lightbody.bmp.filters.HarCaptureFilter} for an example of the latter). + */ +public class ClientRequestCaptureFilter extends HttpFiltersAdapter { + /** + * Populated by clientToProxyRequest() when processing the HttpRequest object. Unlike originalRequest, + * this represents the "real" request that is being sent to the server, including headers. + */ + private volatile HttpRequest httpRequest; + + /** + * Populated by clientToProxyRequest() when processing the HttpContent objects. If the request is chunked, + * it will be populated across multiple calls to clientToProxyRequest(). + */ + private final ByteArrayOutputStream requestContents = new ByteArrayOutputStream(); + + /** + * Populated by clientToProxyRequest() when processing the LastHttpContent. + */ + private volatile HttpHeaders trailingHeaders; + + public ClientRequestCaptureFilter(HttpRequest originalRequest) { + super(originalRequest); + } + + public ClientRequestCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + this.httpRequest = (HttpRequest) httpObject; + } + + if (httpObject instanceof HttpContent) { + HttpContent httpContent = (HttpContent) httpObject; + + storeRequestContent(httpContent); + + if (httpContent instanceof LastHttpContent) { + LastHttpContent lastHttpContent = (LastHttpContent) httpContent; + trailingHeaders = lastHttpContent .trailingHeaders(); + } + } + + return null; + } + + protected void storeRequestContent(HttpContent httpContent) { + ByteBuf bufferedContent = httpContent.content(); + byte[] content = BrowserMobHttpUtil.extractReadableBytes(bufferedContent); + + try { + requestContents.write(content); + } catch (IOException e) { + // can't happen + } + } + + public HttpRequest getHttpRequest() { + return httpRequest; + } + + public byte[] getFullRequestContents() { + return requestContents.toByteArray(); + } + + public HttpHeaders getTrailingHeaders() { + return trailingHeaders; + } + +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java new file mode 100644 index 000000000..ca8c044c2 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java @@ -0,0 +1,766 @@ +package net.lightbody.bmp.filters; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.BaseEncoding; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.cookie.ClientCookieDecoder; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.core.har.HarCookie; +import net.lightbody.bmp.core.har.HarEntry; +import net.lightbody.bmp.core.har.HarNameValuePair; +import net.lightbody.bmp.core.har.HarPostData; +import net.lightbody.bmp.core.har.HarPostDataParam; +import net.lightbody.bmp.core.har.HarRequest; +import net.lightbody.bmp.core.har.HarResponse; +import net.lightbody.bmp.exception.UnsupportedCharsetException; +import net.lightbody.bmp.filters.support.HttpConnectTiming; +import net.lightbody.bmp.filters.util.HarCaptureUtil; +import net.lightbody.bmp.proxy.CaptureType; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.littleshoot.proxy.impl.ProxyUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Calendar; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class HarCaptureFilter extends HttpsAwareFiltersAdapter { + private static final Logger log = LoggerFactory.getLogger(HarCaptureFilter.class); + + /** + * The currently active HAR at the time the current request is received. + */ + private final Har har; + + /** + * The harEntry is created when this filter is constructed and is shared by both the clientToProxyRequest + * and serverToProxyResponse methods. It is added to the HarLog when the request is received from the client. + */ + private final HarEntry harEntry; + + /** + * The requestCaptureFilter captures all request content, including headers, trailing headers, and content. The HarCaptureFilter + * delegates to it when the clientToProxyRequest() callback is invoked. If this request does not need content capture, the + * ClientRequestCaptureFilter filter will not be instantiated and will not capture content. + */ + private final ClientRequestCaptureFilter requestCaptureFilter; + + /** + * Like requestCaptureFilter above, HarCaptureFilter delegates to responseCaptureFilter to capture response contents. If content capture + * is not required for this request, the filter will not be instantiated or invoked. + */ + private final ServerResponseCaptureFilter responseCaptureFilter; + + /** + * The CaptureType data types to capture in this request. + */ + private final EnumSet dataToCapture; + + /** + * Populated by proxyToServerResolutionStarted when DNS resolution starts. If any previous filters already resolved the address, their resolution time + * will not be included in this time. + */ + private volatile long dnsResolutionStartedNanos; + + private volatile long connectionQueuedNanos; + private volatile long connectionStartedNanos; + + private volatile long sendStartedNanos; + private volatile long sendFinishedNanos; + + private volatile long responseReceiveStartedNanos; + + /** + * The address of the client making the request. Captured in the constructor and used when calculating and capturing ssl handshake and connect + * timing information for SSL connections. + */ + private final InetSocketAddress clientAddress; + + /** + * Request body size is determined by the actual size of the data the client sends. The filter does not use the Content-Length header to determine request size. + */ + private final AtomicInteger requestBodySize = new AtomicInteger(0); + + /** + * Response body size is determined by the actual size of the data the server sends. + */ + private final AtomicInteger responseBodySize = new AtomicInteger(0); + + /** + * The "real" original request, as captured by the {@link #clientToProxyRequest(io.netty.handler.codec.http.HttpObject)} method. + */ + private volatile HttpRequest capturedOriginalRequest; + + /** + * True if this filter instance processed a {@link #proxyToServerResolutionSucceeded(String, java.net.InetSocketAddress)} call, indicating + * that the hostname was resolved and populated in the HAR (if this is not a CONNECT). + */ + private volatile boolean addressResolved = false; + + /** + * Create a new instance of the HarCaptureFilter that will capture request and response information. If no har is specified in the + * constructor, this filter will do nothing. + *

+ * Regardless of the CaptureTypes specified in dataToCapture, the HarCaptureFilter will always capture: + *

    + *
  • Request and response sizes
  • + *
  • HTTP request and status lines
  • + *
  • Page timing information
  • + *
+ * + * @param originalRequest the original HttpRequest from the HttpFiltersSource factory + * @param har a reference to the ProxyServer's current HAR file at the time this request is received (can be null if HAR capture is not required) + * @param currentPageRef the ProxyServer's currentPageRef at the time this request is received from the client + * @param dataToCapture the data types to capture for this request. null or empty set indicates only basic information will be + * captured (see {@link net.lightbody.bmp.proxy.CaptureType} for information on data collected for each CaptureType) + */ + public HarCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Har har, String currentPageRef, Set dataToCapture) { + super(originalRequest, ctx); + + if (har == null) { + throw new IllegalStateException("Attempted har capture when har is null"); + } + + if (ProxyUtils.isCONNECT(originalRequest)) { + throw new IllegalStateException("Attempted har capture for HTTP CONNECT request"); + } + + this.clientAddress = (InetSocketAddress) ctx.channel().remoteAddress(); + + if (dataToCapture != null && !dataToCapture.isEmpty()) { + this.dataToCapture = EnumSet.copyOf(dataToCapture); + } else { + this.dataToCapture = EnumSet.noneOf(CaptureType.class); + } + + // we may need to capture both the request and the response, so set up the request/response filters and delegate to them when + // the corresponding filter methods are invoked. to save time and memory, only set up the capturing filters when + // we actually need to capture the data. + if (this.dataToCapture.contains(CaptureType.REQUEST_CONTENT) || this.dataToCapture.contains(CaptureType.REQUEST_BINARY_CONTENT)) { + requestCaptureFilter = new ClientRequestCaptureFilter(originalRequest); + } else { + requestCaptureFilter = null; + } + + if (this.dataToCapture.contains(CaptureType.RESPONSE_CONTENT) || this.dataToCapture.contains(CaptureType.RESPONSE_BINARY_CONTENT)) { + responseCaptureFilter = new ServerResponseCaptureFilter(originalRequest, true); + } else { + responseCaptureFilter = null; + } + + this.har = har; + + this.harEntry = new HarEntry(currentPageRef); + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + // if a ServerResponseCaptureFilter is configured, delegate to it to collect the client request. if it is not + // configured, we still need to capture basic information (timings, possibly client headers, etc.), just not content. + if (requestCaptureFilter != null) { + requestCaptureFilter.clientToProxyRequest(httpObject); + } + + if (httpObject instanceof HttpRequest) { + // link the object up now, before we make the request, so that if we get cut off (ie: favicon.ico request and browser shuts down) + // we still have the attempt associated, even if we never got a response + harEntry.setStartedDateTime(new Date()); + har.getLog().addEntry(harEntry); + + HttpRequest httpRequest = (HttpRequest) httpObject; + this.capturedOriginalRequest = httpRequest; + + // associate this request's HarRequest object with the har entry + HarRequest request = createHarRequestForHttpRequest(httpRequest); + harEntry.setRequest(request); + + // create a "no response received" HarResponse, in case the connection is interrupted, terminated, or the response is not received + // for any other reason. having a "default" HarResponse prevents us from generating an invalid HAR. + HarResponse defaultHarResponse = HarCaptureUtil.createHarResponseForFailure(); + defaultHarResponse.setError(HarCaptureUtil.getNoResponseReceivedErrorMessage()); + harEntry.setResponse(defaultHarResponse); + + captureQueryParameters(httpRequest); + // not capturing user agent: in many cases, it doesn't make sense to capture at the HarLog level, since the proxy could be + // serving requests from many different clients with various user agents. clients can turn on the REQUEST_HEADERS capture type + // in order to capture the User-Agent header, if desired. + captureRequestHeaderSize(httpRequest); + + if (dataToCapture.contains(CaptureType.REQUEST_COOKIES)) { + captureRequestCookies(httpRequest); + } + + if (dataToCapture.contains(CaptureType.REQUEST_HEADERS)) { + captureRequestHeaders(httpRequest); + } + + // The HTTP CONNECT to the proxy server establishes the SSL connection to the remote server, but the + // HTTP CONNECT is not recorded in a separate HarEntry (except in case of error). Instead, the ssl and + // connect times are recorded in the first request between the client and remote server after the HTTP CONNECT. + captureConnectTiming(); + } + + if (httpObject instanceof HttpContent) { + HttpContent httpContent = (HttpContent) httpObject; + + captureRequestSize(httpContent); + } + + if (httpObject instanceof LastHttpContent) { + LastHttpContent lastHttpContent = (LastHttpContent) httpObject; + if (dataToCapture.contains(CaptureType.REQUEST_HEADERS)) { + captureTrailingHeaders(lastHttpContent); + } + + if (dataToCapture.contains(CaptureType.REQUEST_CONTENT)) { + captureRequestContent(requestCaptureFilter.getHttpRequest(), requestCaptureFilter.getFullRequestContents()); + } + + harEntry.getRequest().setBodySize(requestBodySize.get()); + } + + return null; + } + + @Override + public HttpObject serverToProxyResponse(HttpObject httpObject) { + // if a ServerResponseCaptureFilter is configured, delegate to it to collect the server's response. if it is not + // configured, we still need to capture basic information (timings, HTTP status, etc.), just not content. + if (responseCaptureFilter != null) { + responseCaptureFilter.serverToProxyResponse(httpObject); + } + + if (httpObject instanceof HttpResponse) { + HttpResponse httpResponse = (HttpResponse) httpObject; + + captureResponse(httpResponse); + } + + if (httpObject instanceof HttpContent) { + HttpContent httpContent = (HttpContent) httpObject; + + captureResponseSize(httpContent); + } + + if (httpObject instanceof LastHttpContent) { + if (dataToCapture.contains(CaptureType.RESPONSE_CONTENT)) { + captureResponseContent(responseCaptureFilter.getHttpResponse(), responseCaptureFilter.getFullResponseContents()); + } + + harEntry.getResponse().setBodySize(responseBodySize.get()); + } + + return super.serverToProxyResponse(httpObject); + } + + @Override + public void serverToProxyResponseTimedOut() { + // replace any existing HarResponse that was created if the server sent a partial response + HarResponse response = HarCaptureUtil.createHarResponseForFailure(); + harEntry.setResponse(response); + + response.setError(HarCaptureUtil.getResponseTimedOutErrorMessage()); + + + // include this timeout time in the HarTimings object + long timeoutTimestampNanos = System.nanoTime(); + + // if the proxy started to send the request but has not yet finished, we are currently "sending" + if (sendStartedNanos > 0L && sendFinishedNanos == 0L) { + harEntry.getTimings().setSend(timeoutTimestampNanos - sendStartedNanos, TimeUnit.NANOSECONDS); + } + // if the entire request was sent but the proxy has not begun receiving the response, we are currently "waiting" + else if (sendFinishedNanos > 0L && responseReceiveStartedNanos == 0L) { + harEntry.getTimings().setWait(timeoutTimestampNanos - sendFinishedNanos, TimeUnit.NANOSECONDS); + } + // if the proxy has already begun to receive the response, we are currenting "receiving" + else if (responseReceiveStartedNanos > 0L) { + harEntry.getTimings().setReceive(timeoutTimestampNanos - responseReceiveStartedNanos, TimeUnit.NANOSECONDS); + } + } + + /** + * Creates a HarRequest object using the method, url, and HTTP version of the specified request. + * + * @param httpRequest HTTP request on which the HarRequest will be based + * @return a new HarRequest object + */ + private HarRequest createHarRequestForHttpRequest(HttpRequest httpRequest) { + // the HAR spec defines the request.url field as: + // url [string] - Absolute URL of the request (fragments are not included). + // the URI on the httpRequest may only identify the path of the resource, so find the full URL. + // the full URL consists of the scheme + host + port (if non-standard) + path + query params + fragment. + String url = getFullUrl(httpRequest); + + return new HarRequest(httpRequest.getMethod().toString(), url, httpRequest.getProtocolVersion().text()); + } + + //TODO: add unit tests for these utility-like capture() methods + + protected void captureQueryParameters(HttpRequest httpRequest) { + // capture query parameters. it is safe to assume the query string is UTF-8, since it "should" be in US-ASCII (a subset of UTF-8), + // but sometimes does include UTF-8 characters. + QueryStringDecoder queryStringDecoder = new QueryStringDecoder(httpRequest.getUri(), StandardCharsets.UTF_8); + + try { + for (Map.Entry> entry : queryStringDecoder.parameters().entrySet()) { + for (String value : entry.getValue()) { + harEntry.getRequest().getQueryString().add(new HarNameValuePair(entry.getKey(), value)); + } + } + } catch (IllegalArgumentException e) { + // QueryStringDecoder will throw an IllegalArgumentException if it cannot interpret a query string. rather than cause the entire request to + // fail by propagating the exception, simply skip the query parameter capture. + harEntry.setComment("Unable to decode query parameters on URI: " + httpRequest.getUri()); + log.info("Unable to decode query parameters on URI: " + httpRequest.getUri(), e); + } + } + + protected void captureRequestHeaderSize(HttpRequest httpRequest) { + String requestLine = httpRequest.getMethod().toString() + ' ' + httpRequest.getUri() + ' ' + httpRequest.getProtocolVersion().toString(); + // +2 => CRLF after status line, +4 => header/data separation + long requestHeadersSize = requestLine.length() + 6; + + HttpHeaders headers = httpRequest.headers(); + requestHeadersSize += BrowserMobHttpUtil.getHeaderSize(headers); + + harEntry.getRequest().setHeadersSize(requestHeadersSize); + } + + protected void captureRequestCookies(HttpRequest httpRequest) { + String cookieHeader = httpRequest.headers().get(HttpHeaders.Names.COOKIE); + if (cookieHeader == null) { + return; + } + + Set cookies = ServerCookieDecoder.LAX.decode(cookieHeader); + + for (Cookie cookie : cookies) { + HarCookie harCookie = new HarCookie(); + + harCookie.setName(cookie.name()); + harCookie.setValue(cookie.value()); + + harEntry.getRequest().getCookies().add(harCookie); + } + } + + protected void captureRequestHeaders(HttpRequest httpRequest) { + HttpHeaders headers = httpRequest.headers(); + + captureHeaders(headers); + } + + protected void captureTrailingHeaders(LastHttpContent lastHttpContent) { + HttpHeaders headers = lastHttpContent.trailingHeaders(); + + captureHeaders(headers); + } + + protected void captureHeaders(HttpHeaders headers) { + for (Map.Entry header : headers.entries()) { + harEntry.getRequest().getHeaders().add(new HarNameValuePair(header.getKey(), header.getValue())); + } + } + + protected void captureRequestContent(HttpRequest httpRequest, byte[] fullMessage) { + if (fullMessage.length == 0) { + return; + } + + String contentType = HttpHeaders.getHeader(httpRequest, HttpHeaders.Names.CONTENT_TYPE); + if (contentType == null) { + log.warn("No content type specified in request to {}. Content will be treated as {}", httpRequest.getUri(), BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE); + contentType = BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE; + } + + HarPostData postData = new HarPostData(); + harEntry.getRequest().setPostData(postData); + + postData.setMimeType(contentType); + + boolean urlEncoded; + if (contentType.startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { + urlEncoded = true; + } else { + urlEncoded = false; + } + + Charset charset; + try { + charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentType); + } catch (UnsupportedCharsetException e) { + log.warn("Found unsupported character set in Content-Type header '{}' in HTTP request to {}. Content will not be captured in HAR.", contentType, httpRequest.getUri(), e); + return; + } + + if (charset == null) { + // no charset specified, so use the default -- but log a message since this might not encode the data correctly + charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + log.debug("No charset specified; using charset {} to decode contents to {}", charset, httpRequest.getUri()); + } + + if (urlEncoded) { + String textContents = BrowserMobHttpUtil.getContentAsString(fullMessage, charset); + + QueryStringDecoder queryStringDecoder = new QueryStringDecoder(textContents, charset, false); + + ImmutableList.Builder paramBuilder = ImmutableList.builder(); + + for (Map.Entry> entry : queryStringDecoder.parameters().entrySet()) { + for (String value : entry.getValue()) { + paramBuilder.add(new HarPostDataParam(entry.getKey(), value)); + } + } + + harEntry.getRequest().getPostData().setParams(paramBuilder.build()); + } else { + //TODO: implement capture of files and multipart form data + + // not URL encoded, so let's grab the body of the POST and capture that + String postBody = BrowserMobHttpUtil.getContentAsString(fullMessage, charset); + harEntry.getRequest().getPostData().setText(postBody); + } + } + + protected void captureResponseContent(HttpResponse httpResponse, byte[] fullMessage) { + // force binary if the content encoding is not supported + boolean forceBinary = false; + + String contentType = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.CONTENT_TYPE); + if (contentType == null) { + log.warn("No content type specified in response from {}. Content will be treated as {}", originalRequest.getUri(), BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE); + contentType = BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE; + } + + if (responseCaptureFilter.isResponseCompressed() && !responseCaptureFilter.isDecompressionSuccessful()) { + log.warn("Unable to decompress content with encoding: {}. Contents will be encoded as base64 binary data.", responseCaptureFilter.getContentEncoding()); + + forceBinary = true; + } + + Charset charset; + try { + charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentType); + } catch (UnsupportedCharsetException e) { + log.warn("Found unsupported character set in Content-Type header '{}' in HTTP response from {}. Content will not be captured in HAR.", contentType, originalRequest.getUri(), e); + return; + } + + if (charset == null) { + // no charset specified, so use the default -- but log a message since this might not encode the data correctly + charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + log.debug("No charset specified; using charset {} to decode contents from {}", charset, originalRequest.getUri()); + } + + if (!forceBinary && BrowserMobHttpUtil.hasTextualContent(contentType)) { + String text = BrowserMobHttpUtil.getContentAsString(fullMessage, charset); + harEntry.getResponse().getContent().setText(text); + } else if (dataToCapture.contains(CaptureType.RESPONSE_BINARY_CONTENT)) { + harEntry.getResponse().getContent().setText(BaseEncoding.base64().encode(fullMessage)); + harEntry.getResponse().getContent().setEncoding("base64"); + } + + harEntry.getResponse().getContent().setSize(fullMessage.length); + } + + protected void captureResponse(HttpResponse httpResponse) { + HarResponse response = new HarResponse(httpResponse.getStatus().code(), httpResponse.getStatus().reasonPhrase(), httpResponse.getProtocolVersion().text()); + harEntry.setResponse(response); + + captureResponseHeaderSize(httpResponse); + + captureResponseMimeType(httpResponse); + + if (dataToCapture.contains(CaptureType.RESPONSE_COOKIES)) { + captureResponseCookies(httpResponse); + } + + if (dataToCapture.contains(CaptureType.RESPONSE_HEADERS)) { + captureResponseHeaders(httpResponse); + } + + if (BrowserMobHttpUtil.isRedirect(httpResponse)) { + captureRedirectUrl(httpResponse); + } + } + + protected void captureResponseMimeType(HttpResponse httpResponse) { + String contentType = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.CONTENT_TYPE); + // don't set the mimeType to null, since mimeType is a required field + if (contentType != null) { + harEntry.getResponse().getContent().setMimeType(contentType); + } + } + + protected void captureResponseCookies(HttpResponse httpResponse) { + List setCookieHeaders = httpResponse.headers().getAll(HttpHeaders.Names.SET_COOKIE); + if (setCookieHeaders == null) { + return; + } + + for (String setCookieHeader : setCookieHeaders) { + Cookie cookie = ClientCookieDecoder.LAX.decode(setCookieHeader); + if (cookie == null) { + return; + } + + HarCookie harCookie = new HarCookie(); + + harCookie.setName(cookie.name()); + harCookie.setValue(cookie.value()); + // comment is no longer supported in the netty ClientCookieDecoder + harCookie.setDomain(cookie.domain()); + harCookie.setHttpOnly(cookie.isHttpOnly()); + harCookie.setPath(cookie.path()); + harCookie.setSecure(cookie.isSecure()); + if (cookie.maxAge() > 0) { + // use a Calendar with the current timestamp + maxAge seconds. the locale of the calendar is irrelevant, + // since we are dealing with timestamps. + Calendar expires = Calendar.getInstance(); + // zero out the milliseconds, since maxAge is in seconds + expires.set(Calendar.MILLISECOND, 0); + // we can't use Calendar.add, since that only takes ints. TimeUnit.convert handles second->millisecond + // overflow reasonably well by returning the result as Long.MAX_VALUE. + expires.setTimeInMillis(expires.getTimeInMillis() + TimeUnit.MILLISECONDS.convert(cookie.maxAge(), TimeUnit.SECONDS)); + + harCookie.setExpires(expires.getTime()); + } + + harEntry.getResponse().getCookies().add(harCookie); + } + } + + protected void captureResponseHeaderSize(HttpResponse httpResponse) { + String statusLine = httpResponse.getProtocolVersion().toString() + ' ' + httpResponse.getStatus().toString(); + // +2 => CRLF after status line, +4 => header/data separation + long responseHeadersSize = statusLine.length() + 6; + HttpHeaders headers = httpResponse.headers(); + responseHeadersSize += BrowserMobHttpUtil.getHeaderSize(headers); + + harEntry.getResponse().setHeadersSize(responseHeadersSize); + } + + protected void captureResponseHeaders(HttpResponse httpResponse) { + HttpHeaders headers = httpResponse.headers(); + for (Map.Entry header : headers.entries()) { + harEntry.getResponse().getHeaders().add(new HarNameValuePair(header.getKey(), header.getValue())); + } + } + + protected void captureRedirectUrl(HttpResponse httpResponse) { + String locationHeaderValue = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.LOCATION); + if (locationHeaderValue != null) { + harEntry.getResponse().setRedirectURL(locationHeaderValue); + } + } + + /** + * Adds the size of this httpContent to the requestBodySize. + * + * @param httpContent HttpContent to size + */ + protected void captureRequestSize(HttpContent httpContent) { + ByteBuf bufferedContent = httpContent.content(); + int contentSize = bufferedContent.readableBytes(); + requestBodySize.addAndGet(contentSize); + } + + /** + * Adds the size of this httpContent to the responseBodySize. + * + * @param httpContent HttpContent to size + */ + protected void captureResponseSize(HttpContent httpContent) { + ByteBuf bufferedContent = httpContent.content(); + int contentSize = bufferedContent.readableBytes(); + responseBodySize.addAndGet(contentSize); + } + + /** + * Populates ssl and connect timing info in the HAR if an entry for this client and server exist in the cache. + */ + protected void captureConnectTiming() { + HttpConnectTiming httpConnectTiming = HttpConnectHarCaptureFilter.consumeConnectTimingForConnection(clientAddress); + if (httpConnectTiming != null) { + harEntry.getTimings().setSsl(httpConnectTiming.getSslHandshakeTimeNanos(), TimeUnit.NANOSECONDS); + harEntry.getTimings().setConnect(httpConnectTiming.getConnectTimeNanos(), TimeUnit.NANOSECONDS); + harEntry.getTimings().setBlocked(httpConnectTiming.getBlockedTimeNanos(), TimeUnit.NANOSECONDS); + harEntry.getTimings().setDns(httpConnectTiming.getDnsTimeNanos(), TimeUnit.NANOSECONDS); + } + } + + /** + * Populates the serverIpAddress field of the harEntry using the internal hostname->IP address cache. + * + * @param httpRequest HTTP request to take the hostname from + */ + protected void populateAddressFromCache(HttpRequest httpRequest) { + String serverHost = getHost(httpRequest); + + if (serverHost != null && !serverHost.isEmpty()) { + String resolvedAddress = ResolvedHostnameCacheFilter.getPreviouslyResolvedAddressForHost(serverHost); + if (resolvedAddress != null) { + harEntry.setServerIPAddress(resolvedAddress); + } else { + // the resolvedAddress may be null if the ResolvedHostnameCacheFilter has expired the entry (which is unlikely), + // or in the far more common case that the proxy is using a chained proxy to connect to connect to the + // remote host. since the chained proxy handles IP address resolution, the IP address in the HAR must be blank. + log.trace("Unable to find cached IP address for host: {}. IP address in HAR entry will be blank.", serverHost); + } + } else { + log.warn("Unable to identify host from request uri: {}", httpRequest.getUri()); + } + } + + @Override + public InetSocketAddress proxyToServerResolutionStarted(String resolvingServerHostAndPort) { + dnsResolutionStartedNanos = System.nanoTime(); + + // resolution started means the connection is no longer queued, so populate 'blocked' time + if (connectionQueuedNanos > 0L) { + harEntry.getTimings().setBlocked(dnsResolutionStartedNanos - connectionQueuedNanos, TimeUnit.NANOSECONDS); + } else { + harEntry.getTimings().setBlocked(0L, TimeUnit.NANOSECONDS); + } + + return null; + } + + @Override + public void proxyToServerResolutionFailed(String hostAndPort) { + HarResponse response = HarCaptureUtil.createHarResponseForFailure(); + harEntry.setResponse(response); + + response.setError(HarCaptureUtil.getResolutionFailedErrorMessage(hostAndPort)); + + // record the amount of time we attempted to resolve the hostname in the HarTimings object + if (dnsResolutionStartedNanos > 0L) { + harEntry.getTimings().setDns(System.nanoTime() - dnsResolutionStartedNanos, TimeUnit.NANOSECONDS); + } + } + + @Override + public void proxyToServerResolutionSucceeded(String serverHostAndPort, InetSocketAddress resolvedRemoteAddress) { + long dnsResolutionFinishedNanos = System.nanoTime(); + + if (dnsResolutionStartedNanos > 0L) { + harEntry.getTimings().setDns(dnsResolutionFinishedNanos - dnsResolutionStartedNanos, TimeUnit.NANOSECONDS); + } else { + harEntry.getTimings().setDns(0L, TimeUnit.NANOSECONDS); + } + + // the address *should* always be resolved at this point + InetAddress resolvedAddress = resolvedRemoteAddress.getAddress(); + if (resolvedAddress != null) { + addressResolved = true; + + harEntry.setServerIPAddress(resolvedAddress.getHostAddress()); + } + } + + @Override + public void proxyToServerConnectionQueued() { + this.connectionQueuedNanos = System.nanoTime(); + } + + @Override + public void proxyToServerConnectionStarted() { + this.connectionStartedNanos = System.nanoTime(); + } + + @Override + public void proxyToServerConnectionFailed() { + HarResponse response = HarCaptureUtil.createHarResponseForFailure(); + harEntry.setResponse(response); + + response.setError(HarCaptureUtil.getConnectionFailedErrorMessage()); + + // record the amount of time we attempted to connect in the HarTimings object + if (connectionStartedNanos > 0L) { + harEntry.getTimings().setConnect(System.nanoTime() - connectionStartedNanos, TimeUnit.NANOSECONDS); + } + } + + @Override + public void proxyToServerConnectionSucceeded(ChannelHandlerContext serverCtx) { + long connectionSucceededTimeNanos = System.nanoTime(); + + // make sure the previous timestamp was captured, to avoid setting an absurd value in the har (see serverToProxyResponseReceiving()) + if (connectionStartedNanos > 0L) { + harEntry.getTimings().setConnect(connectionSucceededTimeNanos - connectionStartedNanos, TimeUnit.NANOSECONDS); + } else { + harEntry.getTimings().setConnect(0L, TimeUnit.NANOSECONDS); + } + } + + @Override + public void proxyToServerRequestSending() { + this.sendStartedNanos = System.nanoTime(); + + // if the hostname was not resolved (and thus the IP address populated in the har) during this request, populate the IP address from the cache + if (!addressResolved) { + populateAddressFromCache(capturedOriginalRequest); + } + } + + @Override + public void proxyToServerRequestSent() { + this.sendFinishedNanos = System.nanoTime(); + + // make sure the previous timestamp was captured, to avoid setting an absurd value in the har (see serverToProxyResponseReceiving()) + if (sendStartedNanos > 0L) { + harEntry.getTimings().setSend(sendFinishedNanos - sendStartedNanos, TimeUnit.NANOSECONDS); + } else { + harEntry.getTimings().setSend(0L, TimeUnit.NANOSECONDS); + } + } + + @Override + public void serverToProxyResponseReceiving() { + this.responseReceiveStartedNanos = System.nanoTime(); + + // started to receive response, so populate the 'wait' time. if we started receiving a response from the server before we finished + // sending (for example, the server replied with a 404 while we were uploading a large file), there was no wait time, so + // make sure the wait is set to 0. + if (sendFinishedNanos > 0L && sendFinishedNanos < responseReceiveStartedNanos) { + harEntry.getTimings().setWait(responseReceiveStartedNanos - sendFinishedNanos, TimeUnit.NANOSECONDS); + } else { + harEntry.getTimings().setWait(0L, TimeUnit.NANOSECONDS); + } + } + + @Override + public void serverToProxyResponseReceived() { + long responseReceivedNanos = System.nanoTime(); + + // like the wait time, the receive time requires that the serverToProxyResponseReceiving() method be called before this method is invoked. + // typically that should happen, but it has been reported (https://github.com/lightbody/browsermob-proxy/issues/288) that it + // sometimes does not. therefore, to be safe, make sure responseReceiveStartedNanos is populated before setting the receive time. + if (responseReceiveStartedNanos > 0L) { + harEntry.getTimings().setReceive(responseReceivedNanos - responseReceiveStartedNanos, TimeUnit.NANOSECONDS); + } else { + harEntry.getTimings().setReceive(0L, TimeUnit.NANOSECONDS); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java new file mode 100644 index 000000000..1717370a2 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java @@ -0,0 +1,393 @@ +package net.lightbody.bmp.filters; + +import com.google.common.cache.CacheBuilder; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.core.har.HarEntry; +import net.lightbody.bmp.core.har.HarRequest; +import net.lightbody.bmp.core.har.HarResponse; +import net.lightbody.bmp.core.har.HarTimings; +import net.lightbody.bmp.filters.support.HttpConnectTiming; +import net.lightbody.bmp.filters.util.HarCaptureUtil; +import net.lightbody.bmp.util.HttpUtil; +import org.littleshoot.proxy.impl.ProxyUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Date; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +/** + * This filter captures HAR data for HTTP CONNECT requests. CONNECTs are "meta" requests that must be made before HTTPS + * requests, but are not populated as separate requests in the HAR. Most information from HTTP CONNECTs (such as SSL + * handshake time, dns resolution time, etc.) is populated in the HAR entry for the first "true" request following the + * CONNECT. This filter captures the timing-related information and makes it available to subsequent filters through + * static methods. This filter also handles HTTP CONNECT errors and creates HAR entries for those errors, since there + * would otherwise not be any record in the HAR of the error (if the CONNECT fails, there will be no subsequent "real" + * request in which to record the error). + * + */ +public class HttpConnectHarCaptureFilter extends HttpsAwareFiltersAdapter implements ModifiedRequestAwareFilter { + private static final Logger log = LoggerFactory.getLogger(HttpConnectHarCaptureFilter.class); + + /** + * The currently active HAR at the time the current request is received. + */ + private final Har har; + + /** + * The currently active page ref at the time the current request is received. + */ + private final String currentPageRef; + + /** + * The time this CONNECT began. Used to populate the HAR entry in case of failure. + */ + private volatile Date requestStartTime; + + /** + * True if this filter instance processed a {@link #proxyToServerResolutionSucceeded(String, java.net.InetSocketAddress)} call, indicating + * that the hostname was resolved and populated in the HAR (if this is not a CONNECT). + */ +// private volatile boolean addressResolved = false; + private volatile InetAddress resolvedAddress; + + /** + * Populated by proxyToServerResolutionStarted when DNS resolution starts. If any previous filters already resolved the address, their resolution time + * will not be included in this time. See {@link HarCaptureFilter#dnsResolutionStartedNanos}. + */ + private volatile long dnsResolutionStartedNanos; + + private volatile long dnsResolutionFinishedNanos; + + private volatile long connectionQueuedNanos; + private volatile long connectionStartedNanos; + private volatile long connectionSucceededTimeNanos; + private volatile long sendStartedNanos; + private volatile long sendFinishedNanos; + + private volatile long responseReceiveStartedNanos; + private volatile long sslHandshakeStartedNanos; + + /** + * The address of the client making the request. Captured in the constructor and used when calculating and capturing ssl handshake and connect + * timing information for SSL connections. + */ + private final InetSocketAddress clientAddress; + + /** + * Stores HTTP CONNECT timing information for this request, if it is an HTTP CONNECT. + */ + private final HttpConnectTiming httpConnectTiming; + + /** + * The maximum amount of time to save timing information between an HTTP CONNECT and the subsequent HTTP request. Typically this is done + * immediately, but if for some reason it is not (e.g. due to a client crash or dropped connection), the timing information will be + * kept for this long before being evicted to prevent a memory leak. If a subsequent request does come through after eviction, it will still + * be recorded, but the timing information will not be populated in the HAR. + */ + private static final int HTTP_CONNECT_TIMING_EVICTION_SECONDS = 60; + + /** + * Concurrency of the httpConnectTiming map. Should be approximately equal to the maximum number of simultaneous connection + * attempts (but not necessarily simultaneous connections). A lower value will inhibit performance. + * TODO: tune this value for a large number of concurrent requests. develop a non-cache-based mechanism of passing ssl timings to subsequent requests. + */ + private static final int HTTP_CONNECT_TIMING_CONCURRENCY_LEVEL = 50; + + /** + * Stores SSL connection timing information from HTTP CONNNECT requests. This timing information is stored in the first HTTP request + * after the CONNECT, not in the CONNECT itself, so it needs to be stored across requests. + * + * This is the only state stored across multiple requests. + */ + private static final ConcurrentMap httpConnectTimes = + CacheBuilder.newBuilder() + .expireAfterWrite(HTTP_CONNECT_TIMING_EVICTION_SECONDS, TimeUnit.SECONDS) + .concurrencyLevel(HTTP_CONNECT_TIMING_CONCURRENCY_LEVEL) + .build() + .asMap(); + + private volatile HttpRequest modifiedHttpRequest; + + public HttpConnectHarCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Har har, String currentPageRef) { + super(originalRequest, ctx); + + if (har == null) { + throw new IllegalStateException("Attempted har capture when har is null"); + } + + if (!ProxyUtils.isCONNECT(originalRequest)) { + throw new IllegalStateException("Attempted HTTP CONNECT har capture on non-HTTP CONNECT request"); + } + + this.har = har; + this.currentPageRef = currentPageRef; + + this.clientAddress = (InetSocketAddress) ctx.channel().remoteAddress(); + + // create and cache an HTTP CONNECT timing object to capture timing-related information + this.httpConnectTiming = new HttpConnectTiming(); + httpConnectTimes.put(clientAddress, httpConnectTiming); + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + // store the CONNECT start time in case of failure, so we can populate the HarEntry with it + requestStartTime = new Date(); + } + + return null; + } + + @Override + public void proxyToServerResolutionFailed(String hostAndPort) { + // since this is a CONNECT, which is not handled by the HarCaptureFilter, we need to create and populate the + // entire HarEntry and add it to this har. + HarEntry harEntry = createHarEntryForFailedCONNECT(HarCaptureUtil.getResolutionFailedErrorMessage(hostAndPort)); + har.getLog().addEntry(harEntry); + + // record the amount of time we attempted to resolve the hostname in the HarTimings object + if (dnsResolutionStartedNanos > 0L) { + harEntry.getTimings().setDns(System.nanoTime() - dnsResolutionStartedNanos, TimeUnit.NANOSECONDS); + } + + httpConnectTimes.remove(clientAddress); + } + + @Override + public void proxyToServerConnectionFailed() { + // since this is a CONNECT, which is not handled by the HarCaptureFilter, we need to create and populate the + // entire HarEntry and add it to this har. + HarEntry harEntry = createHarEntryForFailedCONNECT(HarCaptureUtil.getConnectionFailedErrorMessage()); + har.getLog().addEntry(harEntry); + + // record the amount of time we attempted to connect in the HarTimings object + if (connectionStartedNanos > 0L) { + harEntry.getTimings().setConnect(System.nanoTime() - connectionStartedNanos, TimeUnit.NANOSECONDS); + } + + httpConnectTimes.remove(clientAddress); + } + + @Override + public void proxyToServerConnectionSucceeded(ChannelHandlerContext serverCtx) { + this.connectionSucceededTimeNanos = System.nanoTime(); + + if (connectionStartedNanos > 0L) { + httpConnectTiming.setConnectTimeNanos(connectionSucceededTimeNanos - connectionStartedNanos); + } else { + httpConnectTiming.setConnectTimeNanos(0L); + } + + if (sslHandshakeStartedNanos > 0L) { + httpConnectTiming.setSslHandshakeTimeNanos(connectionSucceededTimeNanos - sslHandshakeStartedNanos); + } else { + httpConnectTiming.setSslHandshakeTimeNanos(0L); + } + } + + @Override + public void proxyToServerConnectionSSLHandshakeStarted() { + this.sslHandshakeStartedNanos = System.nanoTime(); + } + + @Override + public void serverToProxyResponseTimedOut() { + HarEntry harEntry = createHarEntryForFailedCONNECT(HarCaptureUtil.getResponseTimedOutErrorMessage()); + har.getLog().addEntry(harEntry); + + // include this timeout time in the HarTimings object + long timeoutTimestampNanos = System.nanoTime(); + + // if the proxy started to send the request but has not yet finished, we are currently "sending" + if (sendStartedNanos > 0L && sendFinishedNanos == 0L) { + harEntry.getTimings().setSend(timeoutTimestampNanos - sendStartedNanos, TimeUnit.NANOSECONDS); + } + // if the entire request was sent but the proxy has not begun receiving the response, we are currently "waiting" + else if (sendFinishedNanos > 0L && responseReceiveStartedNanos == 0L) { + harEntry.getTimings().setWait(timeoutTimestampNanos - sendFinishedNanos, TimeUnit.NANOSECONDS); + } + // if the proxy has already begun to receive the response, we are currenting "receiving" + else if (responseReceiveStartedNanos > 0L) { + harEntry.getTimings().setReceive(timeoutTimestampNanos - responseReceiveStartedNanos, TimeUnit.NANOSECONDS); + } + } + + @Override + public void proxyToServerConnectionQueued() { + this.connectionQueuedNanos = System.nanoTime(); + } + + + @Override + public InetSocketAddress proxyToServerResolutionStarted(String resolvingServerHostAndPort) { + dnsResolutionStartedNanos = System.nanoTime(); + + if (connectionQueuedNanos > 0L) { + httpConnectTiming.setBlockedTimeNanos(dnsResolutionStartedNanos - connectionQueuedNanos); + } else { + httpConnectTiming.setBlockedTimeNanos(0L); + } + + return null; + } + + @Override + public void proxyToServerResolutionSucceeded(String serverHostAndPort, InetSocketAddress resolvedRemoteAddress) { + this.dnsResolutionFinishedNanos = System.nanoTime(); + + if (dnsResolutionStartedNanos > 0L) { + httpConnectTiming.setDnsTimeNanos(dnsResolutionFinishedNanos - dnsResolutionStartedNanos); + } else { + httpConnectTiming.setDnsTimeNanos(0L); + } + + // the address *should* always be resolved at this point + this.resolvedAddress = resolvedRemoteAddress.getAddress(); + } + + @Override + public void proxyToServerConnectionStarted() { + this.connectionStartedNanos = System.nanoTime(); + } + + @Override + public void proxyToServerRequestSending() { + this.sendStartedNanos = System.nanoTime(); + } + + @Override + public void proxyToServerRequestSent() { + this.sendFinishedNanos = System.nanoTime(); + } + + @Override + public void serverToProxyResponseReceiving() { + this.responseReceiveStartedNanos = System.nanoTime(); + } + + /** + * Populates timing information in the specified harEntry for failed rquests. Populates as much timing information + * as possible, up to the point of failure. + * + * @param harEntry HAR entry to populate timing information in + */ + private void populateTimingsForFailedCONNECT(HarEntry harEntry) { + HarTimings timings = harEntry.getTimings(); + + if (connectionQueuedNanos > 0L && dnsResolutionStartedNanos > 0L) { + timings.setBlocked(dnsResolutionStartedNanos - connectionQueuedNanos, TimeUnit.NANOSECONDS); + } + + if (dnsResolutionStartedNanos > 0L && dnsResolutionFinishedNanos > 0L) { + timings.setDns(dnsResolutionFinishedNanos - dnsResolutionStartedNanos, TimeUnit.NANOSECONDS); + } + + if (connectionStartedNanos > 0L && connectionSucceededTimeNanos > 0L) { + timings.setConnect(connectionSucceededTimeNanos - connectionStartedNanos, TimeUnit.NANOSECONDS); + + if (sslHandshakeStartedNanos > 0L) { + timings.setSsl(connectionSucceededTimeNanos - this.sslHandshakeStartedNanos, TimeUnit.NANOSECONDS); + } + } + + if (sendStartedNanos > 0L && sendFinishedNanos >= 0L) { + timings.setSend(sendFinishedNanos - sendStartedNanos, TimeUnit.NANOSECONDS); + } + + if (sendFinishedNanos > 0L && responseReceiveStartedNanos >= 0L) { + timings.setWait(responseReceiveStartedNanos - sendFinishedNanos, TimeUnit.NANOSECONDS); + } + + // since this method is for HTTP CONNECT failures only, we can't populate a "received" time, since that would + // require the CONNECT to be successful, in which case this method wouldn't be called. + } + + /** + * Creates a {@link HarEntry} for a failed CONNECT request. Initializes and populates the entry, including the + * {@link HarRequest}, {@link HarResponse}, and {@link HarTimings}. (Note: only successful timing information is + * populated in the timings object; the calling method must populate the timing information for the final, failed + * step. For example, if DNS resolution failed, this method will populate the network 'blocked' time, but not the DNS + * time.) Populates the specified errorMessage in the {@link HarResponse}'s error field. + * + * @param errorMessage error message to place in the har response + * @return a new HAR entry + */ + private HarEntry createHarEntryForFailedCONNECT(String errorMessage) { + HarEntry harEntry = new HarEntry(currentPageRef); + harEntry.setStartedDateTime(requestStartTime); + + HarRequest request = createRequestForFailedConnect(originalRequest); + harEntry.setRequest(request); + + HarResponse response = HarCaptureUtil.createHarResponseForFailure(); + harEntry.setResponse(response); + + response.setError(errorMessage); + + populateTimingsForFailedCONNECT(harEntry); + + populateServerIpAddress(harEntry); + + + return harEntry; + } + + private void populateServerIpAddress(HarEntry harEntry) { + // populate the server IP address if it was resolved as part of this request. otherwise, populate the IP address from the cache. + if (resolvedAddress != null) { + harEntry.setServerIPAddress(resolvedAddress.getHostAddress()); + } else { + String serverHost = HttpUtil.getHostFromRequest(modifiedHttpRequest); + if (serverHost != null && !serverHost.isEmpty()) { + String resolvedAddress = ResolvedHostnameCacheFilter.getPreviouslyResolvedAddressForHost(serverHost); + if (resolvedAddress != null) { + harEntry.setServerIPAddress(resolvedAddress); + } else { + // the resolvedAddress may be null if the ResolvedHostnameCacheFilter has expired the entry (which is unlikely), + // or in the far more common case that the proxy is using a chained proxy to connect to connect to the + // remote host. since the chained proxy handles IP address resolution, the IP address in the HAR must be blank. + log.trace("Unable to find cached IP address for host: {}. IP address in HAR entry will be blank.", serverHost); + } + } else { + log.warn("Unable to identify host from request uri: {}", modifiedHttpRequest.getUri()); + } + } + } + + /** + * Creates a new {@link HarRequest} object for this failed HTTP CONNECT. Does not populate fields within the request, + * such as the error message. + * + * @param httpConnectRequest the HTTP CONNECT request that failed + * @return a new HAR request object + */ + private HarRequest createRequestForFailedConnect(HttpRequest httpConnectRequest) { + String url = getFullUrl(httpConnectRequest); + + return new HarRequest(httpConnectRequest.getMethod().toString(), url, httpConnectRequest.getProtocolVersion().text()); + } + + /** + * Retrieves and removes (thus "consumes") the SSL timing information from the connection cache for the specified address. + * + * @param clientAddress the address of the client connection that established the HTTP tunnel + * @return the timing information for the tunnel previously established from the clientAddress + */ + public static HttpConnectTiming consumeConnectTimingForConnection(InetSocketAddress clientAddress) { + return httpConnectTimes.remove(clientAddress); + } + + @Override + public void setModifiedHttpRequest(HttpRequest modifiedHttpRequest) { + this.modifiedHttpRequest = modifiedHttpRequest; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java new file mode 100644 index 000000000..e727f2b00 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java @@ -0,0 +1,162 @@ +package net.lightbody.bmp.filters; + +import com.google.common.net.HostAndPort; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import net.lightbody.bmp.util.HttpUtil; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.littleshoot.proxy.impl.ProxyUtils; + +/** + * The HttpsAwareFiltersAdapter exposes the original host and the "real" host (after filter modifications) to filters for HTTPS + * requets. HTTPS requests do not normally contain the host in the URI, and the Host header may be missing or spoofed. + *

+ * Note: The {@link #getHttpsRequestHostAndPort()} and {@link #getHttpsOriginalRequestHostAndPort()} methods can only be + * called when the request is an HTTPS request. Otherwise they will throw an IllegalStateException. + */ +public class HttpsAwareFiltersAdapter extends HttpFiltersAdapter { + public static final String IS_HTTPS_ATTRIBUTE_NAME = "isHttps"; + public static final String HOST_ATTRIBUTE_NAME = "host"; + public static final String ORIGINAL_HOST_ATTRIBUTE_NAME = "originalHost"; + + public HttpsAwareFiltersAdapter(HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + } + + /** + * Returns true if this is an HTTPS request. + * + * @return true if https, false if http + */ + public boolean isHttps() { + Attribute isHttpsAttr = ctx.attr(AttributeKey.valueOf(IS_HTTPS_ATTRIBUTE_NAME)); + + Boolean isHttps = isHttpsAttr.get(); + if (isHttps == null) { + return false; + } else { + return isHttps; + } + } + + /** + * Returns the full, absolute URL of the specified request for both HTTP and HTTPS URLs. The request may reflect + * modifications from this or other filters. This filter instance must be currently handling the specified request; + * otherwise the results are undefined. + * + * @param modifiedRequest a possibly-modified version of the request currently being processed + * @return the full URL of the request, including scheme, host, port, path, and query parameters + */ + public String getFullUrl(HttpRequest modifiedRequest) { + // special case: for HTTPS requests, the full URL is scheme (https://) + the URI of this request + if (ProxyUtils.isCONNECT(modifiedRequest)) { + // CONNECT requests contain the default port, even if it isn't specified on the request. + String hostNoDefaultPort = BrowserMobHttpUtil.removeMatchingPort(modifiedRequest.getUri(), 443); + return "https://" + hostNoDefaultPort; + } + + // To get the full URL, we need to retrieve the Scheme, Host + Port, Path, and Query Params from the request. + // If the request URI starts with http:// or https://, it is already a full URL and can be returned directly. + if (HttpUtil.startsWithHttpOrHttps(modifiedRequest.getUri())) { + return modifiedRequest.getUri(); + } + + // The URI did not include the scheme and host, so examine the request to obtain them: + // Scheme: the scheme (HTTP/HTTPS) are based on the type of connection, obtained from isHttps() + // Host and Port: available for HTTP and HTTPS requests using the getHostAndPort() helper method. + // Path + Query Params: since the request URI doesn't start with the scheme, we can safely assume that the URI + // contains only the path and query params. + String hostAndPort = getHostAndPort(modifiedRequest); + String path = modifiedRequest.getUri(); + String url; + if (isHttps()) { + url = "https://" + hostAndPort + path; + } else { + url = "http://" + hostAndPort + path; + } + return url; + } + + /** + * Returns the full, absolute URL of the original request from the client for both HTTP and HTTPS URLs. The URL + * will not reflect modifications from this or other filters. + * + * @return the full URL of the original request, including scheme, host, port, path, and query parameters + */ + public String getOriginalUrl() { + return getFullUrl(originalRequest); + } + + /** + * Returns the hostname (but not the port) the specified request for both HTTP and HTTPS requests. The request may reflect + * modifications from this or other filters. This filter instance must be currently handling the specified request; + * otherwise the results are undefined. + * + * @param modifiedRequest a possibly-modified version of the request currently being processed + * @return hostname of the specified request, without the port + */ + public String getHost(HttpRequest modifiedRequest) { + String serverHost; + if (isHttps()) { + HostAndPort hostAndPort = HostAndPort.fromString(getHttpsRequestHostAndPort()); + serverHost = hostAndPort.getHost(); + } else { + serverHost = HttpUtil.getHostFromRequest(modifiedRequest); + } + return serverHost; + } + + /** + * Returns the host and port of the specified request for both HTTP and HTTPS requests. The request may reflect + * modifications from this or other filters. This filter instance must be currently handling the specified request; + * otherwise the results are undefined. + * + * @param modifiedRequest a possibly-modified version of the request currently being processed + * @return host and port of the specified request + */ + public String getHostAndPort(HttpRequest modifiedRequest) { + // For HTTP requests, the host and port can be read from the request itself using the URI and/or + // Host header. for HTTPS requests, the host and port are not available in the request. by using the + // getHttpsRequestHostAndPort() helper method, we can retrieve the host and port for HTTPS requests. + if (isHttps()) { + return getHttpsRequestHostAndPort(); + } else { + return HttpUtil.getHostAndPortFromRequest(modifiedRequest); + } + } + + /** + * Returns the host and port of this HTTPS request, including any modifications by other filters. + * + * @return host and port of this HTTPS request + * @throws IllegalStateException if this is not an HTTPS request + */ + private String getHttpsRequestHostAndPort() throws IllegalStateException { + if (!isHttps()) { + throw new IllegalStateException("Request is not HTTPS. Cannot get host and port on non-HTTPS request using this method."); + } + + Attribute hostnameAttr = ctx.attr(AttributeKey.valueOf(HOST_ATTRIBUTE_NAME)); + return hostnameAttr.get(); + } + + /** + * Returns the original host and port of this HTTPS request, as sent by the client. Does not reflect any modifications + * by other filters. + * TODO: evaluate this (unused) method and its capture mechanism in HttpsOriginalHostCaptureFilter; remove if not useful. + * + * @return host and port of this HTTPS request + * @throws IllegalStateException if this is not an HTTPS request + */ + private String getHttpsOriginalRequestHostAndPort() throws IllegalStateException { + if (!isHttps()) { + throw new IllegalStateException("Request is not HTTPS. Cannot get original host and port on non-HTTPS request using this method."); + } + + Attribute hostnameAttr = ctx.attr(AttributeKey.valueOf(ORIGINAL_HOST_ATTRIBUTE_NAME)); + return hostnameAttr.get(); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java new file mode 100644 index 000000000..f2a52a014 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java @@ -0,0 +1,43 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.littleshoot.proxy.impl.ProxyUtils; + +/** + * Captures the host for HTTPS requests and stores the value in the ChannelHandlerContext for use by {@link HttpsAwareFiltersAdapter} + * filters. This filter reads the host from the HttpRequest during the HTTP CONNECT call, and therefore MUST be invoked + * after any other filters which modify the host. + * Note: If the request uses the default HTTPS port (443), it will be removed from the hostname captured by this filter. + */ +public class HttpsHostCaptureFilter extends HttpFiltersAdapter { + public HttpsHostCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + if (ProxyUtils.isCONNECT(httpRequest)) { + Attribute hostname = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.HOST_ATTRIBUTE_NAME)); + String hostAndPort = httpRequest.getUri(); + + // CONNECT requests contain the port, even when using the default port. a sensible default is to remove the + // default port, since in most cases it is not explicitly specified and its presence (in a HAR file, for example) + // would be unexpected. + String hostNoDefaultPort = BrowserMobHttpUtil.removeMatchingPort(hostAndPort, 443); + hostname.set(hostNoDefaultPort); + } + } + + return null; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java new file mode 100644 index 000000000..4a6894c4d --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java @@ -0,0 +1,34 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.littleshoot.proxy.impl.ProxyUtils; + +/** + * Captures the original host for HTTPS requests and stores the value in the ChannelHandlerContext for use by {@link HttpsAwareFiltersAdapter} + * filters. This filter sets the isHttps attribute on the ChannelHandlerContext during the HTTP CONNECT and therefore MUST be invoked before + * any other filters calling any of the methods in {@link HttpsAwareFiltersAdapter}. + * This filter extends {@link HttpsHostCaptureFilter} and so also sets the host attribute on the channel for use by filters + * that modify the original host during the CONNECT. If the hostname is modified by filters, it will be overwritten when the {@link HttpsHostCaptureFilter} + * is processed later in the filter chain. + */ +public class HttpsOriginalHostCaptureFilter extends HttpsHostCaptureFilter { + public HttpsOriginalHostCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + + // if this is an HTTP CONNECT, set the isHttps attribute on the ChannelHandlerConect and capture the hostname from the original request. + // capturing the original host (and the remapped/modified host in clientToProxyRequest() below) guarantees that we will + // have the "true" host, rather than relying on the Host header in subsequent requests (which may be absent or spoofed by malicious clients). + if (ProxyUtils.isCONNECT(originalRequest)) { + Attribute originalHostAttr = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.ORIGINAL_HOST_ATTRIBUTE_NAME)); + String hostAndPort = originalRequest.getUri(); + originalHostAttr.set(hostAndPort); + + Attribute isHttpsAttr = ctx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME)); + isHttpsAttr.set(true); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/LatencyFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/LatencyFilter.java new file mode 100644 index 000000000..10f90dffc --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/LatencyFilter.java @@ -0,0 +1,43 @@ +package net.lightbody.bmp.filters; + +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.TimeUnit; + +/** + * Adds latency to a response before sending it to the client. This filter always adds the specified latency, even if the latency + * between the proxy and the remote server already exceeds this value. + */ +public class LatencyFilter extends HttpFiltersAdapter { + private static final Logger log = LoggerFactory.getLogger(HttpFiltersAdapter.class); + + private final int latencyMs; + + public LatencyFilter(HttpRequest originalRequest, int latencyMs) { + super(originalRequest); + + this.latencyMs = latencyMs; + } + + @Override + public HttpObject proxyToClientResponse(HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + if (latencyMs > 0) { + try { + TimeUnit.MILLISECONDS.sleep(latencyMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + log.warn("Interrupted while adding latency to response", e); + } + } + } + + return super.proxyToClientResponse(httpObject); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ModifiedRequestAwareFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ModifiedRequestAwareFilter.java new file mode 100644 index 000000000..c117c0b56 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ModifiedRequestAwareFilter.java @@ -0,0 +1,19 @@ +package net.lightbody.bmp.filters; + +import io.netty.handler.codec.http.HttpRequest; + +/** + * Indicates that a filter wishes to capture the final HttpRequest that is sent to the server, reflecting all + * modifications from request filters. {@link BrowserMobHttpFilterChain#clientToProxyRequest(io.netty.handler.codec.http.HttpObject)} + * will invoke the {@link #setModifiedHttpRequest(HttpRequest)} method after all filters have processed the initial + * {@link HttpRequest} object. + */ +public interface ModifiedRequestAwareFilter { + /** + * Notifies implementing classes of the modified HttpRequest that will be sent to the server, reflecting all + * modifications from filters. + * + * @param modifiedHttpRequest the modified HttpRequest sent to the server + */ + void setModifiedHttpRequest(HttpRequest modifiedHttpRequest); +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/RegisterRequestFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RegisterRequestFilter.java new file mode 100644 index 000000000..32dc1285b --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RegisterRequestFilter.java @@ -0,0 +1,30 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.proxy.ActivityMonitor; +import org.littleshoot.proxy.HttpFiltersAdapter; + +/** + * Registers this request with the {@link net.lightbody.bmp.proxy.ActivityMonitor} when the HttpRequest is received from the client. + */ +public class RegisterRequestFilter extends HttpFiltersAdapter { + private final ActivityMonitor activityMonitor; + + public RegisterRequestFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, ActivityMonitor activityMonitor) { + super(originalRequest, ctx); + + this.activityMonitor = activityMonitor; + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + activityMonitor.requestStarted(); + } + + return super.clientToProxyRequest(httpObject); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/RequestFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RequestFilter.java new file mode 100644 index 000000000..4718d2b92 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RequestFilter.java @@ -0,0 +1,24 @@ +package net.lightbody.bmp.filters; + +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; + +/** + * A functional interface to simplify modification and manipulation of requests. + */ +public interface RequestFilter { + /** + * Implement this method to filter an HTTP request. The HTTP method, URI, headers, etc. are available in the {@code request} parameter, + * while the contents of the message are available in the {@code contents} parameter. The request can be modified directly, while the + * contents may be modified using the {@link HttpMessageContents#setTextContents(String)} or {@link HttpMessageContents#setBinaryContents(byte[])} + * methods. The request can be "short-circuited" by returning a non-null value. + * + * @param request The request object, including method, URI, headers, etc. Modifications to the request object will be reflected in the request sent to the server. + * @param contents The request contents. + * @param messageInfo Additional information relating to the HTTP message. + * @return if the return value is non-null, the proxy will suppress the request and send the specified response to the client immediately + */ + HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo); +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/RequestFilterAdapter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RequestFilterAdapter.java new file mode 100644 index 000000000..2139ceb54 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RequestFilterAdapter.java @@ -0,0 +1,99 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersSourceAdapter; + +/** + * A filter adapter for {@link RequestFilter} implementations. Executes the filter when the {@link HttpFilters#clientToProxyRequest(HttpObject)} + * method is invoked. + */ +public class RequestFilterAdapter extends HttpsAwareFiltersAdapter { + private final RequestFilter requestFilter; + + public RequestFilterAdapter(HttpRequest originalRequest, ChannelHandlerContext ctx, RequestFilter requestFilter) { + super(originalRequest, ctx); + + this.requestFilter = requestFilter; + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + // only filter when the original HttpRequest comes through. the RequestFilterAdapter is not designed to filter + // any subsequent HttpContents. + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + HttpMessageContents contents; + if (httpObject instanceof FullHttpMessage) { + FullHttpMessage httpContent = (FullHttpMessage) httpObject; + contents = new HttpMessageContents(httpContent); + } else { + // the HTTP object is not a FullHttpMessage, which means that message contents are not available on this request and cannot be modified. + contents = null; + } + + HttpMessageInfo messageInfo = new HttpMessageInfo(originalRequest, ctx, isHttps(), getFullUrl(httpRequest), getOriginalUrl()); + + HttpResponse response = requestFilter.filterRequest(httpRequest, contents, messageInfo); + if (response != null) { + return response; + } + } + + return null; + } + + /** + * A {@link HttpFiltersSourceAdapter} for {@link RequestFilterAdapter}s. By default, this FilterSource enables HTTP message aggregation + * and sets a maximum request buffer size of 2 MiB. + */ + public static class FilterSource extends HttpFiltersSourceAdapter { + private static final int DEFAULT_MAXIMUM_REQUEST_BUFFER_SIZE = 2097152; + + private final RequestFilter filter; + private final int maximumRequestBufferSizeInBytes; + + /** + * Creates a new filter source that will invoke the specified filter and uses the {@link #DEFAULT_MAXIMUM_REQUEST_BUFFER_SIZE} as + * the maximum buffer size. + * + * @param filter RequestFilter to invoke + */ + public FilterSource(RequestFilter filter) { + this.filter = filter; + this.maximumRequestBufferSizeInBytes = DEFAULT_MAXIMUM_REQUEST_BUFFER_SIZE; + } + + /** + * Creates a new filter source that will invoke the specified filter and uses the maximumRequestBufferSizeInBytes as the maximum + * buffer size. Set maximumRequestBufferSizeInBytes to 0 to disable aggregation. If message aggregation is disabled, + * the {@link HttpMessageContents} will not be available for modification. (Note: HTTP message aggregation will + * be enabled if any filter has a maximum request or response buffer size greater than 0. See + * {@link org.littleshoot.proxy.HttpFiltersSource#getMaximumRequestBufferSizeInBytes()} for details.) + * + * @param filter RequestFilter to invoke + * @param maximumRequestBufferSizeInBytes maximum buffer size when aggregating Requests for filtering + */ + public FilterSource(RequestFilter filter, int maximumRequestBufferSizeInBytes) { + this.filter = filter; + this.maximumRequestBufferSizeInBytes = maximumRequestBufferSizeInBytes; + } + + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new RequestFilterAdapter(originalRequest, ctx, filter); + } + + @Override + public int getMaximumRequestBufferSizeInBytes() { + return maximumRequestBufferSizeInBytes; + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResolvedHostnameCacheFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResolvedHostnameCacheFilter.java new file mode 100644 index 000000000..0cc4dcaa5 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResolvedHostnameCacheFilter.java @@ -0,0 +1,73 @@ +package net.lightbody.bmp.filters; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.net.HostAndPort; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; +import org.littleshoot.proxy.HttpFiltersAdapter; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; + +/** + * Caches hostname resolutions reported by the {@link org.littleshoot.proxy.HttpFilters#proxyToServerResolutionSucceeded(String, InetSocketAddress)} + * filter method. Allows access to the resolved IP address on subsequent requests, when the address is not re-resolved because + * the connection has already been established. + */ +public class ResolvedHostnameCacheFilter extends HttpFiltersAdapter { + /** + * The maximum amount of time to save host name resolution information. This is done in order to populate the server IP address field in the + * har. Unfortunately there is not currently any way to determine the remote IP address of a keep-alive connection in a filter, so caching the + * resolved hostnames gives a generally-reasonable best guess. + */ + private static final int RESOLVED_ADDRESSES_EVICTION_SECONDS = 600; + + /** + * Concurrency of the resolvedAddresses map. Should be approximately equal to the maximum number of simultaneous connection + * attempts (but not necessarily simultaneous connections). A lower value will inhibit performance. + */ + private static final int RESOLVED_ADDRESSES_CONCURRENCY_LEVEL = 50; + + /** + * A {@code Map} that provides a reasonable estimate of the upstream server's IP address for keep-alive connections. + * The expiration time is renewed after each access, rather than after each write, so if the connection is consistently kept alive and used, + * the cached IP address will not be evicted. + */ + private static final Cache resolvedAddresses = + CacheBuilder.newBuilder() + .expireAfterAccess(RESOLVED_ADDRESSES_EVICTION_SECONDS, TimeUnit.SECONDS) + .concurrencyLevel(RESOLVED_ADDRESSES_CONCURRENCY_LEVEL) + .build(); + + public ResolvedHostnameCacheFilter(HttpRequest originalRequest, ChannelHandlerContext ctx) { + super(originalRequest, ctx); + } + + @Override + public void proxyToServerResolutionSucceeded(String serverHostAndPort, InetSocketAddress resolvedRemoteAddress) { + // the address *should* always be resolved at this point + InetAddress resolvedAddress = resolvedRemoteAddress.getAddress(); + + if (resolvedAddress != null) { + // place the resolved host into the hostname cache, so subsequent requests will be able to identify the IP address + HostAndPort parsedHostAndPort = HostAndPort.fromString(serverHostAndPort); + String host = parsedHostAndPort.getHost(); + + if (host != null && !host.isEmpty()) { + resolvedAddresses.put(host, resolvedAddress.getHostAddress()); + } + } + } + + /** + * Returns the (cached) address that was previously resolved for the specified host. + * + * @param host hostname that was previously resolved (without a port) + * @return the resolved IP address for the host, or null if the resolved address is not in the cache + */ + public static String getPreviouslyResolvedAddressForHost(String host) { + return resolvedAddresses.getIfPresent(host); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java new file mode 100644 index 000000000..14ed7d4c2 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java @@ -0,0 +1,22 @@ +package net.lightbody.bmp.filters; + +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; + +/** + * A functional interface to simplify modification and manipulation of responses. + */ +public interface ResponseFilter { + /** + * Implement this method to filter an HTTP response. The URI, headers, status line, etc. are available in the {@code response} parameter, + * while the contents of the message are available in the {@code contents} parameter. The response can be modified directly, while the + * contents may be modified using the {@link HttpMessageContents#setTextContents(String)} or {@link HttpMessageContents#setBinaryContents(byte[])} + * methods. + * + * @param response The response object, including URI, headers, status line, etc. Modifications to the response object will be reflected in the client response. + * @param contents The response contents. + * @param messageInfo Additional information relating to the HTTP message. + */ + void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo); +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java new file mode 100644 index 000000000..7edc34dd1 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java @@ -0,0 +1,106 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersSourceAdapter; + +/** + * A filter adapter for {@link ResponseFilter} implementations. Executes the filter when the {@link HttpFilters#serverToProxyResponse(HttpObject)} + * method is invoked. + */ +public class ResponseFilterAdapter extends HttpsAwareFiltersAdapter implements ModifiedRequestAwareFilter { + private final ResponseFilter responseFilter; + + /** + * The final HttpRequest sent to the server, reflecting all modifications from request filters. + */ + private HttpRequest modifiedHttpRequest; + + public ResponseFilterAdapter(HttpRequest originalRequest, ChannelHandlerContext ctx, ResponseFilter responseFilter) { + super(originalRequest, ctx); + + this.responseFilter = responseFilter; + } + + @Override + public HttpObject serverToProxyResponse(HttpObject httpObject) { + // only filter when the original HttpResponse comes through. the ResponseFilterAdapter is not designed to filter + // any subsequent HttpContents. + if (httpObject instanceof HttpResponse) { + HttpResponse httpResponse = (HttpResponse) httpObject; + + HttpMessageContents contents; + if (httpObject instanceof FullHttpMessage) { + FullHttpMessage httpContent = (FullHttpMessage) httpObject; + contents = new HttpMessageContents(httpContent); + } else { + // the HTTP object is not a FullHttpMessage, which means that message contents will not be available on this response and cannot be modified. + contents = null; + } + + HttpMessageInfo messageInfo = new HttpMessageInfo(originalRequest, ctx, isHttps(), getFullUrl(modifiedHttpRequest), getOriginalUrl()); + + responseFilter.filterResponse(httpResponse, contents, messageInfo); + } + + return super.serverToProxyResponse(httpObject); + } + + @Override + public void setModifiedHttpRequest(HttpRequest modifiedHttpRequest) { + this.modifiedHttpRequest = modifiedHttpRequest; + } + + /** + * A {@link HttpFiltersSourceAdapter} for {@link ResponseFilterAdapter}s. By default, this FilterSource enables HTTP message aggregation + * and sets a maximum response buffer size of 2 MiB. + */ + public static class FilterSource extends HttpFiltersSourceAdapter { + private static final int DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE = 2097152; + + private final ResponseFilter filter; + private final int maximumResponseBufferSizeInBytes; + + /** + * Creates a new filter source that will invoke the specified filter and uses the {@link #DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE} as + * the maximum buffer size. + * + * @param filter ResponseFilter to invoke + */ + public FilterSource(ResponseFilter filter) { + this.filter = filter; + this.maximumResponseBufferSizeInBytes = DEFAULT_MAXIMUM_RESPONSE_BUFFER_SIZE; + } + + /** + * Creates a new filter source that will invoke the specified filter and uses the maximumResponseBufferSizeInBytes as the maximum + * buffer size. Set maximumResponseBufferSizeInBytes to 0 to disable aggregation. If message aggregation is disabled, + * the {@link HttpMessageContents} will not be available for modification. (Note: HTTP message aggregation will + * be enabled if any filter has a maximum request or response buffer size greater than 0. See + * {@link org.littleshoot.proxy.HttpFiltersSource#getMaximumResponseBufferSizeInBytes()} for details.) + * + * @param filter ResponseFilter to invoke + * @param maximumResponseBufferSizeInBytes maximum buffer size when aggregating responses for filtering + */ + public FilterSource(ResponseFilter filter, int maximumResponseBufferSizeInBytes) { + this.filter = filter; + this.maximumResponseBufferSizeInBytes = maximumResponseBufferSizeInBytes; + } + + @Override + public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new ResponseFilterAdapter(originalRequest, ctx, filter); + } + + @Override + public int getMaximumResponseBufferSizeInBytes() { + return maximumResponseBufferSizeInBytes; + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java new file mode 100644 index 000000000..ef0e223c0 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java @@ -0,0 +1,130 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.util.HttpUtil; +import net.lightbody.bmp.proxy.RewriteRule; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.littleshoot.proxy.impl.ProxyUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URISyntaxException; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; + +/** + * Applies rewrite rules to the specified request. If a rewrite rule matches, the request's URI will be overwritten with the rewritten URI. + * The filter does not make a defensive copy of the rewrite rule collection, so there is no guarantee + * that the collection at the time of construction will contain the same values when the filter is actually invoked, if the collection is + * modified concurrently. + */ +public class RewriteUrlFilter extends HttpsAwareFiltersAdapter { + private static final Logger log = LoggerFactory.getLogger(RewriteUrlFilter.class); + + private final Collection rewriteRules; + + public RewriteUrlFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, Collection rewriterules) { + super(originalRequest, ctx); + + if (rewriterules != null) { + this.rewriteRules = rewriterules; + } else { + this.rewriteRules = Collections.emptyList(); + } + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + // if this is a CONNECT request, don't bother applying the rewrite rules, since CONNECT rewriting is not supported + if (ProxyUtils.isCONNECT(httpRequest)) { + return null; + } + + String originalUrl = getFullUrl(httpRequest); + String rewrittenUrl = originalUrl; + + boolean rewroteUri = false; + for (RewriteRule rule : rewriteRules) { + Matcher matcher = rule.getPattern().matcher(rewrittenUrl); + if (matcher.matches()) { + rewrittenUrl = matcher.replaceAll(rule.getReplace()); + rewroteUri = true; + } + } + + if (rewroteUri) { + // if the URI in the request contains the scheme, host, and port, the request's URI can be replaced + // with the rewritten URI. if not (for example, on HTTPS requests), strip the scheme, host, and port from + // the rewritten URL before replacing the URI on the request. + String uriFromRequest = httpRequest.getUri(); + if (HttpUtil.startsWithHttpOrHttps(uriFromRequest)) { + httpRequest.setUri(rewrittenUrl); + } else { + try { + String resource = BrowserMobHttpUtil.getRawPathAndParamsFromUri(rewrittenUrl); + httpRequest.setUri(resource); + } catch (URISyntaxException e) { + // the rewritten URL couldn't be parsed, possibly due to the rewrite rule mangling the URL. log + // a warning message and replace the resource on the request with the full, rewritten URL. + log.warn("Unable to determine path from rewritten URL. Request URL will be set to the full rewritten URL instead of the resource's path.\n\tOriginal URL: {}\n\tRewritten URL: {}", + originalUrl, + rewrittenUrl, + e); + + httpRequest.setUri(rewrittenUrl); + } + } + + // determine if the hostname and/or port has been changed by the rewrite rule. if so, update the Host + // header for HTTP requests. for HTTPS requests, log a warning, since hostname and port cannot be changed + // by rewrite rules. + + String originalHostAndPort = null; + try { + originalHostAndPort = HttpUtil.getHostAndPortFromUri(originalUrl); + } catch (URISyntaxException e) { + // for some reason we couldn't determine the original host and port from the original URL. log a warning, + // and allow the Host header to be forcibly updated to the rewritten host and port. + log.warn("Unable to determine host and port from original URL. Host header will be set to rewritten URL's host and port.\n\tOriginal URL: {}\n\tRewritten URL: {}", + originalUrl, + rewrittenUrl, + e); + } + + String modifiedHostAndPort = null; + try { + modifiedHostAndPort = HttpUtil.getHostAndPortFromUri(rewrittenUrl); + } catch (URISyntaxException e) { + log.warn("Unable to determine host and port from rewritten URL. Host header will not be updated.\n\tOriginal URL: {}\n\tRewritten URL: {}", + originalUrl, + rewrittenUrl, + e); + } + + // if the modifiedHostAndPort was parsed successfully and is different from the originalHostAndPort, update the Host header + if (modifiedHostAndPort != null && !modifiedHostAndPort.equals(originalHostAndPort)) { + if (isHttps()) { + // for HTTPS requests we cannot modify the host and port, since we are always reusing a persistent connection. + log.warn("Cannot rewrite the host or port of an HTTPS connection.\n\tHost and port from original request: {}\n\tRewritten host and port: {}", + originalHostAndPort, modifiedHostAndPort); + } else { + // only modify the Host header if it already exists + if (httpRequest.headers().contains(HttpHeaders.Names.HOST)) { + HttpHeaders.setHost(httpRequest, modifiedHostAndPort); + } + } + } + } + } + + return null; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java new file mode 100644 index 000000000..d69ad8f76 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java @@ -0,0 +1,215 @@ +package net.lightbody.bmp.filters; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * This filter captures responses from the server (headers and content). The filter can also decompress contents if desired. + *

+ * The filter can be used in one of three ways: (1) directly, by adding the filter to the filter chain; (2) by subclassing + * the filter and overriding its filter methods; or (3) by invoking the filter directly from within another filter (see + * {@link net.lightbody.bmp.filters.HarCaptureFilter} for an example of the latter). + */ +public class ServerResponseCaptureFilter extends HttpFiltersAdapter { + private static final Logger log = LoggerFactory.getLogger(ServerResponseCaptureFilter.class); + + /** + * Populated by serverToProxyResponse() when processing the HttpResponse object + */ + private volatile HttpResponse httpResponse; + + /** + * Populated by serverToProxyResponse() as it receives HttpContent responses. If the response is chunked, it will + * be populated across multiple calls to proxyToServerResponse(). + */ + private final ByteArrayOutputStream rawResponseContents = new ByteArrayOutputStream(); + + /** + * Populated when processing the LastHttpContent. If the response is compressed and decompression is requested, + * this contains the entire decompressed response. Otherwise it contains the raw response. + */ + private volatile byte[] fullResponseContents; + + /** + * Populated by serverToProxyResponse() when it processes the LastHttpContent object. + */ + private volatile HttpHeaders trailingHeaders; + + /** + * Set to true when processing the LastHttpContent if the server indicates there is a content encoding. + */ + private volatile boolean responseCompressed; + + /** + * Set to true when processing the LastHttpContent if decompression was requested and successful. + */ + private volatile boolean decompressionSuccessful; + + /** + * Populated when processing the LastHttpContent. + */ + private volatile String contentEncoding; + + /** + * User option indicating compressed content should be uncompressed. + */ + private final boolean decompressEncodedContent; + + public ServerResponseCaptureFilter(HttpRequest originalRequest, boolean decompressEncodedContent) { + super(originalRequest); + + this.decompressEncodedContent = decompressEncodedContent; + } + + public ServerResponseCaptureFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, boolean decompressEncodedContent) { + super(originalRequest, ctx); + + this.decompressEncodedContent = decompressEncodedContent; + } + + @Override + public HttpObject serverToProxyResponse(HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + httpResponse = (HttpResponse) httpObject; + captureContentEncoding(httpResponse); + } + + if (httpObject instanceof HttpContent) { + HttpContent httpContent = (HttpContent) httpObject; + + storeResponseContent(httpContent); + + if (httpContent instanceof LastHttpContent) { + LastHttpContent lastContent = (LastHttpContent) httpContent; + captureTrailingHeaders(lastContent); + + captureFullResponseContents(); + } + } + + return super.serverToProxyResponse(httpObject); + } + + protected void captureFullResponseContents() { + // start by setting fullResponseContent to the raw, (possibly) compressed byte stream. replace it + // with the decompressed bytes if decompression is successful. + fullResponseContents = getRawResponseContents(); + + // if the content is compressed, we need to decompress it. but don't use + // the netty HttpContentCompressor/Decompressor in the pipeline because we don't actually want it to + // change the message sent to the client + if (contentEncoding != null) { + responseCompressed = true; + + if (decompressEncodedContent) { + decompressContents(); + } else { + // will not decompress response + } + } else { + // no compression + responseCompressed = false; + } + } + + protected void decompressContents() { + if (contentEncoding.equals(HttpHeaders.Values.GZIP)) { + try { + fullResponseContents = BrowserMobHttpUtil.decompressContents(getRawResponseContents()); + decompressionSuccessful = true; + } catch (RuntimeException e) { + log.warn("Failed to decompress response with encoding type " + contentEncoding + " when decoding request from " + originalRequest.getUri(), e); + } + } else { + log.warn("Cannot decode unsupported content encoding type {}", contentEncoding); + } + } + + protected void captureContentEncoding(HttpResponse httpResponse) { + contentEncoding = HttpHeaders.getHeader(httpResponse, HttpHeaders.Names.CONTENT_ENCODING); + } + + protected void captureTrailingHeaders(LastHttpContent lastContent) { + trailingHeaders = lastContent.trailingHeaders(); + + // technically, the Content-Encoding header can be in a trailing header, although this is excruciatingly uncommon + if (trailingHeaders != null) { + String trailingContentEncoding = trailingHeaders.get(HttpHeaders.Names.CONTENT_ENCODING); + if (trailingContentEncoding != null) { + contentEncoding = trailingContentEncoding; + } + } + + } + + protected void storeResponseContent(HttpContent httpContent) { + ByteBuf bufferedContent = httpContent.content(); + byte[] content = BrowserMobHttpUtil.extractReadableBytes(bufferedContent); + + try { + rawResponseContents.write(content); + } catch (IOException e) { + // can't happen + } + } + + public HttpResponse getHttpResponse() { + return httpResponse; + } + + /** + * Returns the contents of the entire response. If the contents were compressed, decompressEncodedContent is true, and + * decompression was successful, this method returns the decompressed contents. + * + * @return entire response contents, decompressed if possible + */ + public byte[] getFullResponseContents() { + return fullResponseContents; + } + + /** + * Returns the raw contents of the entire response, without decompression. + * + * @return entire response contents, without decompression + */ + public byte[] getRawResponseContents() { + return rawResponseContents.toByteArray(); + } + + public HttpHeaders getTrailingHeaders() { + return trailingHeaders; + } + + public boolean isResponseCompressed() { + return responseCompressed; + } + + /** + * @return true if decompression is both enabled and successful + */ + public boolean isDecompressionSuccessful() { + if (!decompressEncodedContent) { + return false; + } + + return decompressionSuccessful; + } + + public String getContentEncoding() { + return contentEncoding; + } + +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/UnregisterRequestFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/UnregisterRequestFilter.java new file mode 100644 index 000000000..1ffff0f72 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/UnregisterRequestFilter.java @@ -0,0 +1,30 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.LastHttpContent; +import net.lightbody.bmp.proxy.ActivityMonitor; +import org.littleshoot.proxy.HttpFiltersAdapter; + +/** + * Unregisters this request with the {@link net.lightbody.bmp.proxy.ActivityMonitor} when the LastHttpContent is sent to the client. + */ +public class UnregisterRequestFilter extends HttpFiltersAdapter { + private final ActivityMonitor activityMonitor; + + public UnregisterRequestFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, ActivityMonitor activityMonitor) { + super(originalRequest, ctx); + + this.activityMonitor = activityMonitor; + } + + @Override + public HttpObject proxyToClientResponse(HttpObject httpObject) { + if (httpObject instanceof LastHttpContent) { + activityMonitor.requestFinished(); + } + + return super.proxyToClientResponse(httpObject); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java new file mode 100644 index 000000000..ad8fbf6f6 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java @@ -0,0 +1,75 @@ +package net.lightbody.bmp.filters; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.littleshoot.proxy.impl.ProxyUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Pattern; + +/** + * Checks this request against the whitelist, and returns the modified response if the request is not in the whitelist. The filter does not + * make a defensive copy of the whitelist URLs, so there is no guarantee that the whitelist URLs at the time of construction will contain the + * same values when the filter is actually invoked, if the URL collection is modified concurrently. + */ +public class WhitelistFilter extends HttpsAwareFiltersAdapter { + private final boolean whitelistEnabled; + private final int whitelistResponseCode; + private final Collection whitelistUrls; + + public WhitelistFilter(HttpRequest originalRequest, ChannelHandlerContext ctx, boolean whitelistEnabled,int whitelistResponseCode, + Collection whitelistUrls) { + super(originalRequest, ctx); + + this.whitelistEnabled = whitelistEnabled; + this.whitelistResponseCode = whitelistResponseCode; + if (whitelistUrls != null) { + this.whitelistUrls = whitelistUrls; + } else { + this.whitelistUrls = Collections.emptyList(); + } + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (!whitelistEnabled) { + return null; + } + + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + + // do not allow HTTP CONNECTs to be short-circuited + if (ProxyUtils.isCONNECT(httpRequest)) { + return null; + } + + boolean urlWhitelisted = false; + + String url = getFullUrl(httpRequest); + + for (Pattern pattern : whitelistUrls) { + if (pattern.matcher(url).matches()) { + urlWhitelisted = true; + break; + } + } + + if (!urlWhitelisted) { + HttpResponseStatus status = HttpResponseStatus.valueOf(whitelistResponseCode); + HttpResponse resp = new DefaultFullHttpResponse(httpRequest.getProtocolVersion(), status); + HttpHeaders.setContentLength(resp, 0L); + + return resp; + } + } + + return null; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/support/HttpConnectTiming.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/support/HttpConnectTiming.java new file mode 100644 index 000000000..c18070f7b --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/support/HttpConnectTiming.java @@ -0,0 +1,51 @@ +package net.lightbody.bmp.filters.support; + +/** + * Holds the connection-related timing information from an HTTP CONNECT request, so it can be added to the HAR timings for the first + * "real" request to the same host. The HTTP CONNECT and the "real" HTTP requests are processed in different HarCaptureFilter instances. + *

+ * Note: The connect time must include the ssl time. According to the HAR spec at https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.htm: +

+ ssl [number, optional] (new in 1.2) - Time required for SSL/TLS negotiation. If this field is defined then the time is also
+ included in the connect field (to ensure backward compatibility with HAR 1.1). Use -1 if the timing does not apply to the
+ current request.
+ 
+ */ +public class HttpConnectTiming { + private volatile long blockedTimeNanos = -1; + private volatile long dnsTimeNanos = -1; + private volatile long connectTimeNanos = -1; + private volatile long sslHandshakeTimeNanos = -1; + + public void setConnectTimeNanos(long connectTimeNanos) { + this.connectTimeNanos = connectTimeNanos; + } + + public void setSslHandshakeTimeNanos(long sslHandshakeTimeNanos) { + this.sslHandshakeTimeNanos = sslHandshakeTimeNanos; + } + + public void setBlockedTimeNanos(long blockedTimeNanos) { + this.blockedTimeNanos = blockedTimeNanos; + } + + public void setDnsTimeNanos(long dnsTimeNanos) { + this.dnsTimeNanos = dnsTimeNanos; + } + + public long getConnectTimeNanos() { + return connectTimeNanos; + } + + public long getSslHandshakeTimeNanos() { + return sslHandshakeTimeNanos; + } + + public long getBlockedTimeNanos() { + return blockedTimeNanos; + } + + public long getDnsTimeNanos() { + return dnsTimeNanos; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/util/HarCaptureUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/util/HarCaptureUtil.java new file mode 100644 index 000000000..1ac47f93f --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/util/HarCaptureUtil.java @@ -0,0 +1,96 @@ +package net.lightbody.bmp.filters.util; + +import net.lightbody.bmp.core.har.HarResponse; + +/** + * Static utility methods for {@link net.lightbody.bmp.filters.HarCaptureFilter} and {@link net.lightbody.bmp.filters.HttpConnectHarCaptureFilter}. + */ +public class HarCaptureUtil { + /** + * The HTTP version string in the {@link HarResponse} for failed requests. + */ + public static final String HTTP_VERSION_STRING_FOR_FAILURE = "unknown"; + + /** + * The HTTP status code in the {@link HarResponse} for failed requests. + */ + public static final int HTTP_STATUS_CODE_FOR_FAILURE = 0; + + /** + * The HTTP status text/reason phrase in the {@link HarResponse} for failed requests. + */ + public static final String HTTP_REASON_PHRASE_FOR_FAILURE = ""; + + /** + * The error message that will be populated in the _error field of the {@link HarResponse} due to a name + * lookup failure. + */ + private static final String RESOLUTION_FAILED_ERROR_MESSAGE = "Unable to resolve host: "; + + /** + * The error message that will be populated in the _error field of the {@link HarResponse} due to a + * connection failure. + */ + private static final String CONNECTION_FAILED_ERROR_MESSAGE = "Unable to connect to host"; + + /** + * The error message that will be populated in the _error field of the {@link HarResponse} when the proxy fails to + * receive a response in a timely manner. + */ + private static final String RESPONSE_TIMED_OUT_ERROR_MESSAGE = "Response timed out"; + + /** + * The error message that will be populated in the _error field of the {@link HarResponse} when no response is received + * from the server for any reason other than a server response timeout. + */ + private static final String NO_RESPONSE_RECEIVED_ERROR_MESSAGE = "No response received"; + + /** + * Creates a HarResponse object for failed requests. Normally the HarResponse is populated when the response is received + * from the server, but if the request fails due to a name resolution issue, connection problem, timeout, etc., no + * HarResponse would otherwise be created. + * + * @return a new HarResponse object with invalid HTTP status code (0) and version string ("unknown") + */ + public static HarResponse createHarResponseForFailure() { + return new HarResponse(HTTP_STATUS_CODE_FOR_FAILURE, HTTP_REASON_PHRASE_FOR_FAILURE, HTTP_VERSION_STRING_FOR_FAILURE); + } + + /** + * Returns the error message for the HAR response when DNS resolution fails. + * + * @param hostAndPort the host and port of the address lookup that failed + * @return the resolution failed error message + */ + public static String getResolutionFailedErrorMessage(String hostAndPort) { + return RESOLUTION_FAILED_ERROR_MESSAGE + hostAndPort; + } + + /** + * Returns the error message for the HAR response when the connection fails. + * + * @return the connection failed error message + */ + public static String getConnectionFailedErrorMessage() { + return CONNECTION_FAILED_ERROR_MESSAGE; + } + + /** + * Returns the error message for the HAR response when the response from the server times out. + * + * @return the response timed out error message + */ + public static String getResponseTimedOutErrorMessage() { + return RESPONSE_TIMED_OUT_ERROR_MESSAGE; + } + + /** + * Returns the error message for the HAR response when no response was received from the server (e.g. when the + * browser is closed). + * + * @return the no response received error message + */ + public static String getNoResponseReceivedErrorMessage() { + return NO_RESPONSE_RECEIVED_ERROR_MESSAGE; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/ActivityMonitor.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/ActivityMonitor.java new file mode 100644 index 000000000..7b6fba109 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/ActivityMonitor.java @@ -0,0 +1,124 @@ +package net.lightbody.bmp.proxy; + +import com.google.common.util.concurrent.Monitor; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Tracks active and total requests on a proxy, and provides a mechanism to wait for active requests to finish. + * See {@link net.lightbody.bmp.proxy.ActivityMonitor#waitForQuiescence(long, long, java.util.concurrent.TimeUnit)}. + */ +public class ActivityMonitor { + private final AtomicInteger activeRequests = new AtomicInteger(0); + private final AtomicInteger totalRequests = new AtomicInteger(0); + + private final AtomicLong lastRequestFinishedNanos = new AtomicLong(System.nanoTime()); + + private final Monitor monitor = new Monitor(); + + private final Monitor.Guard requestNotActive = new Monitor.Guard(monitor) { + @Override + public boolean isSatisfied() { + return activeRequests.get() == 0; + } + }; + + private final Monitor.Guard requestActive = new Monitor.Guard(monitor) { + @Override + public boolean isSatisfied() { + return activeRequests.get() > 0; + } + }; + + public void requestStarted() { + int previousCount = activeRequests.getAndIncrement(); + totalRequests.incrementAndGet(); + if (previousCount == 0) { + // previously there were no active requests, but now there are -- signal to any waitForQuiescence threads that they need to + // begin waiting again + monitor.enter(); + monitor.leave(); + } + } + + public void requestFinished() { + int newCount = activeRequests.decrementAndGet(); + lastRequestFinishedNanos.set(System.nanoTime()); + + if (newCount == 0) { + // there are no active requests, so signal to any waitForQuiescence threads that they can begin waiting for their quietPeriod + monitor.enter(); + monitor.leave(); + } + } + + public int getActiveRequests() { + return activeRequests.get(); + } + + public int getTotalRequests() { + return totalRequests.get(); + } + + public boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit) { + // the minRequestFinishTime is the earliest possible time the current or last request "could" finish. if there is no active + // request, this is simply the lastRequestFinishedNanos time. if there is an active request, it is "now". this helps avoid waiting + // for quiescence if there is an active request and the timeout is less than the quietPeriod. + long minRequestFinishTime; + if (activeRequests.get() == 0) { + if (timeUnit.convert(System.nanoTime() - lastRequestFinishedNanos.get(), TimeUnit.NANOSECONDS) >= quietPeriod) { + return true; + } else { + minRequestFinishTime = lastRequestFinishedNanos.get(); + } + } else { + minRequestFinishTime = System.nanoTime(); + } + + // record the maximum time we can wait until (the current time + the timeout), which will allow us to avoid waiting for + // quiescence if it is not possible to satisfy the quietPeriod before the waitUntil time elapses + long waitUntil = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeout, timeUnit); + + while (minRequestFinishTime + TimeUnit.NANOSECONDS.convert(quietPeriod, timeUnit) <= waitUntil) { + // the maximum amount of time we can wait for active requests to finish that will still allow us to wait for quiescence + // for the quietPeriod. + long maxWaitTimeForActiveRequests = waitUntil - System.nanoTime() - TimeUnit.NANOSECONDS.convert(quietPeriod, timeUnit); + + // wait for active requests to finish + boolean success = monitor.enterWhenUninterruptibly(requestNotActive, maxWaitTimeForActiveRequests, TimeUnit.NANOSECONDS); + + if (!success) { + // timed out waiting for active requests to finish + return false; + } + + monitor.leave(); + + // the time needed to monitor for new active requests is whenever the last request finished + the quiet period. this may be less + // than the actual quiet period if no requests were active when entering waitForQuiescence, but the quietPeriod has not yet elapsed + // since the last request. + long waitForNewRequests = lastRequestFinishedNanos.get() - System.nanoTime() + TimeUnit.NANOSECONDS.convert(quietPeriod, timeUnit); + + // if the quietPeriod has already elapsed since the last request, no need to wait any longer + if (waitForNewRequests < 0) { + return true; + } + + // wait for new requests to come in. if a new request comes in, the loop will restart, waiting for active requests to complete. + boolean requestsActive = monitor.enterWhenUninterruptibly(requestActive, waitForNewRequests, TimeUnit.NANOSECONDS); + + if (requestsActive) { + // a request became active, so we need to wait for all requests to finish again + monitor.leave(); + + continue; + } else { + return true; + } + } + + return false; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/BlacklistEntry.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/BlacklistEntry.java new file mode 100644 index 000000000..671fff9c7 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/BlacklistEntry.java @@ -0,0 +1,92 @@ +package net.lightbody.bmp.proxy; + +import java.util.regex.Pattern; + +/** + * An entry in the Blacklist, consisting of a regular expression to match the URL, an HTTP status code, and a regular expression + * to match the HTTP method. + */ +public class BlacklistEntry { + private final Pattern urlPattern; + private final int statusCode; + private final Pattern httpMethodPattern; + + /** + * Creates a new BlacklistEntry with no HTTP method matching (i.e. all methods will match). + * + * @param urlPattern URL pattern to blacklist + * @param statusCode HTTP status code to return for blacklisted URL + */ + public BlacklistEntry(String urlPattern, int statusCode) { + this(urlPattern, statusCode, null); + } + + /** + * Creates a new BlacklistEntry which will match both a URL and an HTTP method + * + * @param urlPattern URL pattern to blacklist + * @param statusCode status code to return for blacklisted URL + * @param httpMethodPattern HTTP method to match (e.g. GET, PUT, PATCH, etc.) + */ + public BlacklistEntry(String urlPattern, int statusCode, String httpMethodPattern) { + this.urlPattern = Pattern.compile(urlPattern); + this.statusCode = statusCode; + if (httpMethodPattern == null || httpMethodPattern.isEmpty()) { + this.httpMethodPattern = null; + } else { + this.httpMethodPattern = Pattern.compile(httpMethodPattern); + } + } + + /** + * Determines if this BlacklistEntry matches the given URL. Attempts to match both the URL and the + * HTTP method. + * + * @param url possibly-blacklisted URL + * @param httpMethod HTTP method this URL is being accessed with + * @return true if the URL matches this BlacklistEntry + */ + public boolean matches(String url, String httpMethod) { + if (httpMethodPattern != null) { + return urlPattern.matcher(url).matches() && httpMethodPattern.matcher(httpMethod).matches(); + } else { + return urlPattern.matcher(url).matches(); + } + } + + public Pattern getUrlPattern() { + return urlPattern; + } + + public int getStatusCode() { + return statusCode; + } + + public Pattern getHttpMethodPattern() { + return httpMethodPattern; + } + + @Deprecated + /** + * @deprecated use {@link #getUrlPattern()} + */ + public Pattern getPattern() { + return getUrlPattern(); + } + + @Deprecated + /** + * @deprecated use {@link #getStatusCode()} + */ + public int getResponseCode() { + return getStatusCode(); + } + + @Deprecated + /** + * @deprecated use {@link #getHttpMethodPattern()} + */ + public Pattern getMethod() { + return getHttpMethodPattern(); + } +} \ No newline at end of file diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/CaptureType.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/CaptureType.java new file mode 100644 index 000000000..cf3b98ce6 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/CaptureType.java @@ -0,0 +1,113 @@ +package net.lightbody.bmp.proxy; + +import java.util.EnumSet; + +/** + * Data types that the proxy can capture. Data types are organized into two broad categories, REQUEST_* and + * RESPONSE_*, corresponding to client requests and server responses. + */ +public enum CaptureType { + /** + * HTTP request headers, including trailing headers. + */ + REQUEST_HEADERS, + + /** + * HTTP Cookies sent with the request. + */ + REQUEST_COOKIES, + + /** + * Non-binary HTTP request content, such as post data or other text-based request payload. + * See {@link net.lightbody.bmp.util.BrowserMobHttpUtil#hasTextualContent(String)} for a list of Content-Types that + * are considered non-binary. + * + */ + REQUEST_CONTENT, + + /** + * Binary HTTP request content, such as file uploads, or any unrecognized request payload. + */ + REQUEST_BINARY_CONTENT, + + /** + * HTTP response headers, including trailing headers. + */ + RESPONSE_HEADERS, + + /** + * Set-Cookie headers sent with the response. + */ + RESPONSE_COOKIES, + + /** + * Non-binary HTTP response content (typically, HTTP body content). + * See {@link net.lightbody.bmp.util.BrowserMobHttpUtil#hasTextualContent(String)} for a list of Content-Types that + * are considered non-binary. + */ + RESPONSE_CONTENT, + + /** + * Binary HTTP response content, such as image files, or any unrecognized response payload. + */ + RESPONSE_BINARY_CONTENT; + + // the following groups of capture types are private so that clients do not accidentally modify these sets (EnumSets are not immutable) + private static final EnumSet REQUEST_CAPTURE_TYPES = EnumSet.of(REQUEST_HEADERS, REQUEST_CONTENT, REQUEST_BINARY_CONTENT, REQUEST_COOKIES); + private static final EnumSet RESPONSE_CAPTURE_TYPES = EnumSet.of(RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_BINARY_CONTENT, RESPONSE_COOKIES); + private static final EnumSet HEADER_CAPTURE_TYPES = EnumSet.of(REQUEST_HEADERS, RESPONSE_HEADERS); + private static final EnumSet NON_BINARY_CONTENT_CAPTURE_TYPES = EnumSet.of(REQUEST_CONTENT, RESPONSE_CONTENT); + private static final EnumSet BINARY_CONTENT_CAPTURE_TYPES = EnumSet.of(REQUEST_BINARY_CONTENT, RESPONSE_BINARY_CONTENT); + private static final EnumSet ALL_CONTENT_CAPTURE_TYPES = EnumSet.of(REQUEST_CONTENT, RESPONSE_CONTENT, REQUEST_BINARY_CONTENT, RESPONSE_BINARY_CONTENT); + private static final EnumSet COOKIE_CAPTURE_TYPES = EnumSet.of(REQUEST_COOKIES, RESPONSE_COOKIES); + + /** + * @return Set of CaptureTypes for requests. + */ + public static EnumSet getRequestCaptureTypes() { + return EnumSet.copyOf(REQUEST_CAPTURE_TYPES); + } + + /** + * @return Set of CaptureTypes for responses. + */ + public static EnumSet getResponseCaptureTypes() { + return EnumSet.copyOf(RESPONSE_CAPTURE_TYPES); + } + + /** + * @return Set of CaptureTypes for headers. + */ + public static EnumSet getHeaderCaptureTypes() { + return EnumSet.copyOf(HEADER_CAPTURE_TYPES); + } + + /** + * @return Set of CaptureTypes for non-binary content. + */ + public static EnumSet getNonBinaryContentCaptureTypes() { + return EnumSet.copyOf(NON_BINARY_CONTENT_CAPTURE_TYPES); + } + + /** + * @return Set of CaptureTypes for binary content. + */ + public static EnumSet getBinaryContentCaptureTypes() { + return EnumSet.copyOf(BINARY_CONTENT_CAPTURE_TYPES); + } + + /** + * @return Set of CaptureTypes for both binary and non-binary content. + */ + public static EnumSet getAllContentCaptureTypes() { + return EnumSet.copyOf(ALL_CONTENT_CAPTURE_TYPES); + } + + /** + * @return Set of CaptureTypes for cookies. + */ + public static EnumSet getCookieCaptureTypes() { + return EnumSet.copyOf(COOKIE_CAPTURE_TYPES); + } + +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/RewriteRule.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/RewriteRule.java new file mode 100644 index 000000000..c5ec97e3f --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/RewriteRule.java @@ -0,0 +1,44 @@ +package net.lightbody.bmp.proxy; + +import java.util.regex.Pattern; + +/** + * Container for a URL rewrite rule pattern and replacement string. + */ +public class RewriteRule { + private final Pattern pattern; + private final String replace; + + public RewriteRule(String pattern, String replace) { + this.pattern = Pattern.compile(pattern); + this.replace = replace; + } + + public Pattern getPattern() { + return pattern; + } + + public String getReplace() { + return replace; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RewriteRule that = (RewriteRule) o; + + if (!pattern.equals(that.pattern)) return false; + if (!replace.equals(that.replace)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = pattern.hashCode(); + result = 31 * result + replace.hashCode(); + return result; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/Whitelist.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/Whitelist.java new file mode 100644 index 000000000..ea015ee27 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/Whitelist.java @@ -0,0 +1,129 @@ +package net.lightbody.bmp.proxy; + +import com.google.common.collect.ImmutableList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A URL whitelist. This object is immutable and the list of matching patterns and the HTTP status code is unmodifiable + * after creation. Enabling, disabling, or modifying the whitelist can be safely and easily accomplished by updating the + * whitelist reference to a new whitelist. + */ +public class Whitelist { + private final List patterns; + private final int statusCode; + private final boolean enabled; + + /** + * A disabled Whitelist. + */ + public static final Whitelist WHITELIST_DISABLED = new Whitelist(); + + /** + * Creates an empty, disabled Whitelist. + */ + public Whitelist() { + this.patterns = Collections.emptyList(); + this.statusCode = -1; + this.enabled = false; + } + + /** + * Creates an empty, enabled whitelist with the specified response code. + * + * @param statusCode the response code that the (enabled) Whitelist will return for all URLs. + */ + public Whitelist(int statusCode) { + this.patterns = Collections.emptyList(); + this.statusCode = statusCode; + this.enabled = true; + } + + /** + * @deprecated use {@link #Whitelist(java.util.Collection, int)} + */ + @Deprecated + public Whitelist(String[] patterns, int statusCode) { + this(patterns == null ? null : Arrays.asList(patterns), statusCode); + } + + /** + * Creates a whitelist for the specified patterns, returning the given statusCode when a URL does not match one of the patterns. + * A null or empty collection will result in an empty whitelist. + * + * @param patterns URL-matching regular expression patterns to whitelist + * @param statusCode the HTTP status code to return when a request URL matches a whitelist pattern + */ + public Whitelist(Collection patterns, int statusCode) { + if (patterns == null || patterns.isEmpty()) { + this.patterns = Collections.emptyList(); + } else { + ImmutableList.Builder builder = ImmutableList.builder(); + for (String pattern : patterns) { + builder.add(Pattern.compile(pattern)); + } + + this.patterns = builder.build(); + } + + this.statusCode = statusCode; + + this.enabled = true; + } + + /** + * @return true if this whitelist is enabled, otherwise false + */ + public boolean isEnabled() { + return enabled; + } + + /** + * @return regular expression patterns describing the URLs that should be whitelisted, or an empty collection if the whitelist is disabled + */ + public Collection getPatterns() { + return this.patterns; + } + + /** + * @return HTTP status code returned by the whitelist, or -1 if the whitelist is disabled + */ + public int getStatusCode() { + return statusCode; + } + + /** + * @deprecated use {@link #getStatusCode()} + */ + @Deprecated + public int getResponseCode() { + return getStatusCode(); + } + + /** + * Returns true if the specified URL matches a whitelisted URL regular expression. If the whitelist is disabled, this + * method always returns false. + * + * @param url URL to match against the whitelist + * @return true if the whitelist is enabled and the URL matched an entry in the whitelist, otherwise false + */ + public boolean matches(String url) { + if (!enabled) { + return false; + } + + for (Pattern pattern : getPatterns()) { + Matcher matcher = pattern.matcher(url); + if (matcher.matches()) { + return true; + } + } + + return false; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/auth/AuthType.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/auth/AuthType.java new file mode 100644 index 000000000..b5cf93738 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/auth/AuthType.java @@ -0,0 +1,10 @@ +package net.lightbody.bmp.proxy.auth; + +/** + * Authentication types support by BrowserMobProxy. + */ +public enum AuthType { + BASIC, + // TODO: determine if we can actually do NTLM authentication + NTLM +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/AbstractHostNameRemapper.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/AbstractHostNameRemapper.java new file mode 100644 index 000000000..253b5f45d --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/AbstractHostNameRemapper.java @@ -0,0 +1,135 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Base class that provides host name remapping capabilities for AdvancedHostResolvers. Subclasses must implement {@link #resolveRemapped(String)} + * instead of {@link net.lightbody.bmp.proxy.dns.HostResolver#resolve(String)}, which takes the remapped host as the input parameter. + */ +public abstract class AbstractHostNameRemapper implements AdvancedHostResolver { + /** + * Host name remappings, maintained as a reference to an ImmutableMap. The ImmutableMap type is specified explicitly because ImmutableMap + * guarantees the iteration order of the map's entries. Specifying ImmutableMap also makes clear that the underlying map will never change, + * and that any modifications to the host name remappings will result in an entirely new map. + * + * The current implementation does not actually use any of the special features of AtomicReference, but it does rely on synchronizing on + * the AtomicReference when performing write operations. It could be replaced by a volatile reference to a Map and separate lock object. + */ + private final AtomicReference> remappedHostNames = new AtomicReference<>(ImmutableMap.of()); + + @Override + public void remapHosts(Map hostRemappings) { + synchronized (remappedHostNames) { + ImmutableMap newRemappings = ImmutableMap.copyOf(hostRemappings); + + remappedHostNames.set(newRemappings); + } + } + + @Override + public void remapHost(String originalHost, String remappedHost) { + synchronized (remappedHostNames) { + Map currentHostRemappings = remappedHostNames.get(); + + // use a LinkedHashMap to build the new remapping, to avoid duplicate key issues if the originalHost is already in the map + Map builderMap = Maps.newLinkedHashMap(currentHostRemappings); + builderMap.remove(originalHost); + builderMap.put(originalHost, remappedHost); + + ImmutableMap newRemappings = ImmutableMap.copyOf(builderMap); + + remappedHostNames.set(newRemappings); + } + } + + @Override + public void removeHostRemapping(String originalHost) { + synchronized (remappedHostNames) { + Map currentHostRemappings = remappedHostNames.get(); + if (currentHostRemappings.containsKey(originalHost)) { + // use a LinkedHashMap to build the new remapping, to take advantage of the remove() method + Map builderMap = Maps.newLinkedHashMap(currentHostRemappings); + builderMap.remove(originalHost); + + ImmutableMap newRemappings = ImmutableMap.copyOf(builderMap); + + remappedHostNames.set(newRemappings); + } + } + } + + @Override + public void clearHostRemappings() { + synchronized (remappedHostNames) { + remappedHostNames.set(ImmutableMap.of()); + } + } + + @Override + public Map getHostRemappings() { + return remappedHostNames.get(); + } + + @Override + public Collection getOriginalHostnames(String remappedHost) { + //TODO: implement this using a reverse mapping multimap that is guarded by the same lock as remappedHostNames, since this method will likely be called + // very often when forging certificates + List originalHostnames = new ArrayList<>(); + + Map currentRemappings = remappedHostNames.get(); + for (Map.Entry entry : currentRemappings.entrySet()) { + if (entry.getValue().equals(remappedHost)) { + originalHostnames.add(entry.getKey()); + } + } + + return originalHostnames; + } + + /** + * Applies this class's host name remappings to the specified original host, returning the remapped host name (if any), or the originalHost + * if there is no remapped host name. + * + * @param originalHost original host name to resolve + * @return a remapped host, or the original host if no mapping exists + */ + public String applyRemapping(String originalHost) { + String remappedHost = remappedHostNames.get().get(originalHost); + + if (remappedHost != null) { + return remappedHost; + } else { + return originalHost; + } + } + + /** + * Resolves the specified remapped host. Subclasses should provide resolution by implementing this method, rather than overriding + * {@link net.lightbody.bmp.proxy.dns.HostResolver#resolve(String)}. + * + * @param remappedHost remapped hostname to resolve + * @return resolved InetAddresses, or an empty list if no addresses were found + */ + public abstract Collection resolveRemapped(String remappedHost); + + /** + * Retrieves the remapped hostname and resolves it using {@link #resolveRemapped(String)}. + * + * @param originalHost original hostname to resolve + * @return InetAddresses resolved from the remapped hostname, or an empty list if no addresses were found + */ + @Override + public Collection resolve(String originalHost) { + String remappedHost = applyRemapping(originalHost); + + return resolveRemapped(remappedHost); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolver.java new file mode 100644 index 000000000..5773e36c3 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolver.java @@ -0,0 +1,90 @@ +package net.lightbody.bmp.proxy.dns; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * This interface defines the "core" DNS-manipulation functionality that BrowserMob Proxy supports, in addition to the basic name resolution + * capability defined in {@link net.lightbody.bmp.proxy.dns.HostResolver}. AdvancedHostResolvers should apply any remappings before attempting + * to resolve the hostname in the {@link HostResolver#resolve(String)} method. + */ +public interface AdvancedHostResolver extends HostResolver { + /** + * Replaces the host remappings in the existing list of remappings (if any) with the specified remappings. The remappings will be + * applied in the order specified by the Map's iterator. + *

+ * Note: The original hostnames must exactly match the requested hostname. It is not a domain or regular expression match. + * + * @param hostRemappings Map of {@code } + */ + void remapHosts(Map hostRemappings); + + /** + * Remaps an individual host. If there are any existing remappings, the new remapping will be applied last, after all existing + * remappings are applied. If there is already a remapping for the specified originalHost, it will be removed before + * the new remapping is added to the end of the host remapping list (and will therefore be the last remapping applied). + * + * @param originalHost Original host to remap. Must exactly match the requested hostname (not a domain or regular expression match). + * @param remappedHost hostname that will replace originalHost + */ + void remapHost(String originalHost, String remappedHost); + + /** + * Removes the specified host remapping. If the remapping does not exist, this method has no effect. + * + * @param originalHost currently-remapped hostname + */ + void removeHostRemapping(String originalHost); + + /** + * Removes all hostname remappings. + */ + void clearHostRemappings(); + + /** + * Returns all host remappings in effect. Iterating over the returned Map is guaranteed to return remappings in the order in which the + * remappings are actually applied. + * + * @return Map of {@code } + */ + Map getHostRemappings(); + + /** + * Returns the original address or addresses that are remapped to the specified remappedHost. Iterating over the returned Collection is + * guaranteed to return original mappings in the order in which the remappings are applied. + * + * @param remappedHost remapped hostname + * @return original hostnames that are remapped to the specified remappedHost, or an empty Collection if no remapping is defined to the remappedHost + */ + Collection getOriginalHostnames(String remappedHost); + + /** + * Clears both the positive (successful DNS lookups) and negative (failed DNS lookups) cache. + */ + void clearDNSCache(); + + /** + * Sets the positive (successful DNS lookup) timeout when making DNS lookups. + *

+ * Note: The timeUnit parameter does not guarantee the specified precision; implementations may need to reduce precision, depending on the underlying + * DNS implementation. For example, the Oracle JVM's DNS cache only supports timeouts in whole seconds, so specifying a timeout of 1200ms will result + * in a timeout of 1 second. + * + * @param timeout maximum lookup time + * @param timeUnit units of the timeout value + */ + void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit); + + /** + * Sets the negative (failed DNS lookup) timeout when making DNS lookups. + *

+ * Note: The timeUnit parameter does not guarantee the specified precision; implementations may need to reduce precision, depending on the underlying + * DNS implementation. For example, the Oracle JVM's DNS cache only supports timeouts in whole seconds, so specifying a timeout of 1200ms will result + * in a timeout of 1 second. + * + * @param timeout maximum lookup time + * @param timeUnit units of the timeout value + */ + void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit); +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/BasicHostResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/BasicHostResolver.java new file mode 100644 index 000000000..b07bd6d7a --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/BasicHostResolver.java @@ -0,0 +1,57 @@ +package net.lightbody.bmp.proxy.dns; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * An {@link AdvancedHostResolver} that throws UnsupportedOperationException on all methods except {@link HostResolver#resolve(String)}. + * Use this class to supply a {@link HostResolver} to {@link net.lightbody.bmp.BrowserMobProxy#setHostNameResolver(AdvancedHostResolver)} + * if you do not need {@link AdvancedHostResolver} functionality. + */ +public abstract class BasicHostResolver implements AdvancedHostResolver { + @Override + public void remapHosts(Map hostRemappings) { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public void remapHost(String originalHost, String remappedHost) { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public void removeHostRemapping(String originalHost) { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public void clearHostRemappings() { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public Map getHostRemappings() { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public Collection getOriginalHostnames(String remappedHost) { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public void clearDNSCache() { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } + + @Override + public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + throw new UnsupportedOperationException(new Throwable().getStackTrace()[0].getMethodName() + " is not supported by this host resolver (" + this.getClass().getName() + ")"); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/ChainedHostResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/ChainedHostResolver.java new file mode 100644 index 000000000..dd5ae002d --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/ChainedHostResolver.java @@ -0,0 +1,198 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.collect.ImmutableList; + +import java.net.InetAddress; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * An {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} that applies the AdvancedHostResolver methods to multiple implementations. Methods + * are applied to the resolvers in the order specified when the ChainedHostResolver is constructed. AdvancedHostResolver methods that modify the + * resolver are guaranteed to complete atomically over all resolvers. For example, if one thread makes a call to + * {@link #resolve(String)} while another thread is remapping hosts using + * {@link #remapHost(String, String)}, the call to {@link #resolve(String)} is guaranteed to + * apply the newly-remapped hosts to all resolvers managed by this ChainedHostResolver, or to no resolvers, but the call to + * {@link #resolve(String)} will never result in the host name remappings applied only to "some" of the chained resolvers. + *

+ * For getter methods (all read-only methods except {@link #resolve(String)}), the ChainedHostResolver returns results from the first chained resolver. + *

+ * The atomic write methods specified by AdvancedHostResolver are: + *

    + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#remapHost(String, String)}
  • + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#remapHosts(java.util.Map)}
  • + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#removeHostRemapping(String)}
  • + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#clearHostRemappings()}
  • + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#setNegativeDNSCacheTimeout(int, java.util.concurrent.TimeUnit)}
  • + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#setPositiveDNSCacheTimeout(int, java.util.concurrent.TimeUnit)}
  • + *
  • {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#clearDNSCache()}
  • + *
+ */ +public class ChainedHostResolver implements AdvancedHostResolver { + private final List resolvers; + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final Lock readLock = readWriteLock.readLock(); + private final Lock writeLock = readWriteLock.writeLock(); + + /** + * Creates a ChainedHostResolver that applies {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} methods to the specified resolvers + * in the order specified by the collection's iterator. + * + * @param resolvers resolvers to invoke, in the order specified by the collection's iterator + */ + public ChainedHostResolver(Collection resolvers) { + if (resolvers == null) { + this.resolvers = Collections.emptyList(); + } else { + this.resolvers = ImmutableList.copyOf(resolvers); + } + } + + /** + * Returns the resolvers used by this ChainedHostResolver. The iterator of the collection is guaranteed to return the resolvers in the order + * in which they are queried. + * + * @return resolvers used by this ChainedHostResolver + */ + public Collection getResolvers() { + return ImmutableList.copyOf(resolvers); + } + + @Override + public void remapHosts(Map hostRemappings) { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.remapHosts(hostRemappings); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public void remapHost(String originalHost, String remappedHost) { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.remapHost(originalHost, remappedHost); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public void removeHostRemapping(String originalHost) { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.removeHostRemapping(originalHost); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public void clearHostRemappings() { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.clearHostRemappings(); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public Map getHostRemappings() { + readLock.lock(); + try { + if (resolvers.isEmpty()) { + return Collections.emptyMap(); + } else { + return resolvers.get(0).getHostRemappings(); + } + } finally { + readLock.unlock(); + } + } + + @Override + public Collection getOriginalHostnames(String remappedHost) { + readLock.lock(); + try { + if (resolvers.isEmpty()) { + return Collections.emptyList(); + } else { + return resolvers.get(0).getOriginalHostnames(remappedHost); + } + } finally { + readLock.unlock(); + } + } + + @Override + public void clearDNSCache() { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.clearDNSCache(); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.setPositiveDNSCacheTimeout(timeout, timeUnit); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + writeLock.lock(); + try { + for (AdvancedHostResolver resolver : resolvers) { + resolver.setNegativeDNSCacheTimeout(timeout, timeUnit); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public Collection resolve(String host) { + readLock.lock(); + try { + // attempt to resolve the host using all resolvers. returns the results from the first successful resolution. + for (AdvancedHostResolver resolver : resolvers) { + Collection results = resolver.resolve(host); + if (!results.isEmpty()) { + return results; + } + } + + // no resolvers returned results + return Collections.emptyList(); + } finally { + readLock.unlock(); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DelegatingHostResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DelegatingHostResolver.java new file mode 100644 index 000000000..7f6bdf269 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DelegatingHostResolver.java @@ -0,0 +1,45 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.collect.Iterables; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Collection; + +/** + * A LittleProxy HostResolver that delegates to the specified {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} instance. This class + * serves as a bridge between {@link AdvancedHostResolver} and {@link org.littleshoot.proxy.HostResolver}. +*/ +public class DelegatingHostResolver implements org.littleshoot.proxy.HostResolver { + private volatile AdvancedHostResolver resolver; + + /** + * Creates a new resolver that will delegate to the specified resolver. + * + * @param resolver HostResolver to delegate to + */ + public DelegatingHostResolver(AdvancedHostResolver resolver) { + this.resolver = resolver; + } + + public AdvancedHostResolver getResolver() { + return resolver; + } + + public void setResolver(AdvancedHostResolver resolver) { + this.resolver = resolver; + } + + @Override + public InetSocketAddress resolve(String host, int port) throws UnknownHostException { + Collection resolvedAddresses = resolver.resolve(host); + if (!resolvedAddresses.isEmpty()) { + InetAddress resolvedAddress = Iterables.get(resolvedAddresses, 0); + return new InetSocketAddress(resolvedAddress, port); + } + + // no address found by the resolver + throw new UnknownHostException(host); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java new file mode 100644 index 000000000..432032ed7 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java @@ -0,0 +1,144 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.net.InetAddresses; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xbill.DNS.AAAARecord; +import org.xbill.DNS.ARecord; +import org.xbill.DNS.Cache; +import org.xbill.DNS.DClass; +import org.xbill.DNS.Lookup; +import org.xbill.DNS.Record; +import org.xbill.DNS.TextParseException; +import org.xbill.DNS.Type; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * An {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} that uses dnsjava to perform DNS lookups. This implementation provides full + * cache manipulation capabilities. + * + * @deprecated The dnsjava resolver has been deprecated in favor of the standard JVM resolver and will be removed in BMP >2.1. + */ +public class DnsJavaResolver extends AbstractHostNameRemapper implements AdvancedHostResolver { + private static final Logger log = LoggerFactory.getLogger(DnsJavaResolver.class); + + /** + * DNS cache used for dnsjava lookups. + */ + private final Cache cache = new Cache(); + + /** + * Maximum number of times to retry a DNS lookup due to a failure to connect to the DNS server. + */ + private static final int DNS_NETWORK_FAILURE_RETRY_COUNT = 5; + + @Override + public void clearDNSCache() { + cache.clearCache(); + } + + @Override + public void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + cache.setMaxCache((int) TimeUnit.SECONDS.convert(timeout, timeUnit)); + } + + @Override + public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + cache.setMaxNCache((int) TimeUnit.SECONDS.convert(timeout, timeUnit)); + } + + @Override + public Collection resolveRemapped(String remappedHost) { + // special case for IP literals: return the InetAddress without doing a dnsjava lookup. dnsjava seems to handle ipv4 literals + // reasonably well, but does not handle ipv6 literals (with or without [] brackets) correctly. + // note this does not work properly for ipv6 literals with a scope identifier, which is a known issue for InetAddresses.isInetAddress(). + // (dnsjava also handles the situation incorrectly) + if (InetAddresses.isInetAddress(remappedHost)) { + return Collections.singletonList(InetAddresses.forString(remappedHost)); + } + + // retrieve IPv4 addresses, then retrieve IPv6 addresses only if no IPv4 addresses are found. the current implementation always uses the + // first returned address, so there is no need to look for IPv6 addresses if an IPv4 address is found. + Collection ipv4addresses = resolveHostByType(remappedHost, Type.A); + + if (!ipv4addresses.isEmpty()) { + return ipv4addresses; + } else { + return resolveHostByType(remappedHost, Type.AAAA); + } + } + + /** + * Resolves the specified host using dnsjava, retrieving addresses of the specified type. + * + * @param host hostname to resolve + * @param type one of {@link org.xbill.DNS.Type}, typically {@link org.xbill.DNS.Type#A} (IPv4) or {@link org.xbill.DNS.Type#AAAA} (IPv6). + * @return resolved addresses, or an empty collection if no addresses could be resolved + */ + protected Collection resolveHostByType(String host, int type) { + Lookup lookup; + try { + lookup = new Lookup(host, type, DClass.IN); + } catch (TextParseException e) { + return Collections.emptyList(); + } + + lookup.setCache(cache); + + // we set the retry count to -1 because we want the first execution not be counted as a retry. + int retryCount = -1; + Record[] records; + + // we iterate while the status is TRY_AGAIN and DNS_NETWORK_FAILURE_RETRY_COUNT is not exceeded + do { + records = lookup.run(); + retryCount++; + } while (lookup.getResult() == Lookup.TRY_AGAIN && retryCount < DNS_NETWORK_FAILURE_RETRY_COUNT); + + if (records == null) { + // no records found, so could not resolve host + return Collections.emptyList(); + } + + // convert the records we found into IPv4/IPv6 InetAddress objects + List addrList = new ArrayList(records.length); + + // the InetAddresses returned by dnsjava include the trailing dot, e.g. "www.google.com." -- use the passed-in (or remapped) host value instead + for (Record record : records) { + if (record instanceof ARecord) { + ARecord ipv4Record = (ARecord) record; + + try { + InetAddress resolvedAddress = InetAddress.getByAddress(host, ipv4Record.getAddress().getAddress()); + + addrList.add(resolvedAddress); + } catch (UnknownHostException e) { + // this should never happen, unless there is a bug in dnsjava + log.warn("dnsjava resolver returned an invalid InetAddress for host: " + host, e); + continue; + } + } else if (record instanceof AAAARecord) { + AAAARecord ipv6Record = (AAAARecord) record; + + try { + InetAddress resolvedAddress = InetAddress.getByAddress(host, ipv6Record.getAddress().getAddress()); + + addrList.add(resolvedAddress); + } catch (UnknownHostException e) { + // this should never happen, unless there is a bug in dnsjava + log.warn("dnsjava resolver returned an invalid InetAddress for host: " + host, e); + continue; + } + } + } + + return addrList; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/HostResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/HostResolver.java new file mode 100644 index 000000000..c6d380715 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/HostResolver.java @@ -0,0 +1,20 @@ +package net.lightbody.bmp.proxy.dns; + +import java.net.InetAddress; +import java.util.Collection; + +/** + * Defines the basic functionality that {@link net.lightbody.bmp.BrowserMobProxy} implementations require when resolving hostnames. + */ +public interface HostResolver { + /** + * Resolves a hostname to one or more IP addresses. The iterator over the returned Collection is recommended to reflect the ordering + * returned by the underlying name lookup service. For example, if a DNS server returns three IP addresses, 1.1.1.1, 2.2.2.2, and + * 3.3.3.3, corresponding to www.somehost.com, the returned Collection iterator is recommended to iterate in + * the order [1.1.1.1, 2.2.2.2, 3.3.3.3]. + * + * @param host host to resolve + * @return resolved InetAddresses, or an empty collection if no addresses were found + */ + Collection resolve(String host); +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeCacheManipulatingResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeCacheManipulatingResolver.java new file mode 100644 index 000000000..d2734dba1 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeCacheManipulatingResolver.java @@ -0,0 +1,88 @@ +package net.lightbody.bmp.proxy.dns; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.util.LinkedHashMap; +import java.util.concurrent.TimeUnit; + +/** + * An {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} that provides native JVM lookup using {@link net.lightbody.bmp.proxy.dns.NativeResolver} + * but also implements DNS cache manipulation functionality. + *

+ * Important note: The Oracle JVM does not provide any public facility to manipulate the JVM's DNS cache. This class uses reflection to forcibly + * manipulate the cache, which includes access to private class members that are not part of the published Java specification. As such, this + * implementation is brittle and may break in a future Java release, or may not work on non-Oracle JVMs. If this implementation cannot + * perform any of its operations due to a failure to find or set the relevant field using reflection, it will log a warning but will not + * throw an exception. You are using this class at your own risk! JVM cache manipulation does not work on Windows -- this class will behave exactly + * the same as {@link net.lightbody.bmp.proxy.dns.NativeResolver} on that platform. + */ +public class NativeCacheManipulatingResolver extends NativeResolver { + private static final Logger log = LoggerFactory.getLogger(NativeCacheManipulatingResolver.class); + + @Override + public void clearDNSCache() { + // clear the DNS cache but replacing the LinkedHashMaps that contain the positive and negative caches on the + // private static InetAddress.Cache inner class with new, empty maps + try { + Field positiveCacheField = InetAddress.class.getDeclaredField("addressCache"); + positiveCacheField.setAccessible(true); + Object positiveCacheInstance = positiveCacheField.get(null); + + Field negativeCacheField = InetAddress.class.getDeclaredField("negativeCache"); + negativeCacheField.setAccessible(true); + Object negativeCacheInstance = positiveCacheField.get(null); + + Class cacheClass = Class.forName("java.net.InetAddress$Cache"); + Field cacheField = cacheClass.getDeclaredField("cache"); + cacheField.setAccessible(true); + + cacheField.set(positiveCacheInstance, new LinkedHashMap()); + cacheField.set(negativeCacheInstance, new LinkedHashMap()); + } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) { + log.warn("Unable to clear native JVM DNS cache", e); + } + } + + @Override + public void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + try { + Class inetAddressCachePolicyClass = Class.forName("sun.net.InetAddressCachePolicy"); + + Field positiveCacheTimeoutSeconds = inetAddressCachePolicyClass.getDeclaredField("cachePolicy"); + positiveCacheTimeoutSeconds.setAccessible(true); + + if (timeout < 0) { + positiveCacheTimeoutSeconds.setInt(null, -1); + java.security.Security.setProperty("networkaddress.cache.ttl", "-1"); + } else { + positiveCacheTimeoutSeconds.setInt(null, (int) TimeUnit.SECONDS.convert(timeout, timeUnit)); + java.security.Security.setProperty("networkaddress.cache.ttl", Long.toString(TimeUnit.SECONDS.convert(timeout, timeUnit))); + } + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + log.warn("Unable to modify native JVM DNS cache timeouts", e); + } + } + + @Override + public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + try { + Class inetAddressCachePolicyClass = Class.forName("sun.net.InetAddressCachePolicy"); + + Field negativeCacheTimeoutSeconds = inetAddressCachePolicyClass.getDeclaredField("negativeCachePolicy"); + negativeCacheTimeoutSeconds.setAccessible(true); + + if (timeout < 0) { + negativeCacheTimeoutSeconds.setInt(null, -1); + java.security.Security.setProperty("networkaddress.cache.negative.ttl", "-1"); + } else { + negativeCacheTimeoutSeconds.setInt(null, (int) TimeUnit.SECONDS.convert(timeout, timeUnit)); + java.security.Security.setProperty("networkaddress.cache.negative.ttl", Long.toString(TimeUnit.SECONDS.convert(timeout, timeUnit))); + } + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + log.warn("Unable to modify native JVM DNS cache timeouts", e); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java new file mode 100644 index 000000000..c71c61495 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java @@ -0,0 +1,48 @@ +package net.lightbody.bmp.proxy.dns; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +/** + * An {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} that provides native JVM lookup using {@link java.net.InetAddress}. + * This implementation does not provide any cache manipulation. Attempting to manipulate the DNS cache will result in a DEBUG-level + * log statement and will not raise an exception. The {@link net.lightbody.bmp.proxy.dns.DnsJavaResolver} provides support for cache + * manipulation. If you absolutely need to manipulate the native JVM DNS cache, see + * {@link net.lightbody.bmp.proxy.dns.NativeCacheManipulatingResolver} for details. + */ +public class NativeResolver extends AbstractHostNameRemapper implements AdvancedHostResolver { + private static final Logger log = LoggerFactory.getLogger(NativeResolver.class); + + @Override + public void clearDNSCache() { + log.debug("Cannot clear native JVM DNS Cache using this Resolver"); + } + + @Override + public void setPositiveDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + log.debug("Cannot change native JVM DNS cache timeout using this Resolver"); + } + + @Override + public void setNegativeDNSCacheTimeout(int timeout, TimeUnit timeUnit) { + log.debug("Cannot change native JVM DNS cache timeout using this Resolver"); + } + + @Override + public Collection resolveRemapped(String remappedHost) { + try { + Collection addresses = Arrays.asList(InetAddress.getAllByName(remappedHost)); + + return addresses; + } catch (UnknownHostException e) { + return Collections.emptyList(); + } + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java new file mode 100644 index 000000000..98172810f --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java @@ -0,0 +1,300 @@ +package net.lightbody.bmp.util; + +import com.google.common.io.BaseEncoding; +import com.google.common.net.HostAndPort; +import com.google.common.net.MediaType; +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.exception.DecompressionException; +import net.lightbody.bmp.exception.UnsupportedCharsetException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +/** + * Utility class with static methods for processing HTTP requests and responses. + */ +public class BrowserMobHttpUtil { + private static final Logger log = LoggerFactory.getLogger(BrowserMobHttpUtil.class); + + /** + * Default MIME content type if no Content-Type header is present. According to the HTTP 1.1 spec, section 7.2.1: + *

+     *     Any HTTP/1.1 message containing an entity-body SHOULD include a Content-Type header field defining the media
+     *     type of that body. If and only if the media type is not given by a Content-Type field, the recipient MAY
+     *     attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to
+     *     identify the resource. If the media type remains unknown, the recipient SHOULD treat it as
+     *     type "application/octet-stream".
+     * 
+ */ + public static final String UNKNOWN_CONTENT_TYPE = "application/octet-stream"; + + /** + * The default charset when the Content-Type header does not specify a charset. According to RFC 7231 Appendix B: + *
+     *     The default charset of ISO-8859-1 for text media types has been
+     *     removed; the default is now whatever the media type definition says.
+     *     Likewise, special treatment of ISO-8859-1 has been removed from the
+     *     Accept-Charset header field.
+     * 
+ * + * Technically, we would have to determine the charset on a per-content-type basis, but generally speaking, UTF-8 is a + * pretty safe default. (NOTE: In the previous HTTP/1.1 spec, section 3.7.1, the default charset was defined as ISO-8859-1.) + */ + public static final Charset DEFAULT_HTTP_CHARSET = StandardCharsets.UTF_8; + + /** + * Buffer size when decompressing content. + */ + public static final int DECOMPRESS_BUFFER_SIZE = 16192; + + /** + * Returns the size of the headers, including the 2 CRLFs at the end of the header block. + * + * @param headers headers to size + * @return length of the headers, in bytes + */ + public static long getHeaderSize(HttpHeaders headers) { + long headersSize = 0; + for (Map.Entry header : headers.entries()) { + // +2 for ': ', +2 for new line + headersSize += header.getKey().length() + header.getValue().length() + 4; + } + return headersSize; + } + + /** + * Decompresses the gzipped byte stream. + * + * @param fullMessage gzipped byte stream to decomress + * @return decompressed bytes + * @throws DecompressionException thrown if the fullMessage cannot be read or decompressed for any reason + */ + public static byte[] decompressContents(byte[] fullMessage) throws DecompressionException { + InflaterInputStream gzipReader = null; + ByteArrayOutputStream uncompressed; + try { + gzipReader = new GZIPInputStream(new ByteArrayInputStream(fullMessage)); + + uncompressed = new ByteArrayOutputStream(fullMessage.length); + + byte[] decompressBuffer = new byte[DECOMPRESS_BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = gzipReader.read(decompressBuffer)) > -1) { + uncompressed.write(decompressBuffer, 0, bytesRead); + } + + fullMessage = uncompressed.toByteArray(); + } catch (IOException e) { + throw new DecompressionException("Unable to decompress response", e); + } finally { + try { + if (gzipReader != null) { + gzipReader.close(); + } + } catch (IOException e) { + log.warn("Unable to close gzip stream", e); + } + } + return fullMessage; + } + + /** + * Returns true if the content type string indicates textual content. Currently these are any Content-Types that start with one of the + * following: + *
+     *     text/
+     *     application/x-javascript
+     *     application/javascript
+     *     application/json
+     *     application/xml
+     *     application/xhtml+xml
+     * 
+ * + * @param contentType contentType string to parse + * @return true if the content type is textual + */ + public static boolean hasTextualContent(String contentType) { + return contentType != null && + (contentType.startsWith("text/") || + contentType.startsWith("application/x-javascript") || + contentType.startsWith("application/javascript") || + contentType.startsWith("application/json") || + contentType.startsWith("application/xml") || + contentType.startsWith("application/xhtml+xml") + ); + } + + /** + * Extracts all readable bytes from the ByteBuf as a byte array. + * + * @param content ByteBuf to read + * @return byte array containing the readable bytes from the ByteBuf + */ + public static byte[] extractReadableBytes(ByteBuf content) { + byte[] binaryContent = new byte[content.readableBytes()]; + + content.markReaderIndex(); + content.readBytes(binaryContent); + content.resetReaderIndex(); + + return binaryContent; + } + + /** + * Converts the byte array into a String based on the specified charset. The charset cannot be null. + * + * @param content bytes to convert to a String + * @param charset the character set of the content + * @return String containing the converted content + * @throws IllegalArgumentException if charset is null + */ + public static String getContentAsString(byte[] content, Charset charset) { + if (charset == null) { + throw new IllegalArgumentException("Charset cannot be null"); + } + + return new String(content, charset); + } + + /** + * Reads the charset directly from the Content-Type header string. If the Content-Type header does not contain a charset, + * is malformed or unparsable, or if the header is null or empty, this method returns null. + * + * @param contentTypeHeader the Content-Type header string; can be null or empty + * @return the character set indicated in the contentTypeHeader, or null if the charset is not present or is not parsable + * @throws UnsupportedCharsetException if there is a charset specified in the content-type header, but it is not supported on this platform + */ + public static Charset readCharsetInContentTypeHeader(String contentTypeHeader) throws UnsupportedCharsetException { + if (contentTypeHeader == null || contentTypeHeader.isEmpty()) { + return null; + } + + MediaType mediaType; + try { + mediaType = MediaType.parse(contentTypeHeader); + } catch (IllegalArgumentException e) { + log.info("Unable to parse Content-Type header: {}. Content-Type header will be ignored.", contentTypeHeader, e); + return null; + } + + try { + return mediaType.charset().orNull(); + } catch (java.nio.charset.UnsupportedCharsetException e) { + throw new UnsupportedCharsetException(e); + } + } + + /** + * Retrieves the raw (unescaped) path + query string from the specified request. The returned path will not include + * the scheme, host, or port. + * + * @param httpRequest HTTP request + * @return the unescaped path + query string from the HTTP request + * @throws URISyntaxException if the path could not be parsed (due to invalid characters in the URI, etc.) + */ + public static String getRawPathAndParamsFromRequest(HttpRequest httpRequest) throws URISyntaxException { + // if this request's URI contains a full URI (including scheme, host, etc.), strip away the non-path components + if (HttpUtil.startsWithHttpOrHttps(httpRequest.getUri())) { + return getRawPathAndParamsFromUri(httpRequest.getUri()); + } else { + // to provide consistent validation behavior for URIs that contain a scheme and those that don't, attempt to parse + // the URI, even though we discard the parsed URI object + new URI(httpRequest.getUri()); + + return httpRequest.getUri(); + } + } + + /** + * Retrieves the raw (unescaped) path and query parameters from the URI, stripping out the scheme, host, and port. + * The path will begin with a leading '/'. For example, 'http://example.com/some/resource?param%20name=param%20value' + * would return '/some/resource?param%20name=param%20value'. + * + * @param uriString the URI to parse, containing a scheme, host, port, path, and query parameters + * @return the unescaped path and query parameters from the URI + * @throws URISyntaxException if the specified URI is invalid or cannot be parsed + */ + public static String getRawPathAndParamsFromUri(String uriString) throws URISyntaxException { + URI uri = new URI(uriString); + String path = uri.getRawPath(); + String query = uri.getRawQuery(); + + if (query != null) { + return path + '?' + query; + } else { + return path; + } + } + + /** + * Returns true if the specified response is an HTTP redirect response, i.e. a 300, 301, 302, 303, or 307. + * + * @param httpResponse HTTP response + * @return true if the response is a redirect, otherwise false + */ + public static boolean isRedirect(HttpResponse httpResponse) { + switch (httpResponse.getStatus().code()) { + case 300: + case 301: + case 302: + case 303: + case 307: + return true; + + default: + return false; + } + } + + /** + * Removes a port from a host+port if the string contains the specified port. If the host+port does not contain + * a port, or contains another port, the string is returned unaltered. For example, if hostWithPort is the + * string {@code www.website.com:443}, this method will return {@code www.website.com}. + * + * Note: The hostWithPort string is not a URI and should not contain a scheme or resource. This method does + * not attempt to validate the specified host; it might throw IllegalArgumentException if there was a problem + * parsing the hostname, but makes no guarantees. In general, it should be validated externally, if necessary. + * + * @param hostWithPort string containing a hostname and optional port + * @param portNumber port to remove from the string + * @return string with the specified port removed, or the original string if it did not contain the portNumber + */ + public static String removeMatchingPort(String hostWithPort, int portNumber) { + HostAndPort parsedHostAndPort = HostAndPort.fromString(hostWithPort); + if (parsedHostAndPort.hasPort() && parsedHostAndPort.getPort() == portNumber) { + // HostAndPort.getHostText() strips brackets from ipv6 addresses, so reparse using fromHost + return HostAndPort.fromHost(parsedHostAndPort.getHost()).toString(); + } else { + return hostWithPort; + } + } + + /** + * Base64-encodes the specified username and password for Basic Authorization for HTTP requests or upstream proxy + * authorization. The format of Basic auth is "username:password" as a base64 string. + * + * @param username username to encode + * @param password password to encode + * @return a base-64 encoded string containing username:password + */ + public static String base64EncodeBasicCredentials(String username, String password) { + String credentialsToEncode = username + ':' + password; + // using UTF-8, which is the modern de facto standard, and which retains compatibility with US_ASCII for ASCII characters, + // as required by RFC 7616, section 3: http://tools.ietf.org/html/rfc7617#section-3 + byte[] credentialsAsUtf8Bytes = credentialsToEncode.getBytes(StandardCharsets.UTF_8); + return BaseEncoding.base64().encode(credentialsAsUtf8Bytes); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java new file mode 100644 index 000000000..62f33cf06 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java @@ -0,0 +1,124 @@ +package net.lightbody.bmp.util; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.core.har.HarEntry; +import net.lightbody.bmp.core.har.HarLog; +import net.lightbody.bmp.core.har.HarPage; +import net.lightbody.bmp.mitm.exception.UncheckedIOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; + +/** + * General utility class for functionality and classes used mostly internally by BrowserMob Proxy. + */ +public class BrowserMobProxyUtil { + private static final Logger log = LoggerFactory.getLogger(BrowserMobProxyUtil.class); + + /** + * Classpath resource containing this build's version string. + */ + private static final String VERSION_CLASSPATH_RESOURCE = "/net/lightbody/bmp/version"; + + /** + * Default value if the version string cannot be read. + */ + private static final String UNKNOWN_VERSION_STRING = "UNKNOWN-VERSION"; + + /** + * Singleton version string loader. + */ + private static final Supplier version = Suppliers.memoize(new Supplier() { + @Override + public String get() { + return readVersionFileOnClasspath(); + } + }); + + /** + * Copies {@link HarEntry} and {@link HarPage} references from the specified har to a new har copy, up to and including + * the specified pageRef. Does not perform a "deep copy", so any subsequent modification to the entries or pages will + * be reflected in the copied har. + * + * @param har existing har to copy + * @param pageRef last page ID to copy + * @return copy of a {@link Har} with entries and pages from the original har, or null if the input har is null + */ + public static Har copyHarThroughPageRef(Har har, String pageRef) { + if (har == null) { + return null; + } + + if (har.getLog() == null) { + return new Har(); + } + + // collect the page refs that need to be copied to new har copy. + Set pageRefsToCopy = new HashSet(); + + for (HarPage page : har.getLog().getPages()) { + pageRefsToCopy.add(page.getId()); + + if (pageRef.equals(page.getId())) { + break; + } + } + + HarLog logCopy = new HarLog(); + + // copy every entry and page in the HarLog that matches a pageRefToCopy. since getEntries() and getPages() return + // lists, we are guaranteed that we will iterate through the pages and entries in the proper order + for (HarEntry entry : har.getLog().getEntries()) { + if (pageRefsToCopy.contains(entry.getPageref())) { + logCopy.addEntry(entry); + } + } + + for (HarPage page : har.getLog().getPages()) { + if (pageRefsToCopy.contains(page.getId())) { + logCopy.addPage(page); + } + } + + Har harCopy = new Har(); + harCopy.setLog(logCopy); + + return harCopy; + } + + /** + * Returns the version of BrowserMob Proxy, e.g. "2.1.0". + * + * @return BMP version string + */ + public static String getVersionString() { + return version.get(); + } + + /** + * Reads the version of this build from the classpath resource specified by {@link #VERSION_CLASSPATH_RESOURCE}. + * + * @return version string from the classpath version resource + */ + private static String readVersionFileOnClasspath() { + String versionString; + try { + versionString = ClasspathResourceUtil.classpathResourceToString(VERSION_CLASSPATH_RESOURCE, StandardCharsets.UTF_8); + } catch (UncheckedIOException e) { + log.debug("Unable to load version from classpath resource: {}", VERSION_CLASSPATH_RESOURCE, e); + return UNKNOWN_VERSION_STRING; + } + + if (versionString.isEmpty()) { + log.debug("Version file on classpath was empty or could not be read. Resource: {}", VERSION_CLASSPATH_RESOURCE); + return UNKNOWN_VERSION_STRING; + } + + return versionString; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java new file mode 100644 index 000000000..969a4d810 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java @@ -0,0 +1,144 @@ +package net.lightbody.bmp.util; + +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.HttpHeaders; +import net.lightbody.bmp.exception.UnsupportedCharsetException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.Charset; + +/** + * Helper class to wrap the contents of an {@link io.netty.handler.codec.http.HttpMessage}. Contains convenience methods to extract and + * manipulate the contents of the wrapped {@link io.netty.handler.codec.http.HttpMessage}. + * + * TODO: Currently this class only wraps FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well + */ +public class HttpMessageContents { + private static final Logger log = LoggerFactory.getLogger(HttpMessageContents.class); + + private final FullHttpMessage httpMessage; + + // caches for contents, to avoid repeated re-extraction of data + private volatile String textContents; + private volatile byte[] binaryContents; + + public HttpMessageContents(FullHttpMessage httpMessage) { + this.httpMessage = httpMessage; + } + + /** + * Replaces the contents of the wrapped HttpMessage with the specified text contents, encoding them in the character set specified by the + * message's Content-Type header. Note that this method does not update the Content-Type header, so if the content type will change as a + * result of this call, the Content-Type header should be updated before calling this method. + * + * @param newContents new message contents + */ + public void setTextContents(String newContents) { + HttpObjectUtil.replaceTextHttpEntityBody(httpMessage, newContents); + + // replaced the contents, so clear the local cache + textContents = null; + binaryContents = null; + } + + /** + * Replaces the contents of the wrapped HttpMessage with the specified binary contents. Note that this method does not update the + * Content-Type header, so if the content type will change as a result of this call, the Content-Type header should be updated before + * calling this method. + * + * @param newBinaryContents new message contents + */ + public void setBinaryContents(byte[] newBinaryContents) { + HttpObjectUtil.replaceBinaryHttpEntityBody(httpMessage, newBinaryContents); + + // replaced the contents, so clear the local cache + binaryContents = null; + textContents = null; + } + + /** + * Retrieves the contents of this message as a String, decoded according to the message's Content-Type header. This method caches + * the contents, so repeated calls to this method should not incur a penalty; however, modifications to the message contents + * outside of this class will result in stale data returned from this method. + * + * @return String representation of the entity body + * @throws java.nio.charset.UnsupportedCharsetException if the character set declared in the message is not supported on this platform + */ + public String getTextContents() throws java.nio.charset.UnsupportedCharsetException { + // avoid re-extracting the contents if this method is called repeatedly + if (textContents == null) { + textContents = HttpObjectUtil.extractHttpEntityBody(httpMessage); + } + + return textContents; + } + + /** + * Retrieves the binary contents of this message. This method caches the contents, so repeated calls to this method should not incur a + * penalty; however, modifications to the message contents outside of this class will result in stale data returned from this method. + * + * @return binary contents of the entity body + */ + public byte[] getBinaryContents() { + // avoid re-extracting the contents if this method is called repeatedly + if (binaryContents == null) { + binaryContents = HttpObjectUtil.extractBinaryHttpEntityBody(httpMessage); + } + + return binaryContents; + } + + /** + * Retrieves the Content-Type header of this message. If no Content-Type is present, returns the assumed default Content-Type (see + * {@link BrowserMobHttpUtil#UNKNOWN_CONTENT_TYPE}). + * + * @return the message's content type + */ + public String getContentType() { + String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaders.Names.CONTENT_TYPE); + if (contentTypeHeader == null || contentTypeHeader.isEmpty()) { + return BrowserMobHttpUtil.UNKNOWN_CONTENT_TYPE; + } else { + return contentTypeHeader; + } + } + + /** + * Retrieves the character set of the entity body. If the Content-Type is not a textual type, this value is meaningless. + * If no character set is specified, this method will return the default ISO-8859-1 character set. If the Content-Type + * specifies a character set, but the character set is not supported on this platform, this method throws an + * {@link java.nio.charset.UnsupportedCharsetException}. + * + * @return the entity body's character set + * @throws java.nio.charset.UnsupportedCharsetException if the character set declared in the message is not supported on this platform + */ + public Charset getCharset() throws java.nio.charset.UnsupportedCharsetException { + String contentTypeHeader = getContentType(); + + Charset charset = null; + try { + charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); + } catch (UnsupportedCharsetException e) { + java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause(); + log.error("Character set specified in Content-Type header is not supported on this platform. Content-Type header: {}", contentTypeHeader, cause); + + throw cause; + } + + if (charset == null) { + return BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + } + + return charset; + } + + /** + * Returns true if this message's Content-Type header indicates that it contains a textual data type. See {@link BrowserMobHttpUtil#hasTextualContent(String)}. + * + * @return true if the Content-Type header is a textual type, otherwise false + */ + public boolean isText() { + return BrowserMobHttpUtil.hasTextualContent(getContentType()); + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageInfo.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageInfo.java new file mode 100644 index 000000000..303ada6ba --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpMessageInfo.java @@ -0,0 +1,61 @@ +package net.lightbody.bmp.util; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; + +/** + * Encapsulates additional HTTP message data passed to request and response filters. + */ +public class HttpMessageInfo { + private final HttpRequest originalRequest; + private final ChannelHandlerContext channelHandlerContext; + private final boolean isHttps; + private final String url; + private final String originalUrl; + + public HttpMessageInfo(HttpRequest originalRequest, ChannelHandlerContext channelHandlerContext, boolean isHttps, String url, String originalUrl) { + this.originalRequest = originalRequest; + this.channelHandlerContext = channelHandlerContext; + this.isHttps = isHttps; + this.url = url; + this.originalUrl = originalUrl; + } + + /** + * The original request from the client. Does not reflect any modifications from previous filters. + */ + public HttpRequest getOriginalRequest() { + return originalRequest; + } + + /** + * The {@link ChannelHandlerContext} for this request's client connection. + */ + public ChannelHandlerContext getChannelHandlerContext() { + return channelHandlerContext; + } + + /** + * Returns true if this is an HTTPS message. + */ + public boolean isHttps() { + return isHttps; + } + + /** + * Returns the full, absolute URL of the original request from the client for both HTTP and HTTPS URLs. The URL + * will not reflect modifications from this or other filters. + */ + public String getOriginalUrl() { + return originalUrl; + } + + /** + * Returns the full, absolute URL of this request from the client for both HTTP and HTTPS URLs. The URL will reflect + * modifications from filters. If this method is called while a request filter is processing, it will reflect any + * modifications to the URL from all previous filters. + */ + public String getUrl() { + return url; + } +} diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java new file mode 100644 index 000000000..169a878f6 --- /dev/null +++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java @@ -0,0 +1,146 @@ +package net.lightbody.bmp.util; + +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMessage; +import net.lightbody.bmp.exception.UnsupportedCharsetException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.Charset; + +/** + * Utility class to assist with manipulation of {@link io.netty.handler.codec.http.HttpObject} instances, including + * {@link io.netty.handler.codec.http.HttpMessage} and {@link io.netty.handler.codec.http.HttpContent}. + */ +public class HttpObjectUtil { + private static final Logger log = LoggerFactory.getLogger(HttpObjectUtil.class); + + /** + * Replaces the entity body of the message with the specified contents. Encodes the message contents according to charset in the message's + * Content-Type header, or uses {@link BrowserMobHttpUtil#DEFAULT_HTTP_CHARSET} if none is specified. + * Note: If the charset of the message is not supported on this platform, this will throw an {@link java.nio.charset.UnsupportedCharsetException}. + * + * TODO: Currently this method only works for FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well + * + * @param message the HTTP message to manipulate + * @param newContents the new entity body contents + * @throws java.nio.charset.UnsupportedCharsetException if the charset in the message is not supported on this platform + */ + public static void replaceTextHttpEntityBody(FullHttpMessage message, String newContents) { + // get the content type for this message so we can encode the newContents into a byte stream appropriately + String contentTypeHeader = message.headers().get(HttpHeaders.Names.CONTENT_TYPE); + + Charset messageCharset; + try { + messageCharset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); + } catch (UnsupportedCharsetException e) { + java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause() ; + log.error("Found unsupported character set in Content-Type header '{}' while attempting to replace contents of HTTP message.", contentTypeHeader, cause); + + throw cause; + } + + if (messageCharset == null) { + messageCharset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + log.warn("No character set declared in HTTP message. Replacing text using default charset {}.", messageCharset); + } + + byte[] contentBytes = newContents.getBytes(messageCharset); + + replaceBinaryHttpEntityBody(message, contentBytes); + } + + /** + * Replaces an HTTP entity body with the specified binary contents. + * TODO: Currently this method only works for FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well + * + * @param message the HTTP message to manipulate + * @param newBinaryContents the new entity body contents + */ + public static void replaceBinaryHttpEntityBody(FullHttpMessage message, byte[] newBinaryContents) { + message.content().resetWriterIndex(); + // resize the buffer if needed, since the new message may be longer than the old one + message.content().ensureWritable(newBinaryContents.length, true); + message.content().writeBytes(newBinaryContents); + + // update the Content-Length header, since the size may have changed + message.headers().set(HttpHeaders.Names.CONTENT_LENGTH, newBinaryContents.length); + } + + /** + * Extracts the entity body from an HTTP content object, according to the specified character set. The character set cannot be null. If + * the character set is not specified or is unknown, you still must specify a suitable default charset (see {@link BrowserMobHttpUtil#DEFAULT_HTTP_CHARSET}). + * + * @param httpContent HTTP content object to extract the entity body from + * @param charset character set of the entity body + * @return String representation of the entity body + * @throws IllegalArgumentException if the charset is null + */ + public static String extractHttpEntityBody(HttpContent httpContent, Charset charset) { + if (charset == null) { + throw new IllegalArgumentException("No charset specified when extracting the contents of an HTTP message"); + } + + byte[] contentBytes = BrowserMobHttpUtil.extractReadableBytes(httpContent.content()); + + return new String(contentBytes, charset); + } + + /** + * Extracts the entity body from a FullHttpMessage, according to the character set in the message's Content-Type header. If the Content-Type + * header is not present or does not specify a charset, assumes the ISO-8859-1 character set (see {@link BrowserMobHttpUtil#DEFAULT_HTTP_CHARSET}). + * + * @param httpMessage HTTP message to extract entity body from + * @return String representation of the entity body + * @throws java.nio.charset.UnsupportedCharsetException if there is a charset specified in the content-type header, but it is not supported + */ + public static String extractHttpEntityBody(FullHttpMessage httpMessage) { + Charset charset; + try { + charset = getCharsetFromMessage(httpMessage); + } catch (UnsupportedCharsetException e) { + // the declared character set is not supported, so it is impossible to decode the contents of the message. log an error and throw an exception + // to alert the client code. + java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause(); + + String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaders.Names.CONTENT_TYPE); + log.error("Cannot retrieve text contents of message because HTTP message declares a character set that is not supported on this platform. Content type header: {}.", contentTypeHeader, cause); + + throw cause; + } + + return extractHttpEntityBody(httpMessage, charset); + } + + /** + * Derives the charset from the Content-Type header in the HttpMessage. If the Content-Type header is not present or does not contain + * a character set, this method returns the ISO-8859-1 character set. See {@link BrowserMobHttpUtil#readCharsetInContentTypeHeader(String)} + * for more details. + * + * @param httpMessage HTTP message to extract charset from + * @return the charset associated with the HTTP message, or the default charset if none is present + * @throws UnsupportedCharsetException if there is a charset specified in the content-type header, but it is not supported + */ + public static Charset getCharsetFromMessage(HttpMessage httpMessage) throws UnsupportedCharsetException { + String contentTypeHeader = HttpHeaders.getHeader(httpMessage, HttpHeaders.Names.CONTENT_TYPE); + + Charset charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); + if (charset == null) { + return BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + } + + return charset; + } + + /** + * Extracts the binary contents from an HTTP message. + * + * @param httpContent HTTP content object to extract the entity body from + * @return binary contents of the HTTP message + */ + public static byte[] extractBinaryHttpEntityBody(HttpContent httpContent) { + return BrowserMobHttpUtil.extractReadableBytes(httpContent.content()); + } +} diff --git a/browsermob-core/src/main/resources/net/lightbody/bmp/version b/browsermob-core/src/main/resources/net/lightbody/bmp/version new file mode 100644 index 000000000..f2ab45c3b --- /dev/null +++ b/browsermob-core/src/main/resources/net/lightbody/bmp/version @@ -0,0 +1 @@ +${project.version} \ No newline at end of file diff --git a/browsermob-core/src/main/resources/sslSupport/ca-certificate-ec.cer b/browsermob-core/src/main/resources/sslSupport/ca-certificate-ec.cer new file mode 100644 index 000000000..63d902340 --- /dev/null +++ b/browsermob-core/src/main/resources/sslSupport/ca-certificate-ec.cer @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8jCCAZigAwIBAgIUUridrS1mqPKh8aA7igVOQRUc8P8wCgYIKoZIzj0EAwQw +RjEZMBcGA1UEAwwQTGl0dGxlUHJveHkgTUlUTTEpMCcGA1UECgwgTGl0dGxlUHJv +eHkgRUNDIEltcGVyc29uYXRpb24gQ0EwHhcNMTUwMTAyMDAwMDAwWhcNMjUwMTAy +MDAwMDAwWjBGMRkwFwYDVQQDDBBMaXR0bGVQcm94eSBNSVRNMSkwJwYDVQQKDCBM +aXR0bGVQcm94eSBFQ0MgSW1wZXJzb25hdGlvbiBDQTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABB9DdlM/uhkMWTYFo9ETzPrMWBlfhCD0z3J2F1aH9a3OPiPYBio6 +fzTVSZO2rU9ItfcRRpCGeMzY+pilfUNkPXyjZDBiMB0GA1UdDgQWBBQ0TT/oOVF2 +mT10+X9W3NDESql7ZzAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBtjAjBgNV +HSUEHDAaBggrBgEFBQcDAQYIKwYBBQUHAwIGBFUdJQAwCgYIKoZIzj0EAwQDSAAw +RQIhAOb/s6H8v1XeEPGEmMdVEhRnhJgTYAktQKQLZid8QBzsAiA7zc1mFLRAKs98 +5d9+qGFsv7Fy0yTNO3vFyL7DL2mykg== +-----END CERTIFICATE----- diff --git a/browsermob-core/src/main/resources/sslSupport/ca-certificate-rsa.cer b/browsermob-core/src/main/resources/sslSupport/ca-certificate-rsa.cer new file mode 100644 index 000000000..b962a08f2 --- /dev/null +++ b/browsermob-core/src/main/resources/sslSupport/ca-certificate-rsa.cer @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDfzCCAmegAwIBAgIVAMFQpicWi3EjPX08LgeuA8nAOEfIMA0GCSqGSIb3DQEB +DQUAMEYxGTAXBgNVBAMMEExpdHRsZVByb3h5IE1JVE0xKTAnBgNVBAoMIExpdHRs +ZVByb3h5IFJTQSBJbXBlcnNvbmF0aW9uIENBMB4XDTE1MDEwMjAwMDAwMFoXDTI1 +MDEwMjAwMDAwMFowRjEZMBcGA1UEAwwQTGl0dGxlUHJveHkgTUlUTTEpMCcGA1UE +CgwgTGl0dGxlUHJveHkgUlNBIEltcGVyc29uYXRpb24gQ0EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC141M+lc046DJaNqIARozRPROGt/s5Ng1UOE84 +tKhd+M/REaOeNovW+42uMa4ZifJAK7Csc0dx54Iq35LXy0tMw6ly/MB0pFi+aFCJ +VzXZhbAWIsUmjU8t6z2Y0sjKVX/g3HkdXqaX94jlDtsTjeQXvFhiJNRlX/Locc/f +/oNYZWhg7IPGyQglRY9Dco9kZMSbh5y0yfM8002PNPbNOP4dMX4yYqovT90XbvQ2 +rCBbiS6Cys7j44vwOcra9srlb3YQiOCOsYCf7eIhT1GH8tqQ84CHblufqxcGIvXv +V1ex6bDFy63tiPySsOwuVnZglkQ0MDl1GMKVySdPw/qQM5v9AgMBAAGjZDBiMB0G +A1UdDgQWBBRFMQtpkCyZIK9NxaEJDvbfaV1QOzAPBgNVHRMBAf8EBTADAQH/MAsG +A1UdDwQEAwIBtjAjBgNVHSUEHDAaBggrBgEFBQcDAQYIKwYBBQUHAwIGBFUdJQAw +DQYJKoZIhvcNAQENBQADggEBAJuYv1NuxPHom579iAjs19YrFGewHpv4aZC7aWTt +oC1y9418w7QzVOAz2VzluURazUdg/HS9s8abJ8IS0iD0xLz0B1cvJ6F2BezjAwyG +2LxZggmBdLqwjdRkX0Mx3a2HqUpEqaNeKyE8VmzwPuDHN1AqbFcuOPHN7fm7kAtL +4bxFmjgSt7PjEdYwysdjkLC6m+236tuFydpVkXMjuBthsk/hZ1Y/3tbCj/B9a9// +5O+HhYEy+Oa64iFvxfgDfKKUQR3VmwThj1Dh2iJw/kbPJEuQ/PtfcnQhOqyliwg6 +Edxd1kaO4HU8Am6TwpmpPFWHRqhM2xj2PAGyfFtN1WfBEQ4= +-----END CERTIFICATE----- diff --git a/browsermob-core/src/main/resources/sslSupport/ca-keystore-ec.p12 b/browsermob-core/src/main/resources/sslSupport/ca-keystore-ec.p12 new file mode 100644 index 000000000..ef708aad8 Binary files /dev/null and b/browsermob-core/src/main/resources/sslSupport/ca-keystore-ec.p12 differ diff --git a/browsermob-core/src/main/resources/sslSupport/ca-keystore-rsa.p12 b/browsermob-core/src/main/resources/sslSupport/ca-keystore-rsa.p12 new file mode 100644 index 000000000..f995c1774 Binary files /dev/null and b/browsermob-core/src/main/resources/sslSupport/ca-keystore-rsa.p12 differ diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy new file mode 100644 index 000000000..0a6991b3e --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/filters/RewriteUrlFilterTest.groovy @@ -0,0 +1,172 @@ +package net.lightbody.bmp.filters + +import com.google.common.collect.ImmutableList +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.HttpHeaders +import io.netty.handler.codec.http.HttpRequest +import io.netty.util.Attribute +import io.netty.util.AttributeKey +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.RewriteRule +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockserver.matchers.Times + +import static org.junit.Assert.assertEquals +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class RewriteUrlFilterTest extends MockServerTest { + BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testRewriteWithCaptureGroups() { + HttpHeaders mockHeaders = mock(HttpHeaders.class) + when(mockHeaders.contains(HttpHeaders.Names.HOST)).thenReturn(false) + + HttpRequest request = mock(HttpRequest.class); + when(request.getUri()).thenReturn('http://www.yahoo.com?param=someValue'); + when(request.headers()).thenReturn(mockHeaders) + + Collection rewriteRules = ImmutableList.of(new RewriteRule('http://www\\.(yahoo|bing)\\.com\\?(\\w+)=(\\w+)', 'http://www.google.com?originalDomain=$1&$2=$3')); + + // mock out the netty ChannelHandlerContext for the isHttps() call in the filter + Attribute mockIsHttpsAttribute = mock(Attribute) + when(mockIsHttpsAttribute.get()).thenReturn(Boolean.FALSE) + + ChannelHandlerContext mockCtx = mock(ChannelHandlerContext) + when(mockCtx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME))) + .thenReturn(mockIsHttpsAttribute) + + RewriteUrlFilter filter = new RewriteUrlFilter(request, mockCtx, rewriteRules); + filter.clientToProxyRequest(request); + + verify(request).setUri('http://www.google.com?originalDomain=yahoo¶m=someValue'); + } + + @Test + void testRewriteMultipleMatches() { + HttpHeaders mockHeaders = mock(HttpHeaders.class) + when(mockHeaders.contains(HttpHeaders.Names.HOST)).thenReturn(false) + + HttpRequest request = mock(HttpRequest.class); + when(request.getUri()).thenReturn('http://www.yahoo.com?param=someValue'); + when(request.headers()).thenReturn(mockHeaders) + + Collection rewriteRules = ImmutableList.of( + new RewriteRule('http://www\\.yahoo\\.com\\?(\\w+)=(\\w+)', 'http://www.bing.com?new$1=new$2'), + new RewriteRule('http://www\\.(yahoo|bing)\\.com\\?(\\w+)=(\\w+)', 'http://www.google.com?originalDomain=$1&$2=$3') + ); + + // mock out the netty ChannelHandlerContext for the isHttps() call in the filter + Attribute mockIsHttpsAttribute = mock(Attribute) + when(mockIsHttpsAttribute.get()).thenReturn(Boolean.FALSE) + + ChannelHandlerContext mockCtx = mock(ChannelHandlerContext) + when(mockCtx.attr(AttributeKey.valueOf(HttpsAwareFiltersAdapter.IS_HTTPS_ATTRIBUTE_NAME))) + .thenReturn(mockIsHttpsAttribute) + + RewriteUrlFilter filter = new RewriteUrlFilter(request, mockCtx, rewriteRules); + filter.clientToProxyRequest(request); + + verify(request).setUri('http://www.google.com?originalDomain=bing&newparam=newsomeValue'); + } + + @Test + void testRewriteHttpHost() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testRewriteHttpHost") + .withHeader("Host", "localhost:${mockServerPort}"), + Times.exactly(2)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer() + proxy.rewriteUrl('http://www\\.someotherhost\\.com:(\\d+)/(\\w+)', 'http://localhost:$1/$2') + + proxy.start() + + String url = "http://www.someotherhost.com:${mockServerPort}/testRewriteHttpHost" + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse firstResponse = it.execute(new HttpGet(url)) + assertEquals("Did not receive HTTP 200 from mock server", 200, firstResponse.getStatusLine().getStatusCode()) + + String firstResponseBody = NewProxyServerTestUtil.toStringAndClose(firstResponse.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", firstResponseBody); + + CloseableHttpResponse secondResponse = it.execute(new HttpGet(url)) + assertEquals("Did not receive HTTP 200 from mock server", 200, secondResponse.getStatusLine().getStatusCode()) + + String secondResponseBody = NewProxyServerTestUtil.toStringAndClose(secondResponse.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", secondResponseBody); + }; + } + + @Test + void testRewriteHttpResource() { + mockServer.when(request() + .withMethod("GET") + .withPath("/rewrittenresource"), + Times.exactly(2)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer() + proxy.rewriteUrl('http://badhost:(\\d+)/badresource', 'http://localhost:$1/rewrittenresource') + + proxy.start() + + String url = "http://badhost:${mockServerPort}/badresource" + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String firstResponseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(url)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", firstResponseBody); + + String secondResponseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(url)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", secondResponseBody); + }; + } + + @Test + void testRewriteHttpsResource() { + mockServer.when(request() + .withMethod("GET") + .withPath("/rewrittenresource"), + Times.exactly(2)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.rewriteUrl('https://localhost:(\\d+)/badresource', 'https://localhost:$1/rewrittenresource') + + proxy.start() + + String url = "https://localhost:${mockServerPort}/badresource" + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String firstResponseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(url)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", firstResponseBody); + + String secondResponseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(url)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", secondResponseBody); + }; + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/AutoAuthTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/AutoAuthTest.groovy new file mode 100644 index 000000000..cb3b903b7 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/AutoAuthTest.groovy @@ -0,0 +1,99 @@ +package net.lightbody.bmp.proxy + +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.auth.AuthType +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockserver.matchers.Times +import org.mockserver.model.NottableString + +import static org.junit.Assert.assertEquals +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class AutoAuthTest extends MockServerTest { + BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testBasicAuthAddedToHttpRequest() { + // the base64-encoded rendering of "testUsername:testPassword" is dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA== + mockServer.when(request() + .withMethod("GET") + .withPath("/basicAuthHttp") + .withHeader("Authorization", "Basic dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA=="), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.autoAuthorization("localhost", "testUsername", "testPassword", AuthType.BASIC) + proxy.setTrustAllServers(true) + proxy.start() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/basicAuthHttp")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testBasicAuthAddedToHttpsRequest() { + // the base64-encoded rendering of "testUsername:testPassword" is dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA== + mockServer.when(request() + .withMethod("GET") + .withPath("/basicAuthHttp") + .withHeader("Authorization", "Basic dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA=="), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.autoAuthorization("localhost", "testUsername", "testPassword", AuthType.BASIC) + proxy.setTrustAllServers(true) + proxy.start() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("https://localhost:${mockServerPort}/basicAuthHttp")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testCanStopBasicAuth() { + // the base64-encoded rendering of "testUsername:testPassword" is dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA== + mockServer.when(request() + .withMethod("GET") + .withPath("/basicAuthHttp") + // require that the Auth header NOT be present + .withHeader(NottableString.not("Authorization"), NottableString.not("Basic dGVzdFVzZXJuYW1lOnRlc3RQYXNzd29yZA==")), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.autoAuthorization("localhost", "testUsername", "testPassword", AuthType.BASIC) + proxy.setTrustAllServers(true) + proxy.start() + + proxy.stopAutoAuthorization("localhost") + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/basicAuthHttp")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy new file mode 100644 index 000000000..16fd4b5c4 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BindAddressTest.groovy @@ -0,0 +1,106 @@ +package net.lightbody.bmp.proxy + +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.HttpGet +import org.apache.http.conn.HttpHostConnectException +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockserver.matchers.Times + +import static org.junit.Assert.assertEquals +import static org.junit.Assume.assumeNoException +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class BindAddressTest extends MockServerTest { + private BrowserMobProxy proxy + + @Before + void setUp() { + } + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testClientBindAddress() { + mockServer.when( + request().withMethod("GET") + .withPath("/clientbind"), + Times.unlimited() + ).respond(response().withStatusCode(200)) + + // bind to loopback. ProxyServerTest.getNewHtpClient creates an HTTP client that connects to a proxy at 127.0.0.1 + proxy = new BrowserMobProxyServer() + proxy.start(0, InetAddress.getLoopbackAddress()) + + NewProxyServerTestUtil.getNewHttpClient(proxy.getPort()).withCloseable { + def response = it.execute(new HttpGet("http://127.0.0.1:${mockServerPort}/clientbind")) + assertEquals(200, response.statusLine.statusCode) + } + } + + @Test(expected = HttpHostConnectException.class) + void testClientBindAddressCannotConnect() { + mockServer.when( + request().withMethod("GET") + .withPath("/clientbind"), + Times.unlimited() + ).respond(response().withStatusCode(200)) + + // find the local host address to bind to that isn't loopback. since ProxyServerTest.getNewHtpClient creates an HTTP client that + // connects to a proxy at 127.0.0.1, the HTTP client should *not* be able to connect to the proxy + InetAddress localHostAddr + try { + localHostAddr = InetAddress.getLocalHost() + } catch (UnknownHostException e) { + assumeNoException("Could not get a localhost address. Skipping test.", e) + return + } + + proxy = new BrowserMobProxyServer() + proxy.start(0, localHostAddr) + + NewProxyServerTestUtil.getNewHttpClient(proxy.getPort()).withCloseable { + it.execute(new HttpGet("http://127.0.0.1:${mockServerPort}/clientbind")) + } + } + + @Test + void testServerBindAddress() { + mockServer.when( + request().withMethod("GET") + .withPath("/serverbind"), + Times.unlimited() + ).respond(response().withStatusCode(200)) + + // bind outgoing traffic to loopback. since the mockserver is running on localhost with a wildcard address, this should succeed. + proxy = new BrowserMobProxyServer() + proxy.start(0, null, InetAddress.getLoopbackAddress()) + + NewProxyServerTestUtil.getNewHttpClient(proxy.getPort()).withCloseable { + def response = it.execute(new HttpGet("http://127.0.0.1:${mockServerPort}/serverbind")) + assertEquals(200, response.statusLine.statusCode) + } + } + + @Test + void testServerBindAddressCannotConnect() { + // bind outgoing traffic to loopback. since loopback cannot reach external addresses, this should fail. + proxy = new BrowserMobProxyServer() + proxy.start(0, null, InetAddress.getLoopbackAddress()) + + NewProxyServerTestUtil.getNewHttpClient(proxy.getPort()).withCloseable { + def response = it.execute(new HttpGet("http://www.google.com")) + assertEquals("Expected a 502 Bad Gateway when connecting to an external address after binding to loopback", 502, response.statusLine.statusCode) + } + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BlacklistTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BlacklistTest.groovy new file mode 100644 index 000000000..a7f3f85a8 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/BlacklistTest.groovy @@ -0,0 +1,204 @@ +package net.lightbody.bmp.proxy + +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockserver.matchers.Times + +import static org.hamcrest.Matchers.isEmptyOrNullString +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertThat +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class BlacklistTest extends MockServerTest { + BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testBlacklistedHttpRequestReturnsBlacklistStatusCode() { + proxy = new BrowserMobProxyServer() + proxy.start() + int proxyPort = proxy.getPort() + + proxy.blacklistRequests("http://www\\.blacklisted\\.domain/.*", 405) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("http://www.blacklisted.domain/someresource")) + assertEquals("Did not receive blacklisted status code in response", 405, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertThat("Expected blacklisted response to contain 0-length body", responseBody, isEmptyOrNullString()) + } + } + + @Test + void testBlacklistedHttpsRequestReturnsBlacklistStatusCode() { + // need to set up a mock server to handle the CONNECT, since that is not blacklisted + mockServer.when(request() + .withMethod("GET") + .withPath("/thisrequestshouldnotoccur"), + Times.unlimited()) + .respond(response() + .withStatusCode(500) + .withBody("this URL should never be called")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + int proxyPort = proxy.getPort() + + proxy.blacklistRequests("https://localhost:${mockServerPort}/.*", 405) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("https://localhost:${mockServerPort}/thisrequestshouldnotoccur")) + assertEquals("Did not receive blacklisted status code in response", 405, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertThat("Expected blacklisted response to contain 0-length body", responseBody, isEmptyOrNullString()) + } + } + + @Test + void testCanBlacklistSingleHttpResource() { + mockServer.when(request() + .withMethod("GET") + .withPath("/blacklistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(500) + .withBody("this URL should never be called")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/nonblacklistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("not blacklisted")) + + proxy = new BrowserMobProxyServer() + proxy.start() + int proxyPort = proxy.getPort() + + proxy.blacklistRequests("http://localhost:${mockServerPort}/blacklistedresource", 405) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse nonBlacklistedResourceResponse = it.execute(new HttpGet("http://localhost:${mockServerPort}/nonblacklistedresource")) + assertEquals("Did not receive blacklisted status code in response", 200, nonBlacklistedResourceResponse.getStatusLine().getStatusCode()) + + String nonBlacklistedResponseBody = NewProxyServerTestUtil.toStringAndClose(nonBlacklistedResourceResponse.getEntity().getContent()) + assertEquals("Did not receive expected response from mock server", "not blacklisted", nonBlacklistedResponseBody) + + CloseableHttpResponse blacklistedResourceResponse = it.execute(new HttpGet("http://localhost:${mockServerPort}/blacklistedresource")) + assertEquals("Did not receive blacklisted status code in response", 405, blacklistedResourceResponse.getStatusLine().getStatusCode()) + + String blacklistedResponseBody = NewProxyServerTestUtil.toStringAndClose(blacklistedResourceResponse.getEntity().getContent()) + assertThat("Expected blacklisted response to contain 0-length body", blacklistedResponseBody, isEmptyOrNullString()) + } + } + + @Test + void testCanBlacklistSingleHttpsResource() { + mockServer.when(request() + .withMethod("GET") + .withPath("/blacklistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(500) + .withBody("this URL should never be called")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/nonblacklistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("not blacklisted")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + int proxyPort = proxy.getPort() + + proxy.blacklistRequests("https://localhost:${mockServerPort}/blacklistedresource", 405) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse nonBlacklistedResourceResponse = it.execute(new HttpGet("https://localhost:${mockServerPort}/nonblacklistedresource")) + assertEquals("Did not receive blacklisted status code in response", 200, nonBlacklistedResourceResponse.getStatusLine().getStatusCode()) + + String nonBlacklistedResponseBody = NewProxyServerTestUtil.toStringAndClose(nonBlacklistedResourceResponse.getEntity().getContent()) + assertEquals("Did not receive expected response from mock server", "not blacklisted", nonBlacklistedResponseBody) + + CloseableHttpResponse blacklistedResourceResponse = it.execute(new HttpGet("https://localhost:${mockServerPort}/blacklistedresource")) + assertEquals("Did not receive blacklisted status code in response", 405, blacklistedResourceResponse.getStatusLine().getStatusCode()) + + String blacklistedResponseBody = NewProxyServerTestUtil.toStringAndClose(blacklistedResourceResponse.getEntity().getContent()) + assertThat("Expected blacklisted response to contain 0-length body", blacklistedResponseBody, isEmptyOrNullString()) + } + } + + @Test + void testCanBlacklistConnectExplicitly() { + mockServer.when(request() + .withMethod("GET") + .withPath("/blacklistconnect"), + Times.unlimited()) + .respond(response() + .withStatusCode(500) + .withBody("this URL should never be called")) + + proxy = new BrowserMobProxyServer() + proxy.start() + int proxyPort = proxy.getPort() + + // CONNECT requests don't contain the path to the resource, only the server and port + proxy.blacklistRequests("https://localhost:${mockServerPort}", 405, "CONNECT") + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse blacklistedResourceResponse = it.execute(new HttpGet("https://localhost:${mockServerPort}/blacklistconnect")) + assertEquals("Did not receive blacklisted status code in response", 405, blacklistedResourceResponse.getStatusLine().getStatusCode()) + + String blacklistedResponseBody = NewProxyServerTestUtil.toStringAndClose(blacklistedResourceResponse.getEntity().getContent()) + assertThat("Expected blacklisted response to contain 0-length body", blacklistedResponseBody, isEmptyOrNullString()) + } + } + + @Test + void testBlacklistDoesNotApplyToCONNECT() { + mockServer.when(request() + .withMethod("GET") + .withPath("/connectNotBlacklisted"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + int proxyPort = proxy.getPort() + + // HTTP CONNECTs should not be blacklisted unless the method is explicitly specified + proxy.blacklistRequests("https://localhost:${mockServerPort}", 405) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("https://localhost:${mockServerPort}/connectNotBlacklisted")) + assertEquals("Expected to receive response from mock server after successful CONNECT", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertEquals("Expected to receive HTTP 200 and success message from server", "success", responseBody) + } + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/ChainedProxyAuthTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/ChainedProxyAuthTest.groovy new file mode 100644 index 000000000..7ce23012b --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/ChainedProxyAuthTest.groovy @@ -0,0 +1,114 @@ +package net.lightbody.bmp.proxy + +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.auth.AuthType +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.littleshoot.proxy.HttpProxyServer +import org.littleshoot.proxy.ProxyAuthenticator +import org.littleshoot.proxy.impl.DefaultHttpProxyServer +import org.mockserver.matchers.Times + +import static org.junit.Assert.assertEquals +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class ChainedProxyAuthTest extends MockServerTest { + BrowserMobProxy proxy + + HttpProxyServer upstreamProxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + + upstreamProxy?.abort() + } + + @Test + void testAutoProxyAuthSuccessful() { + String proxyUser = "proxyuser" + String proxyPassword = "proxypassword" + + upstreamProxy = DefaultHttpProxyServer.bootstrap() + .withProxyAuthenticator(new ProxyAuthenticator() { + @Override + boolean authenticate(String user, String password) { + return proxyUser.equals(user) && proxyPassword.equals(password) + } + + @Override + String getRealm() { + return "some-realm" + } + }) + .withPort(0) + .start() + + mockServer.when(request() + .withMethod("GET") + .withPath("/proxyauth"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.setChainedProxy(upstreamProxy.getListenAddress()) + proxy.chainedProxyAuthorization(proxyUser, proxyPassword, AuthType.BASIC) + proxy.setTrustAllServers(true) + proxy.start() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("https://localhost:${mockServerPort}/proxyauth")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testAutoProxyAuthFailure() { + String proxyUser = "proxyuser" + String proxyPassword = "proxypassword" + + upstreamProxy = DefaultHttpProxyServer.bootstrap() + .withProxyAuthenticator(new ProxyAuthenticator() { + @Override + boolean authenticate(String user, String password) { + return proxyUser.equals(user) && proxyPassword.equals(password) + } + + @Override + String getRealm() { + return "some-realm" + } + }) + .withPort(0) + .start() + + mockServer.when(request() + .withMethod("GET") + .withPath("/proxyauth"), + Times.exactly(1)) + .respond(response() + .withStatusCode(500) + .withBody("shouldn't happen")) + + proxy = new BrowserMobProxyServer(); + proxy.setChainedProxy(upstreamProxy.getListenAddress()) + proxy.chainedProxyAuthorization(proxyUser, "wrongpassword", AuthType.BASIC) + proxy.setTrustAllServers(true) + proxy.start() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("https://localhost:${mockServerPort}/proxyauth")) + assertEquals("Expected to receive a Bad Gateway due to incorrect proxy authentication credentials", 502, response.getStatusLine().statusCode) + }; + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/FilterChainTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/FilterChainTest.groovy new file mode 100644 index 000000000..d6c9e7ed1 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/FilterChainTest.groovy @@ -0,0 +1,319 @@ +package net.lightbody.bmp.proxy + +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.http.HttpObject +import io.netty.handler.codec.http.HttpRequest +import io.netty.handler.codec.http.HttpResponse +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.littleshoot.proxy.HttpFilters +import org.littleshoot.proxy.HttpFiltersAdapter +import org.littleshoot.proxy.HttpFiltersSourceAdapter +import org.mockserver.matchers.Times + +import java.util.concurrent.atomic.AtomicBoolean + +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertTrue +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +/** + * Tests for the {@link net.lightbody.bmp.filters.BrowserMobHttpFilterChain}. + */ +class FilterChainTest extends MockServerTest { + private BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testFilterExceptionDoesNotAbortRequest() { + // tests that an exception in one filter does not prevent the request from completing + + mockServer.when(request() + .withMethod("GET") + .withPath("/testfilterexceptionpreservesrequest"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + + proxy.addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new ThrowExceptionFilter() + } + }) + + proxy.start() + + String requestUrl = "http://localhost:${mockServerPort}/testfilterexceptionpreservesrequest" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testFilterExceptionDoesNotAbortFilterChain() { + // tests that an exception in the first filter in a filter chain does not prevent subsequent filters from being invoked + + mockServer.when(request() + .withMethod("GET") + .withPath("/testfilterexceptionpreserveschain"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + + proxy.addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new ThrowExceptionFilter() + } + }) + + // rather than test every filter method (which would be verbose), test three filter methods that are representative + // of the entire request-response lifecycle are still fired + final AtomicBoolean clientToProxyRequest = new AtomicBoolean() + final AtomicBoolean serverToProxyResponse = new AtomicBoolean() + final AtomicBoolean proxyToClientResponse = new AtomicBoolean() + + proxy.addHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { + return new HttpFiltersAdapter(originalRequest) { + @Override + HttpResponse clientToProxyRequest(HttpObject httpObject) { + clientToProxyRequest.set(true) + return super.clientToProxyRequest(httpObject) + } + + @Override + HttpObject serverToProxyResponse(HttpObject httpObject) { + serverToProxyResponse.set(true) + return super.serverToProxyResponse(httpObject) + } + + @Override + HttpObject proxyToClientResponse(HttpObject httpObject) { + proxyToClientResponse.set(true) + return super.proxyToClientResponse(httpObject) + } + } + } + }) + + proxy.start() + + String requestUrl = "http://localhost:${mockServerPort}/testfilterexceptionpreserveschain" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + assertTrue("Expected second filter method to be invoked after first filter threw exception", clientToProxyRequest.get()) + assertTrue("Expected second filter method to be invoked after first filter threw exception", serverToProxyResponse.get()) + assertTrue("Expected second filter method to be invoked after first filter threw exception", proxyToClientResponse.get()) + } + + @Test + void testRequestResponseFilterExceptionsDoNotAbortRequest() { + // tests that exceptions thrown by the RequestFilter and ResponseFilter do not abort the request + mockServer.when(request() + .withMethod("GET") + .withPath("/testrequestresponsefilterpreservesrequest"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + + proxy.addRequestFilter({ a, b, c -> + throw new RuntimeException("Throwing exception from RequestFilter") + }) + + proxy.addResponseFilter({ a, b, c -> + throw new RuntimeException("Throwing exception from ResponseFilter") + }) + + proxy.start() + + String requestUrl = "http://localhost:${mockServerPort}/testrequestresponsefilterpreservesrequest" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testRequestResponseFilterExceptionsDoNotAbortFilterChain() { + // tests that exceptions thrown by the RequestFilter and ResponseFilter do not prevent subsequent filters from being invoked + mockServer.when(request() + .withMethod("GET") + .withPath("/testrequestresponsefilterpreserveschain"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + + final AtomicBoolean secondRequestFilterInvoked = new AtomicBoolean() + final AtomicBoolean secondResponseFilterInvoked = new AtomicBoolean() + + proxy.addRequestFilter({ a, b, c -> + // actually the second filter invoked, since the following addRequestFilter will place itself at the front of the filter chain + secondRequestFilterInvoked.set(true) + }) + + proxy.addRequestFilter({ a, b, c -> + assertFalse("Did not expect second request filter to be invoked yet", secondRequestFilterInvoked.get()) + throw new RuntimeException("Throwing exception from RequestFilter") + }) + + proxy.addResponseFilter({ a, b, c -> + assertFalse("Did not expect second response filter to be invoked yet", secondResponseFilterInvoked.get()) + throw new RuntimeException("Throwing exception from ResponseFilter") + }) + + proxy.addResponseFilter({ a, b, c -> + secondResponseFilterInvoked.set(true) + }) + + proxy.start() + + String requestUrl = "http://localhost:${mockServerPort}/testrequestresponsefilterpreserveschain" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + assertTrue("Expected second request filter to be invoked", secondRequestFilterInvoked.get()) + assertTrue("Expected second response filter to be invoked", secondResponseFilterInvoked.get()) + } + + /** + * An HttpFilters implementation that throws an exception from every filter method. + */ + static class ThrowExceptionFilter implements HttpFilters { + + @Override + HttpResponse clientToProxyRequest(HttpObject httpObject) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + HttpResponse proxyToServerRequest(HttpObject httpObject) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerRequestSending() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerRequestSent() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + HttpObject serverToProxyResponse(HttpObject httpObject) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void serverToProxyResponseTimedOut() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void serverToProxyResponseReceiving() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void serverToProxyResponseReceived() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + HttpObject proxyToClientResponse(HttpObject httpObject) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerConnectionQueued() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + InetSocketAddress proxyToServerResolutionStarted(String resolvingServerHostAndPort) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerResolutionFailed(String hostAndPort) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerResolutionSucceeded(String serverHostAndPort, InetSocketAddress resolvedRemoteAddress) { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerConnectionStarted() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerConnectionSSLHandshakeStarted() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerConnectionFailed() { + throw new RuntimeException("Throwing exception from filter") + } + + @Override + void proxyToServerConnectionSucceeded(ChannelHandlerContext serverCtx) { + throw new RuntimeException("Throwing exception from filter") + } + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/NewHarTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/NewHarTest.groovy new file mode 100644 index 000000000..80a599754 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/NewHarTest.groovy @@ -0,0 +1,1025 @@ +package net.lightbody.bmp.proxy + +import com.google.common.collect.Iterables +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.core.har.Har +import net.lightbody.bmp.core.har.HarContent +import net.lightbody.bmp.core.har.HarCookie +import net.lightbody.bmp.core.har.HarEntry +import net.lightbody.bmp.core.har.HarNameValuePair +import net.lightbody.bmp.core.har.HarResponse +import net.lightbody.bmp.core.har.HarTimings +import net.lightbody.bmp.filters.util.HarCaptureUtil +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.config.RequestConfig +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockito.invocation.InvocationOnMock +import org.mockito.stubbing.Answer +import org.mockserver.matchers.Times +import org.mockserver.model.Header + +import java.text.SimpleDateFormat +import java.util.concurrent.TimeUnit + +import static org.hamcrest.Matchers.empty +import static org.hamcrest.Matchers.equalTo +import static org.hamcrest.Matchers.greaterThan +import static org.hamcrest.Matchers.greaterThanOrEqualTo +import static org.hamcrest.Matchers.hasSize +import static org.hamcrest.Matchers.not +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertNotNull +import static org.junit.Assert.assertNull +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +/** + * HAR tests using the new interface. When the legacy interface is retired, these tests should be combined with the tests currently in HarTest. + */ +class NewHarTest extends MockServerTest { + private BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testDnsTimingPopulated() { + // mock up a resolver with a DNS resolution delay + AdvancedHostResolver mockResolver = mock(AdvancedHostResolver.class); + when(mockResolver.resolve("localhost")).then(new Answer>() { + @Override + public Collection answer(InvocationOnMock invocationOnMock) throws Throwable { + TimeUnit.SECONDS.sleep(1); + return Collections.singleton(InetAddress.getByName("localhost")); + } + }); + + // mock up a response to serve + mockServer.when(request() + .withMethod("GET") + .withPath("/testDnsTimingPopulated"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setHostNameResolver(mockResolver); + + proxy.start(); + int proxyPort = proxy.getPort(); + + proxy.newHar(); + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testDnsTimingPopulated")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar(); + + assertNotNull("HAR should not be null", har); + assertNotNull("HAR log should not be null", har.getLog()); + assertNotNull("HAR log entries should not be null", har.getLog().getEntries()); + assertFalse("HAR entries should exist", har.getLog().getEntries().isEmpty()); + + HarEntry entry = Iterables.get(har.getLog().getEntries(), 0); + assertThat("Expected at least 1 second DNS delay", entry.getTimings().getDns(), greaterThanOrEqualTo(1000L)); + } + + @Test + void testCaptureResponseCookiesInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testCaptureResponseCookiesInHar"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success") + .withHeader("Set-Cookie", "max-age-cookie=mock-value; Max-Age=3153600000") + .withHeader("Set-Cookie", "expires-cookie=mock-value; Expires=Wed, 15 Mar 2022 12:00:00 GMT")) + + proxy = new BrowserMobProxyServer(); + proxy.setHarCaptureTypes([CaptureType.RESPONSE_COOKIES] as Set) + proxy.setTrustAllServers(true) + proxy.start() + + proxy.newHar() + + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX", Locale.US) + Date expiresDate = df.parse("2022-03-15 12:00:00Z") + + // expiration of the cookie won't be before this date, since the request hasn't yet been issued + Date maxAgeCookieNotBefore = new Date(System.currentTimeMillis() + 3153600000L) + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("https://localhost:${mockServerPort}/testCaptureResponseCookiesInHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + assertThat("Expected to find two cookies in the HAR", har.getLog().getEntries().first().response.cookies, hasSize(2)) + + HarCookie maxAgeCookie = har.getLog().getEntries().first().response.cookies[0] + HarCookie expiresCookie = har.getLog().getEntries().first().response.cookies[1] + + assertEquals("Incorrect cookie name in HAR", "max-age-cookie", maxAgeCookie.name) + assertEquals("Incorrect cookie value in HAR", "mock-value", maxAgeCookie.value) + assertThat("Incorrect expiration date in cookie with Max-Age", maxAgeCookie.expires, greaterThan(maxAgeCookieNotBefore)) + + assertEquals("Incorrect cookie name in HAR", "expires-cookie", expiresCookie.name) + assertEquals("Incorrect cookie value in HAR", "mock-value", expiresCookie.value) + + assertThat("Incorrect expiration date in cookie with Expires", expiresCookie.expires, equalTo(expiresDate)) + } + + @Test + void testCaptureResponseHeaderInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testCaptureResponseHeaderInHar"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success") + .withHeader(new Header("Mock-Header", "mock value"))) + + proxy = new BrowserMobProxyServer(); + proxy.setHarCaptureTypes([CaptureType.RESPONSE_HEADERS] as Set) + proxy.start() + + proxy.newHar() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testCaptureResponseHeaderInHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + List headers = har.getLog().getEntries().first().response.headers + assertThat("Expected to find headers in the HAR", headers, not(empty())) + + HarNameValuePair header = headers.find { it.name == "Mock-Header" } + assertNotNull("Expected to find header with name Mock-Header in HAR", header) + assertEquals("Incorrect header value for Mock-Header", "mock value", header.value) + } + + @Test + void testCaptureResponseContentInHar() { + String expectedResponseBody = "success"; + String responseContentType = "text/plain; charset=UTF-8"; + + mockServer.when(request() + .withMethod("GET") + .withPath("/testCaptureResponseContentInHar"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody(expectedResponseBody) + .withHeader(new Header("Content-Type", responseContentType))) + + proxy = new BrowserMobProxyServer(); + proxy.setHarCaptureTypes(CaptureType.RESPONSE_CONTENT) + proxy.start() + + proxy.newHar() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testCaptureResponseContentInHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", expectedResponseBody, responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + HarContent content = har.getLog().getEntries().first().response.content + assertNotNull("Expected to find HAR content", content) + + assertEquals("Expected to capture response mimeType in HAR", responseContentType, content.mimeType) + + assertEquals("Expected to capture body content in HAR", expectedResponseBody, content.text) + assertEquals("Unexpected response content length", expectedResponseBody.getBytes("UTF-8").length, content.size) + } + + @Test + void testCaptureResponseInfoWhenResponseCaptureDisabled() { + String expectedResponseBody = "success"; + String responseContentType = "text/plain; charset=UTF-8"; + + mockServer.when(request() + .withMethod("GET") + .withPath("/testCaptureResponseContentInHar"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody(expectedResponseBody) + .withHeader(new Header("Content-Type", responseContentType))) + + proxy = new BrowserMobProxyServer(); + proxy.setHarCaptureTypes([] as Set) + proxy.start() + + proxy.newHar() + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testCaptureResponseContentInHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", expectedResponseBody, responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + HarContent content = har.getLog().getEntries().first().response.content + assertNotNull("Expected to find HAR content", content) + + assertEquals("Expected to capture response mimeType in HAR", responseContentType, content.mimeType) + + assertNull("Expected to not capture body content in HAR", content.text) + } + + @Test + void testEndHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testEndHar"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("success") + .withHeader(new Header("Content-Type", "text/plain; charset=UTF-8"))) + + proxy = new BrowserMobProxyServer(); + proxy.setHarCaptureTypes([CaptureType.RESPONSE_CONTENT] as Set) + proxy.start() + + proxy.newHar() + + // putting tests in code blocks to avoid variable name collisions + regularHarCanCapture: { + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testEndHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.endHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + HarContent content = har.getLog().getEntries().first().response.content + assertNotNull("Expected to find HAR content", content) + + assertEquals("Expected to capture body content in HAR", "success", content.text) + + assertThat("Expected HAR page timing onLoad value to be populated", har.log.pages.last().pageTimings.onLoad, greaterThan(0L)) + } + + harEmptyAfterEnd: { + Har emptyHar = proxy.getHar() + + assertNull("Expected getHar() to return null after calling endHar()", emptyHar) + } + + harStillEmptyAfterRequest: { + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testEndHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har stillEmptyHar = proxy.getHar() + + assertNull("Expected getHar() to return null after calling endHar()", stillEmptyHar) + } + + newHarInitiallyEmpty: { + Har newHar = proxy.newHar() + + assertNull("Expected newHar() to return the old (null) har", newHar) + } + + newHarCanCapture: { + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testEndHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har populatedHar = proxy.getHar() + + assertThat("Expected to find entries in the HAR", populatedHar.getLog().getEntries(), not(empty())) + + HarContent newContent = populatedHar.getLog().getEntries().first().response.content + assertNotNull("Expected to find HAR content", newContent) + + assertEquals("Expected to capture body content in HAR", "success", newContent.text) + } + } + + @Test + void testNewPageReturnsHarInPreviousState() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testEndHar"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("success") + .withHeader(new Header("Content-Type", "text/plain; charset=UTF-8"))) + + proxy = new BrowserMobProxyServer(); + proxy.setHarCaptureTypes([CaptureType.RESPONSE_CONTENT] as Set) + proxy.start() + + proxy.newHar("first-page") + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testEndHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + HarContent content = har.getLog().getEntries().first().response.content + assertNotNull("Expected to find HAR content", content) + + assertEquals("Expected to capture body content in HAR", "success", content.text) + + assertEquals("Expected only one HAR page to be created", 1, har.log.pages.size()) + assertEquals("Expected id of HAR page to be 'first-page'", "first-page", har.log.pages.first().id) + + Har harWithFirstPageOnly = proxy.newPage("second-page") + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://localhost:${mockServerPort}/testEndHar")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har harWithSecondPage = proxy.getHar() + + assertEquals("Expected HAR to contain first and second page page", 2, harWithSecondPage.log.pages.size()) + assertEquals("Expected id of second HAR page to be 'second-page'", "second-page", harWithSecondPage.log.pages[1].id) + + assertEquals("Expected HAR returned from newPage() not to contain second page", 1, harWithFirstPageOnly.log.pages.size()) + assertEquals("Expected id of HAR page to be 'first-page'", "first-page", harWithFirstPageOnly.log.pages.first().id) + } + + @Test + void testCaptureHttpRequestUrlInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/httprequesturlcaptured"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.start() + + proxy.newHar() + + String requestUrl = "http://localhost:${mockServerPort}/httprequesturlcaptured" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(requestUrl)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + } + + @Test + void testCaptureHttpRequestUrlWithQueryParamInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/httprequesturlcaptured") + .withQueryStringParameter("param1", "value1"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.start() + + proxy.newHar() + + String requestUrl = "http://localhost:${mockServerPort}/httprequesturlcaptured?param1=value1" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(requestUrl)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + assertThat("Expected to find query parameters in the HAR", har.log.entries[0].request.queryString, not(empty())); + + assertEquals("Expected first query parameter name to be param1", "param1", har.log.entries[0].request.queryString[0].name) + assertEquals("Expected first query parameter value to be value1", "value1", har.log.entries[0].request.queryString[0].value) + } + + @Test + void testCaptureHttpsRequestUrlInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/httpsrequesturlcaptured") + .withQueryStringParameter("param1", "value1"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + + proxy.newHar() + + // use HTTPS to force a CONNECT. subsequent requests through the tunnel will only contain the resource path, not the full hostname. + String requestUrl = "https://localhost:${mockServerPort}/httpsrequesturlcaptured?param1=value1" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(requestUrl)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + assertThat("Expected to find query parameters in the HAR", har.log.entries[0].request.queryString, not(empty())); + + assertEquals("Expected first query parameter name to be param1", "param1", har.log.entries[0].request.queryString[0].name) + assertEquals("Expected first query parameter value to be value1", "value1", har.log.entries[0].request.queryString[0].value) + } + + @Test + void testCaptureHttpsRewrittenUrlInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/httpsrewrittenurlcaptured") + .withQueryStringParameter("param1", "value1"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.rewriteUrl("https://localhost:${mockServerPort}/originalurl(.*)", "https://localhost:${mockServerPort}/httpsrewrittenurlcaptured\$1") + proxy.setTrustAllServers(true) + proxy.start() + + proxy.newHar() + + String requestUrl = "https://localhost:${mockServerPort}/originalurl?param1=value1" + String expectedRewrittenUrl = "https://localhost:${mockServerPort}/httpsrewrittenurlcaptured?param1=value1" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", expectedRewrittenUrl, capturedUrl) + + assertThat("Expected to find query parameters in the HAR", har.log.entries[0].request.queryString, not(empty())); + + assertEquals("Expected first query parameter name to be param1", "param1", har.log.entries[0].request.queryString[0].name) + assertEquals("Expected first query parameter value to be value1", "value1", har.log.entries[0].request.queryString[0].value) + } + + @Test + void testMitmDisabledStopsHTTPCapture() { + mockServer.when(request() + .withMethod("GET") + .withPath("/httpmitmdisabled"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("Response over HTTP")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/httpsmitmdisabled"), + Times.exactly(2)) + .respond(response() + .withStatusCode(200) + .withBody("Response over HTTPS")) + + proxy = new BrowserMobProxyServer(); + proxy.setMitmDisabled(true); + proxy.start() + + proxy.newHar() + + httpsRequest: { + String httpsUrl = "https://localhost:${mockServerPort}/httpsmitmdisabled" + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(httpsUrl)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "Response over HTTPS", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find no entries in the HAR because MITM is disabled", har.getLog().getEntries(), empty()) + } + + httpRequest: { + String httpUrl = "http://localhost:${mockServerPort}/httpmitmdisabled" + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(httpUrl)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "Response over HTTP", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", httpUrl, capturedUrl) + } + + secondHttpsRequest: { + String httpsUrl = "https://localhost:${mockServerPort}/httpsmitmdisabled" + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet(httpsUrl)).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "Response over HTTPS", responseBody); + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find only the HTTP entry in the HAR", har.getLog().getEntries(), hasSize(1)) + } + + } + + @Test + void testHttpDnsFailureCapturedInHar() { + AdvancedHostResolver mockFailingResolver = mock(AdvancedHostResolver) + when(mockFailingResolver.resolve("www.doesnotexist.address")).thenReturn([]) + + proxy = new BrowserMobProxyServer(); + proxy.setHostNameResolver(mockFailingResolver) + proxy.start() + + proxy.newHar() + + String requestUrl = "http://www.doesnotexist.address/some-resource" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 502 from proxy", 502, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Error in HAR response did not match expected DNS failure error message", HarCaptureUtil.getResolutionFailedErrorMessage("www.doesnotexist.address"), harResponse.error) + assertEquals("Expected HTTP status code of 0 for failed request", HarCaptureUtil.HTTP_STATUS_CODE_FOR_FAILURE, harResponse.status) + assertEquals("Expected unknown HTTP version for failed request", HarCaptureUtil.HTTP_VERSION_STRING_FOR_FAILURE, harResponse.httpVersion) + assertEquals("Expected default value for headersSize for failed request", -1L, harResponse.headersSize) + assertEquals("Expected default value for bodySize for failed request", -1L, harResponse.bodySize) + + HarTimings harTimings = har.log.entries[0].timings + assertNotNull("No HAR timings found", harTimings) + + assertThat("Expected dns time to be populated after dns resolution failure", harTimings.getDns(TimeUnit.NANOSECONDS), greaterThan(0L)) + + assertEquals("Expected HAR timings to contain default values after DNS failure", -1L, harTimings.getConnect(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", -1L, harTimings.getSsl(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", 0L, harTimings.getSend(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", 0L, harTimings.getWait(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", 0L, harTimings.getReceive(TimeUnit.NANOSECONDS)) + } + + @Test + void testHttpsDnsFailureCapturedInHar() { + AdvancedHostResolver mockFailingResolver = mock(AdvancedHostResolver) + when(mockFailingResolver.resolve("www.doesnotexist.address")).thenReturn([]) + + proxy = new BrowserMobProxyServer(); + proxy.setHostNameResolver(mockFailingResolver) + proxy.start() + + proxy.newHar() + + String requestUrl = "https://www.doesnotexist.address/some-resource" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 502 from proxy", 502, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match expected HTTP CONNECT URL", "https://www.doesnotexist.address", capturedUrl) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Error in HAR response did not match expected DNS failure error message", HarCaptureUtil.getResolutionFailedErrorMessage("www.doesnotexist.address:443"), harResponse.error) + assertEquals("Expected HTTP status code of 0 for failed request", HarCaptureUtil.HTTP_STATUS_CODE_FOR_FAILURE, harResponse.status) + assertEquals("Expected unknown HTTP version for failed request", HarCaptureUtil.HTTP_VERSION_STRING_FOR_FAILURE, harResponse.httpVersion) + assertEquals("Expected default value for headersSize for failed request", -1L, harResponse.headersSize) + assertEquals("Expected default value for bodySize for failed request", -1L, harResponse.bodySize) + + HarTimings harTimings = har.log.entries[0].timings + assertNotNull("No HAR timings found", harTimings) + + assertThat("Expected dns time to be populated after dns resolution failure", harTimings.getDns(TimeUnit.NANOSECONDS), greaterThan(0L)) + + assertEquals("Expected HAR timings to contain default values after DNS failure", -1L, harTimings.getConnect(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", -1L, harTimings.getSsl(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", 0L, harTimings.getSend(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", 0L, harTimings.getWait(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after DNS failure", 0L, harTimings.getReceive(TimeUnit.NANOSECONDS)) + } + + @Test + void testHttpConnectTimeoutCapturedInHar() { + proxy = new BrowserMobProxyServer(); + proxy.start() + + proxy.newHar() + + // TCP port 2 is reserved for "CompressNET Management Utility". since it's almost certainly not in use, connections + // to port 2 will fail. + String requestUrl = "http://localhost:2/some-resource" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 502 from proxy", 502, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + assertEquals("Expected IP address to be populated", "127.0.0.1", har.log.entries[0].serverIPAddress) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Error in HAR response did not match expected connection failure error message", HarCaptureUtil.getConnectionFailedErrorMessage(), harResponse.error) + assertEquals("Expected HTTP status code of 0 for failed request", HarCaptureUtil.HTTP_STATUS_CODE_FOR_FAILURE, harResponse.status) + assertEquals("Expected unknown HTTP version for failed request", HarCaptureUtil.HTTP_VERSION_STRING_FOR_FAILURE, harResponse.httpVersion) + assertEquals("Expected default value for headersSize for failed request", -1L, harResponse.headersSize) + assertEquals("Expected default value for bodySize for failed request", -1L, harResponse.bodySize) + + HarTimings harTimings = har.log.entries[0].timings + assertNotNull("No HAR timings found", harTimings) + + assertThat("Expected dns time to be populated after connection failure", harTimings.getDns(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected connect time to be populated after connection failure", harTimings.getConnect(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertEquals("Expected HAR timings to contain default values after connection failure", -1L, harTimings.getSsl(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after connection failure", 0L, harTimings.getSend(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after connection failure", 0L, harTimings.getWait(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after connection failure", 0L, harTimings.getReceive(TimeUnit.NANOSECONDS)) + } + + @Test + void testHttpsConnectTimeoutCapturedInHar() { + proxy = new BrowserMobProxyServer(); + proxy.start() + + proxy.newHar() + + // TCP port 2 is reserved for "CompressNET Management Utility". since it's almost certainly not in use, connections + // to port 2 will fail. + String requestUrl = "https://localhost:2/some-resource" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 502 from proxy", 502, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", "https://localhost:2", capturedUrl) + + assertEquals("Expected IP address to be populated", "127.0.0.1", har.log.entries[0].serverIPAddress) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Error in HAR response did not match expected connection failure error message", HarCaptureUtil.getConnectionFailedErrorMessage(), harResponse.error) + assertEquals("Expected HTTP status code of 0 for failed request", HarCaptureUtil.HTTP_STATUS_CODE_FOR_FAILURE, harResponse.status) + assertEquals("Expected unknown HTTP version for failed request", HarCaptureUtil.HTTP_VERSION_STRING_FOR_FAILURE, harResponse.httpVersion) + assertEquals("Expected default value for headersSize for failed request", -1L, harResponse.headersSize) + assertEquals("Expected default value for bodySize for failed request", -1L, harResponse.bodySize) + + HarTimings harTimings = har.log.entries[0].timings + assertNotNull("No HAR timings found", harTimings) + + assertThat("Expected dns time to be populated after connection failure", harTimings.getDns(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected connect time to be populated after connection failure", harTimings.getConnect(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertEquals("Expected HAR timings to contain default values after connection failure", -1L, harTimings.getSsl(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after connection failure", 0L, harTimings.getSend(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after connection failure", 0L, harTimings.getWait(TimeUnit.NANOSECONDS)) + assertEquals("Expected HAR timings to contain default values after connection failure", 0L, harTimings.getReceive(TimeUnit.NANOSECONDS)) + } + + @Test + void testHttpResponseTimeoutCapturedInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testResponseTimeoutCapturedInHar"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withDelay(TimeUnit.SECONDS, 10) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.setIdleConnectionTimeout(3, TimeUnit.SECONDS) + proxy.start() + + proxy.newHar() + + String requestUrl = "http://localhost:${mockServerPort}/testResponseTimeoutCapturedInHar" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 504 from proxy", 504, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + assertEquals("Expected IP address to be populated", "127.0.0.1", har.log.entries[0].serverIPAddress) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Error in HAR response did not match expected response timeout error message", HarCaptureUtil.getResponseTimedOutErrorMessage(), harResponse.error) + assertEquals("Expected HTTP status code of 0 for response timeout", HarCaptureUtil.HTTP_STATUS_CODE_FOR_FAILURE, harResponse.status) + assertEquals("Expected unknown HTTP version for response timeout", HarCaptureUtil.HTTP_VERSION_STRING_FOR_FAILURE, harResponse.httpVersion) + assertEquals("Expected default value for headersSize for response timeout", -1L, harResponse.headersSize) + assertEquals("Expected default value for bodySize for response timeout", -1L, harResponse.bodySize) + + HarTimings harTimings = har.log.entries[0].timings + assertNotNull("No HAR timings found", harTimings) + + assertEquals("Expected ssl timing to contain default value", -1L, harTimings.getSsl(TimeUnit.NANOSECONDS)) + + // this timeout was caused by a failure of the server to respond, so dns, connect, send, and wait should all be populated, + // but receive should not be populated since no response was received. + assertThat("Expected dns time to be populated", harTimings.getDns(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected connect time to be populated", harTimings.getConnect(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected send time to be populated", harTimings.getSend(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected wait time to be populated", harTimings.getWait(TimeUnit.NANOSECONDS), greaterThan(0L)) + + assertEquals("Expected receive time to not be populated", 0L, harTimings.getReceive(TimeUnit.NANOSECONDS)) + } + + @Test + void testHttpsResponseTimeoutCapturedInHar() { + mockServer.when(request() + .withMethod("GET") + .withPath("/testResponseTimeoutCapturedInHar"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withDelay(TimeUnit.SECONDS, 10) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true) + proxy.setIdleConnectionTimeout(3, TimeUnit.SECONDS) + proxy.start() + + proxy.newHar() + + String requestUrl = "https://localhost:${mockServerPort}/testResponseTimeoutCapturedInHar" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 504 from proxy", 504, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + assertEquals("Expected IP address to be populated", "127.0.0.1", har.log.entries[0].serverIPAddress) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Error in HAR response did not match expected response timeout error message", HarCaptureUtil.RESPONSE_TIMED_OUT_ERROR_MESSAGE, harResponse.error) + assertEquals("Expected HTTP status code of 0 for response timeout", HarCaptureUtil.HTTP_STATUS_CODE_FOR_FAILURE, harResponse.status) + assertEquals("Expected unknown HTTP version for response timeout", HarCaptureUtil.HTTP_VERSION_STRING_FOR_FAILURE, harResponse.httpVersion) + assertEquals("Expected default value for headersSize for response timeout", -1L, harResponse.headersSize) + assertEquals("Expected default value for bodySize for response timeout", -1L, harResponse.bodySize) + + HarTimings harTimings = har.log.entries[0].timings + assertNotNull("No HAR timings found", harTimings) + + assertThat("Expected ssl timing to be populated", harTimings.getSsl(TimeUnit.NANOSECONDS), greaterThan(0L)) + + // this timeout was caused by a failure of the server to respond, so dns, connect, send, and wait should all be populated, + // but receive should not be populated since no response was received. + assertThat("Expected dns time to be populated", harTimings.getDns(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected connect time to be populated", harTimings.getConnect(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected send time to be populated", harTimings.getSend(TimeUnit.NANOSECONDS), greaterThan(0L)) + assertThat("Expected wait time to be populated", harTimings.getWait(TimeUnit.NANOSECONDS), greaterThan(0L)) + + assertEquals("Expected receive time to not be populated", 0L, harTimings.getReceive(TimeUnit.NANOSECONDS)) + } + + @Test + void testRedirectUrlCapturedForRedirects() { + mockServer.when(request() + .withMethod("GET") + .withPath("/test300"), + Times.once()) + .respond(response() + .withStatusCode(300) + .withHeader("Location", "/redirected-location")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/test301"), + Times.once()) + .respond(response() + .withStatusCode(301) + .withHeader("Location", "/redirected-location")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/test302"), + Times.once()) + .respond(response() + .withStatusCode(302) + .withHeader("Location", "/redirected-location")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/test303"), + Times.once()) + .respond(response() + .withStatusCode(303) + .withHeader("Location", "/redirected-location")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/test307"), + Times.once()) + .respond(response() + .withStatusCode(307) + .withHeader("Location", "/redirected-location")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/test301-no-location-header"), + Times.once()) + .respond(response() + .withStatusCode(301)) + + proxy = new BrowserMobProxyServer(); + proxy.start() + + proxy.newHar() + + def verifyRedirect = { String requestUrl, expectedStatusCode, expectedLocationValue -> + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + // for some reason, even when the HTTP client is built with .disableRedirectHandling(), it still tries to follow + // the 301. so explicitly disable following redirects at the request level. + def request = new HttpGet(requestUrl) + request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()) + + CloseableHttpResponse response = it.execute(request) + assertEquals("HTTP response code did not match expected response code", expectedStatusCode, response.getStatusLine().getStatusCode()) + }; + + Thread.sleep(500) + Har har = proxy.getHar() + + assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty())) + + // make sure request data is still captured despite the failure + String capturedUrl = har.log.entries[0].request.url + assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl) + + HarResponse harResponse = har.log.entries[0].response + assertNotNull("No HAR response found", harResponse) + + assertEquals("Expected redirect location to be populated in redirectURL field", expectedLocationValue, harResponse.redirectURL); + } + + verifyRedirect("http://localhost:${mockServerPort}/test300", 300, "/redirected-location") + + // clear the HAR between every request, to make the verification step easier + proxy.newHar() + verifyRedirect("http://localhost:${mockServerPort}/test301", 301, "/redirected-location") + + proxy.newHar() + verifyRedirect("http://localhost:${mockServerPort}/test302", 302, "/redirected-location") + + proxy.newHar() + verifyRedirect("http://localhost:${mockServerPort}/test303", 303, "/redirected-location") + + proxy.newHar() + verifyRedirect("http://localhost:${mockServerPort}/test307", 307, "/redirected-location") + + proxy.newHar() + // redirectURL should always be populated or an empty string, never null + verifyRedirect("http://localhost:${mockServerPort}/test301-no-location-header", 301, "") + } + + //TODO: Add Request Capture Type tests +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/RemapHostsTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/RemapHostsTest.groovy new file mode 100644 index 000000000..0edf81be7 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/RemapHostsTest.groovy @@ -0,0 +1,81 @@ +package net.lightbody.bmp.proxy + +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockserver.matchers.Times + +import static org.junit.Assert.assertEquals +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +/** + * Tests host remapping using the {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#remapHost(java.lang.String, java.lang.String)} + * and related methods exposes by {@link BrowserMobProxy#getHostNameResolver()}. + */ +class RemapHostsTest extends MockServerTest { + private BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testRemapHttpHost() { + // mock up a response to serve + mockServer.when(request() + .withMethod("GET") + .withPath("/remapHttpHost"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true) + + proxy.getHostNameResolver().remapHost("www.someaddress.notreal", "localhost") + + proxy.start(); + + int proxyPort = proxy.getPort(); + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("http://www.someaddress.notreal:${mockServerPort}/remapHttpHost")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testRemapHttpsHost() { + // mock up a response to serve + mockServer.when(request() + .withMethod("GET") + .withPath("/remapHttpsHost"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true) + + proxy.getHostNameResolver().remapHost("www.someaddress.notreal", "localhost") + + proxy.start(); + + int proxyPort = proxy.getPort(); + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + String responseBody = NewProxyServerTestUtil.toStringAndClose(it.execute(new HttpGet("https://www.someaddress.notreal:${mockServerPort}/remapHttpsHost")).getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/RewriteRuleTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/RewriteRuleTest.groovy new file mode 100644 index 000000000..6f9a60769 --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/RewriteRuleTest.groovy @@ -0,0 +1,84 @@ +package net.lightbody.bmp.proxy + +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockserver.matchers.Times + +import static org.junit.Assert.assertEquals +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class RewriteRuleTest extends MockServerTest { + private BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testRewriteHttpUrl() { + mockServer.when(request() + .withMethod("GET") + .withPath("/") + .withQueryStringParameter("originalDomain", "yahoo") + .withQueryStringParameter("param1", "value1"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.rewriteUrl('http://www\\.(yahoo|bing)\\.com/\\?(\\w+)=(\\w+)', 'http://localhost:' + mockServerPort + '/?originalDomain=$1&$2=$3'); + proxy.setTrustAllServers(true) + proxy.start() + + String requestUrl = "http://www.yahoo.com?param1=value1" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } + + @Test + void testRewriteHttpsUrl() { + // HTTPS URLs cannot currently be rewritten to another domain, so verify query parameters can be rewritten + + mockServer.when(request() + .withMethod("GET") + .withPath("/") + .withQueryStringParameter("firstParam", "param1") + .withQueryStringParameter("firstValue", "value1"), + Times.once()) + .respond(response() + .withStatusCode(200) + .withBody("success")) + + proxy = new BrowserMobProxyServer(); + proxy.rewriteUrl('https://localhost:' + mockServerPort + '/\\?(\\w+)=(\\w+)', 'https://localhost:' + mockServerPort + '/?firstParam=$1&firstValue=$2'); + proxy.setTrustAllServers(true) + proxy.start() + + String requestUrl = "https://localhost:$mockServerPort?param1=value1" + + NewProxyServerTestUtil.getNewHttpClient(proxy.port).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet(requestUrl)) + assertEquals("Did not receive HTTP 200 from mock server", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + }; + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy new file mode 100644 index 000000000..22dd101bb --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/proxy/WhitelistTest.groovy @@ -0,0 +1,223 @@ +package net.lightbody.bmp.proxy + +import io.netty.handler.codec.http.HttpMethod +import io.netty.handler.codec.http.HttpRequest +import io.netty.handler.codec.http.HttpResponse +import io.netty.handler.codec.http.HttpVersion +import net.lightbody.bmp.BrowserMobProxy +import net.lightbody.bmp.BrowserMobProxyServer +import net.lightbody.bmp.filters.WhitelistFilter +import net.lightbody.bmp.proxy.test.util.MockServerTest +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.junit.After +import org.junit.Test +import org.mockserver.matchers.Times + +import static org.hamcrest.Matchers.isEmptyOrNullString +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertNull +import static org.junit.Assert.assertThat +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class WhitelistTest extends MockServerTest { + BrowserMobProxy proxy + + @After + void tearDown() { + if (proxy?.started) { + proxy.abort() + } + } + + @Test + void testWhitelistCannotShortCircuitCONNECT() { + HttpRequest request = mock(HttpRequest.class) + when(request.getMethod()).thenReturn(HttpMethod.CONNECT) + when(request.getUri()).thenReturn('somedomain.com:443') + when(request.getProtocolVersion()).thenReturn(HttpVersion.HTTP_1_1) + + // create a whitelist filter that whitelists no requests (i.e., all requests should return the specified HTTP 500 status code) + WhitelistFilter filter = new WhitelistFilter(request, null, true, 500, []) + HttpResponse response = filter.clientToProxyRequest(request) + + assertNull("Whitelist short-circuited HTTP CONNECT. Expected all HTTP CONNECTs to be whitelisted.", response) + } + + @Test + void testNonWhitelistedHttpRequestReturnsWhitelistStatusCode() { + proxy = new BrowserMobProxyServer() + proxy.start() + int proxyPort = proxy.getPort() + + proxy.whitelistRequests(["http://localhost/.*"], 500) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("http://www.someother.domain/someresource")) + assertEquals("Did not receive whitelist status code in response", 500, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertThat("Expected whitelist response to contain 0-length body", responseBody, isEmptyOrNullString()) + } + } + + @Test + void testNonWhitelistedHttpsRequestReturnsWhitelistStatusCode() { + mockServer.when(request() + .withMethod("GET") + .withPath("/nonwhitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("should never be returned")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + int proxyPort = proxy.getPort() + + proxy.whitelistRequests(["https://some-other-domain/.*"], 500) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("https://localhost:${mockServerPort}/nonwhitelistedresource")) + assertEquals("Did not receive whitelist status code in response", 500, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertThat("Expected whitelist response to contain 0-length body", responseBody, isEmptyOrNullString()) + } + } + + @Test + void testWhitelistedHttpRequestNotShortCircuited() { + mockServer.when(request() + .withMethod("GET") + .withPath("/whitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("whitelisted")) + + proxy = new BrowserMobProxyServer() + proxy.start() + int proxyPort = proxy.getPort() + + proxy.whitelistRequests(["http://localhost:${mockServerPort}/.*".toString()], 500) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("http://localhost:${mockServerPort}/whitelistedresource")) + assertEquals("Did not receive expected response from mock server for whitelisted url", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertEquals("Did not receive expected response body from mock server for whitelisted url", "whitelisted", responseBody) + } + } + + @Test + void testWhitelistedHttpsRequestNotShortCircuited() { + mockServer.when(request() + .withMethod("GET") + .withPath("/whitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("whitelisted")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + int proxyPort = proxy.getPort() + + proxy.whitelistRequests(["https://localhost:${mockServerPort}/.*".toString()], 500) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse response = it.execute(new HttpGet("https://localhost:${mockServerPort}/whitelistedresource")) + assertEquals("Did not receive expected response from mock server for whitelisted url", 200, response.getStatusLine().getStatusCode()) + + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()) + assertEquals("Did not receive expected response body from mock server for whitelisted url", "whitelisted", responseBody) + } + } + + @Test + void testCanWhitelistSpecificHttpResource() { + mockServer.when(request() + .withMethod("GET") + .withPath("/whitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("whitelisted")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/nonwhitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("should never be returned")) + + proxy = new BrowserMobProxyServer() + proxy.start() + int proxyPort = proxy.getPort() + + proxy.whitelistRequests(["http://localhost:${mockServerPort}/whitelistedresource".toString()], 500) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse nonWhitelistedResponse = it.execute(new HttpGet("http://localhost:${mockServerPort}/nonwhitelistedresource")) + assertEquals("Did not receive whitelist status code in response", 500, nonWhitelistedResponse.getStatusLine().getStatusCode()) + + String nonWhitelistedResponseBody = NewProxyServerTestUtil.toStringAndClose(nonWhitelistedResponse.getEntity().getContent()) + assertThat("Expected whitelist response to contain 0-length body", nonWhitelistedResponseBody, isEmptyOrNullString()) + + CloseableHttpResponse whitelistedResponse = it.execute(new HttpGet("http://localhost:${mockServerPort}/whitelistedresource")) + assertEquals("Did not receive expected response from mock server for whitelisted url", 200, whitelistedResponse.getStatusLine().getStatusCode()) + + String whitelistedResponseBody = NewProxyServerTestUtil.toStringAndClose(whitelistedResponse.getEntity().getContent()) + assertEquals("Did not receive expected response body from mock server for whitelisted url", "whitelisted", whitelistedResponseBody) + } + } + + @Test + void testCanWhitelistSpecificHttpsResource() { + mockServer.when(request() + .withMethod("GET") + .withPath("/whitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("whitelisted")) + + mockServer.when(request() + .withMethod("GET") + .withPath("/nonwhitelistedresource"), + Times.unlimited()) + .respond(response() + .withStatusCode(200) + .withBody("should never be returned")) + + proxy = new BrowserMobProxyServer() + proxy.setTrustAllServers(true) + proxy.start() + int proxyPort = proxy.getPort() + + proxy.whitelistRequests(["https://localhost:${mockServerPort}/whitelistedresource".toString()], 500) + + NewProxyServerTestUtil.getNewHttpClient(proxyPort).withCloseable { + CloseableHttpResponse nonWhitelistedResponse = it.execute(new HttpGet("https://localhost:${mockServerPort}/nonwhitelistedresource")) + assertEquals("Did not receive whitelist status code in response", 500, nonWhitelistedResponse.getStatusLine().getStatusCode()) + + String nonWhitelistedResponseBody = NewProxyServerTestUtil.toStringAndClose(nonWhitelistedResponse.getEntity().getContent()) + assertThat("Expected whitelist response to contain 0-length body", nonWhitelistedResponseBody, isEmptyOrNullString()) + + CloseableHttpResponse whitelistedResponse = it.execute(new HttpGet("https://localhost:${mockServerPort}/whitelistedresource")) + assertEquals("Did not receive expected response from mock server for whitelisted url", 200, whitelistedResponse.getStatusLine().getStatusCode()) + + String whitelistedResponseBody = NewProxyServerTestUtil.toStringAndClose(whitelistedResponse.getEntity().getContent()) + assertEquals("Did not receive expected response body from mock server for whitelisted url", "whitelisted", whitelistedResponseBody) + } + } +} diff --git a/browsermob-core/src/test/groovy/net/lightbody/bmp/util/BrowserMobHttpUtilTest.groovy b/browsermob-core/src/test/groovy/net/lightbody/bmp/util/BrowserMobHttpUtilTest.groovy new file mode 100644 index 000000000..6fc391a1c --- /dev/null +++ b/browsermob-core/src/test/groovy/net/lightbody/bmp/util/BrowserMobHttpUtilTest.groovy @@ -0,0 +1,152 @@ +package net.lightbody.bmp.util + +import org.junit.Test + +import java.nio.charset.Charset + +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertFalse +import static org.junit.Assert.assertNull +import static org.junit.Assert.assertTrue + +class BrowserMobHttpUtilTest { + @Test + void testGetResourceFromUri() { + Map uriToResource = [ + 'http://www.example.com/the/resource': '/the/resource', + 'https://example/resource': '/resource', + 'http://127.0.0.1/ip/address/resource': '/ip/address/resource', + 'https://hostname:8080/host/and/port/resource': '/host/and/port/resource', + 'http://hostname/': '/', + 'https://127.0.0.1/': '/', + 'http://127.0.0.1:1950/ip/port/resource': '/ip/port/resource', + 'https://[abcd:1234::17]/ipv6/literal/resource': '/ipv6/literal/resource', + 'http://[abcd:1234::17]:50/ipv6/with/port/literal/resource': '/ipv6/with/port/literal/resource', + 'https://hostname/query/param/resource?param=value': '/query/param/resource?param=value', + ] + + uriToResource.each {uri, expectedResource -> + String parsedResource = BrowserMobHttpUtil.getRawPathAndParamsFromUri(uri) + assertEquals("Parsed resource from URL did not match expected resource for URL: " + uri, expectedResource, parsedResource) + } + } + + @Test + void testGetHostAndPortFromUri() { + Map uriToHostAndPort = [ + 'http://www.example.com/some/resource': 'www.example.com', + 'https://www.example.com:8080/some/resource': 'www.example.com:8080', + 'http://127.0.0.1/some/resource': '127.0.0.1', + 'https://127.0.0.1:8080/some/resource?param=value': '127.0.0.1:8080', + 'http://localhost/some/resource': 'localhost', + 'https://localhost:1820/': 'localhost:1820', + 'http://[abcd:1234::17]/ipv6/literal/resource': '[abcd:1234::17]', + 'https://[abcd:1234::17]:50/ipv6/with/port/literal/resource': '[abcd:1234::17]:50', + ] + + uriToHostAndPort.each {uri, expectedHostAndPort -> + String parsedHostAndPort = HttpUtil.getHostAndPortFromUri(uri) + assertEquals("Parsed host and port from URL did not match expected host and port for URL: " + uri, expectedHostAndPort, parsedHostAndPort) + } + } + + @Test + void testReadCharsetInContentTypeHeader() { + Map contentTypeHeaderAndCharset = [ + 'text/html; charset=UTF-8' : Charset.forName('UTF-8'), + 'text/html; charset=US-ASCII' : Charset.forName('US-ASCII'), + 'text/html' : null, + 'application/json;charset=utf-8' : Charset.forName('UTF-8'), + 'text/*; charset=US-ASCII' : Charset.forName('US-ASCII'), + 'unknown-type/something-incredible' : null, + 'unknown-type/something-incredible;charset=UTF-8' : Charset.forName('UTF-8'), + '1234 & extremely malformed!' : null, + '1234 & extremely malformed!;charset=UTF-8' : null, // malformed content-types result in unparseable charsets + '' : null, + ] + + contentTypeHeaderAndCharset.each {contentTypeHeader, expectedCharset -> + Charset derivedCharset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader) + assertEquals("Charset derived from parsed content type header did not match expected charset for content type header: " + contentTypeHeader, expectedCharset, derivedCharset) + } + + Charset derivedCharset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(null) + assertNull("Expected null Content-Type header to return a null charset", derivedCharset) + + boolean threwException = false + try { + BrowserMobHttpUtil.readCharsetInContentTypeHeader('text/html; charset=FUTURE_CHARSET') + } catch (UnsupportedCharsetException) { + threwException = true + } + + assertTrue('Expected an UnsupportedCharsetException to occur when parsing the content type header text/html; charset=FUTURE_CHARSET', threwException) + } + + @Test + void testHasTextualContent() { + Map contentTypeHeaderAndTextFlag = [ + 'text/html' : true, + 'text/*' : true, + 'application/x-javascript' : true, + 'application/javascript' : true, + 'application/xml' : true, + 'application/xhtml+xml' : true, + 'application/xhtml+xml; charset=UTF-8' : true, + 'application/octet-stream' : false, + '': false, + ] + + contentTypeHeaderAndTextFlag.each {contentTypeHeader, expectedIsText -> + boolean isTextualContent = BrowserMobHttpUtil.hasTextualContent(contentTypeHeader) + assertEquals("hasTextualContent did not return expected value for content type header: " + contentTypeHeader, expectedIsText, isTextualContent) + } + + boolean isTextualContent = BrowserMobHttpUtil.hasTextualContent(null) + assertFalse("Expected hasTextualContent to return false for null content type", isTextualContent) + } + + @Test + void testGetRawPathWithQueryParams() { + String path = "/some%20resource?param%20name=value" + + assertEquals(path, BrowserMobHttpUtil.getRawPathAndParamsFromUri("https://www.example.com" + path)) + } + + @Test + void testGetRawPathWithoutQueryParams() { + String path = "/some%20resource" + + assertEquals(path, BrowserMobHttpUtil.getRawPathAndParamsFromUri("https://www.example.com" + path)) + } + + @Test + void testRemoveMatchingPort() { + def portRemoved = BrowserMobHttpUtil.removeMatchingPort("www.example.com:443", 443) + assertEquals("www.example.com", portRemoved) + + def hostnameWithNonMatchingPort = BrowserMobHttpUtil.removeMatchingPort("www.example.com:443", 1234) + assertEquals("www.example.com:443", hostnameWithNonMatchingPort) + + def hostnameNoPort = BrowserMobHttpUtil.removeMatchingPort("www.example.com", 443) + assertEquals("www.example.com", hostnameNoPort) + + def ipv4WithoutPort = BrowserMobHttpUtil.removeMatchingPort("127.0.0.1:443", 443) + assertEquals("127.0.0.1", ipv4WithoutPort) + + def ipv4WithNonMatchingPort = BrowserMobHttpUtil.removeMatchingPort("127.0.0.1:443", 1234) + assertEquals("127.0.0.1:443", ipv4WithNonMatchingPort); + + def ipv4NoPort = BrowserMobHttpUtil.removeMatchingPort("127.0.0.1", 443) + assertEquals("127.0.0.1", ipv4NoPort); + + def ipv6WithoutPort = BrowserMobHttpUtil.removeMatchingPort("[::1]:443", 443) + assertEquals("[::1]", ipv6WithoutPort) + + def ipv6WithNonMatchingPort = BrowserMobHttpUtil.removeMatchingPort("[::1]:443", 1234) + assertEquals("[::1]:443", ipv6WithNonMatchingPort); + + def ipv6NoPort = BrowserMobHttpUtil.removeMatchingPort("[::1]", 443) + assertEquals("[::1]", ipv6NoPort); + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java new file mode 100644 index 000000000..3df880424 --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/InterceptorTest.java @@ -0,0 +1,959 @@ +package net.lightbody.bmp.proxy; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.BrowserMobProxyServer; +import net.lightbody.bmp.filters.RequestFilter; +import net.lightbody.bmp.filters.RequestFilterAdapter; +import net.lightbody.bmp.filters.ResponseFilter; +import net.lightbody.bmp.filters.ResponseFilterAdapter; +import net.lightbody.bmp.proxy.test.util.MockServerTest; +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; +import net.lightbody.bmp.util.HttpObjectUtil; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.After; +import org.junit.Test; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.littleshoot.proxy.HttpFiltersSourceAdapter; +import org.mockserver.matchers.Times; +import org.mockserver.model.Header; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +public class InterceptorTest extends MockServerTest { + private BrowserMobProxy proxy; + + @After + public void tearDown() { + if (proxy != null && proxy.isStarted()) { + proxy.abort(); + } + } + + @Test + public void testCanShortCircuitResponse() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/regular200"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + // this response should be "short-circuited" by the interceptor + mockServer.when(request() + .withMethod("GET") + .withPath("/shortcircuit204"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + final AtomicBoolean interceptorFired = new AtomicBoolean(false); + final AtomicBoolean shortCircuitFired= new AtomicBoolean(false); + + proxy.addFirstHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new HttpFiltersAdapter(originalRequest) { + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + interceptorFired.set(true); + + HttpRequest httpRequest = (HttpRequest) httpObject; + + if (httpRequest.getMethod().equals(HttpMethod.GET) && httpRequest.getUri().contains("/shortcircuit204")) { + HttpResponse httpResponse = new DefaultHttpResponse(httpRequest.getProtocolVersion(), HttpResponseStatus.NO_CONTENT); + + shortCircuitFired.set(true); + + return httpResponse; + } + } + + return super.clientToProxyRequest(httpObject); + } + }; + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/regular200")); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertTrue("Expected interceptor to fire", interceptorFired.get()); + assertFalse("Did not expected short circuit interceptor code to execute", shortCircuitFired.get()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + } + + interceptorFired.set(false); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/shortcircuit204")); + + assertTrue("Expected interceptor to fire", interceptorFired.get()); + assertTrue("Expected interceptor to short-circuit response", shortCircuitFired.get()); + + assertEquals("Expected interceptor to return a 204 (No Content)", 204, response.getStatusLine().getStatusCode()); + assertNull("Expected no entity attached to response", response.getEntity()); + } + } + + @Test + public void testCanModifyResponseBodyLarger() throws IOException { + final String originalText = "The quick brown fox jumps over the lazy dog"; + final String newText = "The quick brown frog jumps over the lazy aardvark"; + + testModifiedResponse(originalText, newText); + } + + @Test + public void testCanModifyResponseBodySmaller() throws IOException { + final String originalText = "The quick brown fox jumps over the lazy dog"; + final String newText = "The quick brown fox jumped."; + + testModifiedResponse(originalText, newText); + } + + @Test + public void testCanModifyRequest() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyrequest"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + proxy.addFirstHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new HttpFiltersAdapter(originalRequest) { + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + httpRequest.setUri(httpRequest.getUri().replace("/originalrequest", "/modifyrequest")); + } + + return super.clientToProxyRequest(httpObject); + } + }; + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/originalrequest")); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + } + } + + @Test + public void testRequestFilterCanModifyHttpRequestBody() throws IOException { + final String originalText = "original body"; + final String newText = "modified body"; + + mockServer.when(request() + .withMethod("PUT") + .withPath("/modifyrequest") + .withBody(newText), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); + } + } + + return null; + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpPut request = new HttpPut("http://localhost:" + mockServerPort + "/modifyrequest"); + request.setEntity(new StringEntity(originalText)); + CloseableHttpResponse response = httpClient.execute(request); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + } + } + + @Test + public void testRequestFilterCanModifyHttpsRequestBody() throws IOException { + final String originalText = "original body"; + final String newText = "modified body"; + + mockServer.when(request() + .withMethod("PUT") + .withPath("/modifyrequest") + .withBody(newText), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true); + proxy.start(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); + } + } + + return null; + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpPut request = new HttpPut("https://localhost:" + mockServerPort + "/modifyrequest"); + request.setEntity(new StringEntity(originalText)); + CloseableHttpResponse response = httpClient.execute(request); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + } + } + + @Test + public void testResponseFilterCanModifyBinaryContents() throws IOException { + final byte[] originalBytes = new byte[] {1, 2, 3, 4, 5}; + final byte[] newBytes = new byte[] {20, 30, 40, 50, 60}; + + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyresponse"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream")) + .withBody(originalBytes)); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (!contents.isText()) { + if (Arrays.equals(originalBytes, contents.getBinaryContents())) { + contents.setBinaryContents(newBytes); + } + } + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpGet request = new HttpGet("http://localhost:" + mockServerPort + "/modifyresponse"); + CloseableHttpResponse response = httpClient.execute(request); + byte[] responseBytes = org.apache.commons.io.IOUtils.toByteArray(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertThat("Did not receive expected response from mock server", responseBytes, equalTo(newBytes)); + } + } + + @Test + public void testResponseFilterCanModifyHttpTextContents() throws IOException { + final String originalText = "The quick brown fox jumps over the lazy dog"; + final String newText = "The quick brown fox jumped."; + + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyresponse"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withBody(originalText)); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); + } + } + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpGet request = new HttpGet("http://localhost:" + mockServerPort + "/modifyresponse"); + request.addHeader("Accept-Encoding", "gzip"); + CloseableHttpResponse response = httpClient.execute(request); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", newText, responseBody); + } + } + + @Test + public void testResponseFilterCanModifyHttpsTextContents() throws IOException { + final String originalText = "The quick brown fox jumps over the lazy dog"; + final String newText = "The quick brown fox jumped."; + + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyresponse"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withBody(originalText)); + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true); + proxy.start(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (contents.isText()) { + if (contents.getTextContents().equals(originalText)) { + contents.setTextContents(newText); + } + } + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpGet request = new HttpGet("https://localhost:" + mockServerPort + "/modifyresponse"); + request.addHeader("Accept-Encoding", "gzip"); + CloseableHttpResponse response = httpClient.execute(request); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", newText, responseBody); + } + } + + @Test + public void testResponseInterceptorWithoutBody() throws IOException { + mockServer.when(request() + .withMethod("HEAD") + .withPath("/interceptortest"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream"))); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + final AtomicReference responseContents = new AtomicReference<>(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + responseContents.set(contents.getBinaryContents()); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpHead("http://localhost:" + mockServerPort + "/interceptortest")); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Expected binary contents captured in interceptor to be empty", 0, responseContents.get().length); + } + } + + @Test + public void testResponseFilterOriginalRequestNotModified() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/modifiedendpoint"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (request.getUri().endsWith("/originalendpoint")) { + request.setUri(request.getUri().replaceAll("originalendpoint", "modifiedendpoint")); + } + + return null; + } + }); + + final AtomicReference originalRequestUri = new AtomicReference<>(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + originalRequestUri.set(messageInfo.getOriginalRequest().getUri()); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/originalendpoint")); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertThat("Expected URI on originalRequest to match actual URI of original HTTP request", originalRequestUri.get(), endsWith("/originalendpoint")); + } + } + + @Test + public void testMessageContentsNotAvailableWithoutAggregation() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/endpoint"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + final AtomicBoolean requestContentsNull = new AtomicBoolean(false); + final AtomicBoolean responseContentsNull = new AtomicBoolean(false); + + proxy.addFirstHttpFilterFactory(new RequestFilterAdapter.FilterSource(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (contents == null) { + requestContentsNull.set(true); + } + + return null; + } + }, 0)); + + proxy.addFirstHttpFilterFactory(new ResponseFilterAdapter.FilterSource(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (contents == null) { + responseContentsNull.set(true); + } + } + }, 0)); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/endpoint")); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertTrue("Expected HttpMessageContents to be null in RequestFilter because HTTP message aggregation is disabled", requestContentsNull.get()); + assertTrue("Expected HttpMessageContents to be null in ResponseFilter because HTTP message aggregation is disabled", responseContentsNull.get()); + } + } + + @Test + public void testMitmDisabledHttpsRequestFilterNotAvailable() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/mitmdisabled"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setMitmDisabled(true); + + proxy.start(); + + final AtomicBoolean connectRequestFilterFired = new AtomicBoolean(false); + final AtomicBoolean getRequestFilterFired = new AtomicBoolean(false); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (request.getMethod().equals(HttpMethod.CONNECT)) { + connectRequestFilterFired.set(true); + } else if (request.getMethod().equals(HttpMethod.GET)) { + getRequestFilterFired.set(true); + } + return null; + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("https://localhost:" + mockServerPort + "/mitmdisabled")); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + + assertTrue("Expected request filter to fire on CONNECT", connectRequestFilterFired.get()); + assertFalse("Expected request filter to fail to fire on GET because MITM is disabled", getRequestFilterFired.get()); + } + } + + @Test + public void testMitmDisabledHttpsResponseFilterNotAvailable() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/mitmdisabled"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setMitmDisabled(true); + + proxy.start(); + + // unlike the request filter, the response filter doesn't fire when the 200 response to the CONNECT is sent to the client. + // this is because the response filter is triggered when the serverToProxyResponse() filtering method is called, and + // the "200 Connection established" is generated by the proxy itself. + + final AtomicBoolean responseFilterFired = new AtomicBoolean(false); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + responseFilterFired.set(true); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("https://localhost:" + mockServerPort + "/mitmdisabled")); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertFalse("Expected response filter to fail to fire because MITM is disabled", responseFilterFired.get()); + } + } + + /** + * Helper method for executing response modification tests. + */ + private void testModifiedResponse(final String originalText, final String newText) throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyresponse"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8")) + .withBody(originalText)); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + proxy.addFirstHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new HttpFiltersAdapter(originalRequest) { + @Override + public HttpObject proxyToClientResponse(HttpObject httpObject) { + if (httpObject instanceof FullHttpResponse) { + FullHttpResponse httpResponseAndContent = (FullHttpResponse) httpObject; + + String bodyContent = HttpObjectUtil.extractHttpEntityBody(httpResponseAndContent); + + if (bodyContent.equals(originalText)) { + HttpObjectUtil.replaceTextHttpEntityBody(httpResponseAndContent, newText); + } + } + + return super.proxyToClientResponse(httpObject); + } + }; + } + + @Override + public int getMaximumResponseBufferSizeInBytes() { + return 10000; + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/modifyresponse")); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", newText, responseBody); + } + } + + @Test + public void testCanBypassFilterForRequest() throws IOException, InterruptedException { + mockServer.when(request() + .withMethod("GET") + .withPath("/bypassfilter"), + Times.exactly(2)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + final AtomicInteger filtersSourceHitCount = new AtomicInteger(); + final AtomicInteger filterHitCount = new AtomicInteger(); + + proxy.addFirstHttpFilterFactory(new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + if (filtersSourceHitCount.getAndIncrement() == 0) { + return null; + } else { + return new HttpFiltersAdapter(originalRequest) { + @Override + public void serverToProxyResponseReceived() { + filterHitCount.incrementAndGet(); + } + }; + } + } + }); + + // during the first request, the filterRequest(...) method should return null, which will prevent the filter instance from + // being added to the filter chain + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/bypassfilter")); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + } + + Thread.sleep(500); + + assertEquals("Expected filters source to be invoked on first request", 1, filtersSourceHitCount.get()); + assertEquals("Expected filter instance to be bypassed on first request", 0, filterHitCount.get()); + + // during the second request, the filterRequest(...) method will return a filter instance, which should be invoked during processing + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/bypassfilter")); + String responseBody = NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent()); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Did not receive expected response from mock server", "success", responseBody); + } + + Thread.sleep(500); + + assertEquals("Expected filters source to be invoked again on second request", 2, filtersSourceHitCount.get()); + assertEquals("Expected filter instance to be invoked on second request (only)", 1, filterHitCount.get()); + } + + @Test + public void testHttpResponseFilterMessageInfoPopulated() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/httpmessageinfopopulated") + .withQueryStringParameter("param1", "value1"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + final AtomicReference requestCtx = new AtomicReference<>(); + final AtomicReference requestOriginalRequest = new AtomicReference<>(); + final AtomicBoolean requestIsHttps = new AtomicBoolean(false); + final AtomicReference requestFilterOriginalUrl = new AtomicReference<>(); + final AtomicReference requestFilterUrl = new AtomicReference<>(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + requestCtx.set(messageInfo.getChannelHandlerContext()); + requestOriginalRequest.set(messageInfo.getOriginalRequest()); + requestIsHttps.set(messageInfo.isHttps()); + requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + requestFilterUrl.set(messageInfo.getUrl()); + return null; + } + }); + + final AtomicReference responseCtx = new AtomicReference<>(); + final AtomicReference responseOriginalRequest = new AtomicReference<>(); + final AtomicBoolean responseIsHttps = new AtomicBoolean(false); + final AtomicReference responseFilterOriginalUrl = new AtomicReference<>(); + final AtomicReference responseFilterUrl = new AtomicReference<>(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + responseCtx.set(messageInfo.getChannelHandlerContext()); + responseOriginalRequest.set(messageInfo.getOriginalRequest()); + responseIsHttps.set(messageInfo.isHttps()); + responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + responseFilterUrl.set(messageInfo.getUrl()); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + String requestUrl = "http://localhost:" + mockServerPort + "/httpmessageinfopopulated?param1=value1"; + CloseableHttpResponse response = httpClient.execute(new HttpGet(requestUrl)); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertNotNull("Expected ChannelHandlerContext to be populated in request filter", requestCtx.get()); + assertNotNull("Expected originalRequest to be populated in request filter", requestOriginalRequest.get()); + assertFalse("Expected isHttps to return false in request filter", requestIsHttps.get()); + assertEquals("Expected originalUrl in request filter to match actual request URL", requestUrl, requestFilterOriginalUrl.get()); + assertEquals("Expected url in request filter to match actual request URL", requestUrl, requestFilterUrl.get()); + + assertNotNull("Expected ChannelHandlerContext to be populated in response filter", responseCtx.get()); + assertNotNull("Expected originalRequest to be populated in response filter", responseOriginalRequest.get()); + assertFalse("Expected isHttps to return false in response filter", responseIsHttps.get()); + assertEquals("Expected originalUrl in response filter to match actual request URL", requestUrl, responseFilterOriginalUrl.get()); + assertEquals("Expected url in response filter to match actual request URL", requestUrl, responseFilterUrl.get()); + } + } + + @Test + public void testHttpResponseFilterUrlReflectsModifications() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/urlreflectsmodifications"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.start(); + + final AtomicReference requestFilterOriginalUrl = new AtomicReference<>(); + final AtomicReference requestFilterUrl = new AtomicReference<>(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + requestFilterUrl.set(messageInfo.getUrl()); + return null; + } + }); + + // request filters get added to the beginning of the filter chain, so add this uri-modifying request filter after + // adding the capturing request filter above. + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (request.getUri().endsWith("/originalurl")) { + String newUrl = request.getUri().replaceAll("originalurl", "urlreflectsmodifications"); + request.setUri(newUrl); + } + return null; + } + }); + + final AtomicReference responseFilterOriginalUrl = new AtomicReference<>(); + final AtomicReference responseFilterUrl = new AtomicReference<>(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + responseFilterUrl.set(messageInfo.getUrl()); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + String originalRequestUrl = "http://localhost:" + mockServerPort + "/originalurl"; + String modifiedRequestUrl = "http://localhost:" + mockServerPort + "/urlreflectsmodifications"; + CloseableHttpResponse response = httpClient.execute(new HttpGet(originalRequestUrl)); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Expected originalUrl in request filter to match actual request URL", originalRequestUrl, requestFilterOriginalUrl.get()); + assertEquals("Expected url in request filter to match modified request URL", modifiedRequestUrl, requestFilterUrl.get()); + + assertEquals("Expected originalUrl in response filter to match actual request URL", originalRequestUrl, responseFilterOriginalUrl.get()); + assertEquals("Expected url in response filter to match modified request URL", modifiedRequestUrl, responseFilterUrl.get()); + } + } + + @Test + public void testHttpsResponseFilterUrlReflectsModifications() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/urlreflectsmodifications"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true); + proxy.start(); + + final AtomicReference requestFilterOriginalUrl = new AtomicReference<>(); + final AtomicReference requestFilterUrl = new AtomicReference<>(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + requestFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + requestFilterUrl.set(messageInfo.getUrl()); + return null; + } + }); + + // request filters get added to the beginning of the filter chain, so add this uri-modifying request filter after + // adding the capturing request filter above. + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (request.getUri().endsWith("/originalurl")) { + String newUrl = request.getUri().replaceAll("originalurl", "urlreflectsmodifications"); + request.setUri(newUrl); + } + return null; + } + }); + + final AtomicReference responseFilterOriginalUrl = new AtomicReference<>(); + final AtomicReference responseFilterUrl = new AtomicReference<>(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + responseFilterOriginalUrl.set(messageInfo.getOriginalUrl()); + responseFilterUrl.set(messageInfo.getUrl()); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + String originalRequestUrl = "https://localhost:" + mockServerPort + "/originalurl"; + String modifiedRequestUrl = "https://localhost:" + mockServerPort + "/urlreflectsmodifications"; + CloseableHttpResponse response = httpClient.execute(new HttpGet(originalRequestUrl)); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + assertEquals("Expected originalUrl in request filter to match actual request URL", originalRequestUrl, requestFilterOriginalUrl.get()); + assertEquals("Expected url in request filter to match modified request URL", modifiedRequestUrl, requestFilterUrl.get()); + + assertEquals("Expected originalUrl in response filter to match actual request URL", originalRequestUrl, responseFilterOriginalUrl.get()); + assertEquals("Expected url in response filter to match modified request URL", modifiedRequestUrl, responseFilterUrl.get()); + } + } + + @Test + public void testHttpsResponseFilterMessageInfoPopulated() throws IOException { + mockServer.when(request() + .withMethod("GET") + .withPath("/httpmessageinfopopulated") + .withQueryStringParameter("param1", "value1"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withBody("success")); + + proxy = new BrowserMobProxyServer(); + proxy.setTrustAllServers(true); + proxy.start(); + + final AtomicReference requestCtx = new AtomicReference<>(); + final AtomicReference requestOriginalRequest = new AtomicReference<>(); + final AtomicBoolean requestIsHttps = new AtomicBoolean(false); + final AtomicReference requestOriginalUrl = new AtomicReference<>(); + + proxy.addRequestFilter(new RequestFilter() { + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + requestCtx.set(messageInfo.getChannelHandlerContext()); + requestOriginalRequest.set(messageInfo.getOriginalRequest()); + requestIsHttps.set(messageInfo.isHttps()); + requestOriginalUrl.set(messageInfo.getOriginalUrl()); + return null; + } + }); + + final AtomicReference responseCtx = new AtomicReference<>(); + final AtomicReference responseOriginalRequest = new AtomicReference<>(); + final AtomicBoolean responseIsHttps = new AtomicBoolean(false); + final AtomicReference responseOriginalUrl = new AtomicReference<>(); + + proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + responseCtx.set(messageInfo.getChannelHandlerContext()); + responseOriginalRequest.set(messageInfo.getOriginalRequest()); + responseIsHttps.set(messageInfo.isHttps()); + responseOriginalUrl.set(messageInfo.getOriginalUrl()); + } + }); + + try (CloseableHttpClient httpClient = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + String requestUrl = "https://localhost:" + mockServerPort + "/httpmessageinfopopulated?param1=value1"; + CloseableHttpResponse response = httpClient.execute(new HttpGet(requestUrl)); + + assertEquals("Expected server to return a 200", 200, response.getStatusLine().getStatusCode()); + + assertNotNull("Expected ChannelHandlerContext to be populated in request filter", requestCtx.get()); + assertNotNull("Expected originalRequest to be populated in request filter", requestOriginalRequest.get()); + assertTrue("Expected isHttps to return true in request filter", requestIsHttps.get()); + assertEquals("Expected originalUrl in request filter to match actual request URL", requestUrl, requestOriginalUrl.get()); + + assertNotNull("Expected ChannelHandlerContext to be populated in response filter", responseCtx.get()); + assertNotNull("Expected originalRequest to be populated in response filter", responseOriginalRequest.get()); + assertTrue("Expected isHttps to return true in response filter", responseIsHttps.get()); + assertEquals("Expected originalUrl in response filter to match actual request URL", requestUrl, responseOriginalUrl.get()); + } + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/NetworkTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/NetworkTest.java new file mode 100644 index 000000000..dd19b461b --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/NetworkTest.java @@ -0,0 +1,94 @@ +package net.lightbody.bmp.proxy; + +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.BrowserMobProxyServer; +import net.lightbody.bmp.proxy.test.util.MockServerTest; +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.junit.Test; +import org.mockserver.matchers.Times; +import org.mockserver.model.Delay; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +/** + * Network manipulation tests using the new interface. + */ +public class NetworkTest extends MockServerTest { + @Test + public void testConnectTimeout() throws IOException { + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.setConnectTimeout(1, TimeUnit.SECONDS); + proxy.start(); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + long start = System.nanoTime(); + HttpResponse response = client.execute(new HttpGet("http://1.2.3.4:53540/connecttimeout")); + long stop = System.nanoTime(); + + assertEquals("Expected to receive an HTTP 502 (Bad Gateway) response after proxy could not connect within 1 second", 502, response.getStatusLine().getStatusCode()); + assertTrue("Expected connection timeout to happen after approximately 1 second. Total time was: " + TimeUnit.MILLISECONDS.convert(stop - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.SECONDS.convert(stop - start, TimeUnit.NANOSECONDS) < 2); + } finally { + proxy.abort(); + } + } + + @Test + public void testIdleConnectionTimeout() throws IOException { + mockServer.when( + request().withMethod("GET") + .withPath("/idleconnectiontimeout"), + Times.exactly(1) + ).respond(response().withDelay(new Delay(TimeUnit.SECONDS, 5))); + + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.setIdleConnectionTimeout(1, TimeUnit.SECONDS); + proxy.start(); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + long start = System.nanoTime(); + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/idleconnectiontimeout")); + long stop = System.nanoTime(); + + assertEquals("Expected to receive an HTTP 504 (Gateway Timeout) response after proxy did not receive a response within 1 second", 504, response.getStatusLine().getStatusCode()); + assertTrue("Expected idle connection timeout to happen after approximately 1 second. Total time was: " + TimeUnit.MILLISECONDS.convert(stop - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.SECONDS.convert(stop - start, TimeUnit.NANOSECONDS) < 2); + } finally { + proxy.abort(); + } + } + + @Test + public void testLatency() throws IOException { + mockServer.when( + request().withMethod("GET") + .withPath("/latency"), + Times.exactly(1) + ).respond(response().withStatusCode(200)); + + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.setLatency(2, TimeUnit.SECONDS); + proxy.start(); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + long start = System.nanoTime(); + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/latency")); + long stop = System.nanoTime(); + + assertEquals("Expected to receive an HTTP 200 from the upstream server", 200, response.getStatusLine().getStatusCode()); + assertTrue("Expected latency to be at least 2 seconds. Total time was: " + TimeUnit.MILLISECONDS.convert(stop - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.SECONDS.convert(stop - start, TimeUnit.NANOSECONDS) >= 2); + } finally { + proxy.abort(); + } + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java new file mode 100644 index 000000000..60842b0be --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/QuiescenceTest.java @@ -0,0 +1,338 @@ +package net.lightbody.bmp.proxy; + +import net.lightbody.bmp.proxy.test.util.NewProxyServerTest; +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.junit.Ignore; +import org.junit.Test; +import org.mockserver.matchers.Times; +import org.mockserver.model.Delay; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +public class QuiescenceTest extends NewProxyServerTest { + private static final Logger log = LoggerFactory.getLogger(QuiescenceTest.class); + + @Test + public void testWaitForQuiescenceSuccessful() throws IOException, InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiescencesuccessful"), + Times.exactly(1) + ).respond(response().withStatusCode(200).withDelay(new Delay(TimeUnit.SECONDS, 4))); + + final AtomicLong requestComplete = new AtomicLong(); + + new Thread(new Runnable() { + @Override + public void run() { + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiescencesuccessful")); + EntityUtils.consumeQuietly(response.getEntity()); + + requestComplete.set(System.nanoTime()); + + assertEquals("Expected successful response from server", 200, response.getStatusLine().getStatusCode()); + } catch (IOException e) { + fail("Error occurred while connecting to server"); + log.error("Error occurred while connecting to server", e); + } + } + }).start(); + + // wait for the request to start before waiting for quiescence + Thread.sleep(1000); + + boolean waitSuccessful = proxy.waitForQuiescence(2, 10, TimeUnit.SECONDS); + + long waitForQuiescenceFinished = System.nanoTime(); + + assertTrue("Expected to successfully wait for quiescence", waitSuccessful); + assertTrue("Expected request to be complete after waiting for quiescence", requestComplete.get() > 0); + + // the total wait time after the request is complete should be approximately 2 seconds + long wait = TimeUnit.MILLISECONDS.convert(waitForQuiescenceFinished - requestComplete.get(), TimeUnit.NANOSECONDS); + + assertTrue("Expected time to wait for quiescence to be approximately 2s. Waited for: " + wait + "ms", wait < 3000); + } + + @Test + public void testWaitForQuiescenceUnsuccessful() throws IOException, InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiescenceunsuccessful"), + Times.exactly(1) + ).respond(response().withStatusCode(200).withDelay(new Delay(TimeUnit.MINUTES, 1))); + + final AtomicBoolean requestCompleted = new AtomicBoolean(false); + + new Thread(new Runnable() { + @Override + public void run() { + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiescenceunsuccessful")); + + requestCompleted.set(true); + } catch (IOException e) { + // ignore any exceptions -- we don't expect this call to complete + } + } + }).start(); + + // wait for the request to start before waiting for quiescence + Thread.sleep(1000); + + boolean waitSuccessful = proxy.waitForQuiescence(1, 3, TimeUnit.SECONDS); + + assertFalse("Expected waitForQuiescence to time out while waiting for traffic to stop", waitSuccessful); + + assertFalse("Did not expect request to complete", requestCompleted.get()); + } + + @Test + public void testWaitForQuiescenceAfterRequestCompleted() throws IOException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiescencecompleted"), + Times.exactly(1) + ).respond(response().withStatusCode(200)); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiescencecompleted")); + EntityUtils.consumeQuietly(response.getEntity()); + + assertEquals("Expected successful response from server", 200, response.getStatusLine().getStatusCode()); + } + + // wait for 2s of quiescence, now that the call has already completed + long start = System.nanoTime(); + boolean waitSuccessful = proxy.waitForQuiescence(2, 5, TimeUnit.SECONDS); + long finish = System.nanoTime(); + + assertTrue("Expected to successfully wait for quiescence", waitSuccessful); + + assertTrue("Expected to wait for quiescence for approximately 2s. Actual wait time was: " + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) >= 1500 && TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) <= 2500); + } + + @Test + public void testWaitForQuiescenceQuietPeriodAlreadySatisfied() throws IOException, InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiescencesatisfied"), + Times.exactly(1) + ).respond(response().withStatusCode(200)); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiescencesatisfied")); + EntityUtils.consumeQuietly(response.getEntity()); + + assertEquals("Expected successful response from server", 200, response.getStatusLine().getStatusCode()); + } + + // wait for 2s, then wait for 1s of quiescence, which should already be satisfied + Thread.sleep(2000); + + long start = System.nanoTime(); + boolean waitSuccessful = proxy.waitForQuiescence(1, 5, TimeUnit.SECONDS); + long finish = System.nanoTime(); + + assertTrue("Expected to successfully wait for quiescence", waitSuccessful); + + assertTrue("Expected wait for quiescence to return immediately. Actual wait time was: " + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) <= 1); + } + + @Test + public void testWaitForQuiescenceTimeoutLessThanQuietPeriodSuccessful() throws IOException, InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiescencesmalltimeoutsuccess"), + Times.exactly(1) + ).respond(response().withStatusCode(200)); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiescencesmalltimeoutsuccess")); + EntityUtils.consumeQuietly(response.getEntity()); + + assertEquals("Expected successful response from server", 200, response.getStatusLine().getStatusCode()); + } + + Thread.sleep(2500); + + // wait for 3s of quiescence, which should wait no more than 500ms + + long start = System.nanoTime(); + boolean waitSuccessful = proxy.waitForQuiescence(3, 1, TimeUnit.SECONDS); + long finish = System.nanoTime(); + + assertTrue("Expected to successfully wait for quiescence", waitSuccessful); + + assertTrue("Expected to wait for quiescence for approximately 500ms. Actual wait time was: " + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) >= 300 && TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) <= 700); + } + + @Test + public void testWaitForQuiescenceTimeoutLessThanQuietPeriodUnuccessful() throws IOException, InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiescencesmalltimeoutunsuccessful"), + Times.exactly(1) + ).respond(response().withStatusCode(200)); + + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiescencesmalltimeoutunsuccessful")); + EntityUtils.consumeQuietly(response.getEntity()); + + assertEquals("Expected successful response from server", 200, response.getStatusLine().getStatusCode()); + } + + Thread.sleep(1000); + + // wait for 3s of quiescence within 1s, which should not be possible since the last request just finished. waitForQuiescence should + // be able to detect that and return immediately. + long start = System.nanoTime(); + boolean waitSuccessful = proxy.waitForQuiescence(3, 1, TimeUnit.SECONDS); + long finish = System.nanoTime(); + + assertFalse("Expected to unsuccessfully wait for quiescence", waitSuccessful); + + assertTrue("Expected wait for quiescence to return immediately. Actual wait time was: " + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) >= 0 && TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) <= 10); + } + + @Test + @Ignore //TODO: ignoring this test because it seems to fail on Java 8 under travis-ci. determine if there is an actual code defect, or just a test/environment defect. + public void testWaitForQuiescenceInterruptedBySecondRequestSuccessful() throws InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/successquiesence2s"), + Times.exactly(2) + ).respond(response() + .withStatusCode(200) + .withDelay(new Delay(TimeUnit.SECONDS, 2))); + + final AtomicLong secondRequestFinished = new AtomicLong(); + + final AtomicInteger firstRequestStatusCode = new AtomicInteger(); + final AtomicInteger secondRequestStatusCode = new AtomicInteger(); + + final AtomicBoolean exceptionOccurred = new AtomicBoolean(); + + new Thread(new Runnable() { + @Override + public void run() { + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/successquiesence2s")); + EntityUtils.consumeQuietly(response.getEntity()); + firstRequestStatusCode.set(response.getStatusLine().getStatusCode()); + + Thread.sleep(1000); + + response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/successquiesence2s")); + EntityUtils.consumeQuietly(response.getEntity()); + + secondRequestFinished.set(System.nanoTime()); + + secondRequestStatusCode.set(response.getStatusLine().getStatusCode()); + } catch (IOException | InterruptedException e) { + exceptionOccurred.set(true); + + log.error("Exception occurred while making HTTP request", e); + } + } + }).start(); + + // wait for the request to start before waiting for quiescence + Thread.sleep(1000); + + long start = System.nanoTime(); + boolean waitSuccessful = proxy.waitForQuiescence(2, 10, TimeUnit.SECONDS); + long finish = System.nanoTime(); + + assertFalse("An exception occurred while making an HTTP request", exceptionOccurred.get()); + assertEquals("Expected successful response from server on first request", 200, firstRequestStatusCode.get()); + assertTrue("Expected second request to be finished", secondRequestFinished.get() > 0); + assertEquals("Expected successful response from server on second request", 200, secondRequestStatusCode.get()); + + assertTrue("Expected to successfully wait for quiescence", waitSuccessful); + + assertTrue("Expected waitForQuiescence to return after approximately 2s of quiescence. Actual time: " + + TimeUnit.MILLISECONDS.convert(finish - secondRequestFinished.get(), TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - secondRequestFinished.get(), TimeUnit.NANOSECONDS) >= 1600 + && TimeUnit.MILLISECONDS.convert(finish - secondRequestFinished.get(), TimeUnit.NANOSECONDS) <= 2400); + + assertTrue("Expected to wait for quiescence for approximately 6s. Actual wait time was: " + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) >= 5000 && TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) <= 7000); + } + + @Test + public void testWaitForQuiescenceInterruptedBySecondRequestUnsuccessful() throws InterruptedException { + mockServer.when( + request().withMethod("GET") + .withPath("/quiesence2s"), + Times.unlimited() + ).respond(response().withStatusCode(200).withDelay(new Delay(TimeUnit.SECONDS, 2))); + + mockServer.when( + request().withMethod("GET") + .withPath("/quiesence5s"), + Times.unlimited() + ).respond(response().withStatusCode(200).withDelay(new Delay(TimeUnit.SECONDS, 5))); + + final AtomicInteger firstResponseStatusCode = new AtomicInteger(); + final AtomicBoolean secondRequestCompleted = new AtomicBoolean(false); + + new Thread(new Runnable() { + @Override + public void run() { + try (CloseableHttpClient client = NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) { + HttpResponse response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiesence2s")); + EntityUtils.consumeQuietly(response.getEntity()); + + firstResponseStatusCode.set(response.getStatusLine().getStatusCode()); + + Thread.sleep(1000); + + response = client.execute(new HttpGet("http://127.0.0.1:" + mockServerPort + "/quiesence5s")); + EntityUtils.consumeQuietly(response.getEntity()); + + secondRequestCompleted.set(true); + } catch (IOException | InterruptedException e) { + } + } + }).start(); + + // wait for the request to start before waiting for quiescence + Thread.sleep(1000); + + // waitForQuiescence should exit after 3s, since it will not be possible to satisfy the 2s quietPeriod while a request is in progress at 3s + long start = System.nanoTime(); + boolean waitSuccessful = proxy.waitForQuiescence(2, 5, TimeUnit.SECONDS); + long finish = System.nanoTime(); + + assertFalse("Expected to unsuccessfully wait for quiescence", waitSuccessful); + + assertTrue("Expected to wait for quiescence for approximately 3s. Actual wait time was: " + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) + "ms", + TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) >= 2500 && TimeUnit.MILLISECONDS.convert(finish - start, TimeUnit.NANOSECONDS) <= 3500); + + assertEquals("Expected successful response from server on first request", 200, firstResponseStatusCode.get()); + assertFalse("Did not expect second request to complete", secondRequestCompleted.get()); + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolverCacheTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolverCacheTest.java new file mode 100644 index 000000000..6ea903e3e --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolverCacheTest.java @@ -0,0 +1,225 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.collect.ImmutableList; +import net.lightbody.bmp.proxy.test.util.NewProxyServerTestUtil; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; + +@RunWith(Parameterized.class) +public class AdvancedHostResolverCacheTest { + private static final Logger log = LoggerFactory.getLogger(AdvancedHostResolverCacheTest.class); + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][]{ + // skip DNS cache operations for NativeResolver + {NativeCacheManipulatingResolver.class}, {ChainedHostResolver.class} + }); + } + + public final AdvancedHostResolver resolver; + + public AdvancedHostResolverCacheTest(Class resolverClass) throws IllegalAccessException, InstantiationException { + // this is a hacky way to allow us to test the ChainedHostResolver, even though it doesn't have a no-arg constructor + if (resolverClass.equals(ChainedHostResolver.class)) { + this.resolver = new ChainedHostResolver(ImmutableList.of(new NativeCacheManipulatingResolver())); + } else { + this.resolver = resolverClass.newInstance(); + } + } + + @Before + public void skipForTravisCi() { + // skip these tests on the CI server since the DNS lookup is extremely fast, even when cached + assumeFalse("true".equals(System.getenv("TRAVIS"))); + } + + @Before + public void skipOnWindows() { + // DNS cache-manipulating features are not available on Windows, because the NativeCacheManipulatingResolver does + // not work, since Java seems to use to the OS-level cache. + assumeFalse("NativeCacheManipulatingResolver does not support cache manipulation on Windows", + NewProxyServerTestUtil.isWindows()); + } + + @Test + public void testCanClearDNSCache() { + // populate the cache + resolver.resolve("www.msn.com"); + + resolver.clearDNSCache(); + + long start = System.nanoTime(); + resolver.resolve("www.msn.com"); + long finish = System.nanoTime(); + + assertNotEquals("Expected non-zero DNS lookup time for www.msn.com after clearing DNS cache", 0, finish - start); + } + + @Test + public void testCachedPositiveLookup() { + long start = System.nanoTime(); + // must use an address that we haven't already resolved in another test + resolver.resolve("news.bing.com"); + long finish = System.nanoTime(); + + long uncachedLookupNs = finish - start; + + assertNotEquals("Expected non-zero DNS lookup time for news.bing.com on first lookup", 0, uncachedLookupNs); + + start = System.nanoTime(); + resolver.resolve("news.bing.com"); + finish = System.nanoTime(); + + long cachedLookupNs = finish - start; + + assertTrue("Expected extremely fast DNS lookup time for news.bing.com on second (cached) lookup. Uncached: " + uncachedLookupNs + "ns; cached: " + cachedLookupNs + "ns.", cachedLookupNs <= uncachedLookupNs / 2); + } + + @Test + public void testCachedNegativeLookup() { + long start = System.nanoTime(); + resolver.resolve("fake.notarealaddress"); + long finish = System.nanoTime(); + + long uncachedLookupNs = finish - start; + + assertNotEquals("Expected non-zero DNS lookup time for fake.notarealaddress on first lookup", 0, uncachedLookupNs); + + start = System.nanoTime(); + resolver.resolve("fake.notarealaddress"); + finish = System.nanoTime(); + + long cachedLookupNs = finish - start; + + assertTrue("Expected extremely fast DNS lookup time for fake.notarealaddress on second (cached) lookup. Uncached: " + uncachedLookupNs + "ns; cached: " + cachedLookupNs + "ns.", cachedLookupNs <= uncachedLookupNs / 2); + } + + @Test + public void testSetPositiveCacheTtl() throws InterruptedException { + resolver.clearDNSCache(); + resolver.setPositiveDNSCacheTimeout(2, TimeUnit.SECONDS); + + // populate the cache + Collection addresses = resolver.resolve("www.msn.com"); + + // make sure there are addresses, since this is a *positive* TTL test + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find addresses for www.msn.com", 0, addresses.size()); + + // wait for the cache to clear + Thread.sleep(2500); + + long start = System.nanoTime(); + addresses = resolver.resolve("www.msn.com"); + long finish = System.nanoTime(); + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find addresses for www.msn.com", 0, addresses.size()); + + assertNotEquals("Expected non-zero DNS lookup time for www.msn.com after setting positive cache TTL", 0, finish - start); + } + + @Test + public void testSetNegativeCacheTtl() throws InterruptedException { + Random random = new Random(); + String fakeAddress = random.nextInt() + ".madeup.thisisafakeaddress"; + + resolver.clearDNSCache(); + resolver.setNegativeDNSCacheTimeout(2, TimeUnit.SECONDS); + + // populate the cache + Collection addresses = resolver.resolve(fakeAddress); + + // make sure there are no addresses, since this is a *negative* TTL test + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertEquals("Expected to find no addresses for " + fakeAddress, 0, addresses.size()); + + // wait for the cache to clear + Thread.sleep(2500); + + long start = System.nanoTime(); + addresses = resolver.resolve(fakeAddress); + long finish = System.nanoTime(); + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertEquals("Expected to find no addresses for " + fakeAddress, 0, addresses.size()); + + assertNotEquals("Expected non-zero DNS lookup time for " + fakeAddress + " after setting negative cache TTL", 0, finish - start); + } + + @Test + public void testSetEternalNegativeCacheTtl() { + Random random = new Random(); + String fakeAddress = random.nextInt() + ".madeup.thisisafakeaddress"; + + resolver.clearDNSCache(); + resolver.setNegativeDNSCacheTimeout(-1, TimeUnit.SECONDS); + + // populate the cache + Collection addresses = resolver.resolve(fakeAddress); + + // make sure there are no addresses, since this is a *negative* TTL test + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertEquals("Expected to find no addresses for " + fakeAddress, 0, addresses.size()); + + long start = System.nanoTime(); + addresses = resolver.resolve(fakeAddress); + long finish = System.nanoTime(); + + long cachedLookupNs = finish - start; + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertEquals("Expected to find no addresses for " + fakeAddress, 0, addresses.size()); + + assertTrue("Expected extremely fast DNS lookup time for " + fakeAddress + " after setting eternal negative cache TTL. Cached lookup time: " + cachedLookupNs + "ns.", cachedLookupNs <= TimeUnit.NANOSECONDS.convert(10, TimeUnit.MILLISECONDS)); + } + + @Test + public void testSetEternalPositiveCacheTtl() { + resolver.clearDNSCache(); + resolver.setPositiveDNSCacheTimeout(-1, TimeUnit.SECONDS); + + log.info("Using resolver: {}", resolver.getClass().getSimpleName()); + + // populate the cache + long one = System.nanoTime(); + Collection addresses = resolver.resolve("www.msn.com"); + long two = System.nanoTime(); + log.info("Time to resolve address without cache: {}ns", two - one); + + // make sure there are addresses, since this is a *positive* TTL test + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find addresses for www.msn.com", 0, addresses.size()); + + long start = System.nanoTime(); + addresses = resolver.resolve("www.msn.com"); + long finish = System.nanoTime(); + + long cachedLookupNs = finish - start; + + log.info("Time to resolve address with cache: {}ns", cachedLookupNs); + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find addresses for www.msn.com", 0, addresses.size()); + + assertTrue("Expected extremely fast DNS lookup time for www.msn.com after setting eternal negative cache TTL. Cached lookup time: " + cachedLookupNs + "ns.", cachedLookupNs <= TimeUnit.NANOSECONDS.convert(10, TimeUnit.MILLISECONDS)); + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolverTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolverTest.java new file mode 100644 index 000000000..605e1a190 --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolverTest.java @@ -0,0 +1,201 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.collect.ImmutableList; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +@RunWith(Parameterized.class) +public class AdvancedHostResolverTest { + private static final Logger log = LoggerFactory.getLogger(AdvancedHostResolverTest.class); + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][]{ + {NativeResolver.class}, {NativeCacheManipulatingResolver.class}, {ChainedHostResolver.class} + }); + } + + public AdvancedHostResolver resolver; + + public AdvancedHostResolverTest(Class resolverClass) throws IllegalAccessException, InstantiationException { + // this is a hacky way to allow us to test the ChainedHostResolver, even though it doesn't have a no-arg constructor + if (resolverClass.equals(ChainedHostResolver.class)) { + this.resolver = new ChainedHostResolver(ImmutableList.of(new NativeResolver(), new NativeCacheManipulatingResolver())); + } else { + this.resolver = resolverClass.newInstance(); + } + } + + private boolean ipv6Enabled = false; + + @Before + public void testForIPv6() throws UnknownHostException { + InetAddress[] addresses = InetAddress.getAllByName("::1"); + if (addresses != null) { + for (InetAddress addr : addresses) { + if (addr.getClass() == Inet6Address.class) { + ipv6Enabled = true; + + return; + } + } + } + } + + @Test + public void testResolveAddress() { + Collection yahooAddresses = resolver.resolve("www.yahoo.com"); + + assertNotNull("Collection of resolved addresses should never be null", yahooAddresses); + + assertNotEquals("Expected to find at least one address for www.yahoo.com", 0, yahooAddresses.size()); + } + + @Test + public void testCannotResolveAddress() { + Collection noAddresses = resolver.resolve("www.notarealaddress.grenyarnia"); + + assertNotNull("Collection of resolved addresses should never be null", noAddresses); + + assertEquals("Expected to find no address for www.notarealaddress.grenyarnia", 0, noAddresses.size()); + } + + @Test + public void testResolveIPv4AndIPv6Addresses() { + assumeTrue("Skipping test because IPv6 is not enabled", ipv6Enabled); + + boolean foundIPv4 = false; + boolean foundIPv6 = false; + Collection addresses = resolver.resolve("www.google.com"); + for (InetAddress address : addresses) { + if (address.getClass() == Inet4Address.class) { + foundIPv4 = true; + } else if (address.getClass() == Inet6Address.class) { + foundIPv6 = true; + } + } + + assertTrue("Expected to find at least one IPv4 address for www.google.com", foundIPv4); + + // disabling this assert to prevent test failures on systems without ipv6 access, or when the DNS server does not return IPv6 addresses + //assertTrue("Expected to find at least one IPv6 address for www.google.com", foundIPv6); + + } + + @Test + public void testResolveLocalhost() { + Collection addresses = resolver.resolve("localhost"); + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find at least one address for localhost", 0, addresses.size()); + } + + @Test + public void testResolveIPv4Address() { + Collection addresses = resolver.resolve("127.0.0.1"); + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find at least one address for 127.0.0.1", 0, addresses.size()); + } + + @Test + public void testResolveIPv6Address() { + assumeTrue("Skipping test because IPv6 is not enabled", ipv6Enabled); + + Collection addresses = resolver.resolve("::1"); + + assertNotNull("Collection of resolved addresses should never be null", addresses); + assertNotEquals("Expected to find at least one address for ::1", 0, addresses.size()); + } + + @Test + public void testResolveRemappedHost() { + Collection originalAddresses = resolver.resolve("www.google.com"); + + assertNotNull("Collection of resolved addresses should never be null", originalAddresses); + assertNotEquals("Expected to find at least one address for www.google.com", 0, originalAddresses.size()); + + resolver.remapHost("www.google.com", "www.bing.com"); + + Collection remappedAddresses = resolver.resolve("www.google.com"); + assertNotNull("Collection of resolved addresses should never be null", remappedAddresses); + assertNotEquals("Expected to find at least one address for www.google.com remapped to www.bing.com", 0, remappedAddresses.size()); + + InetAddress firstRemappedAddr = remappedAddresses.iterator().next(); + + //TODO: verify this is correct -- should remapping return the remapped hostname, or the original hostname but with an IP address corresponding to the remapped hostname? + assertEquals("Expected hostname for returned address to reflect the remapped address.", "www.bing.com", firstRemappedAddr.getHostName()); + } + + @Test + public void testReplaceRemappedHostWithNewRemapping() { + // remap the hostname twice. the second remapping should supercede the first. + resolver.remapHost("www.google.com", "www.yahoo.com"); + resolver.remapHost("www.google.com", "www.bing.com"); + + Collection remappedAddresses = resolver.resolve("www.google.com"); + assertNotNull("Collection of resolved addresses should never be null", remappedAddresses); + assertNotEquals("Expected to find at least one address for www.google.com remapped to www.bing.com", 0, remappedAddresses.size()); + + InetAddress firstRemappedAddr = remappedAddresses.iterator().next(); + + //TODO: verify this is correct -- should remapping return the remapped hostname, or the original hostname but with an IP address corresponding to the remapped hostname? + assertEquals("Expected hostname for returned address to reflect the remapped address.", "www.bing.com", firstRemappedAddr.getHostName()); + } + + @Test + public void testRetrieveOriginalHostByRemappedHost() { + resolver.remapHost("www.google.com", "www.bing.com"); + + Collection originalHostnames = resolver.getOriginalHostnames("www.bing.com"); + assertEquals("Expected to find one original hostname after remapping", 1, originalHostnames.size()); + + String original = originalHostnames.iterator().next(); + assertEquals("Expected to find original hostname of www.google.com after remapping to www.bing.com", "www.google.com", original); + } + + @Test + public void testRemoveHostRemapping() { + resolver.remapHost("www.google.com", "www.notarealaddress"); + + Collection remappedAddresses = resolver.resolve("www.google.com"); + assertEquals("Expected to find no address for remapped www.google.com", 0, remappedAddresses.size()); + + resolver.removeHostRemapping("www.google.com"); + + Collection regularAddress = resolver.resolve("www.google.com"); + assertNotNull("Collection of resolved addresses should never be null", remappedAddresses); + assertNotEquals("Expected to find at least one address for www.google.com after removing remapping", 0, regularAddress.size()); + } + + @Test + public void testClearHostRemappings() { + resolver.remapHost("www.google.com", "www.notarealaddress"); + + Collection remappedAddresses = resolver.resolve("www.google.com"); + assertEquals("Expected to find no address for remapped www.google.com", 0, remappedAddresses.size()); + + resolver.clearHostRemappings(); + + Collection regularAddress = resolver.resolve("www.google.com"); + assertNotNull("Collection of resolved addresses should never be null", remappedAddresses); + assertNotEquals("Expected to find at least one address for www.google.com after removing remapping", 0, regularAddress.size()); + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java new file mode 100644 index 000000000..fbf09d63c --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/dns/ChainedHostResolverTest.java @@ -0,0 +1,168 @@ +package net.lightbody.bmp.proxy.dns; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import net.lightbody.bmp.proxy.test.util.TestConstants; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.net.InetAddress; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ChainedHostResolverTest { + @Test + public void testEmptyResolver() { + ChainedHostResolver resolver = new ChainedHostResolver(null); + + Collection results = resolver.resolve("www.google.com"); + + assertNotNull("Resolver should not return null results", results); + assertThat("Empty resolver chain should return empty results", results, empty()); + + Map remappings = resolver.getHostRemappings(); + assertTrue("Empty resolver chain should return empty results", remappings.isEmpty()); + + // make sure no exception is thrown when attempting write operations on an empty resolver + resolver.setNegativeDNSCacheTimeout(1000, TimeUnit.DAYS); + resolver.setPositiveDNSCacheTimeout(1000, TimeUnit.DAYS); + resolver.clearDNSCache(); + resolver.remapHost("", ""); + resolver.remapHosts(ImmutableMap.of("", "")); + resolver.removeHostRemapping(""); + resolver.clearHostRemappings(); + } + + @Test + public void testResolveReturnsFirstResults() { + AdvancedHostResolver firstResolver = mock(AdvancedHostResolver.class); + AdvancedHostResolver secondResolver = mock(AdvancedHostResolver.class); + ChainedHostResolver chainResolver = new ChainedHostResolver(ImmutableList.of(firstResolver, secondResolver)); + + when(firstResolver.resolve("1.1.1.1")).thenReturn(TestConstants.addressOnesList); + when(secondResolver.resolve("1.1.1.1")).thenReturn(Collections.emptyList()); + + Collection results = chainResolver.resolve("1.1.1.1"); + assertNotNull("Resolver should not return null results", results); + assertThat("Expected resolver to return a result", results, not(empty())); + Assert.assertEquals("Resolver returned unexpected result", TestConstants.addressOnes, Iterables.get(results, 0)); + + verify(secondResolver, never()).resolve("1.1.1.1"); + + reset(firstResolver); + reset(secondResolver); + + when(firstResolver.resolve("2.2.2.2")).thenReturn(Collections.emptyList()); + when(secondResolver.resolve("2.2.2.2")).thenReturn(TestConstants.addressTwosList); + + results = chainResolver.resolve("2.2.2.2"); + assertNotNull("Resolver should not return null results", results); + assertThat("Expected resolver to return a result", results, not(empty())); + Assert.assertEquals("Resolver returned unexpected result", TestConstants.addressTwos, Iterables.get(results, 0)); + + verify(firstResolver).resolve("2.2.2.2"); + verify(secondResolver).resolve("2.2.2.2"); + } + + @Test + public void testGetterUsesFirstResolver() { + AdvancedHostResolver firstResolver = mock(AdvancedHostResolver.class); + AdvancedHostResolver secondResolver = mock(AdvancedHostResolver.class); + ChainedHostResolver chainResolver = new ChainedHostResolver(ImmutableList.of(firstResolver, secondResolver)); + + when(firstResolver.getOriginalHostnames("one")).thenReturn(ImmutableList.of("originalOne")); + + Collection results = chainResolver.getOriginalHostnames("one"); + assertNotNull("Resolver should not return null results", results); + assertThat("Expected resolver to return a result", results, not(empty())); + assertEquals("Resolver returned unexpected result", "originalOne", Iterables.get(results, 0)); + + verify(secondResolver, never()).getOriginalHostnames(any(String.class)); + } + + @Test + public void testResolveWaitsForWriteOperation() throws InterruptedException { + AdvancedHostResolver firstResolver = mock(AdvancedHostResolver.class); + AdvancedHostResolver secondResolver = mock(AdvancedHostResolver.class); + final ChainedHostResolver chainResolver = new ChainedHostResolver(ImmutableList.of(firstResolver, secondResolver)); + + final AtomicBoolean secondResolverClearingCache = new AtomicBoolean(false); + // track the time when the second resolver finishes clearing the cache, so we can verify the simultaneous resolve in the + // first resolver starts AFTER the cache clear completes + final AtomicLong secondResolverCacheClearFinishedTime = new AtomicLong(0); + + // set up the second resolver to sleep for a few seconds when a clearDNSCache() call is made + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + secondResolverClearingCache.set(true); + + Thread.sleep(4000); + + secondResolverCacheClearFinishedTime.set(System.nanoTime()); + + return null; + } + }).when(secondResolver).clearDNSCache(); + + // track the time the first resolver starts resolving the address, to make sure it is AFTER the DNS cache clear time + final AtomicLong firstResolverStartedResolvingTime = new AtomicLong(0); + + // set up the first resolver to capture the time it starts resolving the address + when(firstResolver.resolve("1.1.1.1")).then(new Answer>() { + @Override + public Collection answer(InvocationOnMock invocationOnMock) throws Throwable { + firstResolverStartedResolvingTime.set(System.nanoTime()); + return TestConstants.addressOnesList; + } + }); + + // run the DNS cache clear in a separate thread, so it will be running (and sleeping) when we test the resolve() method + new Thread(new Runnable() { + @Override + public void run() { + chainResolver.clearDNSCache(); + } + }).start(); + + // wait for the clearDNSCache() call to start executing + Thread.sleep(1000); + + // make sure the second resolver has started clearing the cache before invoking resolve on the chained resolver, to make sure + // that this call to resolve will result in a wait + assertTrue("Expected resolver to already be clearing the DNS cache in a separate thread", secondResolverClearingCache.get()); + assertEquals("Did not expect resolver to already be finished clearing the DNS cache", 0, secondResolverCacheClearFinishedTime.get()); + + Collection results = chainResolver.resolve("1.1.1.1"); + + assertNotNull("Resolver should not return null results", results); + assertThat("Expected resolver to return a result", results, not(empty())); + Assert.assertEquals("Resolver returned unexpected result", TestConstants.addressOnes, Iterables.get(results, 0)); + + assertThat("Expected resolver to be finished clearing DNS cache", secondResolverCacheClearFinishedTime.get(), greaterThan(0L)); + + assertThat("Expected resolver to finish clearing the DNS cache before starting to resolve an address", firstResolverStartedResolvingTime.get(), greaterThan(secondResolverCacheClearFinishedTime.get())); + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java new file mode 100644 index 000000000..9e1ba94ef --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/MockServerTest.java @@ -0,0 +1,26 @@ +package net.lightbody.bmp.proxy.test.util; + +import org.junit.After; +import org.junit.Before; +import org.mockserver.integration.ClientAndServer; + +/** + * Tests can subclass this to get access to a ClientAndServer instance for creating mock responses. + */ +public class MockServerTest { + protected ClientAndServer mockServer; + protected int mockServerPort; + + @Before + public void setUpMockServer() { + mockServer = new ClientAndServer(0); + mockServerPort = mockServer.getPort(); + } + + @After + public void tearDownMockServer() { + if (mockServer != null) { + mockServer.stop(); + } + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTest.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTest.java new file mode 100644 index 000000000..0da4f05a6 --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTest.java @@ -0,0 +1,42 @@ +package net.lightbody.bmp.proxy.test.util; + +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.BrowserMobProxyServer; +import org.apache.http.HttpHost; +import org.apache.http.client.CookieStore; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.After; +import org.junit.Before; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A base class that spins up and shuts down a BrowserMobProxy instance using the new interface. IT also provides mock server support via + * {@link net.lightbody.bmp.proxy.test.util.MockServerTest}. + */ +public class NewProxyServerTest extends MockServerTest { + protected BrowserMobProxy proxy; + + @Before + public void setUpProxyServer() { + proxy = new BrowserMobProxyServer(); + proxy.start(); + } + + @After + public void shutDownProxyServer() { + if (proxy != null) { + proxy.abort(); + } + } + +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java new file mode 100644 index 000000000..af86c4b96 --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/NewProxyServerTestUtil.java @@ -0,0 +1,94 @@ +package net.lightbody.bmp.proxy.test.util; + +import org.apache.http.HttpHost; +import org.apache.http.client.CookieStore; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +public class NewProxyServerTestUtil { + /** + * Creates an all-trusting CloseableHttpClient (for tests ONLY!) that will connect to a proxy at 127.0.0.1:proxyPort, + * with no cookie store. + * + * @param proxyPort port of the proxy running at 127.0.0.1 + * @return a new CloseableHttpClient + */ + public static CloseableHttpClient getNewHttpClient(int proxyPort) { + return getNewHttpClient(proxyPort, null); + } + + /** + * Creates an all-trusting CloseableHttpClient (for tests ONLY!) that will connect to a proxy at 127.0.0.1:proxyPort, + * using the specified cookie store. + * + * @param proxyPort port of the proxy running at 127.0.0.1 + * @param cookieStore CookieStore for HTTP cookies + * @return a new CloseableHttpClient + */ + public static CloseableHttpClient getNewHttpClient(int proxyPort, CookieStore cookieStore) { + try { + // Trust all certs -- under no circumstances should this ever be used outside of testing + SSLContext sslcontext = SSLContexts.custom() + .useTLS() + .loadTrustMaterial(null, new TrustStrategy() { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }) + .build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslcontext, + SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + + CloseableHttpClient httpclient = HttpClients.custom() + .setSSLSocketFactory(sslsf) + .setDefaultCookieStore(cookieStore) + .setProxy(new HttpHost("127.0.0.1", proxyPort)) + // disable decompressing content, since some tests want uncompressed content for testing purposes + .disableContentCompression() + .disableAutomaticRetries() + .build(); + + return httpclient; + } catch (Exception e) { + throw new RuntimeException("Unable to create new HTTP client", e); + } + } + + /** + * Reads and closes the input stream, converting it to a String using the UTF-8 charset. The input stream is guaranteed to be closed, even + * if the reading/conversion throws an exception. + * + * @param in UTF-8-encoded InputStream to read + * @return String of InputStream's contents + * @throws IOException if an error occurs reading from the stream + */ + public static String toStringAndClose(InputStream in) throws IOException { + try { + return org.apache.commons.io.IOUtils.toString(in, StandardCharsets.UTF_8); + } finally { + org.apache.commons.io.IOUtils.closeQuietly(in); + } + } + + /** + * Checks if the test is running on a Windows OS. + * + * @return true if running on Windows, otherwise false + */ + public static boolean isWindows() { + return System.getProperty("os.name").startsWith("Windows"); + } +} diff --git a/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/TestConstants.java b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/TestConstants.java new file mode 100644 index 000000000..d057e4165 --- /dev/null +++ b/browsermob-core/src/test/java/net/lightbody/bmp/proxy/test/util/TestConstants.java @@ -0,0 +1,28 @@ +package net.lightbody.bmp.proxy.test.util; + +import com.google.common.collect.ImmutableList; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; + +/** + * Convenience class for test constants. + */ +public class TestConstants { + public static final InetAddress addressOnes; + public static final InetAddress addressTwos; + public static final List addressOnesList; + public static final List addressTwosList; + + static { + try { + addressOnes = InetAddress.getByName("1.1.1.1"); + addressTwos = InetAddress.getByName("2.2.2.2"); + addressOnesList = ImmutableList.of(TestConstants.addressOnes); + addressTwosList = ImmutableList.of(TestConstants.addressTwos); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} diff --git a/browsermob-core/src/test/resources/log4j2-test.json b/browsermob-core/src/test/resources/log4j2-test.json new file mode 100644 index 000000000..f3e5e72ec --- /dev/null +++ b/browsermob-core/src/test/resources/log4j2-test.json @@ -0,0 +1,23 @@ +{ + "configuration" : { + "name": "test", + "appenders": { + "Console": { + "name": "console", + "target": "SYSTEM_OUT", + "PatternLayout": { + "pattern": "%-7r %date %level [%thread] %logger - %msg%n" + } + } + }, + + "loggers": { + "root": { + "level": "info", + "appender-ref": { + "ref": "console" + } + } + } + } +} \ No newline at end of file diff --git a/browsermob-dist/assembly.xml b/browsermob-dist/assembly.xml new file mode 100644 index 000000000..49ae27db3 --- /dev/null +++ b/browsermob-dist/assembly.xml @@ -0,0 +1,60 @@ + + + bin + + zip + + + + ${project.basedir}/.. + + LICENSE.txt + README.txt + README.md + + ./ + + + ${project.basedir}/src/main/scripts + bin + keep + 0744 + 0755 + true + + + ${project.basedir}/../browsermob-core/target + + *-sources.jar + *-javadoc.jar + + ./ + + + ${project.basedir}/../browsermob-rest/target + + *-sources.jar + *-javadoc.jar + + ./ + + + ${project.basedir}/src/main/resources + + bin/conf + + + ${project.basedir}/target + + ${project.build.finalName}.jar + + lib + + + ${project.basedir}/../browsermob-core/src/main/resources/sslSupport + ./ssl-support + + + diff --git a/browsermob-dist/pom.xml b/browsermob-dist/pom.xml new file mode 100644 index 000000000..59edc89cb --- /dev/null +++ b/browsermob-dist/pom.xml @@ -0,0 +1,150 @@ + + + + browsermob-proxy + net.lightbody.bmp + 2.1.6-SNAPSHOT + + 4.0.0 + + browsermob-dist + jar + + BrowserMob Proxy Distributable + + + ${project.parent.artifactId}-${project.version} + ${netty-4.1.version} + + + + + net.lightbody.bmp + browsermob-core + ${project.version} + + + net.lightbody.bmp + browsermob-rest + ${project.version} + + + + net.lightbody.bmp + browsermob-legacy + ${project.version} + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-slf4j-impl + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + + org.javassist + javassist + + + + + package + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + true + false + + + + org.apache.maven.plugins + maven-shade-plugin + 3.0.0 + + + package + + shade + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + net.lightbody.bmp.proxy.Main + + + false + + + + + + maven-assembly-plugin + 3.0.0 + + + make-bundles + + single + + package + + + assembly.xml + + ${zip.name} + + + + + + + \ No newline at end of file diff --git a/browsermob-dist/src/main/java/net/lightbody/bmp/proxy/Main.java b/browsermob-dist/src/main/java/net/lightbody/bmp/proxy/Main.java new file mode 100644 index 000000000..f82ad9d29 --- /dev/null +++ b/browsermob-dist/src/main/java/net/lightbody/bmp/proxy/Main.java @@ -0,0 +1,153 @@ +package net.lightbody.bmp.proxy; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.servlet.GuiceServletContextListener; +import com.google.sitebricks.SitebricksModule; +import net.lightbody.bmp.exception.JettyException; +import net.lightbody.bmp.proxy.bricks.ProxyResource; +import net.lightbody.bmp.proxy.guice.ConfigModule; +import net.lightbody.bmp.proxy.guice.JettyModule; +import net.lightbody.bmp.util.BrowserMobProxyUtil; +import net.lightbody.bmp.util.DeleteDirectoryTask; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletContextEvent; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class Main { + // the static final logger is in this static inner class to allow the logging configuration code to execute before the logger is initialized. + // since the 'log' field is in a static inner class, it will only be initialized when it is actually used, instead of when the classloader + // loads the Main class. + private static class LogHolder { + private static final Logger log = LoggerFactory.getLogger(Main.class); + } + + private static final String BMP_LOG_CONFIG_NAME = "bmp-logging.yaml"; + private static final String DEFAULT_LOG_CONFIG_LOCATION = "bin/conf/" + BMP_LOG_CONFIG_NAME; + + public static final String LOG4J_CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile"; + + public static void main(String[] args) { + configureLogging(); + + if (args.length > 0 && "--version".equals(args[0])) { + System.out.println("BrowserMob Proxy " + BrowserMobProxyUtil.getVersionString()); + System.exit(0); + } + + final Injector injector = Guice.createInjector(new ConfigModule(args), new JettyModule(), new SitebricksModule() { + @Override + protected void configureSitebricks() { + scan(ProxyResource.class.getPackage()); + } + }); + + LogHolder.log.info("Starting BrowserMob Proxy version {}", BrowserMobProxyUtil.getVersionString()); + + Server server = injector.getInstance(Server.class); + GuiceServletContextListener gscl = new GuiceServletContextListener() { + @Override + protected Injector getInjector() { + return injector; + } + }; + try { + server.start(); + } catch (Exception e) { + LogHolder.log.error("Failed to start Jetty server. Aborting.", e); + + throw new JettyException("Unable to start Jetty server", e); + } + + ServletContextHandler context = (ServletContextHandler) server.getHandler(); + gscl.contextInitialized(new ServletContextEvent(context.getServletContext())); + + try { + server.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * Configures logging when running the proxy in stand-alone mode. Searches for a configuration file in the following order: + *
    + *
  1. The file specified in the log4j.configurationFile system property, if defined
  2. + *
  3. The bmp-logging.yaml file in (basedir)/bin/conf, if the basedir system property has been defined
  4. + *
  5. The bmp-logging.yaml file in the bin/conf directory relative to the current working directory
  6. + *
  7. The bmp-logging.yaml file in the current working directory
  8. + *
  9. The bmp-logging.yaml file on the classpath
  10. + *
+ */ + private static void configureLogging() { + // allow users to override the log4j config file location. if the log4j.configurationFile property has been set, don't attempt to override it + // with BMP's logging configuration. + String log4jFileLocation = System.getProperty(LOG4J_CONFIGURATION_FILE_PROPERTY); + if (log4jFileLocation == null || log4jFileLocation.isEmpty()) { + // user is not specifying a log file, so look for one on the filesystem. + // the system property "basedir" is set to the location of the browsermob-proxy script or .bat file by the script itself. + String basedir = System.getProperty("basedir"); + // if basedir is not defined, attempt to resolve the file relative to the working directory + if (basedir == null) { + basedir = ""; + } + + Path baseDirPath = Paths.get(basedir); + + Path logFilePath = baseDirPath.resolve(DEFAULT_LOG_CONFIG_LOCATION); + + // if the config file is not readable, attempt to find the config file in the current working directory + if (!Files.isReadable(logFilePath)) { + logFilePath = Paths.get(BMP_LOG_CONFIG_NAME); + + // if the config file is still not readable, fall back to the config file on the classpath. + if (!Files.isReadable(logFilePath)) { + + InputStream defaultLogConfigStream = Main.class.getResourceAsStream("/" + BMP_LOG_CONFIG_NAME); + if (defaultLogConfigStream == null) { + // can't find the default log config file. give up. + return; + } + + Path tempDir; + try { + tempDir = Files.createTempDirectory("browsermob-proxy"); + } catch (IOException e) { + // can't create the temp directory, so nowhere to put the config file. give up. + return; + } + + // delete the temp directory when the VM stops or aborts + Runtime.getRuntime().addShutdownHook(new Thread(new DeleteDirectoryTask(tempDir))); + + // copy the default config file to the temp directory from the classpath + logFilePath = tempDir.resolve(BMP_LOG_CONFIG_NAME); + try { + Files.copy(defaultLogConfigStream, logFilePath); + } catch (IOException e) { + // can't copy the file. give up. + return; + } + } + } + + try { + // convert the path to a URL, to avoid a MalformedURLException with log4j 2 on Windows + System.setProperty("log4j.configurationFile", logFilePath.toAbsolutePath().toFile().toURI().toURL().toString()); + } catch (MalformedURLException | RuntimeException e) { + System.out.println("Could not set log4j.configurationFile to " + logFilePath.toAbsolutePath().toString() + " due to error: " + e.getMessage()); + e.printStackTrace(); + } + } + } +} + diff --git a/browsermob-dist/src/main/resources/bmp-logging.yaml b/browsermob-dist/src/main/resources/bmp-logging.yaml new file mode 100644 index 000000000..3114ff47f --- /dev/null +++ b/browsermob-dist/src/main/resources/bmp-logging.yaml @@ -0,0 +1,37 @@ +# This file controls the logging configuration for browsermob-proxy in "standalone" mode. To adjust the amount of log output, modify the +# 'level' fields below. For more information on log4j configuration files, see http://logging.apache.org/log4j/2.x/manual/configuration.html. + +configuration: + name: standalone + appenders: + console: + name: console + target: SYSTEM_OUT + PatternLayout: + pattern: "[%-5level %date{ISO8601} %logger] (%thread) %msg %xThrowable%n" + file: + - + name: file + fileName: bmp.log + PatternLayout: + pattern: "[%-5level %date{ISO8601} %logger] (%thread) %msg %xThrowable%n" + append: false + loggers: + logger: + - + name: net.lightbody.bmp.proxy.jetty.util.ThreadedServer + level: warn + additivity: false + - + name: net.lightbody.bmp + # to suppress unwanted BMP logging statements, set the level below for the source logger to WARN or ERROR. + # to enable more verbose logging, set the level to DEBUG or TRACE. + level: info + root: + # to suppress unwanted logging statements globally, set the level below to WARN or ERROR. + level: info + appender-ref: + - + ref: console + - + ref: file \ No newline at end of file diff --git a/browsermob-dist/src/main/scripts/browsermob-proxy b/browsermob-dist/src/main/scripts/browsermob-proxy new file mode 100755 index 000000000..dc8f66897 --- /dev/null +++ b/browsermob-dist/src/main/scripts/browsermob-proxy @@ -0,0 +1,29 @@ +#!/bin/sh + +BASEDIR=`dirname $0`/.. +BASEDIR=`(cd "$BASEDIR"; pwd)` + +# if user has not explicitly set a command to use to invoke java, use 'java' and assume it is on the path +if [ -z "$JAVACMD" ] +then + JAVACMD="java" +fi + +"$JAVACMD" $JAVA_OPTS \ + -Dapp.name="browsermob-proxy" \ + -Dbasedir="$BASEDIR" \ + -jar "$BASEDIR/lib/browsermob-dist-${project.version}.jar" \ + "$@" + +# if we couldn't find java, print a helpful error message +if [ $? -eq 127 ] +then + echo + echo "Unable to run java using command: $JAVACMD" + echo "Make sure java is installed and on the path, or set JAVACMD to the java executable before running this script." + echo + echo "Example:" + echo + echo " $ JAVACMD=/var/lib/jdk/bin/java ./browsermob-proxy" + echo +fi diff --git a/browsermob-dist/src/main/scripts/browsermob-proxy.bat b/browsermob-dist/src/main/scripts/browsermob-proxy.bat new file mode 100644 index 000000000..529ea1cfb --- /dev/null +++ b/browsermob-dist/src/main/scripts/browsermob-proxy.bat @@ -0,0 +1,105 @@ +@REM ---------------------------------------------------------------------------- +@REM Copyright 2001-2004 The Apache Software Foundation. +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. +@REM ---------------------------------------------------------------------------- +@REM + +@echo off + +set ERROR_CODE=0 + +:init +@REM Decide how to startup depending on the version of windows + +@REM -- Win98ME +if NOT "%OS%"=="Windows_NT" goto Win9xArg + +@REM set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" @setlocal + +@REM -- 4NT shell +if "%eval[2+2]" == "4" goto 4NTArgs + +@REM -- Regular WinNT shell +set CMD_LINE_ARGS=%* +goto WinNTGetScriptDir + +@REM The 4NT Shell from jp software +:4NTArgs +set CMD_LINE_ARGS=%$ +goto WinNTGetScriptDir + +:Win9xArg +@REM Slurp the command line arguments. This loop allows for an unlimited number +@REM of arguments (up to the command line limit, anyway). +set CMD_LINE_ARGS= +:Win9xApp +if %1a==a goto Win9xGetScriptDir +set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1 +shift +goto Win9xApp + +:Win9xGetScriptDir +set SAVEDIR=%CD% +%0\ +cd %0\..\.. +set BASEDIR=%CD% +cd %SAVEDIR% +set SAVE_DIR= +goto repoSetup + +:WinNTGetScriptDir +set BASEDIR=%~dp0\.. + +:repoSetup + + +if "%JAVACMD%"=="" set JAVACMD=java + +if "%REPO%"=="" set REPO=%BASEDIR%\lib + +set CLASSPATH="%BASEDIR%"\etc;"%REPO%"\* +set EXTRA_JVM_ARGUMENTS= +goto endInit + +@REM Reaching here means variables are defined and arguments have been captured +:endInit + +%JAVACMD% %JAVA_OPTS% %EXTRA_JVM_ARGUMENTS% -classpath %CLASSPATH_PREFIX%;%CLASSPATH% -Dapp.name="browsermob-proxy" -Dapp.repo="%REPO%" -Dbasedir="%BASEDIR%" net.lightbody.bmp.proxy.Main %CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +if "%OS%"=="Windows_NT" @endlocal +set ERROR_CODE=1 + +:end +@REM set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" goto endNT + +@REM For old DOS remove the set variables from ENV - we assume they were not set +@REM before we started - at least we don't leave any baggage around +set CMD_LINE_ARGS= +goto postExec + +:endNT +@endlocal + +:postExec + +if "%FORCE_EXIT_ON_ERROR%" == "on" ( + if %ERROR_CODE% NEQ 0 exit %ERROR_CODE% +) + +exit /B %ERROR_CODE% diff --git a/browsermob-legacy/pom.xml b/browsermob-legacy/pom.xml new file mode 100644 index 000000000..592543c91 --- /dev/null +++ b/browsermob-legacy/pom.xml @@ -0,0 +1,155 @@ + + + jar + + + browsermob-proxy + net.lightbody.bmp + 2.1.6-SNAPSHOT + + 4.0.0 + + browsermob-legacy + BrowserMob Proxy Legacy (Jetty) Module + + + 7.6.16.v20140903 + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Xmx1g -XX:MaxPermSize=256m + + + ${use.littleproxy} + + + + + + + + + net.lightbody.bmp + browsermob-core + ${project.version} + + + + net.lightbody.bmp + mitm + ${project.version} + + + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpmime + + + + commons-io + commons-io + 2.5 + + + + javax.servlet + servlet-api + 2.5 + + + + org.seleniumhq.selenium + selenium-api + true + + + + org.seleniumhq.selenium + selenium-firefox-driver + test + + + + junit + junit + test + + + + org.hamcrest + hamcrest-library + test + + + + org.mockito + mockito-core + test + + + + org.eclipse.jetty + jetty-server + ${unit-test-jetty.version} + test + + + + org.eclipse.jetty + jetty-servlet + ${unit-test-jetty.version} + test + + + + org.eclipse.jetty + jetty-servlets + ${unit-test-jetty.version} + test + + + + + + + + legacy + + false + + + + + \ No newline at end of file diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/BrowserMobProxyServerLegacyAdapter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/BrowserMobProxyServerLegacyAdapter.java new file mode 100644 index 000000000..b2c0ad061 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/BrowserMobProxyServerLegacyAdapter.java @@ -0,0 +1,516 @@ +package net.lightbody.bmp; + +import com.google.common.collect.ImmutableList; +import com.google.common.net.HostAndPort; +import net.lightbody.bmp.client.ClientUtil; +import net.lightbody.bmp.exception.NameResolutionException; +import net.lightbody.bmp.proxy.BlacklistEntry; +import net.lightbody.bmp.proxy.CaptureType; +import net.lightbody.bmp.proxy.LegacyProxyServer; +import net.lightbody.bmp.proxy.auth.AuthType; +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import net.lightbody.bmp.proxy.http.RequestInterceptor; +import net.lightbody.bmp.proxy.http.ResponseInterceptor; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponseInterceptor; +import org.java_bandwidthlimiter.StreamManager; +import org.openqa.selenium.Proxy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +/** + * Allows legacy code using the {@link LegacyProxyServer} interface to use the modern littleproxy-based implementation. + * + * @deprecated Use {@link BrowserMobProxyServer}. This adapter class will be removed in a future release. + */ +@Deprecated +public class BrowserMobProxyServerLegacyAdapter extends BrowserMobProxyServer implements LegacyProxyServer { + private static final Logger log = LoggerFactory.getLogger(BrowserMobProxyServerLegacyAdapter.class); + + /** + * When true, throw an UnsupportedOperationException instead of logging a warning when an operation is not supported by the LittleProxy-based + * implementation of the BrowserMobProxy interface. Once all operations are implemented and the legacy interface is retired, this will be removed. + */ + private volatile boolean errorOnUnsupportedOperation = false; + + /** + * The port to start the proxy on, if set using {@link #setPort(int)}. + */ + private volatile int port; + + /** + * The address to listen on, if set using {@link #setLocalHost(InetAddress)}. + */ + private volatile InetAddress clientBindAddress; + + /** + * When true, this implementation of BrowserMobProxy will throw an UnsupportedOperationException when a method is not supported. This + * helps identify functionality that is not supported by the LittleProxy-based implementation. By default, this implementation will + * log a warning rather than throw an UnsupportedOperationException. + * + * @param errorOnUnsupportedOperation when true, throws an exception when an operation is not supported + */ + public void setErrorOnUnsupportedOperation(boolean errorOnUnsupportedOperation) { + this.errorOnUnsupportedOperation = errorOnUnsupportedOperation; + } + + /** + * @deprecated use {@link net.lightbody.bmp.client.ClientUtil#createSeleniumProxy(BrowserMobProxy)} + */ + @Override + @Deprecated + public Proxy seleniumProxy() throws NameResolutionException { + return ClientUtil.createSeleniumProxy(this); + } + + /** + * @deprecated specify the port using {@link #start(int)} or other start() methods with port parameters + */ + @Override + @Deprecated + public void setPort(int port) { + this.port = port; + } + + /** + * @deprecated use {@link #getClientBindAddress()} + */ + @Override + @Deprecated + public InetAddress getLocalHost() { + return this.getClientBindAddress(); + } + + /** + * @deprecated use {@link net.lightbody.bmp.client.ClientUtil#getConnectableAddress()} + */ + @Override + @Deprecated + public InetAddress getConnectableLocalHost() throws UnknownHostException { + return ClientUtil.getConnectableAddress(); + } + + /** + * @deprecated use {@link #start(int, java.net.InetAddress)} or {@link #start(int, java.net.InetAddress, java.net.InetAddress)} + */ + @Override + @Deprecated + public void setLocalHost(InetAddress localHost) { + this.clientBindAddress = localHost; + } + + @Override + public void start() { + super.start(port, clientBindAddress); + } + + /** + * Adapter to enable clients to switch to a LittleProxy implementation of BrowserMobProxy but maintain compatibility with + * the 2.0.0 interface. + */ + @Deprecated + private class StreamManagerLegacyAdapter extends StreamManager { + private StreamManagerLegacyAdapter() { + super(0); + } + + @Override + public void setDownstreamKbps(long downstreamKbps) { + BrowserMobProxyServerLegacyAdapter.this.setDownstreamKbps(downstreamKbps); + } + + @Override + public void setUpstreamKbps(long upstreamKbps) { + BrowserMobProxyServerLegacyAdapter.this.setUpstreamKbps(upstreamKbps); + } + + @Override + public void setLatency(long latency) { + BrowserMobProxyServerLegacyAdapter.this.setLatency(latency); + } + + @Override + public void setDownstreamMaxKB(long downstreamMaxKB) { + BrowserMobProxyServerLegacyAdapter.this.setWriteLimitKbps(downstreamMaxKB); + } + + @Override + public void setUpstreamMaxKB(long upstreamMaxKB) { + BrowserMobProxyServerLegacyAdapter.this.setReadLimitKbps(upstreamMaxKB); + } + + @Override + public long getMaxUpstreamKB() { + return BrowserMobProxyServerLegacyAdapter.this.getReadBandwidthLimit() / 1024L; + } + + /** + * @deprecated This method is no longer supported and does not return correct values. + */ + @Override + public long getRemainingUpstreamKB() { + return super.getRemainingUpstreamKB(); + } + + @Override + public long getMaxDownstreamKB() { + return BrowserMobProxyServerLegacyAdapter.this.getWriteBandwidthLimit() / 1024L; + } + + /** + * @deprecated This method is no longer supported and does not return correct values. + */ + @Override + public long getRemainingDownstreamKB() { + return super.getRemainingDownstreamKB(); + } + } + + @Override + public void setRetryCount(int count) { + if (errorOnUnsupportedOperation) { + throw new UnsupportedOperationException("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } else { + log.warn("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } + } + + /** + * StreamManagerLegacyAdapter bound to this BrowserMob Proxy instance, to route the bandwidth control calls to the appropriate + * LittleProxy-compatible methods. + */ + private final StreamManagerLegacyAdapter streamManagerAdapter = new StreamManagerLegacyAdapter(); + + /** + * This method returns a "fake" StreamManager whose setter methods will wrap LittleProxy-compatible bandwidth control methods. No other methods + * in the returned StreamManager should be used; they will have no effect. + * + * @return fake StreamManager object that wraps LitteProxy-compatible bandwidth control methods + * @deprecated use bandwidth control methods from the {@link BrowserMobProxy} + */ + @Deprecated + public StreamManager getStreamManager() { + return streamManagerAdapter; + } + + /** + * @deprecated use {@link #setWriteBandwidthLimit(long)} + */ + @Deprecated + public void setDownstreamKbps(long downstreamKbps) { + this.setWriteBandwidthLimit(downstreamKbps * 1024); + } + + /** + * @deprecated use {@link #setReadBandwidthLimit(long)} + */ + @Deprecated + public void setUpstreamKbps(long upstreamKbps) { + this.setReadBandwidthLimit(upstreamKbps * 1024); + } + + /** + * @deprecated use {@link #setLatency(long, java.util.concurrent.TimeUnit)} + */ + @Deprecated + public void setLatency(long latencyMs) { + setLatency(latencyMs, TimeUnit.MILLISECONDS); + } + + /** + * @deprecated use {@link #setReadBandwidthLimit(long)} + */ + @Deprecated + public void setReadLimitKbps(long readLimitKbps) { + setReadBandwidthLimit(readLimitKbps * 1024); + } + + /** + * @deprecated use {@link #setWriteBandwidthLimit(long)} + */ + @Deprecated + public void setWriteLimitKbps(long writeLimitKbps) { + setWriteBandwidthLimit(writeLimitKbps * 1024); + } + + /** + * @deprecated + */ + @Override + @Deprecated + public void addRequestInterceptor(HttpRequestInterceptor i) { + if (errorOnUnsupportedOperation) { + throw new UnsupportedOperationException("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } else { + log.warn("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } + } + + /** + * @deprecated + */ + @Override + @Deprecated + public void addRequestInterceptor(RequestInterceptor interceptor) { + if (errorOnUnsupportedOperation) { + throw new UnsupportedOperationException("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } else { + log.warn("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } + } + + /** + * @deprecated + */ + @Override + @Deprecated + public void addResponseInterceptor(HttpResponseInterceptor i) { + if (errorOnUnsupportedOperation) { + throw new UnsupportedOperationException("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } else { + log.warn("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } + } + + /** + * @deprecated + */ + @Override + @Deprecated + public void addResponseInterceptor(ResponseInterceptor interceptor) { + if (errorOnUnsupportedOperation) { + throw new UnsupportedOperationException("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } else { + log.warn("No LittleProxy implementation for operation: " + new Throwable().getStackTrace()[0].getMethodName()); + } + } + + /** + * @deprecated use getBlacklist() + */ + @Deprecated + public List getBlacklistedRequests() { + return ImmutableList.copyOf(getBlacklist()); + } + + /** + * @deprecated use {@link #getBlacklist()} + */ + @Override + @Deprecated + public Collection getBlacklistedUrls() { + return getBlacklist(); + } + + /** + * @deprecated use {@link #getWhitelistStatusCode()} + */ + @Override + @Deprecated + public int getWhitelistResponseCode() { + return getWhitelistStatusCode(); + } + + /** + * @deprecated use {@link #disableWhitelist()} + */ + @Override + @Deprecated + public void clearWhitelist() { + this.disableWhitelist(); + } + + /** + * @deprecated use {@link #getWhitelistUrls()} + */ + @Deprecated + public List getWhitelistRequests() { + ImmutableList.Builder builder = ImmutableList.builder(); + + for (String patternString : getWhitelistUrls()) { + Pattern pattern = Pattern.compile(patternString); + builder.add(pattern); + } + + return builder.build(); + } + + /** + * @deprecated use {@link #waitForQuiescence(long, long, java.util.concurrent.TimeUnit)} + */ + @Override + @Deprecated + public void waitForNetworkTrafficToStop(long quietPeriodInMs, long timeoutInMs) { + waitForQuiescence(quietPeriodInMs, timeoutInMs, TimeUnit.MILLISECONDS); + } + + /** + * @deprecated use {@link #getHarCaptureTypes()} to check for relevant {@link net.lightbody.bmp.proxy.CaptureType} + */ + @Deprecated + public boolean isCaptureHeaders() { + return getHarCaptureTypes().containsAll(CaptureType.getHeaderCaptureTypes()); + } + + /** + * @deprecated use {@link #setHarCaptureTypes(java.util.Set)} to set the appropriate {@link net.lightbody.bmp.proxy.CaptureType} + */ + @Deprecated + public void setCaptureHeaders(boolean captureHeaders) { + if (captureHeaders) { + enableHarCaptureTypes(CaptureType.getHeaderCaptureTypes()); + } else { + disableHarCaptureTypes(CaptureType.getHeaderCaptureTypes()); + } + } + + /** + * @deprecated use {@link #getHarCaptureTypes()} to check for relevant {@link net.lightbody.bmp.proxy.CaptureType} + */ + @Deprecated + public boolean isCaptureContent() { + return getHarCaptureTypes().containsAll(CaptureType.getNonBinaryContentCaptureTypes()); + } + + /** + * @deprecated use {@link #setHarCaptureTypes(java.util.Set)} to set the appropriate {@link net.lightbody.bmp.proxy.CaptureType} + */ + @Deprecated + public void setCaptureContent(boolean captureContent) { + if (captureContent) { + enableHarCaptureTypes(CaptureType.getAllContentCaptureTypes()); + } else { + disableHarCaptureTypes(CaptureType.getAllContentCaptureTypes()); + } + } + + /** + * @deprecated use {@link #getHarCaptureTypes()} to check for relevant {@link net.lightbody.bmp.proxy.CaptureType} + */ + @Deprecated + public boolean isCaptureBinaryContent() { + return getHarCaptureTypes().containsAll(CaptureType.getBinaryContentCaptureTypes()); + } + + /** + * @deprecated use {@link #setHarCaptureTypes(java.util.Set)} to set the appropriate {@link net.lightbody.bmp.proxy.CaptureType} + */ + @Deprecated + public void setCaptureBinaryContent(boolean captureBinaryContent) { + if (captureBinaryContent) { + enableHarCaptureTypes(CaptureType.getBinaryContentCaptureTypes()); + } else { + disableHarCaptureTypes(CaptureType.getBinaryContentCaptureTypes()); + } + } + + /** + * @deprecated this method has no effect and will be removed from a future version + */ + @Deprecated + public void cleanup() { + } + + /** + * @deprecated Remap hosts directly using {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#remapHost(String, String)}. + * See {@link #getHostNameResolver()} and the default implementation in {@link net.lightbody.bmp.proxy.dns.ChainedHostResolver}. + */ + @Override + @Deprecated + public void remapHost(String source, String target) { + getHostNameResolver().remapHost(source, target); + } + + /** + * @deprecated Manipulate the DNS cache directly using {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#clearDNSCache()}. + * See {@link #getHostNameResolver()} and the default implementation in {@link net.lightbody.bmp.proxy.dns.ChainedHostResolver}. + */ + @Override + @Deprecated + public void clearDNSCache() { + getHostNameResolver().clearDNSCache(); + } + + /** + * @deprecated Manipulate the DNS cache directly using {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#setNegativeDNSCacheTimeout(int, java.util.concurrent.TimeUnit)} + * and {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver#setPositiveDNSCacheTimeout(int, java.util.concurrent.TimeUnit)}. + * See {@link #getHostNameResolver()} and the default implementation in {@link net.lightbody.bmp.proxy.dns.ChainedHostResolver}. + */ + @Override + @Deprecated + public void setDNSCacheTimeout(int timeout) { + AdvancedHostResolver resolver = getHostNameResolver(); + resolver.setPositiveDNSCacheTimeout(timeout, TimeUnit.SECONDS); + resolver.setNegativeDNSCacheTimeout(timeout, TimeUnit.SECONDS); + } + + + /** + * @deprecated use {@link #setConnectTimeout(int, java.util.concurrent.TimeUnit)} + */ + @Deprecated + @Override + public void setConnectionTimeout(int connectionTimeoutMs) { + setConnectTimeout(connectionTimeoutMs, TimeUnit.MILLISECONDS); + } + + /** + * @deprecated use {@link #setIdleConnectionTimeout(int, java.util.concurrent.TimeUnit)} + */ + @Deprecated + @Override + public void setSocketOperationTimeout(int readTimeoutMs) { + setIdleConnectionTimeout(readTimeoutMs, TimeUnit.MILLISECONDS); + } + + /** + * @deprecated use {@link #setRequestTimeout(int, java.util.concurrent.TimeUnit)} + */ + @Deprecated + @Override + public void setRequestTimeout(int requestTimeoutMs) { + setRequestTimeout(requestTimeoutMs, TimeUnit.MILLISECONDS); + } + + /** + * @deprecated use {@link #autoAuthorization(String, String, String, net.lightbody.bmp.proxy.auth.AuthType)} + */ + @Deprecated + @Override + public void autoBasicAuthorization(String domain, String username, String password) { + autoAuthorization(domain, username, password, AuthType.BASIC); + } + + /** + * @deprecated use {@link #setChainedProxy(java.net.InetSocketAddress)} to set an upstream proxy + */ + @Deprecated + public void setOptions(Map options) { + if (options == null || options.isEmpty()) { + return; + } + + String httpProxy = options.get("httpProxy"); + if (httpProxy != null) { + log.warn("Chained proxy support through setOptions is deprecated. Use setUpstreamProxy() to enable chained proxy support."); + + HostAndPort hostAndPort = HostAndPort.fromString(httpProxy); + this.setChainedProxy(new InetSocketAddress(hostAndPort.getHost(), hostAndPort.getPortOrDefault(80))); + } else { + if (errorOnUnsupportedOperation) { + throw new UnsupportedOperationException("The LittleProxy-based implementation of BrowserMob Proxy does not support the setOptions method. Use the methods defined in the BrowserMobProxy interface to set connection parameters."); + } else { + log.warn("The LittleProxy-based implementation of BrowserMob Proxy does not support the setOptions method. Use the methods defined in the BrowserMobProxy interface to set connection parameters."); + } + } + } +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/core/util/ThreadUtils.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/core/util/ThreadUtils.java new file mode 100644 index 000000000..bdcef0367 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/core/util/ThreadUtils.java @@ -0,0 +1,99 @@ +package net.lightbody.bmp.core.util; + +import java.lang.ref.WeakReference; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +public class ThreadUtils { + /** + * Functional interface to specify a condition to wait for. The checkCondition() method should take a minimal amount of time to execute. + */ + public static interface WaitCondition { + public boolean checkCondition(); + } + + private static final int DEFAULT_POLL_INTERVAL_MS = 500; + + // use a single threaded executor to poll for all conditions -- WaitConditions should be very fast! + private static final ScheduledExecutorService waitConditionPollingExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("wait-condition-polling-thread"); + thread.setDaemon(true); + return thread; + } + }); + + /** + * Waits for the specified condition to be true. The condition will be evaluated when the method is invoked, and then may be polled + * periodically, until the condition returns true or the specified timeout has elapsed. If the condition is not true after the timeout + * has elapsed, it is guaranteed to be evaluated at least once more before the method returns. + * + * @param condition condition to wait on + * @param timeout time to wait for condition to be true + * @param timeUnit unit of time to wait + * @return true if the condition was satisfied within the specified time, otherwise false + */ + public static boolean pollForCondition(WaitCondition condition, long timeout, TimeUnit timeUnit) { + return pollForCondition(condition, timeout, timeUnit, DEFAULT_POLL_INTERVAL_MS); + } + + /** + * Waits for the specified condition to be true. The condition will be evaluated when the method is invoked, and then may be polled + * periodically, until the condition returns true or the specified timeout has elapsed. If the condition is not true after the timeout + * has elapsed, it is guaranteed to be evaluated at least once more before the method returns. + * + * @param condition condition to wait on + * @param timeout time to wait for condition to be true + * @param timeUnit unit of time to wait + * @param pollIntervalMs interval at which to poll, in milliseconds + * @return true if the condition was satisfied within the specified time, otherwise false + */ + public static boolean pollForCondition(WaitCondition condition, final long timeout, final TimeUnit timeUnit, long pollIntervalMs) { + if (condition.checkCondition()) { + return true; + } + + // wrap the WaitCondition in a WeakReference, just to make sure it hasn't been orphaned + final WeakReference conditionWrapper = new WeakReference(condition); + final CountDownLatch latch = new CountDownLatch(1); + + ScheduledFuture pollingTask = waitConditionPollingExecutor.scheduleWithFixedDelay(new Runnable() { + private final long taskStart = System.currentTimeMillis(); + + @Override + public void run() { + WaitCondition condition = conditionWrapper.get(); + if (condition == null || (System.currentTimeMillis() - taskStart > TimeUnit.MILLISECONDS.convert(timeout, timeUnit))) { + // max time limit elapsed or condition was garbage collected, so throw an exception to prevent this task from being rescheduled again + throw new RuntimeException("Stopped polling for condition because time limit elapsed or condition was garbage collected"); + } + + if (condition.checkCondition()) { + latch.countDown(); + } + } + }, pollIntervalMs, pollIntervalMs, TimeUnit.MILLISECONDS); + + try { + boolean conditionMet = latch.await(timeout, timeUnit); + + if (conditionMet || condition.checkCondition()) { + return true; + } else { + return false; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } finally { + // cancel the polling task so it is not executed anymore + pollingTask.cancel(true); + } + } +} \ No newline at end of file diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/exception/JettyException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/exception/JettyException.java new file mode 100644 index 000000000..62d81a422 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/exception/JettyException.java @@ -0,0 +1,25 @@ +package net.lightbody.bmp.exception; + +/** + * A wrapper for exceptions coming from Jetty methods that throw Exception, + * rather than a useful Exception subclass. + */ +public class JettyException extends RuntimeException { + private static final long serialVersionUID = 8125833642102189196L; + + public JettyException() { + } + + public JettyException(String message) { + super(message); + } + + public JettyException(Throwable cause) { + super(cause); + } + + public JettyException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/exception/NameResolutionException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/exception/NameResolutionException.java new file mode 100644 index 000000000..5cd62e052 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/exception/NameResolutionException.java @@ -0,0 +1,24 @@ +package net.lightbody.bmp.exception; + +/** + * Exception indicating some sort of unrecoverable name resolution error occurred. + */ +public class NameResolutionException extends RuntimeException { + private static final long serialVersionUID = -3358213880037217337L; + + public NameResolutionException() { + } + + public NameResolutionException(String message) { + super(message); + } + + public NameResolutionException(Throwable cause) { + super(cause); + } + + public NameResolutionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/l10n/MessagesUtil.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/l10n/MessagesUtil.java new file mode 100644 index 000000000..55364a1f2 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/l10n/MessagesUtil.java @@ -0,0 +1,30 @@ +package net.lightbody.bmp.l10n; + +import java.util.ResourceBundle; + +/** + * Convenience class to retrieve messages from the localized BrowserMob Proxy resources file. Loads messages for the default locale. + */ +public class MessagesUtil { + private static final String BROWSERMOB_MESSAGE_BUNDLE_NAME = "net.lightbody.bmp.l10n.messages"; + + private static final ResourceBundle MESSAGES_BUNDLE = ResourceBundle.getBundle(BROWSERMOB_MESSAGE_BUNDLE_NAME); + + /** + * Retrieves the message with the given key from the locale-specific properties file. If there are any String.format()-style + * parameters in the message, this method can optionally apply formatting parameters. + * + * @param key message key + * @param parameters formatting parameters to apply to the message + * @return formatted, locale-specific message + */ + public static String getMessage(String key, Object... parameters) { + String response = MESSAGES_BUNDLE.getString(key); + + if (parameters == null || parameters.length == 0) { + return response; + } else { + return String.format(response, parameters); + } + } +} diff --git a/src/main/java/org/browsermob/proxy/BrowserMobProxyHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/BrowserMobProxyHandler.java similarity index 77% rename from src/main/java/org/browsermob/proxy/BrowserMobProxyHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/BrowserMobProxyHandler.java index 7237189fc..41578f7c4 100644 --- a/src/main/java/org/browsermob/proxy/BrowserMobProxyHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/BrowserMobProxyHandler.java @@ -1,27 +1,47 @@ -package org.browsermob.proxy; +package net.lightbody.bmp.proxy; + +import net.lightbody.bmp.proxy.error.ProxyError; +import net.lightbody.bmp.proxy.http.BadURIException; +import net.lightbody.bmp.proxy.http.BrowserMobHttpClient; +import net.lightbody.bmp.proxy.http.BrowserMobHttpRequest; +import net.lightbody.bmp.proxy.http.BrowserMobHttpResponse; +import net.lightbody.bmp.proxy.http.RequestCallback; +import net.lightbody.bmp.proxy.jetty.http.EOFException; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpListener; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.HttpServer; +import net.lightbody.bmp.proxy.jetty.http.HttpTunnel; +import net.lightbody.bmp.proxy.jetty.http.SocketListener; +import net.lightbody.bmp.proxy.jetty.jetty.Server; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.URI; +import net.lightbody.bmp.proxy.selenium.SeleniumProxyHandler; import org.apache.http.Header; import org.apache.http.NoHttpResponseException; import org.apache.http.StatusLine; import org.apache.http.conn.ConnectTimeoutException; -import org.browsermob.proxy.http.*; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.jetty.Server; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.URI; -import org.browsermob.proxy.selenium.SeleniumProxyHandler; -import org.browsermob.proxy.util.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; -import java.net.*; +import java.net.BindException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.URL; +import java.net.UnknownHostException; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; +@SuppressWarnings("serial") public class BrowserMobProxyHandler extends SeleniumProxyHandler { - private static final Log LOG = new Log(); + private static final Logger LOG = LoggerFactory.getLogger(BrowserMobProxyHandler.class); private static final int HEADER_BUFFER_DEFAULT = 2; @@ -77,7 +97,7 @@ public void handleConnect(String pathInContext, String pathParams, HttpRequest r } @Override - protected void wireUpSslWithCyberVilliansCA(String host, SeleniumProxyHandler.SslRelay listener) { + protected void wireUpSslWithImpersonationCA(String host, SeleniumProxyHandler.SslRelay listener) { List originalHosts = httpClient.originalHosts(host); if (originalHosts != null && !originalHosts.isEmpty()) { if (originalHosts.size() == 1) { @@ -89,7 +109,7 @@ protected void wireUpSslWithCyberVilliansCA(String host, SeleniumProxyHandler.Ss host = "*" + first.substring(first.indexOf('.')); } } - super.wireUpSslWithCyberVilliansCA(host, listener); + super.wireUpSslWithImpersonationCA(host, listener); } @Override @@ -122,7 +142,11 @@ private void startRelayWithPortTollerance(HttpServer server, SslRelay relay, int } } catch (BindException e) { // doh - the port is being used up, let's pick a new port - LOG.info("Unable to bind to port %d, going to try port %d now", relay.getPort(), relay.getPort() + 1); + if (LOG.isDebugEnabled()) { + LOG.info("Unable to bind to port {}, going to try port {} now", relay.getPort(), relay.getPort() + 1, e); + } else { + LOG.info("Unable to bind to port {}, going to try port {} now", relay.getPort(), relay.getPort() + 1); + } relay.setPort(relay.getPort() + 1); startRelayWithPortTollerance(server, relay, tries + 1); } @@ -136,7 +160,6 @@ protected HttpTunnel newHttpTunnel(HttpRequest httpRequest, HttpResponse httpRes return super.newHttpTunnel(httpRequest, httpResponse, inetAddress, i, i1); } - @SuppressWarnings({"unchecked"}) protected long proxyPlainTextRequest(final URL url, String pathInContext, String pathParams, HttpRequest request, final HttpResponse response) throws IOException { try { String urlStr = url.toString(); @@ -184,8 +207,12 @@ protected long proxyPlainTextRequest(final URL url, String pathInContext, String httpReq = httpClient.newOptions(urlStr, request); } else if ("HEAD".equals(request.getMethod())) { httpReq = httpClient.newHead(urlStr, request); + } else if ("PATCH".equals(request.getMethod())) { + httpReq = httpClient.newPatch(urlStr, request); + } else if ("TRACE".equals(request.getMethod())) { + httpReq = httpClient.newTrace(urlStr, request); } else { - LOG.warn("Unexpected request method %s, giving up", request.getMethod()); + LOG.warn("Unexpected request method {}, giving up", request.getMethod()); request.setHandled(true); return -1; } @@ -201,18 +228,19 @@ protected long proxyPlainTextRequest(final URL url, String pathInContext, String if (!isGet && HttpFields.__ContentType.equals(hdr)) { hasContent = true; } - if (!isGet && HttpFields.__ContentLength.equals(hdr)) { - contentLength = Long.parseLong(request.getField(hdr)); - continue; - } + Enumeration vals = request.getFieldValues(hdr); while (vals.hasMoreElements()) { String val = (String) vals.nextElement(); if (val != null) { - if (!isGet && HttpFields.__ContentLength.equals(hdr) && Integer.parseInt(val) > 0) { - hasContent = true; - } + + if (!isGet && HttpFields.__ContentLength.equals(hdr)) { + contentLength = Long.parseLong(request.getField(hdr)); + if(contentLength > 0) { + hasContent = true; + } + } if (!_DontProxyHeaders.containsKey(hdr)) { httpReq.addRequestHeader(hdr, val); @@ -221,15 +249,11 @@ protected long proxyPlainTextRequest(final URL url, String pathInContext, String } } - try { - // do input thang! - InputStream in = request.getInputStream(); - if (hasContent) { - httpReq.setRequestInputStream(in, contentLength); - } - } catch (Exception e) { - LOG.fine(e.getMessage(), e); - } + // do input thang! + InputStream in = request.getInputStream(); + if (hasContent) { + httpReq.setRequestInputStream(in, contentLength); + } // execute the request httpReq.setOutputStream(response.getOutputStream()); @@ -270,10 +294,10 @@ public void reportError(Exception e) { return httpRes.getEntry().getResponse().getBodySize(); } catch (BadURIException e) { // this is a known error case (see MOB-93) - LOG.info(e.getMessage()); + LOG.info("Encountered bad URI exception while proxying " + url, e); BrowserMobProxyHandler.reportError(e, url, response); return -1; - } catch (Exception e) { + } catch (RuntimeException e) { LOG.info("Exception while proxying " + url, e); BrowserMobProxyHandler.reportError(e, url, response); return -1; @@ -281,33 +305,49 @@ public void reportError(Exception e) { } private static void reportError(Exception e, URL url, HttpResponse response) { - FirefoxErrorContent error = FirefoxErrorContent.GENERIC; + boolean timeout = false; + + ProxyError error = ProxyError.GENERIC; if (e instanceof UnknownHostException) { - error = FirefoxErrorContent.DNS_NOT_FOUND; + error = ProxyError.DNS_NOT_FOUND; } else if (e instanceof ConnectException) { - error = FirefoxErrorContent.CONN_FAILURE; + error = ProxyError.CONN_FAILURE; } else if (e instanceof ConnectTimeoutException) { - error = FirefoxErrorContent.NET_TIMEOUT; + timeout = true; + error = ProxyError.NET_TIMEOUT; } else if (e instanceof NoHttpResponseException) { - error = FirefoxErrorContent.NET_RESET; + error = ProxyError.NET_RESET; } else if (e instanceof EOFException) { - error = FirefoxErrorContent.NET_INTERRUPT; + error = ProxyError.NET_INTERRUPT; } else if (e instanceof IllegalArgumentException && e.getMessage().startsWith("Host name may not be null")){ - error = FirefoxErrorContent.DNS_NOT_FOUND; + error = ProxyError.DNS_NOT_FOUND; } else if (e instanceof BadURIException){ - error = FirefoxErrorContent.MALFORMED_URI; + error = ProxyError.MALFORMED_URI; } - String shortDesc = String.format(error.getShortDesc(), url.getHost()); - String text = String.format(FirefoxErrorConstants.ERROR_PAGE, error.getTitle(), shortDesc, error.getLongDesc()); - + String text = error.getHtml(url.toString()); try { - response.setStatus(HttpResponse.__502_Bad_Gateway); + /* For timeout exceptions, return a 504. Otherwise, return 502. + http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + The HTTP spec describes 502 as: + The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in + attempting to fulfill the request. + While 504 is: + The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by + the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in attempting to complete + the request. + */ + if (timeout) { + response.setStatus(HttpResponse.__504_Gateway_Timeout); + } else { + response.setStatus(HttpResponse.__502_Bad_Gateway); + } + response.setContentLength(text.length()); response.getOutputStream().write(text.getBytes()); } catch (IOException e1) { - LOG.warn("IOException while trying to report an HTTP error"); + LOG.warn("IOException while trying to report an HTTP error", e1); } } diff --git a/src/main/java/org/browsermob/proxy/HttpObject.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/HttpObject.java similarity index 99% rename from src/main/java/org/browsermob/proxy/HttpObject.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/HttpObject.java index 729fa5650..ad97405aa 100644 --- a/src/main/java/org/browsermob/proxy/HttpObject.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/HttpObject.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy; +package net.lightbody.bmp.proxy; import org.apache.commons.io.IOUtils; diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java new file mode 100644 index 000000000..103f2f25a --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/LegacyProxyServer.java @@ -0,0 +1,138 @@ +package net.lightbody.bmp.proxy; + +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.exception.NameResolutionException; +import net.lightbody.bmp.proxy.http.RequestInterceptor; +import net.lightbody.bmp.proxy.http.ResponseInterceptor; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponseInterceptor; +import org.java_bandwidthlimiter.StreamManager; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Describes the legacy BrowserMob Proxy 2.0 interface. Clients should not implement or use this interface. + * + * Use {@link BrowserMobProxy}. + */ +public interface LegacyProxyServer { + void start(); + + org.openqa.selenium.Proxy seleniumProxy() throws NameResolutionException; + + void cleanup(); + + void stop(); + + void abort(); + + int getPort(); + + void setPort(int port); + + InetAddress getLocalHost(); + + InetAddress getConnectableLocalHost() throws UnknownHostException; + + void setLocalHost(InetAddress localHost); + + Har getHar(); + + Har newHar(String initialPageRef); + + Har newHar(String initialPageRef, String initialPageTitle); + + Har newPage(String pageRef); + + Har newPage(String pageRef, String pageTitle); + + void endPage(); + + void setRetryCount(int count); + + void remapHost(String source, String target); + + @Deprecated + void addRequestInterceptor(HttpRequestInterceptor i); + + void addRequestInterceptor(RequestInterceptor interceptor); + + @Deprecated + void addResponseInterceptor(HttpResponseInterceptor i); + + void addResponseInterceptor(ResponseInterceptor interceptor); + + StreamManager getStreamManager(); + + //use getStreamManager().setDownstreamKbps instead + @Deprecated + void setDownstreamKbps(long downstreamKbps); + + //use getStreamManager().setUpstreamKbps instead + @Deprecated + void setUpstreamKbps(long upstreamKbps); + + //use getStreamManager().setLatency instead + @Deprecated + void setLatency(long latency); + + void setRequestTimeout(int requestTimeout); + + void setSocketOperationTimeout(int readTimeout); + + void setConnectionTimeout(int connectionTimeout); + + void autoBasicAuthorization(String domain, String username, String password); + + void rewriteUrl(String match, String replace); + + void clearRewriteRules(); + + void blacklistRequests(String pattern, int responseCode); + + void blacklistRequests(String pattern, int responseCode, String method); + + @Deprecated + List getBlacklistedRequests(); + + Collection getBlacklistedUrls(); + + boolean isWhitelistEnabled(); + + @Deprecated + List getWhitelistRequests(); + + Collection getWhitelistUrls(); + + int getWhitelistResponseCode(); + + void clearBlacklist(); + + void whitelistRequests(String[] patterns, int responseCode); + + void enableEmptyWhitelist(int responseCode); + + void clearWhitelist(); + + void addHeader(String name, String value); + + void setCaptureHeaders(boolean captureHeaders); + + void setCaptureContent(boolean captureContent); + + void setCaptureBinaryContent(boolean captureBinaryContent); + + void clearDNSCache(); + + void setDNSCacheTimeout(int timeout); + + void waitForNetworkTrafficToStop(long quietPeriodInMs, long timeoutInMs); + + void setOptions(Map options); +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java new file mode 100644 index 000000000..29c5f897f --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/ProxyServer.java @@ -0,0 +1,972 @@ +package net.lightbody.bmp.proxy; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.client.ClientUtil; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.core.har.HarEntry; +import net.lightbody.bmp.core.har.HarLog; +import net.lightbody.bmp.core.har.HarNameVersion; +import net.lightbody.bmp.core.har.HarPage; +import net.lightbody.bmp.core.util.ThreadUtils; +import net.lightbody.bmp.exception.JettyException; +import net.lightbody.bmp.exception.NameResolutionException; +import net.lightbody.bmp.filters.RequestFilter; +import net.lightbody.bmp.filters.ResponseFilter; +import net.lightbody.bmp.mitm.TrustSource; +import net.lightbody.bmp.proxy.auth.AuthType; +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import net.lightbody.bmp.proxy.http.BrowserMobHttpClient; +import net.lightbody.bmp.proxy.http.RequestInterceptor; +import net.lightbody.bmp.proxy.http.ResponseInterceptor; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpListener; +import net.lightbody.bmp.proxy.jetty.http.SocketListener; +import net.lightbody.bmp.proxy.jetty.jetty.Server; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.util.BrowserMobProxyUtil; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponseInterceptor; +import org.java_bandwidthlimiter.BandwidthLimiter; +import org.java_bandwidthlimiter.StreamManager; +import org.littleshoot.proxy.HttpFiltersSource; +import org.littleshoot.proxy.MitmManager; +import org.openqa.selenium.Proxy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; + +/** + * The legacy, Jetty 5-based implementation of BrowserMobProxy. This class implements the {@link net.lightbody.bmp.proxy.LegacyProxyServer} + * interface that defines the BMP 2.0 contact, as well as the 2.1+ {@link BrowserMobProxy} interface. Important: if + * you are implementing new code, use the {@link BrowserMobProxy} interface. The + * {@link net.lightbody.bmp.proxy.LegacyProxyServer} interface is deprecated and will be removed in a future release. + *

Unsupported operations

+ * The following {@link BrowserMobProxy} operations are not supported and will be ignored: + *
    + *
  • {@link BrowserMobProxy#getServerBindAddress()} and {@link #start(int, java.net.InetAddress, java.net.InetAddress)} - server bind addresses are not supported
  • + *
  • {@link BrowserMobProxy#stopAutoAuthorization(String)}
  • + *
+ * + * @deprecated Use the {@link BrowserMobProxy} interface to preserve compatibility with future BrowserMob Proxy versions. + */ +@Deprecated +public class ProxyServer implements LegacyProxyServer, BrowserMobProxy { + private static final HarNameVersion CREATOR = new HarNameVersion("BrowserMob Proxy", BrowserMobProxyUtil.getVersionString() + "-legacy"); + private static final Logger LOG = LoggerFactory.getLogger(ProxyServer.class); + + /** + * System property to allow fallback to the native Java hostname lookup mechanism when dnsjava (xbill) cannot resolve the hostname. Native fallback + * is enabled by default and will be disabled only if the value of this property is explicitly set to false. + */ + public static final String ALLOW_NATIVE_DNS_FALLBACK = "bmp.allowNativeDnsFallback"; + + /* + * The Jetty HttpServer use in BrowserMobProxyHandler + */ + private Server server; + + /* + * Proxy port. Defaults to 0 (JVM-assigned). + */ + private int port = 0; + private InetAddress localHost; + private BrowserMobHttpClient client; + private StreamManager streamManager; + private HarPage currentPage; + private BrowserMobProxyHandler handler; + private AtomicInteger pageCount = new AtomicInteger(1); + private AtomicInteger requestCounter = new AtomicInteger(0); + + private boolean started; + + private InetSocketAddress chainedProxyAddress; + + public ProxyServer() { + } + + public ProxyServer(int port) { + this.port = port; + } + + @Override + public void start() { + //create a stream manager that will be capped to 100 Megabits + //remember that by default it is disabled! + streamManager = new StreamManager( 100 * BandwidthLimiter.OneMbps ); + + server = new Server(); + HttpListener listener = new SocketListener(new InetAddrPort(getLocalHost(), getPort())); + server.addListener(listener); + HttpContext context = new HttpContext(); + context.setContextPath("/"); + server.addContext(context); + + handler = new BrowserMobProxyHandler(); + handler.setJettyServer(server); + handler.setShutdownLock(new Object()); + client = new BrowserMobHttpClient(streamManager, requestCounter); + + // if native DNS fallback is explicitly disabled, replace the default resolver with a dnsjava-only resolver + if ("false".equalsIgnoreCase(System.getProperty(ALLOW_NATIVE_DNS_FALLBACK))) { + client.setResolver(ClientUtil.createDnsJavaResolver()); + } + + client.prepareForBrowser(); + handler.setHttpClient(client); + + context.addHandler(handler); + try { + server.start(); + } catch (Exception e) { + // wrapping unhelpful Jetty Exception into a RuntimeException + throw new JettyException("Exception occurred when starting the server", e); + } + + setPort(listener.getPort()); + + started = true; + } + + @Override + public void start(int port) { + this.port = port; + start(); + } + + @Override + public void start(int port, InetAddress bindAddress) { + setLocalHost(bindAddress); + start(port); + } + + @Override + public void start(int port, InetAddress clientBindAddress, InetAddress serverBindAddress) { + LOG.warn("The legacy ProxyServer implementation does not support a server bind address"); + } + + @Override + public boolean isStarted() { + return started; + } + + @Override + public org.openqa.selenium.Proxy seleniumProxy() throws NameResolutionException { + Proxy proxy = new Proxy(); + proxy.setProxyType(Proxy.ProxyType.MANUAL); + InetAddress connectableLocalHost; + try { + connectableLocalHost = getConnectableLocalHost(); + } catch (UnknownHostException e) { + // InetAddress cannot resolve a local host. since seleniumProxy() is designed to be called within a Selenium test, + // this is most likely a fatal error that does not need to be a checked exception. + throw new NameResolutionException("Error getting local host when creating seleniumProxy", e); + } + String proxyStr = String.format("%s:%d", connectableLocalHost.getCanonicalHostName(), getPort()); + proxy.setHttpProxy(proxyStr); + proxy.setSslProxy(proxyStr); + + return proxy; + } + + @Override + public void cleanup() { + if (handler != null) { + handler.cleanup(); + } + } + + @Override + public void stop() { + cleanup(); + if (client != null) { + client.shutdown(); + } + try { + if (server != null) { + server.stop(); + } + } catch (InterruptedException e) { + // the try/catch block in server.stop() is manufacturing a phantom InterruptedException, so this should not occur + throw new JettyException("Exception occurred when stopping the server", e); + } + } + + @Override + public void abort() { + stop(); + } + + @Override + public InetAddress getClientBindAddress() { + return getLocalHost(); + } + + public int getPort() { + return port; + } + + @Override + public InetAddress getServerBindAddress() { + LOG.warn("LegacyProxyServer does not support a server bind address"); + return null; + } + + public void setPort(int port) { + this.port = port; + } + + /** + * Get the the InetAddress that the Proxy server binds to when it starts. + * + * If not otherwise set via {@link #setLocalHost(InetAddress)}, defaults to + * 0.0.0.0 (i.e. bind to any interface). + * + * Note - just because we bound to the address, doesn't mean that it can be + * reached. E.g. trying to connect to 0.0.0.0 is going to fail. Use + * {@link #getConnectableLocalHost()} if you're looking for a host that can be + * connected to. + */ + @Override + public InetAddress getLocalHost() { + if (localHost == null) { + try { + localHost = InetAddress.getByName("0.0.0.0"); + } catch (UnknownHostException e) { + // InetAddress.getByName javadocs state: "If a literal IP address is supplied, only the validity of the address format is checked." + // Since the format of 0.0.0.0 is valid, getByName should never throw UnknownHostException + throw new RuntimeException("InetAddress.getByName failed to look up 0.0.0.0", e); + } + } + return localHost; + } + + /** + * Return a plausible {@link InetAddress} that other processes can use to + * contact the proxy. + * + * In essence, this is the same as {@link #getLocalHost()}, but avoids + * returning 0.0.0.0. as no-one can connect to that. If no other host has + * been set via {@link #setLocalHost(InetAddress)}, will return + * {@link InetAddress#getLocalHost()} + * + * No attempt is made to check the address for reachability before it is + * returned. + */ + @Override + public InetAddress getConnectableLocalHost() throws UnknownHostException { + + if (getLocalHost().equals(InetAddress.getByName("0.0.0.0"))) { + return InetAddress.getLocalHost(); + } else { + return getLocalHost(); + } + } + + @Override + public void setLocalHost(InetAddress localHost) { + if (localHost.isAnyLocalAddress() || + localHost.isLoopbackAddress()) { + this.localHost = localHost; + } else { + // address is not a local/loopback address, but might still be bound to a local network interface + NetworkInterface localInterface; + try { + localInterface = NetworkInterface.getByInetAddress(localHost); + } catch (SocketException e) { + throw new IllegalArgumentException("localHost address must be address of a local adapter (attempted to use: " + localHost + ")", e); + } + if (localInterface != null) { + this.localHost = localHost; + } else { + throw new IllegalArgumentException("localHost address must be address of a local adapter (attempted to use: " + localHost + ")"); + } + } + + } + + @Override + public Har getHar() { + // Wait up to 5 seconds for all active requests to cease before returning the HAR. + // This helps with race conditions but won't cause deadlocks should a request hang + // or error out in an unexpected way (which of course would be a bug!) + boolean success = ThreadUtils.pollForCondition(new ThreadUtils.WaitCondition() { + @Override + public boolean checkCondition() { + return requestCounter.get() == 0; + } + }, 5, TimeUnit.SECONDS); + + if (!success) { + LOG.warn("Waited 5 seconds for requests to cease before returning HAR; giving up!"); + } + + return client.getHar(); + } + + @Override + public Har newHar() { + return newHar(null); + } + + public Har newHar(String initialPageRef) { + return newHar(initialPageRef, null); + } + + @Override + public Har newHar(String initialPageRef, String initialPageTitle) { + pageCount.set(0); // this will be automatically incremented by newPage() below + + Har oldHar = getHar(); + + Har har = new Har(new HarLog(CREATOR)); + client.setHar(har); + newPage(initialPageRef, initialPageTitle); + + return oldHar; + } + + @Override + public void setHarCaptureTypes(Set captureTypes) { + setCaptureBinaryContent(captureTypes.contains(CaptureType.REQUEST_BINARY_CONTENT) || captureTypes.contains(CaptureType.RESPONSE_BINARY_CONTENT)); + setCaptureContent(captureTypes.contains(CaptureType.REQUEST_CONTENT) || captureTypes.contains(CaptureType.RESPONSE_CONTENT)); + setCaptureHeaders(captureTypes.contains(CaptureType.REQUEST_HEADERS) || captureTypes.contains(CaptureType.RESPONSE_HEADERS)); + } + + @Override + public void setHarCaptureTypes(CaptureType... captureTypes) { + if (captureTypes == null) { + setHarCaptureTypes(EnumSet.noneOf(CaptureType.class)); + } else { + setHarCaptureTypes(EnumSet.copyOf(Arrays.asList(captureTypes))); + } + } + + @Override + public EnumSet getHarCaptureTypes() { + // cookie capture types are always enabled in the legacy ProxyServer + EnumSet captureTypes = CaptureType.getCookieCaptureTypes(); + + if (client.isCaptureBinaryContent()) { + captureTypes.addAll(CaptureType.getBinaryContentCaptureTypes()); + } + + if (client.isCaptureContent()) { + captureTypes.addAll(CaptureType.getNonBinaryContentCaptureTypes()); + } + + if (client.isCaptureHeaders()) { + captureTypes.addAll(CaptureType.getHeaderCaptureTypes()); + } + + return captureTypes; + } + + @Override + public void enableHarCaptureTypes(Set captureTypes) { + if (captureTypes.contains(CaptureType.REQUEST_BINARY_CONTENT) || captureTypes.contains(CaptureType.RESPONSE_BINARY_CONTENT)) { + setCaptureBinaryContent(true); + } + + if (captureTypes.contains(CaptureType.REQUEST_CONTENT) || captureTypes.contains(CaptureType.RESPONSE_CONTENT)) { + setCaptureContent(true); + } + + if (captureTypes.contains(CaptureType.REQUEST_HEADERS) || captureTypes.contains(CaptureType.RESPONSE_HEADERS)) { + setCaptureHeaders(true); + } + } + + @Override + public void enableHarCaptureTypes(CaptureType... captureTypes) { + if (captureTypes == null) { + enableHarCaptureTypes(EnumSet.noneOf(CaptureType.class)); + } else { + enableHarCaptureTypes(EnumSet.copyOf(Arrays.asList(captureTypes))); + } + } + + @Override + public void disableHarCaptureTypes(Set captureTypes) { + if (captureTypes.contains(CaptureType.REQUEST_BINARY_CONTENT) || captureTypes.contains(CaptureType.RESPONSE_BINARY_CONTENT)) { + setCaptureBinaryContent(false); + } + + if (captureTypes.contains(CaptureType.REQUEST_CONTENT) || captureTypes.contains(CaptureType.RESPONSE_CONTENT)) { + setCaptureContent(false); + } + + if (captureTypes.contains(CaptureType.REQUEST_HEADERS) || captureTypes.contains(CaptureType.RESPONSE_HEADERS)) { + setCaptureHeaders(false); + } + } + + @Override + public void disableHarCaptureTypes(CaptureType... captureTypes) { + if (captureTypes == null) { + disableHarCaptureTypes(EnumSet.noneOf(CaptureType.class)); + } else { + disableHarCaptureTypes(EnumSet.copyOf(Arrays.asList(captureTypes))); + } + } + + @Override + public Har newPage() { + return newPage(null); + } + + public Har newPage(String pageRef) { + return newPage(pageRef, null); + } + + @Override + public Har newPage(String pageRef, String pageTitle) { + if (pageRef == null) { + pageRef = "Page " + pageCount.get(); + } + + if (pageTitle == null) { + pageTitle = pageRef; + } + + client.setHarPageRef(pageRef); + currentPage = new HarPage(pageRef, pageTitle); + client.getHar().getLog().addPage(currentPage); + + pageCount.incrementAndGet(); + + return client.getHar(); + } + + @Override + public Har endHar() { + endPage(); + + return getHar(); + } + + @Override + public void setReadBandwidthLimit(long bytesPerSecond) { + getStreamManager().setDownstreamKbps(bytesPerSecond / 1024L); + } + + @Override + public long getReadBandwidthLimit() { + return getStreamManager().getMaxDownstreamKB() * 1024L; + } + + @Override + public void setWriteBandwidthLimit(long bytesPerSecond) { + getStreamManager().setUpstreamKbps(bytesPerSecond / 1024L); + } + + @Override + public long getWriteBandwidthLimit() { + return getStreamManager().getMaxUpstreamKB() * 1024L; + } + + @Override + public void setLatency(long latency, TimeUnit timeUnit) { + getStreamManager().setLatency(TimeUnit.MILLISECONDS.convert(latency, timeUnit)); + } + + @Override + public void setConnectTimeout(int connectionTimeout, TimeUnit timeUnit) { + setConnectionTimeout((int) TimeUnit.MILLISECONDS.convert(connectionTimeout, timeUnit)); + } + + @Override + public void setIdleConnectionTimeout(int idleConnectionTimeout, TimeUnit timeUnit) { + setSocketOperationTimeout((int) TimeUnit.MILLISECONDS.convert(idleConnectionTimeout, timeUnit)); + } + + @Override + public void setRequestTimeout(int requestTimeout, TimeUnit timeUnit) { + setRequestTimeout((int) TimeUnit.MILLISECONDS.convert(requestTimeout, timeUnit)); + } + + @Override + public void autoAuthorization(String domain, String username, String password, AuthType authType) { + if (authType == AuthType.BASIC) { + autoBasicAuthorization(domain, username, password); + } else { + throw new UnsupportedOperationException("Legacy ProxyServer implementation does not support non-BASIC authorization"); + } + } + + @Override + public void stopAutoAuthorization(String domain) { + LOG.warn("Legacy ProxyServer implementation does not support stopping auto authorization"); + } + + @Override + public void chainedProxyAuthorization(String username, String password, AuthType authType) { + LOG.warn("Legacy ProxyServer implementation does not support chained proxy authorization"); + } + + public void endPage() { + if (currentPage == null) { + return; + } + + currentPage.getPageTimings().setOnLoad(new Date().getTime() - currentPage.getStartedDateTime().getTime()); + client.setHarPageRef(null); + currentPage = null; + } + + @Override + public void setRetryCount(int count) { + client.setRetryCount(count); + } + + @Override + public void addHeaders(Map headers) { + for (Map.Entry entry : headers.entrySet()) { + addHeader(entry.getKey(), entry.getValue()); + } + } + + public void remapHost(String source, String target) { + if (client.getResolver() instanceof AdvancedHostResolver) { + AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) client.getResolver(); + advancedHostResolver.remapHost(source, target); + } else { + LOG.warn("Attempting to remap host, but host resolver is not an AdvancedHostRemapper. Host resolver is: {}", client.getResolver()); + } + } + + @Override + @Deprecated + public void addRequestInterceptor(HttpRequestInterceptor i) { + client.addRequestInterceptor(i); + } + + @Override + public void addRequestInterceptor(RequestInterceptor interceptor) { + client.addRequestInterceptor(interceptor); + } + + @Override + @Deprecated + public void addResponseInterceptor(HttpResponseInterceptor i) { + client.addResponseInterceptor(i); + } + + @Override + public void addResponseInterceptor(ResponseInterceptor interceptor) { + client.addResponseInterceptor(interceptor); + } + + @Override + public StreamManager getStreamManager() { + return streamManager; + } + + //use getStreamManager().setDownstreamKbps instead + @Override + @Deprecated + public void setDownstreamKbps(long downstreamKbps) { + streamManager.setDownstreamKbps(downstreamKbps); + streamManager.enable(); + } + + //use getStreamManager().setUpstreamKbps instead + @Override + @Deprecated + public void setUpstreamKbps(long upstreamKbps) { + streamManager.setUpstreamKbps(upstreamKbps); + streamManager.enable(); + } + + //use getStreamManager().setLatency instead + @Override + @Deprecated + public void setLatency(long latency) { + streamManager.setLatency(latency); + streamManager.enable(); + } + + @Override + public void setRequestTimeout(int requestTimeout) { + client.setRequestTimeout(requestTimeout); + } + + @Override + public void setSocketOperationTimeout(int readTimeout) { + client.setSocketOperationTimeout(readTimeout); + } + + @Override + public void setConnectionTimeout(int connectionTimeout) { + client.setConnectionTimeout(connectionTimeout); + } + + @Override + public void autoBasicAuthorization(String domain, String username, String password) { + client.autoBasicAuthorization(domain, username, password); + } + + @Override + public void rewriteUrl(String match, String replace) { + client.rewriteUrl(match, replace); + } + + @Override + public void rewriteUrls(Map rewriteRules) { + for (Map.Entry entry : rewriteRules.entrySet()) { + rewriteUrl(entry.getKey(), entry.getValue()); + } + } + + @Override + public Map getRewriteRules() { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for (RewriteRule rewriteRule : client.getRewriteRules()) { + builder.put(rewriteRule.getPattern().pattern(), rewriteRule.getReplace()); + } + + return builder.build(); + } + + @Override + public void removeRewriteRule(String urlPattern) { + client.removeRewriteRule(urlPattern); + } + + public void clearRewriteRules() { + client.clearRewriteRules(); + } + + @Override + public void blacklistRequests(String pattern, int responseCode) { + client.blacklistRequests(pattern, responseCode, null); + } + + @Override + public void blacklistRequests(String pattern, int responseCode, String method) { + client.blacklistRequests(pattern, responseCode, method); + } + + @Override + public void setBlacklist(Collection blacklist) { + for (BlacklistEntry entry : blacklist) { + if (entry.getHttpMethodPattern() == null) { + blacklistRequests(entry.getUrlPattern().pattern(), entry.getStatusCode()); + } else { + blacklistRequests(entry.getUrlPattern().pattern(), entry.getStatusCode(), entry.getHttpMethodPattern().pattern()); + } + } + } + + @Override + public Collection getBlacklist() { + return getBlacklistedUrls(); + } + + /** + * @deprecated use getBlacklistedUrls() + */ + @Override + @Deprecated + public List getBlacklistedRequests() { + return client.getBlacklistedRequests(); + } + + @Override + public Collection getBlacklistedUrls() { + return client.getBlacklistedUrls(); + } + + @Override + public boolean isWhitelistEnabled() { + return client.isWhitelistEnabled(); + } + + /** + * @deprecated use getWhitelistUrls() + */ + @Override + @Deprecated + public List getWhitelistRequests() { + return client.getWhitelistRequests(); + } + + @Override + public Collection getWhitelistUrls() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (Pattern pattern : getWhitelistRequests()) { + builder.add(pattern.pattern()); + } + + return builder.build(); + } + + @Override + public int getWhitelistStatusCode() { + return getWhitelistResponseCode(); + } + + public int getWhitelistResponseCode() { + return client.getWhitelistResponseCode(); + } + + @Override + public void clearBlacklist() { + client.clearBlacklist(); + } + + @Override + public void whitelistRequests(Collection urlPatterns, int statusCode) { + whitelistRequests(urlPatterns.toArray(new String[urlPatterns.size()]), statusCode); + } + + @Override + public void addWhitelistPattern(String urlPattern) { + List whitelistUrls = new ArrayList<>(getWhitelistUrls()); + whitelistUrls.add(urlPattern); + + whitelistRequests(whitelistUrls, getWhitelistStatusCode()); + } + + /** + * Whitelists the specified requests. + *

+ * Note: This method overwrites any existing whitelist. + * + * @param patterns regular expression patterns matching URLs to whitelist + * @param responseCode response code to return for non-whitelisted URLs + */ + @Override + public void whitelistRequests(String[] patterns, int responseCode) { + client.whitelistRequests(patterns, responseCode); + } + + /** + * Enables an empty whitelist, which will return the specified responseCode for all requests. + * + * @param responseCode HTTP response code to return for all requests + */ + @Override + public void enableEmptyWhitelist(int responseCode) { + client.whitelistRequests(new String[0], responseCode); + } + + @Override + public void disableWhitelist() { + clearWhitelist(); + } + + public void clearWhitelist() { + client.clearWhitelist(); + } + + @Override + public void addHeader(String name, String value) { + client.addHeader(name, value); + } + + @Override + public void removeHeader(String name) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for (Map.Entry entry : getAllHeaders().entrySet()) { + if (!entry.getKey().equals(name)) { + builder.put(entry.getKey(), entry.getValue()); + } + } + + client.setAdditionalHeaders(builder.build()); + } + + @Override + public void removeAllHeaders() { + client.setAdditionalHeaders(Collections.emptyMap()); + } + + @Override + public Map getAllHeaders() { + return client.getAdditionalHeaders(); + } + + @Override + public void setHostNameResolver(AdvancedHostResolver resolver) { + client.setResolver(resolver); + } + + @Override + public AdvancedHostResolver getHostNameResolver() { + return client.getResolver(); + } + + @Override + public boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit) { + try { + waitForNetworkTrafficToStop(TimeUnit.MILLISECONDS.convert(quietPeriod, timeUnit), TimeUnit.MILLISECONDS.convert(timeout, timeUnit)); + + return true; + } catch (TimeoutException e) { + return false; + } + } + + @Override + public void setChainedProxy(InetSocketAddress chainedProxyAddress) { + this.chainedProxyAddress = chainedProxyAddress; + client.setHttpProxy(chainedProxyAddress.getHostString() + ":" + chainedProxyAddress.getPort()); + } + + @Override + public InetSocketAddress getChainedProxy() { + return this.chainedProxyAddress; + } + + public void setCaptureHeaders(boolean captureHeaders) { + client.setCaptureHeaders(captureHeaders); + } + + @Override + public void setCaptureContent(boolean captureContent) { + client.setCaptureContent(captureContent); + } + + @Override + public void setCaptureBinaryContent(boolean captureBinaryContent) { + client.setCaptureBinaryContent(captureBinaryContent); + } + + @Override + public void clearDNSCache() { + if (client.getResolver() instanceof AdvancedHostResolver) { + AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) client.getResolver(); + advancedHostResolver.clearDNSCache(); + } else { + LOG.warn("Attempting to clear DNS cache, but host resolver is not an AdvancedHostRemapper. Host resolver is: {}", client.getResolver()); + } + } + + @Override + public void setDNSCacheTimeout(int timeout) { + if (client.getResolver() instanceof AdvancedHostResolver) { + AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) client.getResolver(); + advancedHostResolver.setNegativeDNSCacheTimeout(timeout, TimeUnit.MILLISECONDS); + advancedHostResolver.setPositiveDNSCacheTimeout(timeout, TimeUnit.MILLISECONDS); + } else { + LOG.warn("Attempting to set DNS cache timeout, but host resolver is not an AdvancedHostRemapper. Host resolver is: {}", client.getResolver()); + } + } + + @Override + public void waitForNetworkTrafficToStop(final long quietPeriodInMs, long timeoutInMs) { + boolean result = ThreadUtils.pollForCondition(new ThreadUtils.WaitCondition() { + @Override + public boolean checkCondition() { + Date lastCompleted = null; + Har har = client.getHar(); + if (har == null || har.getLog() == null) { + return true; + } + + for (HarEntry entry : har.getLog().getEntries()) { + // if there is an active request, just stop looking + if (entry.getResponse().getStatus() < 0) { + return false; + } + + Date end = new Date(entry.getStartedDateTime().getTime() + entry.getTime()); + if (lastCompleted == null) { + lastCompleted = end; + } else if (end.after(lastCompleted)) { + lastCompleted = end; + } + } + + return lastCompleted != null && System.currentTimeMillis() - lastCompleted.getTime() >= quietPeriodInMs; + } + }, timeoutInMs, TimeUnit.MILLISECONDS); + + if (!result) { + throw new TimeoutException("Timed out after " + timeoutInMs + " ms while waiting for network traffic to stop"); + } + } + + @Override + public void setOptions(Map options) { + if (options.containsKey("httpProxy")) { + client.setHttpProxy(options.get("httpProxy")); + } + } + + @Override + public void addFirstHttpFilterFactory(HttpFiltersSource filterFactory) { + LOG.warn("The legacy ProxyServer implementation does not support HTTP filter factories. Use addRequestInterceptor/addResponseInterceptor instead."); + } + + @Override + public void addLastHttpFilterFactory(HttpFiltersSource filterFactory) { + LOG.warn("The legacy ProxyServer implementation does not support HTTP filter factories. Use addRequestInterceptor/addResponseInterceptor instead."); + } + + @Override + public void addResponseFilter(ResponseFilter filter) { + LOG.warn("The legacy ProxyServer implementation does not support addRequestFilter and addResponseFilter. Use addRequestInterceptor/addResponseInterceptor instead."); + } + + @Override + public void addRequestFilter(RequestFilter filter) { + LOG.warn("The legacy ProxyServer implementation does not support addRequestFilter and addResponseFilter. Use addRequestInterceptor/addResponseInterceptor instead."); + } + + @Override + public void setMitmDisabled(boolean mitmDisabled) { + LOG.warn("The legacy ProxyServer implementation does not support disabling MITM."); + } + + @Override + public void setMitmManager(MitmManager mitmManager) { + LOG.warn("The legacy ProxyServer implementation does not support custom MITM managers."); + } + + @Override + public void setTrustAllServers(boolean trustAllServers) { + LOG.warn("The legacy ProxyServer implementation does not support the trustAllServers option."); + } + + @Override + public void setTrustSource(TrustSource trustSource) { + LOG.warn("The legacy ProxyServer implementation does not support the setTrustSource option."); + } + + public void cleanSslCertificates() { + handler.cleanSslWithCyberVilliansCA(); + } + + /** + * Exception thrown when waitForNetworkTrafficToStop does not successfully wait for network traffic to stop. + */ + public static class TimeoutException extends RuntimeException { + private static final long serialVersionUID = -7179322468198775663L; + + public TimeoutException(String message) { + super(message); + } + } +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/error/ErrorUtil.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/error/ErrorUtil.java new file mode 100644 index 000000000..c594c8fab --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/error/ErrorUtil.java @@ -0,0 +1,63 @@ +package net.lightbody.bmp.proxy.error; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Utility class for error-related methods. + */ +public class ErrorUtil { + private static final Logger log = LoggerFactory.getLogger(ErrorUtil.class); + + private static final String ERROR_HTML_CLASSPATH_LOCATION = "/net/lightbody/bmp/html/error.html"; + + /** + * Minimal HTML for error page if the error.html file cannot be loaded. + */ + private static final String DEFAULT_ERROR_HTML = "%s

%s

%s

%s

"; + + /** + * Lazily-loaded error-page HTML. + */ + private static volatile String errorHtml; + + /** + * Returns the error page HTML. The error page HTML contains four String.format-compatible '%s' placeholders for the page title, + * error title, short description, and long description of the error. + * + * @return error page HTML + */ + public static String getErrorHtml() { + if (errorHtml == null) { + loadErrorHtml(); + } + + return errorHtml; + } + + private static synchronized void loadErrorHtml() { + if (errorHtml == null) { + try (InputStream errorHtmlStream = ErrorUtil.class.getResourceAsStream(ERROR_HTML_CLASSPATH_LOCATION)) { + if (errorHtmlStream != null) { + errorHtml = IOUtils.toString(errorHtmlStream); + } else { + log.error("Could not load error.html file. Defaulting to minimalist error page HTML."); + } + } catch (IOException e) { + // classpath resource should always be closeable, so log and ignore + log.warn("Exception while closing error.html stream", e); + + } catch (RuntimeException e) { + log.error("Could not load error.html file. Defaulting to minimalist error page HTML.", e); + } + + if (errorHtml == null) { + errorHtml = DEFAULT_ERROR_HTML; + } + } + } +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/error/ProxyError.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/error/ProxyError.java new file mode 100644 index 000000000..ac478c7d6 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/error/ProxyError.java @@ -0,0 +1,67 @@ +package net.lightbody.bmp.proxy.error; + +import com.google.common.html.HtmlEscapers; +import net.lightbody.bmp.l10n.MessagesUtil; + +/** + * Proxy errors that can be returned to the client. This enum is a convenience class that collects the title, short description, and + * long description for each error type, and provides a {@link #getHtml(String)} method to return formatted HTML code to return to the + * client. + */ +public enum ProxyError { + CONN_FAILURE( + "response.conn_failure.title", + "response.conn_failure.short", + "response.common_error.long"), + DNS_NOT_FOUND( + "response.dns_not_found.title", + "response.dns_not_found.short", + "response.dns_not_found.long"), + GENERIC( + "response.generic.title", + "response.generic.short", + "response.generic.long"), + MALFORMED_URI( + "response.malformed_uri.title", + "response.malformed_uri.short", + "response.malformed_uri.long"), + NET_INTERRUPT( + "response.net_interrupt.title", + "response.net_interrupt.short", + "response.common_error.long"), + NET_RESET( + "response.net_reset.title", + "response.net_reset.short", + "response.common_error.long"), + NET_TIMEOUT( + "response.net_timeout.title", + "response.net_timeout.short", + "response.common_error.long"), + ; + + private final String errorTitle; + private final String shortDesc; + private final String longDesc; + + ProxyError(String titleMessageKey, String shortDescMessageKey, String longDescMessageKey) { + this.errorTitle = MessagesUtil.getMessage(titleMessageKey); + this.shortDesc = MessagesUtil.getMessage(shortDescMessageKey); + this.longDesc = MessagesUtil.getMessage(longDescMessageKey); + } + + /** + * Returns an HTML message for this error that can be sent to the client. + * + * @param url URL request that caused the error + * @return HTML for this error + */ + public String getHtml(String url) { + String formattedShortDesc = String.format(shortDesc, HtmlEscapers.htmlEscaper().escape(url)); + + String pageTitle = MessagesUtil.getMessage("response.common_error.pagetitle"); + + String errorHtml = ErrorUtil.getErrorHtml(); + + return String.format(errorHtml, pageTitle, errorTitle, formattedShortDesc, longDesc); + } +} diff --git a/src/main/java/org/browsermob/proxy/http/AllowAllHostnameVerifier.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/AllowAllHostnameVerifier.java similarity index 89% rename from src/main/java/org/browsermob/proxy/http/AllowAllHostnameVerifier.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/AllowAllHostnameVerifier.java index 60f3b4ade..2be6c652e 100644 --- a/src/main/java/org/browsermob/proxy/http/AllowAllHostnameVerifier.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/AllowAllHostnameVerifier.java @@ -1,10 +1,11 @@ -package org.browsermob.proxy.http; +package net.lightbody.bmp.proxy.http; import org.apache.http.conn.ssl.X509HostnameVerifier; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; + import java.io.IOException; import java.security.cert.X509Certificate; @@ -29,7 +30,7 @@ public void verify(String string, String[] strings, String[] strings1) throws SS } @Override - public boolean verify(String string, SSLSession ssls) { - return true; - } + public boolean verify(String hostname, SSLSession session) { + return true; + } } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BadURIException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BadURIException.java new file mode 100644 index 000000000..af670e7bd --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BadURIException.java @@ -0,0 +1,21 @@ +package net.lightbody.bmp.proxy.http; + +public class BadURIException extends RuntimeException { + private static final long serialVersionUID = 5106174610603303551L; + + public BadURIException() { + super(); + } + + public BadURIException(String message, Throwable cause) { + super(message, cause); + } + + public BadURIException(Throwable cause) { + super(cause); + } + + public BadURIException(String message) { + super(message); + } +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java new file mode 100644 index 000000000..00cdd8179 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpClient.java @@ -0,0 +1,1534 @@ +package net.lightbody.bmp.proxy.http; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.BaseEncoding; +import net.lightbody.bmp.client.ClientUtil; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.core.har.HarCookie; +import net.lightbody.bmp.core.har.HarEntry; +import net.lightbody.bmp.core.har.HarNameValuePair; +import net.lightbody.bmp.core.har.HarPostData; +import net.lightbody.bmp.core.har.HarPostDataParam; +import net.lightbody.bmp.core.har.HarRequest; +import net.lightbody.bmp.core.har.HarResponse; +import net.lightbody.bmp.proxy.BlacklistEntry; +import net.lightbody.bmp.proxy.RewriteRule; +import net.lightbody.bmp.proxy.Whitelist; +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import net.lightbody.bmp.proxy.jetty.util.MultiMap; +import net.lightbody.bmp.proxy.jetty.util.UrlEncoded; +import net.lightbody.bmp.proxy.util.CappedByteArrayOutputStream; +import net.lightbody.bmp.proxy.util.ClonedOutputStream; +import net.lightbody.bmp.proxy.util.IOUtils; +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponse; +import org.apache.http.HttpResponseInterceptor; +import org.apache.http.HttpVersion; +import org.apache.http.NameValuePair; +import org.apache.http.ParseException; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.AuthState; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.NTCredentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpTrace; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.ConnectionPoolTimeoutException; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.cookie.Cookie; +import org.apache.http.cookie.CookieOrigin; +import org.apache.http.cookie.CookieSpec; +import org.apache.http.cookie.CookieSpecProvider; +import org.apache.http.cookie.MalformedCookieException; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.impl.cookie.BasicClientCookie; +import org.apache.http.impl.cookie.BestMatchSpecFactory; +import org.apache.http.impl.cookie.BrowserCompatSpec; +import org.apache.http.impl.cookie.BrowserCompatSpecFactory; +import org.apache.http.message.BasicStatusLine; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.ExecutionContext; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpProcessorBuilder; +import org.apache.http.protocol.HttpRequestExecutor; +import org.java_bandwidthlimiter.StreamManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * WARN : Require zlib > 1.1.4 (deflate support) + */ +public class BrowserMobHttpClient { + // No longer getting the version from Main.getVersion(). + private static final String VERSION = "2.1"; + + private static final Logger LOG = LoggerFactory.getLogger(BrowserMobHttpClient.class); + + private static final int BUFFER = 4096; + + private volatile Har har; + private volatile String harPageRef; + + /** + * keep headers + */ + private volatile boolean captureHeaders; + + /** + * keep contents + */ + private volatile boolean captureContent; + + /** + * keep binary contents (if captureContent is set to true, default policy is to capture binary contents too) + */ + private volatile boolean captureBinaryContent = true; + + /** + * socket factory dedicated to port 80 (HTTP) + */ + private final SimulatedSocketFactory socketFactory; + + /** + * socket factory dedicated to port 443 (HTTPS) + */ + private final TrustingSSLSocketFactory sslSocketFactory; + + + private final PoolingHttpClientConnectionManager httpClientConnMgr; + + /** + * Builders for httpClient + * Each time you change their configuration you should call updateHttpClient() + */ + private final Builder requestConfigBuilder; + private final HttpClientBuilder httpClientBuilder; + + /** + * The current httpClient which will execute HTTP requests + */ + private volatile CloseableHttpClient httpClient; + + private final BasicCookieStore cookieStore = new BasicCookieStore(); + + /** + * List of rejected URL patterns + */ + private final Collection blacklistEntries = new CopyOnWriteArrayList(); + + /** + * List of accepted URL patterns + */ + private volatile Whitelist whitelist = Whitelist.WHITELIST_DISABLED; + + /** + * List of URLs to rewrite + */ + private final CopyOnWriteArrayList rewriteRules = new CopyOnWriteArrayList(); + + /** + * triggers to process when sending request + */ + private final List requestInterceptors = new CopyOnWriteArrayList(); + + /** + * triggers to process when receiving response + */ + private final List responseInterceptors = new CopyOnWriteArrayList(); + + /** + * additional headers sent with request + */ + private final Map additionalHeaders = new ConcurrentHashMap(); + + /** + * request timeout: set to -1 to disable timeout + */ + private volatile int requestTimeout = -1; + + /** + * is it possible to add a new request? + */ + private final AtomicBoolean allowNewRequests = new AtomicBoolean(true); + + /** + * Hostname resolver that wraps a {@link net.lightbody.bmp.proxy.dns.HostResolver}. The wrapped HostResolver can be replaced safely at + * runtime using {@link LegacyHostResolverAdapter#setResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. + * See {@link #setResolver(net.lightbody.bmp.proxy.dns.AdvancedHostResolver)}. + */ + private final LegacyHostResolverAdapter resolverWrapper = new LegacyHostResolverAdapter(ClientUtil.createDnsJavaWithNativeFallbackResolver()); + + /** + * does the proxy support gzip compression? (set to false if you go through a browser) + */ + private boolean decompress = true; + + /** + * set of active requests + */ + private final Set activeRequests = Collections.newSetFromMap(new ConcurrentHashMap()); + + /** + * credentials used for authentication + */ + private WildcardMatchingCredentialsProvider credsProvider; + + /** + * is the client shutdown? + */ + private volatile boolean shutdown = false; + + /** + * authentication type used + */ + private AuthType authType; + + /** + * does the proxy follow redirects? (set to false if you go through a browser) + */ + private boolean followRedirects = true; + + /** + * maximum redirects supported by the proxy + */ + private static final int MAX_REDIRECT = 10; + + /** + * remaining requests counter + */ + private final AtomicInteger requestCounter; + + /** + * Init HTTP client + * @param streamManager will be capped to 100 Megabits (by default it is disabled) + * @param requestCounter indicates the number of remaining requests + */ + public BrowserMobHttpClient(final StreamManager streamManager, AtomicInteger requestCounter) { + this.requestCounter = requestCounter; + socketFactory = new SimulatedSocketFactory(streamManager); + sslSocketFactory = new TrustingSSLSocketFactory(new AllowAllHostnameVerifier(), streamManager); + + requestConfigBuilder = RequestConfig.custom() + .setConnectionRequestTimeout(60000) + .setConnectTimeout(2000) + .setSocketTimeout(60000); + + // we associate each SocketFactory with their protocols + Registry registry = RegistryBuilder.create() + .register("http", this.socketFactory) + .register("https", this.sslSocketFactory) + .build(); + + httpClientConnMgr = new PoolingHttpClientConnectionManager(registry, resolverWrapper) { + @Override + public ConnectionRequest requestConnection(HttpRoute route, Object state) { + final ConnectionRequest wrapped = super.requestConnection(route, state); + return new ConnectionRequest() { + @Override + public HttpClientConnection get(long timeout, TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { + long start = System.nanoTime(); + try { + return wrapped.get(timeout, tunit); + } finally { + RequestInfo.get().blocked(start, System.nanoTime()); + } + } + + @Override + public boolean cancel() { + return wrapped.cancel(); + } + }; + } + }; + + // we set high limits for request parallelism to let the browser set is own limit + httpClientConnMgr.setMaxTotal(600); + httpClientConnMgr.setDefaultMaxPerRoute(300); + credsProvider = new WildcardMatchingCredentialsProvider(); + httpClientBuilder = getDefaultHttpClientBuilder(streamManager); + httpClient = httpClientBuilder.build(); + + HttpClientInterrupter.watch(this); + } + + private HttpClientBuilder getDefaultHttpClientBuilder(final StreamManager streamManager) { + assert requestConfigBuilder != null; + return HttpClientBuilder.create() + .setConnectionManager(httpClientConnMgr) + .setRequestExecutor(new HttpRequestExecutor() { + @Override + protected HttpResponse doSendRequest(HttpRequest request, HttpClientConnection conn, HttpContext context) throws IOException, HttpException { + long start = System.nanoTime(); + + // send request + HttpResponse response = super.doSendRequest(request, conn, context); + + // set "sending" for resource + RequestInfo.get().send(start, System.nanoTime()); + return response; + } + + @Override + protected HttpResponse doReceiveResponse(HttpRequest request, HttpClientConnection conn, HttpContext context) throws HttpException, IOException { + long start = System.nanoTime(); + HttpResponse response = super.doReceiveResponse(request, conn, context); + + // +4 => header/data separation + long responseHeadersSize = response.getStatusLine().toString().length() + 4; + for (Header header : response.getAllHeaders()) { + // +2 => new line + responseHeadersSize += header.toString().length() + 2; + } + // set current entry response + HarEntry entry = RequestInfo.get().getEntry(); + if (entry != null) { + entry.getResponse().setHeadersSize(responseHeadersSize); + } + if (streamManager.getLatency() > 0) { + // retrieve real latency discovered in connect SimulatedSocket + long realLatency = RequestInfo.get().getLatency(TimeUnit.MILLISECONDS); + // add latency + if (realLatency < streamManager.getLatency()) { + try { + Thread.sleep(streamManager.getLatency() - realLatency); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + // set waiting time + RequestInfo.get().wait(start, System.nanoTime()); + + return response; + } + }) + .setDefaultRequestConfig(requestConfigBuilder.build()) + .setDefaultCredentialsProvider(credsProvider) + .setDefaultCookieStore(cookieStore) + .addInterceptorLast(new PreemptiveAuth()) + .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) + // we set an empty httpProcessorBuilder to remove the automatic compression management + .setHttpProcessor(HttpProcessorBuilder.create().build()) + // we always set this to false so it can be handled manually: + .disableRedirectHandling(); + } + + public void setRetryCount(int count) { + httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(count, false)); + updateHttpClient(); + } + + public void remapHost(String source, String target) { + if (resolverWrapper.getResolver() instanceof AdvancedHostResolver) { + AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) resolverWrapper.getResolver(); + advancedHostResolver.remapHost(source, target); + } else { + LOG.warn("Attempting to remap host, but resolver is not an AdvancedHostResolver. Resolver: {}", resolverWrapper.getResolver()); + } + } + + @Deprecated + public void addRequestInterceptor(HttpRequestInterceptor i) { + httpClientBuilder.addInterceptorLast(i); + updateHttpClient(); + } + + public void addRequestInterceptor(RequestInterceptor interceptor) { + requestInterceptors.add(interceptor); + } + + @Deprecated + public void addResponseInterceptor(HttpResponseInterceptor i) { + httpClientBuilder.addInterceptorLast(i); + updateHttpClient(); + } + + public void addResponseInterceptor(ResponseInterceptor interceptor) { + responseInterceptors.add(interceptor); + } + + public void createCookie(String name, String value, String domain) { + createCookie(name, value, domain, null); + } + + public void createCookie(String name, String value, String domain, String path) { + BasicClientCookie cookie = new BasicClientCookie(name, value); + cookie.setDomain(domain); + if (path != null) { + cookie.setPath(path); + } + cookieStore.addCookie(cookie); + } + + public void clearCookies() { + cookieStore.clear(); + } + + public Cookie getCookie(String name) { + return getCookie(name, null, null); + } + + public Cookie getCookie(String name, String domain) { + return getCookie(name, domain, null); + } + + public Cookie getCookie(String name, String domain, String path) { + for (Cookie cookie : cookieStore.getCookies()) { + if(cookie.getName().equals(name)) { + if(domain != null && !domain.equals(cookie.getDomain())) { + continue; + } + if(path != null && !path.equals(cookie.getPath())) { + continue; + } + + return cookie; + } + } + + return null; + } + + public BrowserMobHttpRequest newPost(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpPost(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "POST", e); + } + } + + public BrowserMobHttpRequest newGet(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpGet(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "GET", e); + } + } + + public BrowserMobHttpRequest newPatch(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpPatch(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "PATCH", e); + } + } + + public BrowserMobHttpRequest newPut(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpPut(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "PUT", e); + } + } + + public BrowserMobHttpRequest newDelete(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpDeleteWithBody(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "DELETE", e); + } + } + + public BrowserMobHttpRequest newOptions(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpOptions(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "OPTIONS", e); + } + } + + public BrowserMobHttpRequest newHead(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpHead(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "HEAD", e); + } + } + + public BrowserMobHttpRequest newTrace(String url, net.lightbody.bmp.proxy.jetty.http.HttpRequest proxyRequest) { + try { + URI uri = makeUri(url); + return new BrowserMobHttpRequest(new HttpTrace(uri), this, -1, captureContent, proxyRequest); + } catch (URISyntaxException e) { + throw reportBadURI(url, "TRACE", e); + } + } + + private URI makeUri(String url) throws URISyntaxException { + // MOB-120: check for | character and change to correctly escaped %7C + url = url.replace(" ", "%20"); + url = url.replace(">", "%3C"); + url = url.replace("<", "%3E"); + url = url.replace("#", "%23"); + url = url.replace("{", "%7B"); + url = url.replace("}", "%7D"); + url = url.replace("|", "%7C"); + url = url.replace("\\", "%5C"); + url = url.replace("^", "%5E"); + url = url.replace("~", "%7E"); + url = url.replace("[", "%5B"); + url = url.replace("]", "%5D"); + url = url.replace("`", "%60"); + url = url.replace("\"", "%22"); + + URI uri = new URI(url); + + // are we using the default ports for http/https? if so, let's rewrite the URI to make sure the :80 or :443 + // is NOT included in the string form the URI. The reason we do this is that in HttpClient 4.0 the Host header + // would include a value such as "yahoo.com:80" rather than "yahoo.com". Not sure why this happens but we don't + // want it to, and rewriting the URI solves it + if ((uri.getPort() == 80 && "http".equals(uri.getScheme())) + || (uri.getPort() == 443 && "https".equals(uri.getScheme()))) { + // we rewrite the URL with a StringBuilder (vs passing in the components of the URI) because if we were + // to pass in these components using the URI's 7-arg constructor query parameters get double escaped (bad!) + StringBuilder sb = new StringBuilder(uri.getScheme()).append("://"); + if (uri.getRawUserInfo() != null) { + sb.append(uri.getRawUserInfo()).append("@"); + } + sb.append(uri.getHost()); + if (uri.getRawPath() != null) { + sb.append(uri.getRawPath()); + } + if (uri.getRawQuery() != null) { + sb.append("?").append(uri.getRawQuery()); + } + if (uri.getRawFragment() != null) { + sb.append("#").append(uri.getRawFragment()); + } + + uri = new URI(sb.toString()); + } + return uri; + } + + private BadURIException reportBadURI(String url, String method, URISyntaxException cause) { + if (this.har != null && harPageRef != null) { + HarEntry entry = new HarEntry(harPageRef); + entry.setStartedDateTime(new Date()); + entry.setRequest(new HarRequest(method, url, "HTTP/1.1")); + entry.setResponse(new HarResponse(-998, "Bad URI", "HTTP/1.1")); + har.getLog().addEntry(entry); + } + + throw new BadURIException("Bad URI requested: " + url, cause); + } + + public void checkTimeout() { + for (ActiveRequest activeRequest : activeRequests) { + activeRequest.checkTimeout(); + } + + // Close expired connections + httpClientConnMgr.closeExpiredConnections(); + // Optionally, close connections + // that have been idle longer than 30 sec + httpClientConnMgr.closeIdleConnections(30, TimeUnit.SECONDS); + } + + public BrowserMobHttpResponse execute(BrowserMobHttpRequest req) { + if (!allowNewRequests.get()) { + throw new RuntimeException("No more requests allowed"); + } + + try { + requestCounter.incrementAndGet(); + + for (RequestInterceptor interceptor : requestInterceptors) { + interceptor.process(req, har); + } + + BrowserMobHttpResponse response = execute(req, 1); + for (ResponseInterceptor interceptor : responseInterceptors) { + interceptor.process(response, har); + } + + return response; + } finally { + requestCounter.decrementAndGet(); + } + } + + // + //If we were making cake, this would be the filling :) + // + private BrowserMobHttpResponse execute(BrowserMobHttpRequest req, int depth) { + if (depth >= MAX_REDIRECT) { + throw new IllegalStateException("Max number of redirects (" + MAX_REDIRECT + ") reached"); + } + + RequestCallback callback = req.getRequestCallback(); + + HttpRequestBase method = req.getMethod(); + String url = method.getURI().toString(); + + // process any rewrite requests + boolean rewrote = false; + String newUrl = url; + for (RewriteRule rule : rewriteRules) { + Matcher matcher = rule.getPattern().matcher(newUrl); + newUrl = matcher.replaceAll(rule.getReplace()); + rewrote = true; + } + + if (rewrote) { + try { + method.setURI(new URI(newUrl)); + url = newUrl; + } catch (URISyntaxException e) { + LOG.warn("Could not rewrite url to " + newUrl, e); + } + } + + // handle whitelist and blacklist entries + int mockResponseCode = -1; + // alias the current whitelist, in case the whitelist is changed while processing this request + Whitelist currentWhitelist = whitelist; + if (currentWhitelist.isEnabled()) { + boolean found = false; + for (Pattern pattern : currentWhitelist.getPatterns()) { + if (pattern.matcher(url).matches()) { + found = true; + break; + } + } + + // url does not match whitelist, set the response code + if (!found) { + mockResponseCode = currentWhitelist.getResponseCode(); + } + } + + for (BlacklistEntry blacklistEntry : blacklistEntries) { + if (blacklistEntry.matches(url, method.getMethod())) { + mockResponseCode = blacklistEntry.getResponseCode(); + break; + } + } + + if (!additionalHeaders.isEmpty()) { + // Set the additional headers + for (Map.Entry entry : additionalHeaders.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + method.removeHeaders(key); + method.addHeader(key, value); + } + } + + + String charSet = "UTF-8"; + InputStream is = null; + int statusCode = -998; + long bytes = 0; + boolean gzipping = false; + boolean deflating = false; + OutputStream os = req.getOutputStream(); + if (os == null) { + os = new CappedByteArrayOutputStream(1024 * 1024); // MOB-216 don't buffer more than 1 MB + } + + // link the object up now, before we make the request, so that if we get cut off (ie: favicon.ico request and browser shuts down) + // we still have the attempt associated, even if we never got a response + HarEntry entry = new HarEntry(harPageRef); + entry.setStartedDateTime(new Date()); + + // clear out any connection-related information so that it's not stale from previous use of this thread. + RequestInfo.clear(url, entry); + RequestInfo.get().start(); + + entry.setRequest(new HarRequest(method.getMethod(), url, method.getProtocolVersion().toString())); + entry.setResponse(new HarResponse(-999, "NO RESPONSE", method.getProtocolVersion().toString())); + if (this.har != null && harPageRef != null) { + har.getLog().addEntry(entry); + } + + String query = method.getURI().getRawQuery(); + if (query != null) { + MultiMap params = new MultiMap(); + UrlEncoded.decodeTo(query, params, "UTF-8"); + for (Object k : params.keySet()) { + for (Object v : params.getValues(k)) { + entry.getRequest().getQueryString().add(new HarNameValuePair((String) k, (String) v)); + } + } + } + + String errorMessage = null; + CloseableHttpResponse response = null; + + BasicHttpContext ctx = new BasicHttpContext(); + + ActiveRequest activeRequest = new ActiveRequest(method, entry.getStartedDateTime()); + activeRequests.add(activeRequest); + + // for dealing with automatic authentication + if (authType == AuthType.NTLM) { + // todo: not supported yet + //ctx.setAttribute("preemptive-auth", new NTLMScheme(new JCIFSEngine())); + } else if (authType == AuthType.BASIC) { + ctx.setAttribute("preemptive-auth", new BasicScheme()); + } + + StatusLine statusLine = null; + try { + // set the User-Agent if it's not already set + if (method.getHeaders("User-Agent").length == 0) { + method.addHeader("User-Agent", "bmp.lightbody.net/" + VERSION); + } + + // was the request mocked out? + if (mockResponseCode != -1) { + statusCode = mockResponseCode; + + // TODO: HACKY!! + callback.handleHeaders(new Header[]{ + new Header(){ + @Override + public String getName() { + return "Content-Type"; + } + + @Override + public String getValue() { + return "text/plain"; + } + + @Override + public HeaderElement[] getElements() throws ParseException { + return new HeaderElement[0]; + } + } + }); + // Make sure we set the status line here too. + // Use the version number from the request + ProtocolVersion version = null; + int reqDotVersion = req.getProxyRequest().getDotVersion(); + if (reqDotVersion == -1) { + version = new HttpVersion(0, 9); + } else if (reqDotVersion == 0) { + version = new HttpVersion(1, 0); + } else if (reqDotVersion == 1) { + version = new HttpVersion(1, 1); + } + // and if not any of these, trust that a Null version will + // cause an appropriate error + callback.handleStatusLine(new BasicStatusLine(version, statusCode, "Status set by browsermob-proxy")); + // No mechanism to look up the response text by status code, + // so include a notification that this is a synthetic error code. + } else { + response = httpClient.execute(method, ctx); + statusLine = response.getStatusLine(); + statusCode = statusLine.getStatusCode(); + if (callback != null) { + callback.handleStatusLine(statusLine); + callback.handleHeaders(response.getAllHeaders()); + } + + if (response.getEntity() != null) { + is = response.getEntity().getContent(); + } + + // check for null (resp 204 can cause HttpClient to return null, which is what Google does with http://clients1.google.com/generate_204) + if (is != null) { + Header contentEncodingHeader = response.getFirstHeader("Content-Encoding"); + if(contentEncodingHeader != null) { + if ("gzip".equalsIgnoreCase(contentEncodingHeader.getValue())) { + gzipping = true; + } else if ("deflate".equalsIgnoreCase(contentEncodingHeader.getValue())) { + deflating = true; + } + } + + // deal with GZIP content! + if(decompress && response.getEntity().getContentLength() != 0) { //getContentLength<0 if unknown + if (gzipping) { + is = new GZIPInputStream(is); + } else if (deflating) { + // RAW deflate only + // WARN : if system is using zlib<=1.1.4 the stream must be append with a dummy byte + // that is not requiered for zlib>1.1.4 (not mentioned on current Inflater javadoc) + is = new InflaterInputStream(is, new Inflater(true)); + } + } + + if (captureContent) { + // todo - something here? + os = new ClonedOutputStream(os); + } + bytes = copyWithStats(is, os); + } + } + } catch (IOException e) { + errorMessage = e.toString(); + + if (callback != null) { + callback.reportError(e); + } + + // only log it if we're not shutdown (otherwise, errors that happen during a shutdown can likely be ignored) + if (!shutdown) { + if (LOG.isDebugEnabled()) { + LOG.info(String.format("%s when requesting %s", errorMessage, url), e); + } else { + LOG.info(String.format("%s when requesting %s", errorMessage, url)); + } + } + } finally { + // the request is done, get it out of here + activeRequests.remove(activeRequest); + + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // this is OK to ignore + LOG.info("Error closing input stream", e); + } + } + if (response != null) { + try { + response.close(); + } catch (IOException e) { + // nothing to do + LOG.info("Error closing response stream", e); + } + } + } + + // record the response as ended + RequestInfo.get().finish(); + + // set the start time and other timings + entry.setStartedDateTime(RequestInfo.get().getStartDate()); + entry.setTimings(RequestInfo.get().getTimings()); + entry.setServerIPAddress(RequestInfo.get().getResolvedAddress()); + + // todo: where you store this in HAR? + // obj.setErrorMessage(errorMessage); + entry.getResponse().setBodySize(bytes); + entry.getResponse().getContent().setSize(bytes); + entry.getResponse().setStatus(statusCode); + if (response != null) { + entry.getResponse().setHttpVersion(response.getProtocolVersion().toString()); + } + if (statusLine != null) { + entry.getResponse().setStatusText(statusLine.getReasonPhrase()); + } + + boolean urlEncoded = false; + if (captureHeaders || captureContent) { + for (Header header : method.getAllHeaders()) { + if (header.getValue() != null && header.getValue().startsWith(URLEncodedUtils.CONTENT_TYPE)) { + urlEncoded = true; + } + + entry.getRequest().getHeaders().add(new HarNameValuePair(header.getName(), header.getValue())); + } + + if (response != null) { + for (Header header : response.getAllHeaders()) { + entry.getResponse().getHeaders().add(new HarNameValuePair(header.getName(), header.getValue())); + } + } + } + + + // +4 => header/data separation + long requestHeadersSize = method.getRequestLine().toString().length() + 4; + long requestBodySize = 0; + for (Header header : method.getAllHeaders()) { + // +2 => new line + requestHeadersSize += header.toString().length() + 2; + // get body size + if (header.getName().equals("Content-Length")) { + requestBodySize += Integer.valueOf(header.getValue()); + } + } + entry.getRequest().setHeadersSize(requestHeadersSize); + entry.getRequest().setBodySize(requestBodySize); + if (captureContent) { + + // can we understand the POST data at all? + if (method instanceof HttpEntityEnclosingRequestBase && req.getCopy() != null) { + HttpEntityEnclosingRequestBase enclosingReq = (HttpEntityEnclosingRequestBase) method; + HttpEntity entity = enclosingReq.getEntity(); + + + HarPostData data = new HarPostData(); + data.setMimeType(req.getMethod().getFirstHeader("Content-Type").getValue()); + entry.getRequest().setPostData(data); + + if (urlEncoded || URLEncodedUtils.isEncoded(entity)) { + try { + String content = req.getCopy().toString("UTF-8"); + + if (content != null && content.length() > 0) { + List result = new ArrayList(); + URLEncodedUtils.parse(result, new Scanner(content), null); + + List params = new ArrayList(result.size()); + data.setParams(params); + + for (NameValuePair pair : result) { + params.add(new HarPostDataParam(pair.getName(), pair.getValue())); + } + } + } catch (UnsupportedEncodingException e) { + // realistically this should never happen, since UTF-8 is always a supported encoding + LOG.info("Unexpected problem when parsing input copy", e); + } catch (RuntimeException e) { + LOG.info("Unexpected problem when parsing input copy", e); + } + } else { + // not URL encoded, so let's grab the body of the POST and capture that + try { + String postBody = req.getCopy().toString("UTF-8"); + data.setText(postBody); + } catch (UnsupportedEncodingException e) { + // realistically this should never happen, since UTF-8 is always a supported encoding + LOG.info("Unexpected problem when parsing post body", e); + } + } + } + } + + //capture request cookies + javax.servlet.http.Cookie[] cookies = req.getProxyRequest().getCookies(); + for (javax.servlet.http.Cookie cookie : cookies) { + HarCookie hc = new HarCookie(); + hc.setName(cookie.getName()); + hc.setValue(cookie.getValue()); + entry.getRequest().getCookies().add(hc); + } + + String contentType = null; + + if (response != null) { + Header contentTypeHdr = response.getFirstHeader("Content-Type"); + if (contentTypeHdr != null) { + contentType = contentTypeHdr.getValue(); + entry.getResponse().getContent().setMimeType(contentType); + + if (captureContent && os != null && os instanceof ClonedOutputStream) { + ByteArrayOutputStream copy = ((ClonedOutputStream) os).getOutput(); + + if (entry.getResponse().getBodySize() != 0 && (gzipping || deflating)) { + // ok, we need to decompress it before we can put it in the har file + try { + InputStream temp = null; + if(gzipping){ + temp = new GZIPInputStream(new ByteArrayInputStream(copy.toByteArray())); + } else if (deflating) { + // RAW deflate only? + // WARN : if system is using zlib<=1.1.4 the stream must be append with a dummy byte + // that is not requiered for zlib>1.1.4 (not mentioned on current Inflater javadoc) + temp = new InflaterInputStream(new ByteArrayInputStream(copy.toByteArray()), new Inflater(true)); + } + copy = new ByteArrayOutputStream(); + IOUtils.copyAndClose(temp, copy); + } catch (IOException e) { + throw new RuntimeException("Error when decompressing input stream", e); + } + } + + if (hasTextualContent(contentType)) { + setTextOfEntry(entry, copy, contentType); + } else if(captureBinaryContent){ + setBinaryContentOfEntry(entry, copy); + } + } + + NameValuePair nvp = contentTypeHdr.getElements()[0].getParameterByName("charset"); + + if (nvp != null) { + charSet = nvp.getValue(); + } + } + } + + if (contentType != null) { + entry.getResponse().getContent().setMimeType(contentType); + } + + // checking to see if the client is being redirected + boolean isRedirect = false; + + String location = null; + if (response != null && statusCode >= 300 && statusCode < 400 && statusCode != 304) { + isRedirect = true; + + // pulling the header for the redirect + Header locationHeader = response.getLastHeader("location"); + if (locationHeader != null) { + location = locationHeader.getValue(); + } else if (this.followRedirects) { + throw new RuntimeException("Invalid redirect - missing location header"); + } + } + + // + // Response validation - they only work if we're not following redirects + // + + int expectedStatusCode = req.getExpectedStatusCode(); + + // if we didn't mock out the actual response code and the expected code isn't what we saw, we have a problem + if (mockResponseCode == -1 && expectedStatusCode > -1) { + if (this.followRedirects) { + throw new RuntimeException("Response validation cannot be used while following redirects"); + } + if (expectedStatusCode != statusCode) { + if (isRedirect) { + throw new RuntimeException("Expected status code of " + expectedStatusCode + " but saw " + statusCode + + " redirecting to: " + location); + } else { + throw new RuntimeException("Expected status code of " + expectedStatusCode + " but saw " + statusCode); + } + } + } + + // Location header check: + if (isRedirect && (req.getExpectedLocation() != null)) { + if (this.followRedirects) { + throw new RuntimeException("Response validation cannot be used while following redirects"); + } + + if (location.compareTo(req.getExpectedLocation()) != 0) { + throw new RuntimeException("Expected a redirect to " + req.getExpectedLocation() + " but saw " + location); + } + } + + // end of validation logic + + // basic tail recursion for redirect handling + if (isRedirect && this.followRedirects) { + // updating location: + try { + URI redirectUri = new URI(location); + URI newUri = method.getURI().resolve(redirectUri); + method.setURI(newUri); + + return execute(req, ++depth); + } catch (URISyntaxException e) { + LOG.warn("Could not parse URL", e); + } + } + + return new BrowserMobHttpResponse(entry, method, response, errorMessage, contentType, charSet); + } + + private boolean hasTextualContent(String contentType) { + return contentType != null && contentType.startsWith("text/") || + contentType.startsWith("application/x-javascript") || + contentType.startsWith("application/javascript") || + contentType.startsWith("application/json") || + contentType.startsWith("application/xml") || + contentType.startsWith("application/xhtml+xml"); + } + + private void setBinaryContentOfEntry(HarEntry entry, ByteArrayOutputStream copy) { + entry.getResponse().getContent().setText(BaseEncoding.base64().encode(copy.toByteArray())); + entry.getResponse().getContent().setEncoding("base64"); + } + + private void setTextOfEntry(HarEntry entry, ByteArrayOutputStream copy, String contentType) { + ContentType contentTypeCharset = ContentType.parse(contentType); + Charset charset = contentTypeCharset.getCharset(); + if (charset != null) { + entry.getResponse().getContent().setText(new String(copy.toByteArray(), charset)); + } else { + entry.getResponse().getContent().setText(new String(copy.toByteArray())); + } + } + + + public void shutdown() { + shutdown = true; + abortActiveRequests(); + rewriteRules.clear(); + blacklistEntries.clear(); + credsProvider.clear(); + httpClientConnMgr.shutdown(); + } + + public void abortActiveRequests() { + allowNewRequests.set(false); + + for (ActiveRequest activeRequest : activeRequests) { + activeRequest.abort(); + } + + activeRequests.clear(); + } + + public void setHar(Har har) { + this.har = har; + } + + public void setHarPageRef(String harPageRef) { + this.harPageRef = harPageRef; + } + + public void setRequestTimeout(int requestTimeout) { + this.requestTimeout = requestTimeout; + } + + public void setSocketOperationTimeout(int readTimeout) { + requestConfigBuilder.setSocketTimeout(readTimeout); + httpClientBuilder.setDefaultRequestConfig(requestConfigBuilder.build()); + updateHttpClient(); + } + + public void setConnectionTimeout(int connectionTimeout) { + requestConfigBuilder.setConnectTimeout(connectionTimeout); + httpClientBuilder.setDefaultRequestConfig(requestConfigBuilder.build()); + updateHttpClient(); + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + + } + + public boolean isFollowRedirects() { + return followRedirects; + } + + public void autoBasicAuthorization(String domain, String username, String password) { + authType = AuthType.BASIC; + credsProvider.setCredentials( + new AuthScope(domain, -1), + new UsernamePasswordCredentials(username, password)); + } + + public void autoNTLMAuthorization(String domain, String username, String password) { + authType = AuthType.NTLM; + credsProvider.setCredentials( + new AuthScope(domain, -1), + new NTCredentials(username, password, "workstation", domain)); + } + + public void rewriteUrl(String match, String replace) { + rewriteRules.add(new RewriteRule(match, replace)); + } + + public List getRewriteRules() { + return rewriteRules; + } + + public void removeRewriteRule(String urlPattern) { + for (RewriteRule rewriteRule : rewriteRules) { + if (rewriteRule.getPattern().pattern().equals(urlPattern)) { + // rewriteRules is a CopyOnWriteArrayList, so we can modify it while iterating over it + rewriteRules.remove(rewriteRule); + } + } + } + + public void clearRewriteRules() { + rewriteRules.clear(); + } + + /** + * this method is provided for backwards compatibility before we renamed it to blacklistRequests (note the plural) + * @deprecated use blacklistRequests(String pattern, int responseCode) + */ + @Deprecated + public void blacklistRequest(String pattern, int responseCode, String method) { + blacklistRequests(pattern, responseCode, method); + } + + public void blacklistRequests(String pattern, int responseCode, String method) { + blacklistEntries.add(new BlacklistEntry(pattern, responseCode, method)); + } + + /** + * @deprecated Use getBlacklistedUrls() + */ + @Deprecated + public List getBlacklistedRequests() { + List blacklist = new ArrayList(blacklistEntries.size()); + blacklist.addAll(blacklistEntries); + + return blacklist; + } + + public Collection getBlacklistedUrls() { + return blacklistEntries; + } + + public void clearBlacklist() { + blacklistEntries.clear(); + } + + public boolean isWhitelistEnabled() { + return whitelist.isEnabled(); + } + + /** + * @deprecated use getWhitelistUrls() + * @return unmodifiable list of whitelisted Patterns + */ + @Deprecated + public List getWhitelistRequests() { + List whitelistPatterns = new ArrayList(whitelist.getPatterns().size()); + whitelistPatterns.addAll(whitelist.getPatterns()); + + return Collections.unmodifiableList(whitelistPatterns); + } + + /** + * Retrieves Patterns of URLs that have been whitelisted. + * + * @return unmodifiable whitelisted URL Patterns + */ + public Collection getWhitelistUrls() { + return whitelist.getPatterns(); + } + + public int getWhitelistResponseCode() { + return whitelist.getResponseCode(); + } + + /** + * Whitelist the specified request patterns, returning the specified responseCode for non-whitelisted + * requests. + * + * @param patterns regular expression strings matching URL patterns to whitelist. if empty or null, + * the whitelist will be enabled but will not match any URLs. + * @param responseCode the HTTP response code to return for non-whitelisted requests + */ + public void whitelistRequests(String[] patterns, int responseCode) { + if (patterns == null || patterns.length == 0) { + whitelist = new Whitelist(responseCode); + } else { + whitelist = new Whitelist(patterns, responseCode); + } + } + + /** + * Clears and disables the current whitelist. + */ + public void clearWhitelist() { + whitelist = Whitelist.WHITELIST_DISABLED; + } + + public void addHeader(String name, String value) { + additionalHeaders.put(name, value); + } + + public void setAdditionalHeaders(Map additionalHeaders) { + additionalHeaders.clear(); + additionalHeaders.putAll(additionalHeaders); + } + + public Map getAdditionalHeaders() { + return ImmutableMap.builder().putAll(additionalHeaders).build(); + } + + /** + * init HTTP client, using a browser which handle cookies, gzip compression and redirects + */ + public void prepareForBrowser() { + // Clear cookies, let the browser handle them + cookieStore.clear(); + CookieSpecProvider easySpecProvider = new CookieSpecProvider() { + public CookieSpec create(HttpContext context) { + return new BrowserCompatSpec() { + @Override + public void validate(Cookie cookie, CookieOrigin origin) + throws MalformedCookieException { + // Oh, I am easy + } + }; + } + }; + + Registry r = RegistryBuilder.create() + .register(CookieSpecs.BEST_MATCH, + new BestMatchSpecFactory()) + .register(CookieSpecs.BROWSER_COMPATIBILITY, + new BrowserCompatSpecFactory()) + .register("easy", easySpecProvider) + .build(); + + RequestConfig requestConfig = RequestConfig.custom() + .setCookieSpec("easy") + .build(); + + httpClientBuilder.setDefaultCookieSpecRegistry(r) + .setDefaultRequestConfig(requestConfig); + updateHttpClient(); + + decompress = false; + setFollowRedirects(false); + } + + /** + * CloseableHttpClient doesn't permit anymore to change parameters easily. + * This method allow you to rebuild the httpClientBuilder to get the CloseableHttpClient + * When the config is changed. + * + * So httpClient reference change this may lead to concurrency issue. + */ + private void updateHttpClient(){ + httpClient = httpClientBuilder.build(); + } + + public String remappedHost(String host) { + if (resolverWrapper.getResolver() instanceof AdvancedHostResolver) { + AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) resolverWrapper.getResolver(); + + return advancedHostResolver.getHostRemappings().get(host); + } else { + LOG.warn("Attempting to find remapped host for {}, but resolver is not an AdvancedHostResolver. Resolver: {}", host, resolverWrapper.getResolver()); + + return ""; + } + } + + public List originalHosts(String host) { + if (resolverWrapper.getResolver() instanceof AdvancedHostResolver) { + AdvancedHostResolver advancedHostResolver = (AdvancedHostResolver) resolverWrapper.getResolver(); + return ImmutableList.copyOf(advancedHostResolver.getOriginalHostnames(host)); + } else { + LOG.warn("Attempting to find original hosts for {}, but resolver is not an AdvancedHostResolver. Resolver: {}", host, resolverWrapper.getResolver()); + + return Collections.emptyList(); + } + } + + public Har getHar() { + return har; + } + + public void setCaptureHeaders(boolean captureHeaders) { + this.captureHeaders = captureHeaders; + } + + public void setCaptureContent(boolean captureContent) { + this.captureContent = captureContent; + } + + public void setCaptureBinaryContent(boolean captureBinaryContent) { + this.captureBinaryContent = captureBinaryContent; + } + + public void setHttpProxy(String httpProxy) { + String host = httpProxy.split(":")[0]; + Integer port = Integer.parseInt(httpProxy.split(":")[1]); + HttpHost proxy = new HttpHost(host, port); + httpClientBuilder.setProxy(proxy); + updateHttpClient(); + } + + static class PreemptiveAuth implements HttpRequestInterceptor { + public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { + AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE); + + // If no auth scheme avaialble yet, try to initialize it preemptively + if (authState.getAuthScheme() == null) { + AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth"); + CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER); + HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); + if (authScheme != null) { + Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort())); + if (creds != null) { + authState.update(authScheme, creds); + } + } + } + } + } + + class ActiveRequest { + private final HttpRequestBase request; + private final Date start; + private final AtomicBoolean aborting = new AtomicBoolean(false); + + ActiveRequest(HttpRequestBase request, Date start) { + this.request = request; + this.start = start; + } + + /** + * Checks the timeout for this request, and aborts if necessary. + * @return true if the request was aborted for exceeding its timeout, otherwise false. + */ + boolean checkTimeout() { + if (aborting.get()) { + return false; + } + + if (requestTimeout != -1) { + if (request != null && start != null && new Date(System.currentTimeMillis() - requestTimeout).after(start)) { + boolean okayToAbort = aborting.compareAndSet(false, true); + if (okayToAbort) { + LOG.info("Aborting request to {} after it failed to complete in {} ms", request.getURI().toString(), requestTimeout); + + abort(); + + return true; + } + } + } + + return false; + } + + public void abort() { + request.abort(); + + // no need to close the connection -- the call to request.abort() releases the connection itself + } + } + + private enum AuthType { + NONE, BASIC, NTLM + } + + public boolean isShutdown() { + return shutdown; + } + +// public void clearDNSCache() { +// this.hostNameResolver.clearCache(); +// } + +// public void setDNSCacheTimeout(int timeout) { +// this.hostNameResolver.setCacheTimeout(timeout); +// } + + public static long copyWithStats(InputStream is, OutputStream os) throws IOException { + long bytesCopied = 0; + byte[] buffer = new byte[BUFFER]; + int length; + + try { + // read the first byte + int firstByte = is.read(); + + if (firstByte == -1) { + return 0; + } + + os.write(firstByte); + bytesCopied++; + + do { + length = is.read(buffer, 0, BUFFER); + if (length != -1) { + bytesCopied += length; + os.write(buffer, 0, length); + os.flush(); + } + } while (length != -1); + } finally { + try { + is.close(); + } catch (IOException e) { + // ok to ignore + } + + try { + os.close(); + } catch (IOException e) { + // ok to ignore + } + } + + return bytesCopied; + } + + public boolean isCaptureBinaryContent() { + return captureBinaryContent; + } + + public boolean isCaptureContent() { + return captureContent; + } + + public boolean isCaptureHeaders() { + return captureHeaders; + } + + public AdvancedHostResolver getResolver() { + return resolverWrapper.getResolver(); + } + + public void setResolver(AdvancedHostResolver resolver) { + resolverWrapper.setResolver(resolver); + } +} diff --git a/src/main/java/org/browsermob/proxy/http/BrowserMobHttpRequest.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpRequest.java similarity index 79% rename from src/main/java/org/browsermob/proxy/http/BrowserMobHttpRequest.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpRequest.java index bd646e6ac..fc16dad95 100644 --- a/src/main/java/org/browsermob/proxy/http/BrowserMobHttpRequest.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpRequest.java @@ -1,40 +1,41 @@ -package org.browsermob.proxy.http; +package net.lightbody.bmp.proxy.http; +import com.google.common.io.BaseEncoding; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.util.ClonedInputStream; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.message.BasicNameValuePair; -import org.apache.http.protocol.HTTP; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.util.Base64; -import org.browsermob.proxy.util.ClonedInputStream; -import org.browsermob.proxy.util.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.List; public class BrowserMobHttpRequest { - private static final Log LOG = new Log(); + private static final Logger LOG = LoggerFactory.getLogger(BrowserMobHttpRequest.class); private HttpRequestBase method; private BrowserMobHttpClient client; private int expectedStatusCode; - private String verificationText; private List nvps = new ArrayList(); private StringEntity stringEntity; private ByteArrayEntity byteArrayEntity; - private InputStreamEntity inputStreamEntity; + private RepeatableInputStreamRequestEntity inputStreamEntity; private MultipartEntity multipartEntity; private OutputStream outputStream; private RequestCallback requestCallback; @@ -53,6 +54,10 @@ protected BrowserMobHttpRequest(HttpRequestBase method, BrowserMobHttpClient cli this.proxyRequest = proxyRequest; } + public RepeatableInputStreamRequestEntity getInputStreamEntity(){ + return inputStreamEntity; + } + public String getExpectedLocation() { return expectedLocation; } @@ -71,16 +76,10 @@ public void addRequestParameter(String key, String value) { public void setRequestBody(String body, String contentType, String charSet) { try { - stringEntity = new StringEntity(body, charSet); - } catch (UnsupportedEncodingException e) { - try { - stringEntity = new StringEntity(body, (String) null); - } catch (UnsupportedEncodingException e1) { - // this won't happen - } + stringEntity = new StringEntity(body, ContentType.create(contentType, charSet)); + } catch (UnsupportedCharsetException e) { + stringEntity = new StringEntity(body, ContentType.create(contentType, (String) null)); } - - stringEntity.setContentType(contentType); } public void setRequestBody(String body) { @@ -88,7 +87,7 @@ public void setRequestBody(String body) { } public void setRequestBodyAsBase64EncodedBytes(String bodyBase64Encoded) { - byteArrayEntity = new ByteArrayEntity(Base64.base64ToByteArray(bodyBase64Encoded)); + byteArrayEntity = new ByteArrayEntity(BaseEncoding.base64().decode(bodyBase64Encoded)); } public void setRequestInputStream(InputStream is, long length) { @@ -98,18 +97,10 @@ public void setRequestInputStream(InputStream is, long length) { copy = cis.getOutput(); } - inputStreamEntity = new InputStreamEntity(is, length); + inputStreamEntity = new RepeatableInputStreamRequestEntity(is, length); } - public String getVerificationText() { - return verificationText; - } - - public void setVerificationText(String verificationText) { - this.verificationText = verificationText; - } - public HttpRequestBase getMethod() { return method; } @@ -133,7 +124,7 @@ public BrowserMobHttpResponse execute() { if (!nvps.isEmpty()) { try { if (!multiPart) { - enclodingRequest.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); + enclodingRequest.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8)); } else { for (NameValuePair nvp : nvps) { multipartEntity.addPart(nvp.getName(), new StringBody(nvp.getValue())); @@ -141,7 +132,7 @@ public BrowserMobHttpResponse execute() { enclodingRequest.setEntity(multipartEntity); } } catch (UnsupportedEncodingException e) { - LOG.severe("Could not find UTF-8 encoding, something is really wrong", e); + LOG.error("Could not find UTF-8 encoding, something is really wrong", e); } } else if (multipartEntity != null) { enclodingRequest.setEntity(multipartEntity); diff --git a/src/main/java/org/browsermob/proxy/http/BrowserMobHttpResponse.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpResponse.java similarity index 58% rename from src/main/java/org/browsermob/proxy/http/BrowserMobHttpResponse.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpResponse.java index 8c503620a..52942d0ce 100644 --- a/src/main/java/org/browsermob/proxy/http/BrowserMobHttpResponse.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/BrowserMobHttpResponse.java @@ -1,41 +1,27 @@ -package org.browsermob.proxy.http; +package net.lightbody.bmp.proxy.http; +import net.lightbody.bmp.core.har.HarEntry; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpRequestBase; -import org.browsermob.core.har.HarEntry; public class BrowserMobHttpResponse { private HarEntry entry; private HttpRequestBase method; private HttpResponse response; - private boolean contentMatched; - private String verificationText; private String errorMessage; - private String body; private String contentType; private String charSet; - public BrowserMobHttpResponse(HarEntry entry, HttpRequestBase method, HttpResponse response, boolean contentMatched, String verificationText, String errorMessage, String body, String contentType, String charSet) { + public BrowserMobHttpResponse(HarEntry entry, HttpRequestBase method, HttpResponse response, String errorMessage, String contentType, String charSet) { this.entry = entry; this.method = method; this.response = response; - this.contentMatched = contentMatched; - this.verificationText = verificationText; this.errorMessage = errorMessage; - this.body = body; this.contentType = contentType; this.charSet = charSet; } - public boolean isContentMatched() { - return contentMatched; - } - - public String getBody() { - return body; - } - public String getContentType() { return contentType; } @@ -61,12 +47,6 @@ public HttpResponse getRawResponse() { return response; } - public void checkContentMatched(String info) { - if (!isContentMatched()) { - throw new RuntimeException("Content match failure. Expected '" + verificationText + "'." + (info != null ? " " + info : "")); - } - } - public HarEntry getEntry() { return entry; } diff --git a/src/main/java/org/browsermob/proxy/http/CookieHeadersParser.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/CookieHeadersParser.java similarity index 89% rename from src/main/java/org/browsermob/proxy/http/CookieHeadersParser.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/CookieHeadersParser.java index 28d5bd83e..088cc5c58 100644 --- a/src/main/java/org/browsermob/proxy/http/CookieHeadersParser.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/CookieHeadersParser.java @@ -1,9 +1,9 @@ -package org.browsermob.proxy.http; +package net.lightbody.bmp.proxy.http; +import net.lightbody.bmp.core.har.HarCookie; +import net.lightbody.bmp.core.har.HarNameValuePair; import org.apache.http.Header; import org.apache.http.HttpRequest; -import org.browsermob.core.har.HarCookie; -import org.browsermob.core.har.HarNameValuePair; import java.util.List; diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/HttpClientInterrupter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/HttpClientInterrupter.java new file mode 100644 index 000000000..57543d72b --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/HttpClientInterrupter.java @@ -0,0 +1,55 @@ +package net.lightbody.bmp.proxy.http; + +import java.lang.ref.WeakReference; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HttpClientInterrupter { + private static final Logger LOG = LoggerFactory.getLogger(HttpClientInterrupter.class); + + private static final int TIMEOUT_POLL_INTERVAL_MS = 1000; + + private static final ScheduledExecutorService timeoutPollExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("browsermob-client-timeout-thread"); + thread.setDaemon(true); + return thread; + } + }); + + private static class PollHttpClientTask implements Runnable { + private final WeakReference clientWrapper; + + public PollHttpClientTask(BrowserMobHttpClient client) { + this.clientWrapper = new WeakReference(client); + } + + @Override + public void run() { + BrowserMobHttpClient client = clientWrapper.get(); + if (client != null && !client.isShutdown()) { + try { + client.checkTimeout(); + } catch (RuntimeException e) { + LOG.warn("Unexpected problem while checking timeout on a client", e); + } + } else { + // the client was garbage collected or it was shut down, so it no longer needs to check for timeout. throw an exception + // to prevent the scheduled executor from re-scheduling the cleanup task for this instance + throw new RuntimeException("Exiting PollHttpClientTask because BrowserMobHttpClient was garbage collected or shut down"); + } + } + } + + public static void watch(BrowserMobHttpClient client) { + timeoutPollExecutor.scheduleWithFixedDelay(new PollHttpClientTask(client), TIMEOUT_POLL_INTERVAL_MS, TIMEOUT_POLL_INTERVAL_MS, TimeUnit.MILLISECONDS); + } + +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/HttpDeleteWithBody.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/HttpDeleteWithBody.java new file mode 100644 index 000000000..0bc403a76 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/HttpDeleteWithBody.java @@ -0,0 +1,36 @@ +package net.lightbody.bmp.proxy.http; + +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; + +import java.net.URI; + +// Allows for HTTP DELETE requests to contain a body, which the HttpDelete +// class does not support. Please see: +// http://stackoverflow.com/a/3820549/581722 +public class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "DELETE"; + + public HttpDeleteWithBody() { + super(); + } + + public HttpDeleteWithBody(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpDeleteWithBody(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/LegacyHostResolverAdapter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/LegacyHostResolverAdapter.java new file mode 100644 index 000000000..c7adfb35d --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/LegacyHostResolverAdapter.java @@ -0,0 +1,48 @@ +package net.lightbody.bmp.proxy.http; + +import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import org.apache.http.conn.DnsResolver; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * An adapter that allows the legacy {@link net.lightbody.bmp.proxy.http.BrowserMobHttpClient} to use the new + * {@link net.lightbody.bmp.proxy.dns.AdvancedHostResolver} implementations. In addition to implementing the + * {@link org.apache.http.conn.DnsResolver} interface that BrowserMobHttpClient needs, this adapter also populates timing and address + * info in the RequestInfo class. + */ +public class LegacyHostResolverAdapter implements DnsResolver { + private volatile AdvancedHostResolver resolver; + + public LegacyHostResolverAdapter(AdvancedHostResolver resolver) { + this.resolver = resolver; + } + + public void setResolver(AdvancedHostResolver resolver) { + this.resolver = resolver; + } + + public AdvancedHostResolver getResolver() { + return resolver; + } + + @Override + public InetAddress[] resolve(String s) throws UnknownHostException { + long start = System.nanoTime(); + + InetAddress[] addresses = resolver.resolve(s).toArray(new InetAddress[0]); + if (addresses.length == 0) { + throw new UnknownHostException(s); + } + + long end = System.nanoTime(); + + // Associate the the host name with the connection. We do this because when using persistent + // connections there won't be a lookup on the 2nd, 3rd, etc requests, and as such we wouldn't be able to + // know what IP address we were requesting. + RequestInfo.get().dns(start, end, addresses[0].getHostAddress()); + + return addresses; + } +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RepeatableInputStreamRequestEntity.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RepeatableInputStreamRequestEntity.java new file mode 100644 index 000000000..ac49f4249 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RepeatableInputStreamRequestEntity.java @@ -0,0 +1,70 @@ +package net.lightbody.bmp.proxy.http; + +import org.apache.http.entity.ContentType; +import org.apache.http.entity.InputStreamEntity; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class RepeatableInputStreamRequestEntity extends InputStreamEntity { + private final static int BUFFER_SIZE = 2048; + + private BufferedInputStream content; + + public RepeatableInputStreamRequestEntity(InputStream instream, long length, ContentType contentType) { + super(instream, length, contentType); + content = new BufferedInputStream(instream,BUFFER_SIZE); + } + + public RepeatableInputStreamRequestEntity(InputStream instream, long length) { + super(instream, length); + content = new BufferedInputStream(instream,BUFFER_SIZE); + } + + @Override + public boolean isChunked() { + return false; + } + + @Override + public boolean isRepeatable() { + return true; + } + + @Override + public void writeTo(OutputStream outstream) throws IOException { + if (outstream == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + + content.mark((int) this.getContentLength()); + try { + byte[] buffer = new byte[BUFFER_SIZE]; + int l; + if (this.getContentLength() < 0) { + // consume until EOF + while ((l = content.read(buffer)) != -1) { + outstream.write(buffer, 0, l); + } + } else { + // consume no more than length + long remaining = this.getContentLength(); + while (remaining > 0) { + l = content.read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining)); + if (l == -1) { + break; + } + outstream.write(buffer, 0, l); + remaining -= l; + } + + } + } finally { + content.reset(); + } + + + + } +} diff --git a/src/main/java/org/browsermob/proxy/http/RequestCallback.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestCallback.java similarity index 87% rename from src/main/java/org/browsermob/proxy/http/RequestCallback.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestCallback.java index 69b3e31a4..91d59a93d 100644 --- a/src/main/java/org/browsermob/proxy/http/RequestCallback.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestCallback.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.http; +package net.lightbody.bmp.proxy.http; import org.apache.http.Header; import org.apache.http.StatusLine; diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestInfo.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestInfo.java new file mode 100644 index 000000000..32b6dde33 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestInfo.java @@ -0,0 +1,232 @@ +package net.lightbody.bmp.proxy.http; + +import net.lightbody.bmp.core.har.HarEntry; +import net.lightbody.bmp.core.har.HarTimings; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RequestInfo { + private static final Logger LOG = LoggerFactory.getLogger(RequestInfo.class); + + private static ThreadLocal instance = new ThreadLocal() { + @Override + protected RequestInfo initialValue() { + return new RequestInfo(); + } + }; + + public static RequestInfo get() { + return instance.get(); + } + + public static void clear(String url, HarEntry entry) { + clear(); + RequestInfo info = get(); + info.url = url; + info.entry = entry; + } + + private static void clear() { + RequestInfo info = get(); + info.blockedNanos = -1; + info.dnsNanos = -1; + info.connectNanos = -1; + info.sslNanos = -1; + info.sendNanos = 0; + info.waitNanos = 0; + info.receiveNanos = 0; + info.resolvedAddress = null; + info.startDate = null; + info.startNanos = 0; + info.endNanos = 0; + } + + private long blockedNanos; + private long dnsNanos; + private long latencyNanos; + private long connectNanos; + // ssl timing can be populated from the separate ssl handshake notifier thread + private volatile long sslNanos; + private long sendNanos; + private long waitNanos; + private long receiveNanos; + private String resolvedAddress; + private Date startDate; + private long startNanos; + private long endNanos; + private String url; + private HarEntry entry; + + private long ping(long start, long end) { + if (this.startDate == null || this.startNanos == 0) { + LOG.error("Request start time was not set correctly; using current time"); + + if (this.startDate == null) { + this.startDate = new Date(); + } + + if (this.startNanos == 0) { + this.startNanos = System.nanoTime(); + } + } + + return end - start; + } + + public Long getBlocked() { + // return blocked; + // purposely not sending back blocked timings for now until we know it's reliable + return null; + } + + public long getDns() { + return dnsNanos; + } + + public long getConnect() { + return connectNanos; + } + + public long getSsl() { + return sslNanos; + } + + public long getSend() { + return sendNanos; + } + + public long getWait() { + return waitNanos; + } + + public long getReceive() { + return receiveNanos; + } + + public String getResolvedAddress() { + return resolvedAddress; + } + + public void blocked(long start, long end) { + // blocked is special - we don't record this start time as we don't want it to count towards receive time and + // total time + blockedNanos = end - start; + } + + public void dns(long start, long end, String resolvedAddress) { + dnsNanos = ping(start, end); + this.resolvedAddress = resolvedAddress; + } + + public void connect(long start, long end) { + connectNanos = ping(start, end); + } + + public void latency(long start, long end) { + latencyNanos = ping(start, end); + } + + public void ssl(long start, long end) { + sslNanos = ping(start, end); + } + + public void send(long start, long end) { + sendNanos = ping(start, end); + } + + public void wait(long start, long end) { + waitNanos = ping(start, end); + } + + public void start() { + this.startNanos = System.nanoTime(); + this.startDate = new Date(); + } + + public Date getStartDate() { + return this.startDate; + } + + public void finish() { + if (startDate == null) { + startDate = new Date(); + } + + if (startNanos == 0) { + startNanos = System.nanoTime(); + } + + endNanos = System.nanoTime(); + + receiveNanos = endNanos - startNanos - norm(waitNanos) - norm(sendNanos) - norm(sslNanos) - norm(connectNanos) - norm(dnsNanos); + + // as per the Har 1.2 spec (to maintain backwards compatibility with 1.1) the connect time should actually + // include the ssl handshaking time, so doing that here after everything has been calculated + if (norm(sslNanos) > 0L) { + connectNanos += sslNanos; + } + + if (receiveNanos < 0L) { + LOG.error("Got a negative receiving time ({}) for URL {}", receiveNanos, url); + receiveNanos = 0L; + } + } + + private long norm(Long val) { + if (val == null || val == -1) { + return 0; + } else { + return val; + } + } + + public long getTotalTime(TimeUnit timeUnit) { + if (endNanos == 0 || startNanos == 0) { + return -1; + } + + return timeUnit.convert(endNanos - startNanos, TimeUnit.NANOSECONDS); + } + + @Override + public String toString() { + long totalTimeNanos = getTotalTime(TimeUnit.NANOSECONDS); + + return "RequestInfo{" + + "blocked=" + blockedNanos + "ns" + + ", dns=" + dnsNanos + "ns" + + ", connect=" + connectNanos + "ns" + + ", ssl=" + sslNanos + "ns" + + ", send=" + sendNanos + "ns" + + ", wait=" + waitNanos + "ns" + + ", receive=" + receiveNanos + "ns" + + ", total=" + totalTimeNanos + "ns" + + ", resolvedAddress='" + resolvedAddress + '\'' + + '}'; + } + + public HarTimings getTimings() { + HarTimings harTimings = new HarTimings(); + harTimings.setBlocked(blockedNanos, TimeUnit.NANOSECONDS); + harTimings.setDns(dnsNanos, TimeUnit.NANOSECONDS); + harTimings.setConnect(connectNanos, TimeUnit.NANOSECONDS); + harTimings.setSend(sendNanos, TimeUnit.NANOSECONDS); + harTimings.setWait(waitNanos, TimeUnit.NANOSECONDS); + harTimings.setReceive(receiveNanos, TimeUnit.NANOSECONDS); + harTimings.setSsl(sslNanos, TimeUnit.NANOSECONDS); + + return harTimings; + } + + public HarEntry getEntry() { + return entry; + } + + public long getLatency(TimeUnit timeUnit) { + return timeUnit.convert(latencyNanos, TimeUnit.NANOSECONDS); + } +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestInterceptor.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestInterceptor.java new file mode 100644 index 000000000..6dc5767b9 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/RequestInterceptor.java @@ -0,0 +1,7 @@ +package net.lightbody.bmp.proxy.http; + +import net.lightbody.bmp.core.har.Har; + +public interface RequestInterceptor { + void process(BrowserMobHttpRequest request, Har har); +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/ResponseInterceptor.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/ResponseInterceptor.java new file mode 100644 index 000000000..af32a3234 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/ResponseInterceptor.java @@ -0,0 +1,7 @@ +package net.lightbody.bmp.proxy.http; + +import net.lightbody.bmp.core.har.Har; + +public interface ResponseInterceptor { + void process(BrowserMobHttpResponse response, Har har); +} diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/SimulatedSocket.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/SimulatedSocket.java new file mode 100644 index 000000000..57a073f54 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/SimulatedSocket.java @@ -0,0 +1,80 @@ +package net.lightbody.bmp.proxy.http; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketAddress; + +import org.java_bandwidthlimiter.StreamManager; + +/** + * We extends socket in order to get some timings from connection process + * + * we just need to override methods for connect to get some metrics + * and get-in-out streams to provide throttling + */ +public class SimulatedSocket extends Socket { + private final StreamManager streamManager; + + public SimulatedSocket(StreamManager streamManager) { + this.streamManager = streamManager; + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + long start = System.nanoTime(); + super.connect(endpoint); + long end = System.nanoTime(); + // we simulate latency if necessary + simulateLatency(start, end, streamManager); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + long start = System.nanoTime(); + super.connect(endpoint, timeout); + long end = System.nanoTime(); + // we simulate latency if necessary + simulateLatency(start, end, streamManager); + } + + @Override + public InputStream getInputStream() throws IOException { + // whenever this socket is asked for its input stream + // we get it ourselves via socket.getInputStream() + // and register it to the stream manager so it will + // automatically be throttled + return streamManager.registerStream(super.getInputStream()); + } + + @Override + public OutputStream getOutputStream() throws IOException { + // whenever this socket is asked for its output stream + // we get it ourselves via socket.getOutputStream() + // and register it to the stream manager so it will + // automatically be throttled + return streamManager.registerStream(super.getOutputStream()); + } + + private void simulateLatency(long start, long end, StreamManager streamManager) { + // the end before adding latency + long realEnd = end; + long connectReal = end - start; + + // add latency + if(connectReal < streamManager.getLatency()){ + try { + Thread.sleep(streamManager.getLatency()-connectReal); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + // the end after adding latency + end = System.nanoTime(); + } + // set real latency time + RequestInfo.get().latency(start, realEnd); + // set connect time + RequestInfo.get().connect(start, end); + } +} \ No newline at end of file diff --git a/src/main/java/org/browsermob/proxy/http/SimulatedSocketFactory.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/SimulatedSocketFactory.java similarity index 51% rename from src/main/java/org/browsermob/proxy/http/SimulatedSocketFactory.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/SimulatedSocketFactory.java index 13a52fe1c..4b7a0c1b6 100644 --- a/src/main/java/org/browsermob/proxy/http/SimulatedSocketFactory.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/SimulatedSocketFactory.java @@ -1,37 +1,32 @@ -package org.browsermob.proxy.http; - -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.HttpInetSocketAddress; -import org.apache.http.conn.scheme.HostNameResolver; -import org.apache.http.conn.scheme.SchemeSocketFactory; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.browsermob.proxy.util.Log; -import org.java_bandwidthlimiter.StreamManager; +package net.lightbody.bmp.proxy.http; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.InetSocketAddress; import java.net.Socket; -import java.net.SocketAddress; import java.net.SocketTimeoutException; -import java.util.Date; -public class SimulatedSocketFactory implements SchemeSocketFactory { - private static Log LOG = new Log(); +import org.apache.http.HttpHost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.HttpContext; +import org.java_bandwidthlimiter.StreamManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimulatedSocketFactory implements ConnectionSocketFactory { + private static final int DEFAULT_SOCKET_TIMEOUT = 2000; + private static Logger LOG = LoggerFactory.getLogger(SimulatedSocketFactory.class); - private HostNameResolver hostNameResolver; private StreamManager streamManager; - public SimulatedSocketFactory(HostNameResolver hostNameResolver, StreamManager streamManager) { + public SimulatedSocketFactory(StreamManager streamManager) { super(); - assert hostNameResolver != null; assert streamManager != null; - this.hostNameResolver = hostNameResolver; this.streamManager = streamManager; } @@ -56,50 +51,13 @@ public static void configure(T sock) { } catch (Exception e) {} } - @Override - public Socket createSocket(HttpParams httpParams) { - //Ignoring httpParams - //apparently it's only useful to pass through a SOCKS server - //see: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/examples/org/apache/http/examples/client/ClientExecuteSOCKS.java - - //creating an anonymous class deriving from socket - //we just need to override methods for connect to get some metrics - //and get-in-out streams to provide throttling - Socket newSocket = new Socket() { - @Override - public void connect(SocketAddress endpoint) throws IOException { - Date start = new Date(); - super.connect(endpoint); - Date end = new Date(); - RequestInfo.get().connect(start, end); - } - @Override - public void connect(SocketAddress endpoint, int timeout) throws IOException { - Date start = new Date(); - super.connect(endpoint, timeout); - Date end = new Date(); - RequestInfo.get().connect(start, end); - } - @Override - public InputStream getInputStream() throws IOException { - // whenever this socket is asked for its input stream - // we get it ourselves via socket.getInputStream() - // and register it to the stream manager so it will - // automatically be throttled - return streamManager.registerStream(super.getInputStream()); - } - @Override - public OutputStream getOutputStream() throws IOException { - // whenever this socket is asked for its output stream - // we get it ourselves via socket.getOutputStream() - // and register it to the stream manager so it will - // automatically be throttled - return streamManager.registerStream(super.getOutputStream()); - } - }; + + @Override + public Socket createSocket(HttpContext context) throws IOException { + Socket newSocket = new SimulatedSocket(streamManager); SimulatedSocketFactory.configure(newSocket); - return newSocket; - } + return newSocket; + } /** * Prevent unnecessary class inspection at runtime. @@ -121,7 +79,7 @@ public OutputStream getOutputStream() throws IOException { LOG.warn("Using InetSocketAddress.getHostName() rather than InetSocketAddress.getHostString(). Consider upgrading to Java 7 for faster performance!"); } catch (NoSuchMethodException e) { String msg = "Something is wrong inside SimulatedSocketFactory and I don't know why!"; - LOG.severe(msg, e); + LOG.error(msg, e); throw new RuntimeException(msg, e); } } @@ -149,66 +107,35 @@ private String resolveHostName(InetSocketAddress remoteAddress) { } @Override - public Socket connectSocket(Socket sock, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpParams params) throws IOException { - if (remoteAddress == null) { + public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { + if (remoteAddress == null) { throw new IllegalArgumentException("Target host may not be null."); } - if (params == null) { - throw new IllegalArgumentException("Parameters may not be null."); - } - if (sock == null) { - sock = createSocket(null); + sock = createSocket(context); } if ((localAddress != null) ) { sock.bind( localAddress ); } - String hostName; - if (remoteAddress instanceof HttpInetSocketAddress) { - hostName = ((HttpInetSocketAddress) remoteAddress).getHttpHost().getHostName(); - } else { - hostName = resolveHostName(remoteAddress); - } + String hostName = resolveHostName(remoteAddress); InetSocketAddress remoteAddr = remoteAddress; - if (this.hostNameResolver != null) { - remoteAddr = new InetSocketAddress(this.hostNameResolver.resolve(hostName), remoteAddress.getPort()); + if (host != null) { + remoteAddr = new InetSocketAddress(hostName, remoteAddress.getPort()); } - int timeout = HttpConnectionParams.getConnectionTimeout(params); - try { - sock.connect(remoteAddr, timeout); + sock.connect(remoteAddr, connectTimeout); } catch (SocketTimeoutException ex) { throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out"); } - return sock; } - - /** - * Checks whether a socket connection is secure. This factory creates plain socket connections which are not - * considered secure. - * - * @param sock the connected socket - * @return false - * @throws IllegalArgumentException if the argument is invalid - */ - @Override - public final boolean isSecure(Socket sock) - throws IllegalArgumentException { - - if (sock == null) { - throw new IllegalArgumentException("Socket may not be null."); - } - // This check is performed last since it calls a method implemented - // by the argument object. getClass() is final in java.lang.Object. - if (sock.isClosed()) { - throw new IllegalArgumentException("Socket is closed."); - } - return false; + + public Socket connectSocket(Socket sock, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpParams params) throws IOException { + return this.connectSocket(DEFAULT_SOCKET_TIMEOUT, sock, null, remoteAddress, localAddress, new BasicHttpContext()); } } \ No newline at end of file diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java new file mode 100644 index 000000000..3b68802c5 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/TrustingSSLSocketFactory.java @@ -0,0 +1,101 @@ +package net.lightbody.bmp.proxy.http; + +import java.io.IOException; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HandshakeCompletedEvent; +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; + +import net.lightbody.bmp.proxy.util.TrustEverythingSSLTrustManager; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.protocol.HttpContext; +import org.java_bandwidthlimiter.StreamManager; + +public class TrustingSSLSocketFactory extends SSLConnectionSocketFactory { + + public enum SSLAlgorithm { + SSLv3, + TLSv1 + } + + private static SSLContext sslContext; + private StreamManager streamManager; + + static { + sslContext = SSLContexts.createDefault(); + try { + sslContext = SSLContexts.custom().loadTrustMaterial(null, + new TrustStrategy() { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + } + ).build(); + + sslContext.init(null, new TrustManager[]{new TrustEverythingSSLTrustManager()}, null); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { + throw new RuntimeException("Unexpected key management error", e); + } + } + + public TrustingSSLSocketFactory(StreamManager streamManager) { + this(new AllowAllHostnameVerifier(), streamManager); + } + + public TrustingSSLSocketFactory(X509HostnameVerifier hostnameVerifier, StreamManager streamManager) { + super(sslContext, hostnameVerifier); + assert streamManager != null; + this.streamManager = streamManager; + } + + @Override + public Socket createSocket(HttpContext context) throws IOException { + //creating an anonymous class deriving from socket + //we just need to override methods for connect to get some metrics + //and get-in-out streams to provide throttling + Socket newSocket = new SimulatedSocket(streamManager); + SimulatedSocketFactory.configure(newSocket); + return newSocket; + } + + @Override + public Socket createLayeredSocket(final Socket socket, final String target, final int port, final HttpContext context) throws IOException { + SSLSocket sslSocket = (SSLSocket) super.createLayeredSocket(socket, target, port, context); +// sslSocket.setEnabledProtocols(new String[] { SSLAlgorithm.SSLv3.name(), SSLAlgorithm.TLSv1.name() } ); +// sslSocket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" }); + return sslSocket; + } + + @Override + /** + * This function is call just before the handshake + * + * @see http://hc.apache.org/httpcomponents-client-ga/httpclient/xref/org/apache/http/conn/ssl/SSLConnectionSocketFactory.html + */ + protected void prepareSocket (SSLSocket socket) throws IOException { + // save this thread's RequestInfo, since it is stored in a ThreadLocal and the handshake completed event fires in a separate thread + final RequestInfo currentThreadRequestInfo = RequestInfo.get(); + + socket.addHandshakeCompletedListener(new HandshakeCompletedListener() { + private final long handshakeStart = System.nanoTime(); + + @Override + public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) { + currentThreadRequestInfo.ssl(handshakeStart, System.nanoTime()); + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/org/browsermob/proxy/http/WildcardMatchingCredentialsProvider.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/WildcardMatchingCredentialsProvider.java similarity index 96% rename from src/main/java/org/browsermob/proxy/http/WildcardMatchingCredentialsProvider.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/WildcardMatchingCredentialsProvider.java index 4e40f2652..b74b98d30 100644 --- a/src/main/java/org/browsermob/proxy/http/WildcardMatchingCredentialsProvider.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/http/WildcardMatchingCredentialsProvider.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.http; +package net.lightbody.bmp.proxy.http; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Applet.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Applet.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/html/Applet.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Applet.java index 54b09fdbd..e629a3fca 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Applet.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Applet.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; @@ -33,7 +33,7 @@ * page.add(new org.mortbay.Applet("org.mortbay.Foo.App")); * * - * @see org.browsermob.proxy.jetty.html.Block + * @see net.lightbody.bmp.proxy.jetty.html.Block * @version $Id: Applet.java,v 1.7 2004/07/19 13:12:58 hlavac Exp $ * @author Matthew Watson */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Block.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Block.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Block.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Block.java index b2b14bf93..b83d5da7b 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Block.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Block.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; @@ -22,7 +22,7 @@ * Block of predefined or arbitrary type. * Block types are predefined for PRE, BLOCKQUOTE, CENTER, LISTING, * PLAINTEXT, XMP, DIV (Left and Right) and SPAN. - * @see org.browsermob.proxy.jetty.html.Composite + * @see net.lightbody.bmp.proxy.jetty.html.Composite */ public class Block extends Composite { diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Break.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Break.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Break.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Break.java index 100c376d2..ec756de9a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Break.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Break.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ /** Break Tag. diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Comment.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Comment.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/html/Comment.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Comment.java index 986ad62d6..0ccd0c6eb 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Comment.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Comment.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Composite.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Composite.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/html/Composite.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Composite.java index a1e4bb06e..7b461191f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Composite.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Composite.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/CompositeFactory.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/CompositeFactory.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/html/CompositeFactory.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/CompositeFactory.java index 4bb41c852..000ad2c49 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/CompositeFactory.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/CompositeFactory.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* --------------------------------------------------------------------- */ /** Composite Factory. diff --git a/src/main/java/org/browsermob/proxy/jetty/html/DefList.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/DefList.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/DefList.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/DefList.java index 0427514d0..97a68fbb3 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/DefList.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/DefList.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; import java.util.Vector; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Element.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Element.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/html/Element.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Element.java index 38faf7ed5..d75de6f42 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Element.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Element.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.*; import java.util.Enumeration; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Font.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Font.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/html/Font.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Font.java index f2e5f152c..25afecf4f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Font.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Font.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ /** HTML Font Block. * Each Element added to the List (which is a Composite) is treated * as a new List Item. - * @see org.browsermob.proxy.jetty.html.Block + * @see net.lightbody.bmp.proxy.jetty.html.Block */ public class Font extends Block { diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Form.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Form.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/html/Form.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Form.java index 46cabfa2a..1afc9ad33 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Form.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Form.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; -import org.browsermob.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Frame.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Frame.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/html/Frame.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Frame.java index 3c604ee0f..0e7964400 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Frame.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Frame.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/FrameSet.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/FrameSet.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/html/FrameSet.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/FrameSet.java index 2da0ffbd8..26240a45c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/FrameSet.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/FrameSet.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; import java.util.Enumeration; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Heading.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Heading.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Heading.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Heading.java index 05bbc58f0..44083a05e 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Heading.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Heading.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ /** HTML Heading. diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Image.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Image.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/html/Image.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Image.java index 7a70ab3f4..91be303ca 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Image.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Image.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.File; import java.io.FileInputStream; @@ -26,7 +26,7 @@ /* ---------------------------------------------------------------- */ /** HTML Image Tag. - * @see org.browsermob.proxy.jetty.html.Block + * @see net.lightbody.bmp.proxy.jetty.html.Block * @version $Id: Image.java,v 1.8 2005/08/13 00:01:23 gregwilkins Exp $ * @author Greg Wilkins */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Include.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Include.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Include.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Include.java index aadc05376..092f64bb5 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Include.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Include.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; import java.io.*; import java.net.URL; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Input.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Input.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/html/Input.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Input.java index 9ff71dfaa..8289db790 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Input.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Input.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ /** HTML Form Input Tag. diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Link.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Link.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Link.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Link.java index 8f34f917f..7f5af2c4f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Link.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Link.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/List.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/List.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/html/List.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/List.java index 83b8b0e45..78da04a19 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/List.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/List.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ /** HTML List Block. * Each Element added to the List (which is a Composite) is treated * as a new List Item. - * @see org.browsermob.proxy.jetty.html.Block + * @see net.lightbody.bmp.proxy.jetty.html.Block */ public class List extends Block { diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Page.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Page.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/html/Page.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Page.java index a6125c1aa..2e4e3c159 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Page.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Page.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; import java.util.Dictionary; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Script.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Script.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Script.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Script.java index 9586e6bd3..90a73ccd8 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Script.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Script.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Select.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Select.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Select.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Select.java index 37194c0a9..0ab48ef70 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Select.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Select.java @@ -13,16 +13,16 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.util.Enumeration; /* -------------------------------------------------------------------- */ /** HTML select Block. - * @see org.browsermob.proxy.jetty.html.Block + * @see net.lightbody.bmp.proxy.jetty.html.Block */ public class Select extends Block { diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Style.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Style.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/html/Style.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Style.java index 033719c6d..d45877927 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Style.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Style.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/StyleLink.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/StyleLink.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/html/StyleLink.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/StyleLink.java index e7a3cc3c9..04c1b6877 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/StyleLink.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/StyleLink.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Table.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Table.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/html/Table.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Table.java index 2f33363d2..9b0154e33 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Table.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Table.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.util.Hashtable; /* --------------------------------------------------------------------- */ @@ -26,7 +26,7 @@ * Once a row and cell have been created, calling add or attributes on * the table actually calls the cell. * - * @see org.browsermob.proxy.jetty.html.Element + * @see net.lightbody.bmp.proxy.jetty.html.Element */ public class Table extends Block { diff --git a/src/main/java/org/browsermob/proxy/jetty/html/TableForm.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/TableForm.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/html/TableForm.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/TableForm.java index 81622aa5e..7eb08e303 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/TableForm.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/TableForm.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; import java.util.Enumeration; diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Tag.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Tag.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/html/Tag.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Tag.java index 4b7541224..6d93452ac 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Tag.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Tag.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.io.IOException; import java.io.Writer; /* -------------------------------------------------------------------- */ /** HTML Tag Element. * A Tag element is of the generic form <TAG attributes... > - * @see org.browsermob.proxy.jetty.html.Element + * @see net.lightbody.bmp.proxy.jetty.html.Element */ public class Tag extends Element { diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Target.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Target.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Target.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Target.java index 914e9db43..4b26b0ee2 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Target.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Target.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/Text.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Text.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/html/Text.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Text.java index 97e76d205..b55f714ff 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/Text.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/Text.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; import java.util.Vector; /* -------------------------------------------------------------------- */ diff --git a/src/main/java/org/browsermob/proxy/jetty/html/TextArea.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/TextArea.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/html/TextArea.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/TextArea.java index 3a04cc263..3e821f882 100644 --- a/src/main/java/org/browsermob/proxy/jetty/html/TextArea.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/html/TextArea.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.html; +package net.lightbody.bmp.proxy.jetty.html; /* -------------------------------------------------------------------- */ /** A Text Area within a form. *

The text in the textarea is handled by the super class, Text - * @see org.browsermob.proxy.jetty.html.Text + * @see net.lightbody.bmp.proxy.jetty.html.Text */ public class TextArea extends Block { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/Authenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/Authenticator.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/Authenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/Authenticator.java index 41a1ce032..5d26a00df 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/Authenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/Authenticator.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; import java.io.IOException; import java.io.Serializable; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/BasicAuthenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/BasicAuthenticator.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/BasicAuthenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/BasicAuthenticator.java index 33cc5da90..0f898aedb 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/BasicAuthenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/BasicAuthenticator.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.B64Code; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.B64Code; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.StringUtil; import java.io.IOException; import java.security.Principal; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/BufferedOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/BufferedOutputStream.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/BufferedOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/BufferedOutputStream.java index ecd599c66..296d2424a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/BufferedOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/BufferedOutputStream.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.ByteArrayISO8859Writer; -import org.browsermob.proxy.jetty.util.ByteBufferOutputStream; -import org.browsermob.proxy.jetty.util.OutputObserver; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayISO8859Writer; +import net.lightbody.bmp.proxy.jetty.util.ByteBufferOutputStream; +import net.lightbody.bmp.proxy.jetty.util.OutputObserver; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ChunkingInputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ChunkingInputStream.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/http/ChunkingInputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ChunkingInputStream.java index c3e47ad49..f5f965775 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ChunkingInputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ChunkingInputStream.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.IOException; import java.io.InputStream; @@ -180,7 +180,7 @@ private int getChunkSize() _chunkSize=-1; // Get next non blank line - org.browsermob.proxy.jetty.util.LineInput.LineBuffer line_buffer + net.lightbody.bmp.proxy.jetty.util.LineInput.LineBuffer line_buffer =_in.readLineBuffer(); while(line_buffer!=null && line_buffer.size==0) line_buffer=_in.readLineBuffer(); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ChunkingOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ChunkingOutputStream.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/ChunkingOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ChunkingOutputStream.java index 305581da0..10457e00b 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ChunkingOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ChunkingOutputStream.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ClientCertAuthenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ClientCertAuthenticator.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/ClientCertAuthenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ClientCertAuthenticator.java index 613d9f16a..c0c8502ec 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ClientCertAuthenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ClientCertAuthenticator.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import javax.net.ssl.SSLSocket; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ContextLoader.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ContextLoader.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/ContextLoader.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ContextLoader.java index 988e6cb39..0150ba069 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ContextLoader.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ContextLoader.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.Resource; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.Resource; import java.io.File; import java.io.FileOutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/DigestAuthenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/DigestAuthenticator.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/DigestAuthenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/DigestAuthenticator.java index fdfd5c040..34b899282 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/DigestAuthenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/DigestAuthenticator.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import java.io.IOException; import java.security.MessageDigest; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/EOFException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/EOFException.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/EOFException.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/EOFException.java index 59fb11f26..d601316ea 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/EOFException.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/EOFException.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HashSSORealm.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HashSSORealm.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/http/HashSSORealm.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HashSSORealm.java index 44f6fb857..d8b23cbe0 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HashSSORealm.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HashSSORealm.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Credential; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Credential; import javax.servlet.http.Cookie; import java.security.Principal; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HashUserRealm.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HashUserRealm.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/HashUserRealm.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HashUserRealm.java index 2ad980d15..6607018df 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HashUserRealm.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HashUserRealm.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Credential; +import net.lightbody.bmp.proxy.jetty.util.Password; +import net.lightbody.bmp.proxy.jetty.util.Resource; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Credential; -import org.browsermob.proxy.jetty.util.Password; -import org.browsermob.proxy.jetty.util.Resource; import java.io.Externalizable; import java.io.IOException; @@ -61,7 +61,7 @@ public class HashUserRealm /** HttpContext Attribute to set to activate SSO. */ - public static final String __SSO = "org.browsermob.proxy.jetty.http.SSO"; + public static final String __SSO = "net.lightbody.bmp.proxy.jetty.http.SSO"; /* ------------------------------------------------------------ */ private String _realmName; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HostSocketListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HostSocketListener.java similarity index 90% rename from src/main/java/org/browsermob/proxy/jetty/http/HostSocketListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HostSocketListener.java index 739326b5a..fd287add0 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HostSocketListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HostSocketListener.java @@ -13,9 +13,9 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; import java.net.Socket; @@ -57,7 +57,7 @@ public void setForcedHost(String host) } /* - * @see org.browsermob.proxy.jetty.http.SocketListener#customizeRequest(java.net.Socket, org.browsermob.proxy.jetty.http.HttpRequest) + * @see SocketListener#customizeRequest(java.net.Socket, HttpRequest) */ protected void customizeRequest(Socket socket, HttpRequest request) { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpConnection.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpConnection.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpConnection.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpConnection.java index b92d0b8bd..9bae752cb 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpConnection.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpConnection.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.net.ssl.SSLSocket; import java.io.IOException; @@ -58,7 +58,7 @@ public class HttpConnection * only be sent if expected. Can be configured with the org.mortbay.http.HttpConnection.2068Continue system * property. */ - private static boolean __2068_Continues=Boolean.getBoolean("org.browsermob.proxy.jetty.http.HttpConnection.2068Continue"); + private static boolean __2068_Continues=Boolean.getBoolean("HttpConnection.2068Continue"); /* ------------------------------------------------------------ */ protected HttpRequest _request; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpContext.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpContext.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpContext.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpContext.java index 55102a36f..51b978b48 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpContext.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpContext.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.http.ResourceCache.ResourceMetaData; +import net.lightbody.bmp.proxy.jetty.http.handler.ErrorPageHandler; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; +import net.lightbody.bmp.proxy.jetty.util.URI; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.ResourceCache.ResourceMetaData; -import org.browsermob.proxy.jetty.http.handler.ErrorPageHandler; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; -import org.browsermob.proxy.jetty.util.URI; import java.io.File; import java.io.IOException; @@ -54,7 +54,7 @@ * * @see HttpServer * @see HttpHandler - * @see org.browsermob.proxy.jetty.jetty.servlet.ServletHttpContext + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHttpContext * @version $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ @@ -73,10 +73,10 @@ public class HttpContext extends Container * context attribute. */ public final static String __fileClassPathAttr= - "org.browsermob.proxy.jetty.http.HttpContext.FileClassPathAttribute"; + "HttpContext.FileClassPathAttribute"; public final static String __ErrorHandler= - "org.browsermob.proxy.jetty.http.ErrorHandler"; + "net.lightbody.bmp.proxy.jetty.http.ErrorHandler"; /* ------------------------------------------------------------ */ @@ -93,8 +93,8 @@ public class HttpContext extends Container private PermissionCollection _permissions; private boolean _classLoaderJava2Compliant=true; private ResourceCache _resources; - private String[] _systemClasses=new String [] {"java.","javax.servlet.","javax.xml.","org.browsermob.proxy.jetty.","org.xml.","org.w3c.","org.apache.commons.logging."}; - private String[] _serverClasses = new String[] {"-org.browsermob.proxy.jetty.http.PathMap","-org.browsermob.proxy.jetty.jetty.servlet.Invoker","-org.browsermob.proxy.jetty.jetty.servlet.JSR154Filter","-org.browsermob.proxy.jetty.jetty.servlet.Default","org.browsermob.proxy.jetty.jetty.Server","org.browsermob.proxy.jetty.http.","org.browsermob.proxy.jetty.start.","org.browsermob.proxy.jetty.stop."}; + private String[] _systemClasses=new String [] {"java.","javax.servlet.","javax.xml.","net.lightbody.bmp.proxy.jetty.","org.xml.","org.w3c.","org.apache.commons.logging."}; + private String[] _serverClasses = new String[] {"-PathMap","-net.lightbody.bmp.proxy.jetty.jetty.servlet.Invoker","-net.lightbody.bmp.proxy.jetty.jetty.servlet.JSR154Filter","-net.lightbody.bmp.proxy.jetty.jetty.servlet.Default","net.lightbody.bmp.proxy.jetty.jetty.Server","net.lightbody.bmp.proxy.jetty.http.","net.lightbody.bmp.proxy.jetty.start.","net.lightbody.bmp.proxy.jetty.stop."}; /* ------------------------------------------------------------ */ private String _contextName; @@ -103,7 +103,7 @@ public class HttpContext extends Container private UserRealm _userRealm; private String _realmName; private PathMap _constraintMap=new PathMap(); - private Authenticator _authenticator; + private net.lightbody.bmp.proxy.jetty.http.Authenticator _authenticator; private RequestLog _requestLog; @@ -1237,13 +1237,13 @@ public UserRealm getRealm() } /* ------------------------------------------------------------ */ - public Authenticator getAuthenticator() + public net.lightbody.bmp.proxy.jetty.http.Authenticator getAuthenticator() { return _authenticator; } /* ------------------------------------------------------------ */ - public void setAuthenticator(Authenticator authenticator) + public void setAuthenticator(net.lightbody.bmp.proxy.jetty.http.Authenticator authenticator) { _authenticator=authenticator; } @@ -2012,7 +2012,7 @@ private static class Scope } /* - * @see org.browsermob.proxy.jetty.http.HttpHandler#getName() + * @see HttpHandler#getName() */ public String getName() { @@ -2020,7 +2020,7 @@ public String getName() } /* - * @see org.browsermob.proxy.jetty.http.HttpHandler#getHttpContext() + * @see HttpHandler#getHttpContext() */ public HttpContext getHttpContext() { @@ -2028,7 +2028,7 @@ public HttpContext getHttpContext() } /* - * @see org.browsermob.proxy.jetty.http.HttpHandler#initialize(org.browsermob.proxy.jetty.http.HttpContext) + * @see HttpHandler#initialize(HttpContext) */ public void initialize(HttpContext context) { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpException.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpException.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpException.java index 071a41517..aaabb74db 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpException.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpException.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.TypeUtil; +import net.lightbody.bmp.proxy.jetty.util.TypeUtil; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpFields.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpFields.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpFields.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpFields.java index c4f7c5c5d..f5107ef74 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpFields.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpFields.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.http.Cookie; import java.io.IOException; @@ -1101,7 +1101,7 @@ public void read(LineInput in) Field last=null; char[] buf=null; int size=0; - org.browsermob.proxy.jetty.util.LineInput.LineBuffer line_buffer; + net.lightbody.bmp.proxy.jetty.util.LineInput.LineBuffer line_buffer; synchronized(in) { line: diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpHandler.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpHandler.java index a112f34c1..e7ff371ac 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpHandler.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import java.io.IOException; import java.io.Serializable; @@ -31,8 +31,8 @@ *

  • org.mortbay.http.handler.ResourceHandler
  • *
  • org.mortbay.jetty.servlet.ServletHandler
  • * - * @see org.browsermob.proxy.jetty.http.HttpServer - * @see org.browsermob.proxy.jetty.http.HttpContext + * @see net.lightbody.bmp.proxy.jetty.http.HttpServer + * @see net.lightbody.bmp.proxy.jetty.http.HttpContext * @version $Id: HttpHandler.java,v 1.11 2005/03/15 10:03:40 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpInputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpInputStream.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpInputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpInputStream.java index 1ace323a3..46c0b908f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpInputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpInputStream.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.StringUtil; import java.io.*; @@ -35,7 +35,7 @@ * This class is not synchronized and should be synchronized * explicitly if an instance is used by multiple threads. * - * @see org.browsermob.proxy.jetty.util.LineInput + * @see net.lightbody.bmp.proxy.jetty.util.LineInput * @version $Id: HttpInputStream.java,v 1.13 2005/08/23 20:02:26 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpListener.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpListener.java index 89bad4e91..44bb551f1 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpListener.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import java.io.Serializable; import java.net.UnknownHostException; @@ -41,7 +41,7 @@ */ public interface HttpListener extends LifeCycle, Serializable { - public static final String ATTRIBUTE="org.browsermob.proxy.jetty.http.HttpListener"; + public static final String ATTRIBUTE="HttpListener"; /* ------------------------------------------------------------ */ /** Set the HttpServer instance for this HttpListener. diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpMessage.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpMessage.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpMessage.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpMessage.java index ce7f53247..2184185b0 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpMessage.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpMessage.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.QuotedStringTokenizer; +import net.lightbody.bmp.proxy.jetty.util.TypeUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.QuotedStringTokenizer; -import org.browsermob.proxy.jetty.util.TypeUtil; import java.io.*; import java.util.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpOnlyCookie.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpOnlyCookie.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpOnlyCookie.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpOnlyCookie.java index a5192bfe7..1f9ce10ac 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpOnlyCookie.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpOnlyCookie.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; import javax.servlet.http.Cookie; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpOutputStream.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpOutputStream.java index 971fa0d94..fb33afa86 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpOutputStream.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import java.io.*; import java.util.ArrayList; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpRequest.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpRequest.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpRequest.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpRequest.java index 3ba771848..dc439cc69 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpRequest.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpRequest.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.http.Cookie; import java.io.IOException; @@ -57,7 +57,7 @@ public class HttpRequest extends HttpMessage * Set via the org.mortbay.http.HttpRequest.maxContentSize system property. */ public static int __maxFormContentSize = Integer.getInteger( - "org.browsermob.proxy.jetty.http.HttpRequest.maxFormContentSize", 200000).intValue(); + "HttpRequest.maxFormContentSize", 200000).intValue(); /* ------------------------------------------------------------ */ /** @@ -188,7 +188,7 @@ public boolean isHandled() if (_handled) return true; HttpResponse response = getHttpResponse(); - return (response != null && response.getState() != HttpMessage.__MSG_EDITABLE); + return (response != null && response.getState() != __MSG_EDITABLE); } /* ------------------------------------------------------------ */ @@ -214,7 +214,7 @@ public void readHeader(LineInput in) throws IOException _state = __MSG_BAD; // Get start line - org.browsermob.proxy.jetty.util.LineInput.LineBuffer line_buffer; + net.lightbody.bmp.proxy.jetty.util.LineInput.LineBuffer line_buffer; do { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpResponse.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpResponse.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpResponse.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpResponse.java index 1a2687bf7..ef3038e2c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpResponse.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpResponse.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; +import net.lightbody.bmp.proxy.jetty.util.TypeUtil; +import net.lightbody.bmp.proxy.jetty.util.UrlEncoded; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.StringUtil; -import org.browsermob.proxy.jetty.util.TypeUtil; -import org.browsermob.proxy.jetty.util.UrlEncoded; import javax.servlet.http.Cookie; import java.io.IOException; @@ -103,7 +103,7 @@ public class HttpResponse extends HttpMessage // Build error code map using reflection try { - Field[] fields = org.browsermob.proxy.jetty.http.HttpResponse.class + Field[] fields = net.lightbody.bmp.proxy.jetty.http.HttpResponse.class .getDeclaredFields(); for (int f=fields.length; f-->0 ;) { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpServer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpServer.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpServer.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpServer.java index 4283791de..5523e28e7 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpServer.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpServer.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.http.handler.DumpHandler; +import net.lightbody.bmp.proxy.jetty.http.handler.NotFoundHandler; +import net.lightbody.bmp.proxy.jetty.http.handler.ResourceHandler; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.handler.DumpHandler; -import org.browsermob.proxy.jetty.http.handler.NotFoundHandler; -import org.browsermob.proxy.jetty.http.handler.ResourceHandler; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import java.io.IOException; import java.io.ObjectInputStream; @@ -48,7 +48,7 @@ * @see HttpHandler * @see HttpConnection * @see HttpListener - * @see org.browsermob.proxy.jetty.jetty.Server + * @see net.lightbody.bmp.proxy.jetty.jetty.Server * @version $Id: HttpServer.java,v 1.70 2005/12/04 11:43:21 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ @@ -915,7 +915,7 @@ public HttpContext service(HttpRequest request,HttpResponse response) { _notFoundContext .addHandler((NotFoundHandler)Class.forName - ("org.browsermob.proxy.jetty.http.handler.RootNotFoundHandler").newInstance()); + ("RootNotFoundHandler").newInstance()); } catch (Exception e) { @@ -1411,9 +1411,9 @@ public static void main(String[] args) if (args.length==0 || args.length>2) { System.err.println - ("\nUsage - java org.browsermob.proxy.jetty.http.HttpServer [:]"); + ("\nUsage - java HttpServer [:]"); System.err.println - ("\nUsage - java org.browsermob.proxy.jetty.http.HttpServer -r [savefile]"); + ("\nUsage - java HttpServer -r [savefile]"); System.err.println (" Serves files from '.' directory"); System.err.println diff --git a/src/main/java/org/browsermob/proxy/jetty/http/HttpTunnel.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpTunnel.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/HttpTunnel.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpTunnel.java index 34224c618..b78e4b4a9 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/HttpTunnel.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/HttpTunnel.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/InclusiveByteRange.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/InclusiveByteRange.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/http/InclusiveByteRange.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/InclusiveByteRange.java index 3c34f8029..1d2668f2e 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/InclusiveByteRange.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/InclusiveByteRange.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LazyList; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LazyList; -import org.browsermob.proxy.jetty.util.LogSupport; import java.util.Enumeration; import java.util.List; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/JDBCUserRealm.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/JDBCUserRealm.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/JDBCUserRealm.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/JDBCUserRealm.java index b864f5d23..70cc1d1ba 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/JDBCUserRealm.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/JDBCUserRealm.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Loader; +import net.lightbody.bmp.proxy.jetty.util.Resource; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Loader; -import org.browsermob.proxy.jetty.util.Resource; import java.io.IOException; import java.security.Principal; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/JsseListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/JsseListener.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/JsseListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/JsseListener.java index 52aa044fb..aa4c150d6 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/JsseListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/JsseListener.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletSSL; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.ServletSSL; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.LogSupport; import javax.net.ssl.*; import java.io.ByteArrayInputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/MultiPartResponse.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/MultiPartResponse.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/MultiPartResponse.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/MultiPartResponse.java index af0b229c4..bc028b793 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/MultiPartResponse.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/MultiPartResponse.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.StringUtil; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/NCSARequestLog.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/NCSARequestLog.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/NCSARequestLog.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/NCSARequestLog.java index 85b9a4280..d94defe84 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/NCSARequestLog.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/NCSARequestLog.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.DateCache; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.RolloverFileOutputStream; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.DateCache; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.RolloverFileOutputStream; -import org.browsermob.proxy.jetty.util.StringUtil; import javax.servlet.http.Cookie; import java.io.IOException; @@ -75,7 +75,7 @@ public NCSARequestLog() /** Constructor. * @param filename Filename, which can be in * rolloverFileOutputStream format - * @see org.browsermob.proxy.jetty.util.RolloverFileOutputStream + * @see net.lightbody.bmp.proxy.jetty.util.RolloverFileOutputStream * @exception IOException */ public NCSARequestLog(String filename) diff --git a/src/main/java/org/browsermob/proxy/jetty/http/PathMap.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/PathMap.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/PathMap.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/PathMap.java index 469616acf..395623970 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/PathMap.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/PathMap.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LazyList; +import net.lightbody.bmp.proxy.jetty.util.SingletonList; +import net.lightbody.bmp.proxy.jetty.util.StringMap; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LazyList; -import org.browsermob.proxy.jetty.util.SingletonList; -import org.browsermob.proxy.jetty.util.StringMap; import java.io.Externalizable; import java.util.*; @@ -61,7 +61,7 @@ public class PathMap extends HashMap implements Externalizable /* ------------------------------------------------------------ */ private static String __pathSpecSeparators = - System.getProperty("org.browsermob.proxy.jetty.http.PathMap.separators",":,"); + System.getProperty("PathMap.separators",":,"); /* ------------------------------------------------------------ */ /** Set the path spec separator. diff --git a/src/main/java/org/browsermob/proxy/jetty/http/RequestLog.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/RequestLog.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/RequestLog.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/RequestLog.java index 824d72399..6ea58faa4 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/RequestLog.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/RequestLog.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import java.io.Serializable; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ResourceCache.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ResourceCache.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/ResourceCache.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ResourceCache.java index b9ff1078e..cfc7282ce 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ResourceCache.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ResourceCache.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import java.io.IOException; import java.io.Serializable; @@ -40,14 +40,14 @@ public class ResourceCache implements LifeCycle, private final static Map __encodings = new HashMap(); static { - ResourceBundle mime = ResourceBundle.getBundle("org/browsermob/proxy/jetty/http/mime"); + ResourceBundle mime = ResourceBundle.getBundle("net/lightbody/bmp/proxy/jetty/http/mime"); Enumeration i = mime.getKeys(); while(i.hasMoreElements()) { String ext = (String)i.nextElement(); __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),mime.getString(ext)); } - ResourceBundle encoding = ResourceBundle.getBundle("org/browsermob/proxy/jetty/http/encoding"); + ResourceBundle encoding = ResourceBundle.getBundle("net/lightbody/bmp/proxy/jetty/http/encoding"); i = encoding.getKeys(); while(i.hasMoreElements()) { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/SSORealm.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SSORealm.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/http/SSORealm.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SSORealm.java index 51bdc5f00..1333f1299 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/SSORealm.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SSORealm.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; -import org.browsermob.proxy.jetty.util.Credential; +import net.lightbody.bmp.proxy.jetty.util.Credential; import java.security.Principal; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/SecurityConstraint.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SecurityConstraint.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/SecurityConstraint.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SecurityConstraint.java index d4e1499e6..bb7e38603 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/SecurityConstraint.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SecurityConstraint.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.FormAuthenticator; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LazyList; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.FormAuthenticator; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LazyList; import java.io.IOException; import java.io.Serializable; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/SocketListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SocketListener.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/SocketListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SocketListener.java index 33ee0d7fb..e09588f93 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/SocketListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SocketListener.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.ThreadedServer; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.ThreadedServer; import java.io.IOException; import java.net.Socket; @@ -271,7 +271,7 @@ public void customizeRequest(HttpConnection connection, HttpRequest request) { if (_identifyListener) - request.setAttribute(HttpListener.ATTRIBUTE,getName()); + request.setAttribute(ATTRIBUTE,getName()); Socket socket=(Socket)(connection.getConnection()); customizeRequest(socket,request); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/SslListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SslListener.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/http/SslListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SslListener.java index e6dde1808..cc42a01b4 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/SslListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SslListener.java @@ -14,15 +14,15 @@ // ======================================================================== // -package org.browsermob.proxy.jetty.http; - +package net.lightbody.bmp.proxy.jetty.http; + +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletSSL; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.Password; +import net.lightbody.bmp.proxy.jetty.util.Resource; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.ServletSSL; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.Password; -import org.browsermob.proxy.jetty.util.Resource; import javax.net.ssl.*; import java.io.ByteArrayInputStream; @@ -77,7 +77,7 @@ public class SslListener extends SocketListener private boolean _wantClientAuth = false; // Set to true if we would like client certificate authentication. private String _protocol= "TLS"; private String _algorithm = (Security.getProperty("ssl.KeyManagerFactory.algorithm")==null?"SunX509":Security.getProperty("ssl.KeyManagerFactory.algorithm")); // cert algorithm - private String _keystoreType = "JKS"; // type of the key store + private String _keystoreType = "PKCS12"; // type of the key store private String _provider = null; @@ -256,7 +256,7 @@ protected SSLServerSocketFactory createFactory() } KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_algorithm); - KeyStore keyStore = KeyStore.getInstance(_keystoreType); + KeyStore keyStore = KeyStore.getInstance(_keystoreType, "SunJSSE"); keyStore.load(Resource.newResource(_keystore).getInputStream(), _password.toString().toCharArray()); keyManagerFactory.init(keyStore,_keypassword.toString().toCharArray()); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/SunJsseListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/SunJsseListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java index e66109a2b..0c2262bba 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/SunJsseListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/SunJsseListener.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; import com.sun.net.ssl.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.Password; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.Password; import javax.net.ssl.SSLServerSocketFactory; import java.io.File; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/UserRealm.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/UserRealm.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/UserRealm.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/UserRealm.java index 5ccb351c5..366092c68 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/UserRealm.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/UserRealm.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; import java.security.Principal; /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/http/Version.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/Version.java similarity index 87% rename from src/main/java/org/browsermob/proxy/jetty/http/Version.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/Version.java index 178384fa1..73b57bbd7 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/Version.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/Version.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http; +package net.lightbody.bmp.proxy.jetty.http; /* ------------------------------------------------------------ */ @@ -30,7 +30,7 @@ public class Version { private static boolean __paranoid = - Boolean.getBoolean("org.browsermob.proxy.jetty.http.Version.paranoid"); + Boolean.getBoolean("Version.paranoid"); private static String __Version="Jetty/5.1"; private static String __VersionImpl=__Version+".x"; @@ -52,9 +52,9 @@ public class Version public static void main(String[] arg) { System.out.println(__notice); - System.out.println("org.browsermob.proxy.jetty.http.Version="+__Version); - System.out.println("org.browsermob.proxy.jetty.http.VersionImpl="+__VersionImpl); - System.out.println("org.browsermob.proxy.jetty.http.VersionDetail="+__VersionDetail); + System.out.println("Version="+__Version); + System.out.println("net.lightbody.bmp.proxy.jetty.http.VersionImpl="+__VersionImpl); + System.out.println("net.lightbody.bmp.proxy.jetty.http.VersionDetail="+__VersionDetail); } public static void updateVersion() diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Connection.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Connection.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Connection.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Connection.java index a04eea968..8b203dc43 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Connection.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Connection.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.URI; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.URI; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -259,7 +259,7 @@ public boolean handleNext() request.setAttribute("javax.servlet.request.X509Certificate",certs); break; case 6: // JVM Route - request.setAttribute("org.browsermob.proxy.jetty.http.ajp.JVMRoute",value); + request.setAttribute("net.lightbody.bmp.proxy.jetty.http.ajp.JVMRoute",value); break; case 5: // Query String request.setQuery(value); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13InputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13InputStream.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13InputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13InputStream.java index c22019327..32bb877b5 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13InputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13InputStream.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Listener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Listener.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Listener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Listener.java index 86053419e..4c6118136 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Listener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Listener.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.ThreadedServer; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.ThreadedServer; import java.io.IOException; import java.net.InetAddress; @@ -236,7 +236,7 @@ protected AJP13Connection createConnection(Socket socket) throws IOException public void customizeRequest(HttpConnection connection, HttpRequest request) { if (_identifyListener) - request.setAttribute(HttpListener.ATTRIBUTE,getName()); + request.setAttribute(ATTRIBUTE,getName()); Socket socket=(Socket)(connection.getConnection()); customizeRequest(socket,request); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13OutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13OutputStream.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13OutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13OutputStream.java index e9036568a..20dd2f291 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13OutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13OutputStream.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; +import net.lightbody.bmp.proxy.jetty.http.BufferedOutputStream; +import net.lightbody.bmp.proxy.jetty.http.HttpMessage; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.BufferedOutputStream; -import org.browsermob.proxy.jetty.http.HttpMessage; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Packet.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Packet.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Packet.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Packet.java index 8900aea27..d04313d92 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13Packet.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13Packet.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayISO8859Writer; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayPool; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.ByteArrayISO8859Writer; -import org.browsermob.proxy.jetty.util.ByteArrayPool; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.StringUtil; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13RequestPacket.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13RequestPacket.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13RequestPacket.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13RequestPacket.java index 70f9f6231..229f1edda 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13RequestPacket.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13RequestPacket.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; /** * AJP13RequestPacket used by AJP13InputStream diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13ResponsePacket.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13ResponsePacket.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13ResponsePacket.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13ResponsePacket.java index 154eed041..20851573f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/AJP13ResponsePacket.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/AJP13ResponsePacket.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp; +package net.lightbody.bmp.proxy.jetty.http.ajp; /** * AJP13ResponsePacket used by AJP13OutputStream diff --git a/src/main/java/org/browsermob/proxy/jetty/http/ajp/jmx/AJP13ListenerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/jmx/AJP13ListenerMBean.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/ajp/jmx/AJP13ListenerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/jmx/AJP13ListenerMBean.java index f5ee00d9f..7ac66d052 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/ajp/jmx/AJP13ListenerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/ajp/jmx/AJP13ListenerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.ajp.jmx; +package net.lightbody.bmp.proxy.jetty.http.ajp.jmx; -import org.browsermob.proxy.jetty.http.jmx.HttpListenerMBean; +import net.lightbody.bmp.proxy.jetty.http.jmx.HttpListenerMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/AbstractHttpHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/AbstractHttpHandler.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/AbstractHttpHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/AbstractHttpHandler.java index e21941531..346cc82b3 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/AbstractHttpHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/AbstractHttpHandler.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayISO8859Writer; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.ByteArrayISO8859Writer; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/DumpHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/DumpHandler.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/DumpHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/DumpHandler.java index 63cf63198..6ca24b49c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/DumpHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/DumpHandler.java @@ -13,16 +13,16 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.StringUtil; import javax.servlet.http.Cookie; import java.io.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/ErrorPageHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ErrorPageHandler.java similarity index 89% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/ErrorPageHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ErrorPageHandler.java index 465893449..c603a95aa 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/ErrorPageHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ErrorPageHandler.java @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.util.ByteArrayISO8859Writer; -import org.browsermob.proxy.jetty.util.StringUtil; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayISO8859Writer; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/ExpiryHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ExpiryHandler.java similarity index 89% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/ExpiryHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ExpiryHandler.java index f6c8d8299..034e110f4 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/ExpiryHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ExpiryHandler.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/ForwardHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ForwardHandler.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/ForwardHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ForwardHandler.java index da9740026..0a869661d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/ForwardHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ForwardHandler.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.URI; +import net.lightbody.bmp.proxy.jetty.util.UrlEncoded; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.URI; -import org.browsermob.proxy.jetty.util.UrlEncoded; import java.io.IOException; import java.util.Map; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/HTAccessHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/HTAccessHandler.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/HTAccessHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/HTAccessHandler.java index ee748a811..5dc31b0d3 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/HTAccessHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/HTAccessHandler.java @@ -7,12 +7,12 @@ // all copies. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import java.io.BufferedReader; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/IPAccessHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/IPAccessHandler.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/IPAccessHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/IPAccessHandler.java index ad892dac4..1b3a0fff6 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/IPAccessHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/IPAccessHandler.java @@ -7,11 +7,11 @@ // all copies. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; import java.io.IOException; import java.util.Hashtable; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/MsieSslHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/MsieSslHandler.java similarity index 82% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/MsieSslHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/MsieSslHandler.java index 4387f3a1d..ef4e51d07 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/MsieSslHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/MsieSslHandler.java @@ -14,14 +14,14 @@ // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; @@ -40,7 +40,7 @@ public class MsieSslHandler extends AbstractHttpHandler private String userAgentSubString="MSIE 5"; /* - * @see org.browsermob.proxy.jetty.http.HttpHandler#handle(java.lang.String, java.lang.String, org.browsermob.proxy.jetty.http.HttpRequest, org.browsermob.proxy.jetty.http.HttpResponse) + * @see HttpHandler#handle(java.lang.String, java.lang.String, HttpRequest, HttpResponse) */ public void handle( String pathInContext, diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/NotFoundHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/NotFoundHandler.java similarity index 90% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/NotFoundHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/NotFoundHandler.java index 1deda04f2..11c268f90 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/NotFoundHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/NotFoundHandler.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/NullHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/NullHandler.java similarity index 77% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/NullHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/NullHandler.java index 180a40a1f..ca704133c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/NullHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/NullHandler.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; import java.io.IOException; @@ -31,7 +31,7 @@ public class NullHandler extends AbstractHttpHandler { /* - * @see org.browsermob.proxy.jetty.http.HttpHandler#handle(java.lang.String, java.lang.String, org.browsermob.proxy.jetty.http.HttpRequest, org.browsermob.proxy.jetty.http.HttpResponse) + * @see HttpHandler#handle(java.lang.String, java.lang.String, HttpRequest, HttpResponse) */ public void handle(String pathInContext, String pathParams, HttpRequest request, HttpResponse response) throws HttpException, IOException { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/ProxyHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ProxyHandler.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/ProxyHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ProxyHandler.java index 14ccec883..924e8c216 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/ProxyHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ProxyHandler.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; +import net.lightbody.bmp.proxy.jetty.util.URI; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; -import org.browsermob.proxy.jetty.util.URI; import java.io.IOException; import java.io.InputStream; @@ -462,7 +462,7 @@ protected HttpTunnel newHttpTunnel(HttpRequest request, HttpResponse response, I if (log.isDebugEnabled()) log.debug("chain proxy socket="+chain_socket); LineInput line_in = new LineInput(chain_socket.getInputStream()); - byte[] connect= request.toString().getBytes(org.browsermob.proxy.jetty.util.StringUtil.__ISO_8859_1); + byte[] connect= request.toString().getBytes(net.lightbody.bmp.proxy.jetty.util.StringUtil.__ISO_8859_1); chain_socket.getOutputStream().write(connect); String chain_response_line = line_in.readLine(); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/ResourceHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ResourceHandler.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/ResourceHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ResourceHandler.java index 37e626fc8..842d8948d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/ResourceHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/ResourceHandler.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/RootNotFoundHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/RootNotFoundHandler.java similarity index 92% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/RootNotFoundHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/RootNotFoundHandler.java index 5dd586524..ef74fd095 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/RootNotFoundHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/RootNotFoundHandler.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayISO8859Writer; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.ByteArrayISO8859Writer; -import org.browsermob.proxy.jetty.util.StringUtil; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/SecurityHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/SecurityHandler.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/SecurityHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/SecurityHandler.java index 5c1ef8806..7c46ae656 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/SecurityHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/SecurityHandler.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/SetResponseHeadersHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/SetResponseHeadersHandler.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/SetResponseHeadersHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/SetResponseHeadersHandler.java index db91982ec..12f1e280f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/SetResponseHeadersHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/SetResponseHeadersHandler.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler; +package net.lightbody.bmp.proxy.jetty.http.handler; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; import java.util.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/handler/jmx/ResourceHandlerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/jmx/ResourceHandlerMBean.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/handler/jmx/ResourceHandlerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/jmx/ResourceHandlerMBean.java index 8d37b7f29..1eb8e46d1 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/handler/jmx/ResourceHandlerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/handler/jmx/ResourceHandlerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.handler.jmx; +package net.lightbody.bmp.proxy.jetty.http.handler.jmx; -import org.browsermob.proxy.jetty.http.jmx.HttpHandlerMBean; +import net.lightbody.bmp.proxy.jetty.http.jmx.HttpHandlerMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpContextMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpContextMBean.java similarity index 69% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpContextMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpContextMBean.java index 5add8e972..cb8e15322 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpContextMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpContextMBean.java @@ -13,15 +13,16 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; - +package net.lightbody.bmp.proxy.jetty.http.jmx; + +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LifeCycleEvent; +import net.lightbody.bmp.proxy.jetty.util.LifeCycleListener; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.jmx.LifeCycleMBean; +import net.lightbody.bmp.proxy.jetty.util.jmx.ModelMBeanImpl; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LifeCycleEvent; -import org.browsermob.proxy.jetty.util.LifeCycleListener; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.jmx.LifeCycleMBean; import javax.management.MBeanException; import javax.management.MBeanServer; @@ -60,8 +61,8 @@ protected void defineManagedResource() defineAttribute("hosts"); defineAttribute("contextPath"); - defineAttribute("handlers",READ_ONLY,ON_MBEAN); - defineAttribute("requestLog",READ_ONLY,ON_MBEAN); + defineAttribute("handlers", ModelMBeanImpl.READ_ONLY, ModelMBeanImpl.ON_MBEAN); + defineAttribute("requestLog", ModelMBeanImpl.READ_ONLY, ModelMBeanImpl.ON_MBEAN); defineAttribute("classPath"); @@ -73,26 +74,26 @@ protected void defineManagedResource() defineAttribute("maxCachedFileSize"); defineAttribute("maxCacheSize"); defineOperation("flushCache", - IMPACT_ACTION); + ModelMBeanImpl.IMPACT_ACTION); defineOperation("getResource", - new String[] {STRING}, - IMPACT_ACTION); + new String[] {ModelMBeanImpl.STRING}, + ModelMBeanImpl.IMPACT_ACTION); defineAttribute("welcomeFiles"); defineOperation("addWelcomeFile", - new String[] {STRING}, - IMPACT_INFO); + new String[] {ModelMBeanImpl.STRING}, + ModelMBeanImpl.IMPACT_INFO); defineOperation("removeWelcomeFile", - new String[] {STRING}, - IMPACT_INFO); + new String[] {ModelMBeanImpl.STRING}, + ModelMBeanImpl.IMPACT_INFO); defineAttribute("mimeMap"); - defineOperation("setMimeMapping",new String[] {STRING,STRING},IMPACT_ACTION); + defineOperation("setMimeMapping",new String[] {ModelMBeanImpl.STRING, ModelMBeanImpl.STRING}, ModelMBeanImpl.IMPACT_ACTION); defineAttribute("statsOn"); defineAttribute("statsOnMs"); - defineOperation("statsReset",IMPACT_ACTION); + defineOperation("statsReset", ModelMBeanImpl.IMPACT_ACTION); defineAttribute("requests"); defineAttribute("requestsActive"); defineAttribute("requestsActiveMax"); @@ -102,29 +103,29 @@ protected void defineManagedResource() defineAttribute("responses4xx"); defineAttribute("responses5xx"); - defineOperation("stop",new String[] {"java.lang.Boolean.TYPE"},IMPACT_ACTION); + defineOperation("stop",new String[] {"java.lang.Boolean.TYPE"}, ModelMBeanImpl.IMPACT_ACTION); defineOperation("destroy", - IMPACT_ACTION); + ModelMBeanImpl.IMPACT_ACTION); defineOperation("setInitParameter", - new String[] {STRING,STRING}, - IMPACT_ACTION); + new String[] {ModelMBeanImpl.STRING, ModelMBeanImpl.STRING}, + ModelMBeanImpl.IMPACT_ACTION); defineOperation("getInitParameter", - new String[] {STRING}, - IMPACT_INFO); + new String[] {ModelMBeanImpl.STRING}, + ModelMBeanImpl.IMPACT_INFO); defineOperation("getInitParameterNames", - NO_PARAMS, - IMPACT_INFO); + ModelMBeanImpl.NO_PARAMS, + ModelMBeanImpl.IMPACT_INFO); - defineOperation("setAttribute",new String[] {STRING,OBJECT},IMPACT_ACTION); - defineOperation("getAttribute",new String[] {STRING},IMPACT_INFO); - defineOperation("getAttributeNames",NO_PARAMS,IMPACT_INFO); - defineOperation("removeAttribute",new String[] {STRING},IMPACT_ACTION); + defineOperation("setAttribute",new String[] {ModelMBeanImpl.STRING, ModelMBeanImpl.OBJECT}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("getAttribute",new String[] {ModelMBeanImpl.STRING}, ModelMBeanImpl.IMPACT_INFO); + defineOperation("getAttributeNames", ModelMBeanImpl.NO_PARAMS, ModelMBeanImpl.IMPACT_INFO); + defineOperation("removeAttribute",new String[] {ModelMBeanImpl.STRING}, ModelMBeanImpl.IMPACT_ACTION); - defineOperation("addHandler",new String[] {"org.browsermob.proxy.jetty.http.HttpHandler"},IMPACT_ACTION); - defineOperation("addHandler",new String[] {INT,"org.browsermob.proxy.jetty.http.HttpHandler"},IMPACT_ACTION); - defineOperation("removeHandler",new String[] {INT},IMPACT_ACTION); + defineOperation("addHandler",new String[] {"HttpHandler"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("addHandler",new String[] {ModelMBeanImpl.INT,"HttpHandler"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("removeHandler",new String[] {ModelMBeanImpl.INT}, ModelMBeanImpl.IMPACT_ACTION); _httpContext=(HttpContext)getManagedResource(); diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpHandlerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpHandlerMBean.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpHandlerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpHandlerMBean.java index 9f8379c84..d5cb33041 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpHandlerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpHandlerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; -import org.browsermob.proxy.jetty.util.jmx.LifeCycleMBean; +import net.lightbody.bmp.proxy.jetty.util.jmx.LifeCycleMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpListenerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpListenerMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpListenerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpListenerMBean.java index 25bdb19bf..351287d08 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpListenerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpListenerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; -import org.browsermob.proxy.jetty.util.jmx.ThreadedServerMBean; +import net.lightbody.bmp.proxy.jetty.util.jmx.ThreadedServerMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpServerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpServerMBean.java similarity index 82% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpServerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpServerMBean.java index e04dce6b2..6b2f1ffc1 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/HttpServerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/HttpServerMBean.java @@ -13,17 +13,17 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; +import net.lightbody.bmp.proxy.jetty.http.HttpServer; +import net.lightbody.bmp.proxy.jetty.http.Version; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.ComponentEvent; +import net.lightbody.bmp.proxy.jetty.util.ComponentListener; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.jmx.LifeCycleMBean; +import net.lightbody.bmp.proxy.jetty.util.jmx.ModelMBeanImpl; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpServer; -import org.browsermob.proxy.jetty.http.Version; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.ComponentEvent; -import org.browsermob.proxy.jetty.util.ComponentListener; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.jmx.LifeCycleMBean; -import org.browsermob.proxy.jetty.util.jmx.ModelMBeanImpl; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; @@ -89,29 +89,29 @@ protected void defineManagedResource() { super.defineManagedResource(); - defineAttribute("listeners",READ_ONLY); - defineAttribute("contexts",READ_ONLY); - defineAttribute("version",READ_ONLY,ON_MBEAN); - defineAttribute("components",READ_ONLY,ON_MBEAN); + defineAttribute("listeners", ModelMBeanImpl.READ_ONLY); + defineAttribute("contexts", ModelMBeanImpl.READ_ONLY); + defineAttribute("version", ModelMBeanImpl.READ_ONLY, ModelMBeanImpl.ON_MBEAN); + defineAttribute("components", ModelMBeanImpl.READ_ONLY, ModelMBeanImpl.ON_MBEAN); defineAttribute("requestLog"); defineAttribute("trace"); - defineOperation("addListener",new String[]{"java.lang.String"},IMPACT_ACTION); - defineOperation("addListener",new String[]{"org.browsermob.proxy.jetty.util.InetAddrPort"},IMPACT_ACTION); - defineOperation("addListener",new String[]{"org.browsermob.proxy.jetty.http.HttpListener"},IMPACT_ACTION); - defineOperation("removeListener",new String[]{"org.browsermob.proxy.jetty.http.HttpListener"},IMPACT_ACTION); - defineOperation("addContext",new String[]{"org.browsermob.proxy.jetty.http.HttpContext"},IMPACT_ACTION); - defineOperation("removeContext",new String[]{"org.browsermob.proxy.jetty.http.HttpContext"},IMPACT_ACTION); - defineOperation("addContext",new String[]{"java.lang.String"},IMPACT_ACTION); - defineOperation("addContext",new String[]{"java.lang.String","java.lang.String"},IMPACT_ACTION); + defineOperation("addListener",new String[]{"java.lang.String"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("addListener",new String[]{"net.lightbody.bmp.proxy.jetty.util.InetAddrPort"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("addListener",new String[]{"HttpListener"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("removeListener",new String[]{"HttpListener"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("addContext",new String[]{"HttpContext"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("removeContext",new String[]{"HttpContext"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("addContext",new String[]{"java.lang.String"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("addContext",new String[]{"java.lang.String","java.lang.String"}, ModelMBeanImpl.IMPACT_ACTION); defineAttribute("requestsPerGC"); defineAttribute("statsOn"); defineAttribute("statsOnMs"); - defineOperation("statsReset",IMPACT_ACTION); + defineOperation("statsReset", ModelMBeanImpl.IMPACT_ACTION); defineAttribute("connections"); defineAttribute("connectionsOpen"); defineAttribute("connectionsOpenMin"); @@ -132,9 +132,9 @@ protected void defineManagedResource() defineAttribute("requestsDurationMin"); defineAttribute("requestsDurationMax"); - defineOperation("stop",new String[]{"java.lang.Boolean.TYPE"},IMPACT_ACTION); - defineOperation("save",new String[]{"java.lang.String"},IMPACT_ACTION); - defineOperation("destroy",IMPACT_ACTION); + defineOperation("stop",new String[]{"java.lang.Boolean.TYPE"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("save",new String[]{"java.lang.String"}, ModelMBeanImpl.IMPACT_ACTION); + defineOperation("destroy", ModelMBeanImpl.IMPACT_ACTION); } /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/JsseListenerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/JsseListenerMBean.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/JsseListenerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/JsseListenerMBean.java index 3c923e765..bf6aee89e 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/JsseListenerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/JsseListenerMBean.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/NCSARequestLogMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/NCSARequestLogMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/NCSARequestLogMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/NCSARequestLogMBean.java index 1784012c9..5b728a9df 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/NCSARequestLogMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/NCSARequestLogMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; -import org.browsermob.proxy.jetty.util.jmx.LifeCycleMBean; +import net.lightbody.bmp.proxy.jetty.util.jmx.LifeCycleMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/SocketChannelListenerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SocketChannelListenerMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/SocketChannelListenerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SocketChannelListenerMBean.java index 0d9b8d9d1..df6612468 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/SocketChannelListenerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SocketChannelListenerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; -import org.browsermob.proxy.jetty.util.jmx.ThreadPoolMBean; +import net.lightbody.bmp.proxy.jetty.util.jmx.ThreadPoolMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/SocketListenerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SocketListenerMBean.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/SocketListenerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SocketListenerMBean.java index 67336e73c..bb440d9d1 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/SocketListenerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SocketListenerMBean.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/jmx/SunJsseListenerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SunJsseListenerMBean.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/jmx/SunJsseListenerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SunJsseListenerMBean.java index 247978ec4..0788029af 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/jmx/SunJsseListenerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/jmx/SunJsseListenerMBean.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.jmx; +package net.lightbody.bmp.proxy.jetty.http.jmx; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/nio/ByteBufferInputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/ByteBufferInputStream.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/http/nio/ByteBufferInputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/ByteBufferInputStream.java index db3b50fab..34ad6c87c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/nio/ByteBufferInputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/ByteBufferInputStream.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.nio; +package net.lightbody.bmp.proxy.jetty.http.nio; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LazyList; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LazyList; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/http/nio/SocketChannelListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/SocketChannelListener.java similarity index 90% rename from src/main/java/org/browsermob/proxy/jetty/http/nio/SocketChannelListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/SocketChannelListener.java index 31e6d57f1..cab008a3c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/nio/SocketChannelListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/SocketChannelListener.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.nio; +package net.lightbody.bmp.proxy.jetty.http.nio; +import net.lightbody.bmp.proxy.jetty.http.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.ThreadPool; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.ThreadPool; import java.io.IOException; import java.net.InetSocketAddress; @@ -71,7 +71,7 @@ public SocketChannelListener() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#setHttpServer(org.browsermob.proxy.jetty.http.HttpServer) + * @see HttpListener#setHttpServer(HttpServer) */ public void setHttpServer(HttpServer server) { @@ -80,7 +80,7 @@ public void setHttpServer(HttpServer server) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getHttpServer() + * @see HttpListener#getHttpServer() */ public HttpServer getHttpServer() { @@ -89,7 +89,7 @@ public HttpServer getHttpServer() /* ------------------------------------------------------------------------------- */ /** - * @see org.browsermob.proxy.jetty.http.HttpListener#setHost(java.lang.String) + * @see net.lightbody.bmp.proxy.jetty.http.HttpListener#setHost(java.lang.String) */ public void setHost(String host) throws UnknownHostException { @@ -98,7 +98,7 @@ public void setHost(String host) throws UnknownHostException /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getHost() + * @see HttpListener#getHost() */ public String getHost() { @@ -109,7 +109,7 @@ public String getHost() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#setPort(int) + * @see HttpListener#setPort(int) */ public void setPort(int port) { @@ -121,7 +121,7 @@ public void setPort(int port) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getPort() + * @see HttpListener#getPort() */ public int getPort() { @@ -138,7 +138,7 @@ public void setBufferSize(int size) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getBufferSize() + * @see HttpListener#getBufferSize() */ public int getBufferSize() { @@ -153,7 +153,7 @@ public void setBufferReserve(int size) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getBufferReserve() + * @see HttpListener#getBufferReserve() */ public int getBufferReserve() { @@ -162,7 +162,7 @@ public int getBufferReserve() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getDefaultScheme() + * @see HttpListener#getDefaultScheme() */ public String getDefaultScheme() { @@ -171,7 +171,7 @@ public String getDefaultScheme() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#customizeRequest(org.browsermob.proxy.jetty.http.HttpConnection, org.browsermob.proxy.jetty.http.HttpRequest) + * @see HttpListener#customizeRequest(HttpConnection, HttpRequest) */ public void customizeRequest(HttpConnection connection, HttpRequest request) { @@ -180,7 +180,7 @@ public void customizeRequest(HttpConnection connection, HttpRequest request) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#persistConnection(org.browsermob.proxy.jetty.http.HttpConnection) + * @see HttpListener#persistConnection(HttpConnection) */ public void persistConnection(HttpConnection connection) { @@ -189,7 +189,7 @@ public void persistConnection(HttpConnection connection) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#isLowOnResources() + * @see HttpListener#isLowOnResources() */ public boolean isLowOnResources() { @@ -218,7 +218,7 @@ else if (!low && _isLow) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#isOutOfResources() + * @see HttpListener#isOutOfResources() */ public boolean isOutOfResources() { @@ -257,7 +257,7 @@ public void setSslPort(int p) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#isIntegral(org.browsermob.proxy.jetty.http.HttpConnection) + * @see HttpListener#isIntegral(HttpConnection) */ public boolean isIntegral(HttpConnection connection) { @@ -266,7 +266,7 @@ public boolean isIntegral(HttpConnection connection) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getIntegralScheme() + * @see HttpListener#getIntegralScheme() */ public String getIntegralScheme() { @@ -275,7 +275,7 @@ public String getIntegralScheme() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getIntegralPort() + * @see HttpListener#getIntegralPort() */ public int getIntegralPort() { @@ -284,7 +284,7 @@ public int getIntegralPort() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#isConfidential(org.browsermob.proxy.jetty.http.HttpConnection) + * @see HttpListener#isConfidential(HttpConnection) */ public boolean isConfidential(HttpConnection connection) { @@ -293,7 +293,7 @@ public boolean isConfidential(HttpConnection connection) /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getConfidentialScheme() + * @see HttpListener#getConfidentialScheme() */ public String getConfidentialScheme() { @@ -302,7 +302,7 @@ public String getConfidentialScheme() /* ------------------------------------------------------------------------------- */ /* - * @see org.browsermob.proxy.jetty.http.HttpListener#getConfidentialPort() + * @see HttpListener#getConfidentialPort() */ public int getConfidentialPort() { @@ -335,7 +335,7 @@ public void setHttpHandler(HttpHandler handler) /* ------------------------------------------------------------------------------- */ /** - * @see org.browsermob.proxy.jetty.http.HttpListener#getHttpHandler() + * @see net.lightbody.bmp.proxy.jetty.http.HttpListener#getHttpHandler() */ public HttpHandler getHttpHandler() { diff --git a/src/main/java/org/browsermob/proxy/jetty/http/nio/SocketChannelOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/SocketChannelOutputStream.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/http/nio/SocketChannelOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/SocketChannelOutputStream.java index bea4baa95..b0d46227b 100644 --- a/src/main/java/org/browsermob/proxy/jetty/http/nio/SocketChannelOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/http/nio/SocketChannelOutputStream.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.http.nio; +package net.lightbody.bmp.proxy.jetty.http.nio; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/Server.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/jetty/Server.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java index 8e20bf948..6b04eafb6 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/Server.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/Server.java @@ -13,17 +13,17 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty; +package net.lightbody.bmp.proxy.jetty.jetty; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpServer; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHttpContext; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.Resource; +import net.lightbody.bmp.proxy.jetty.xml.XmlConfiguration; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpServer; -import org.browsermob.proxy.jetty.jetty.servlet.ServletHttpContext; -import org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.Resource; -import org.browsermob.proxy.jetty.xml.XmlConfiguration; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -51,8 +51,8 @@ * property JETTY_NO_SHUTDOWN_HOOK is not set to true, then a shutdown * hook is thread is registered to stop these servers. * - * @see org.browsermob.proxy.jetty.xml.XmlConfiguration - * @see org.browsermob.proxy.jetty.jetty.servlet.ServletHttpContext + * @see net.lightbody.bmp.proxy.jetty.xml.XmlConfiguration + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHttpContext * @version $Revision: 1.40 $ * @author Greg Wilkins (gregw) */ @@ -60,7 +60,7 @@ public class Server extends HttpServer { static Log log = LogFactory.getLog(Server.class); private String[] _webAppConfigurationClassNames = - new String[]{"org.browsermob.proxy.jetty.jetty.servlet.XMLConfiguration", "org.browsermob.proxy.jetty.jetty.servlet.JettyWebConfiguration"}; + new String[]{"XMLConfiguration", "JettyWebConfiguration"}; private String _configuration; private String _rootWebApp; private static ShutdownHookThread hookThread = new ShutdownHookThread(); diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/jmx/ServerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/jmx/ServerMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/jetty/jmx/ServerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/jmx/ServerMBean.java index 319228837..f02846e32 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/jmx/ServerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/jmx/ServerMBean.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.jmx; +import net.lightbody.bmp.proxy.jetty.http.jmx.HttpServerMBean; +import net.lightbody.bmp.proxy.jetty.jetty.Server; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.jmx.HttpServerMBean; -import org.browsermob.proxy.jetty.jetty.Server; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/AbstractSessionManager.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/AbstractSessionManager.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/AbstractSessionManager.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/AbstractSessionManager.java index 56cdc0276..9532f2019 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/AbstractSessionManager.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/AbstractSessionManager.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpOnlyCookie; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LazyList; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.MultiMap; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpOnlyCookie; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LazyList; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.MultiMap; import javax.servlet.ServletContext; import javax.servlet.http.*; @@ -51,7 +51,7 @@ public abstract class AbstractSessionManager implements SessionManager /* ------------------------------------------------------------ */ public final static int __distantFuture = 60*60*24*7*52*20; - private final static String __NEW_SESSION_ID="org.browsermob.proxy.jetty.jetty.newSessionId"; + private final static String __NEW_SESSION_ID="net.lightbody.bmp.proxy.jetty.jetty.newSessionId"; /* ------------------------------------------------------------ */ /* global Map of ID to session */ @@ -217,7 +217,7 @@ private String newSessionId(HttpServletRequest request,long created) r=-r; id=Long.toString(r,36); - String worker = (String)request.getAttribute("org.browsermob.proxy.jetty.http.ajp.JVMRoute"); + String worker = (String)request.getAttribute("net.lightbody.bmp.proxy.jetty.http.ajp.JVMRoute"); if (worker!=null) id+="."+worker; else if (_workerName!=null) diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/BasicAuthenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/BasicAuthenticator.java similarity index 77% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/BasicAuthenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/BasicAuthenticator.java index 7b2f9fd6b..1e8d75fed 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/BasicAuthenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/BasicAuthenticator.java @@ -13,11 +13,11 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.http.UserRealm; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.UserRealm; import java.io.IOException; @@ -26,12 +26,12 @@ * @author gregw * */ -public class BasicAuthenticator extends org.browsermob.proxy.jetty.http.BasicAuthenticator +public class BasicAuthenticator extends net.lightbody.bmp.proxy.jetty.http.BasicAuthenticator { /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.http.BasicAuthenticator#sendChallenge(org.browsermob.proxy.jetty.http.UserRealm, org.browsermob.proxy.jetty.http.HttpResponse) + * @see BasicAuthenticator#sendChallenge(UserRealm, HttpResponse) */ public void sendChallenge(UserRealm realm, HttpResponse response) throws IOException { diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Default.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Default.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Default.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Default.java index 63da9ae8f..420fe947a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Default.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Default.java @@ -13,12 +13,18 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; - +package net.lightbody.bmp.proxy.jetty.jetty.servlet; + +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.InclusiveByteRange; +import net.lightbody.bmp.proxy.jetty.http.MultiPartResponse; +import net.lightbody.bmp.proxy.jetty.http.ResourceCache; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/DigestAuthenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/DigestAuthenticator.java similarity index 83% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/DigestAuthenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/DigestAuthenticator.java index 21d828029..cdc470c5c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/DigestAuthenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/DigestAuthenticator.java @@ -13,12 +13,12 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.http.UserRealm; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.UserRealm; import java.io.IOException; @@ -27,7 +27,7 @@ * @author gregw * */ -public class DigestAuthenticator extends org.browsermob.proxy.jetty.http.DigestAuthenticator +public class DigestAuthenticator extends net.lightbody.bmp.proxy.jetty.http.DigestAuthenticator { /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Dispatcher.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Dispatcher.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Dispatcher.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Dispatcher.java index c9566eb8e..d0943de57 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Dispatcher.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Dispatcher.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpConnection; +import net.lightbody.bmp.proxy.jetty.http.PathMap; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpConnection; -import org.browsermob.proxy.jetty.http.PathMap; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.*; import javax.servlet.http.*; @@ -622,7 +622,7 @@ public HttpSession getSession(boolean create) { if (_xSession==null) { - if (getAttribute("org.browsermob.proxy.jetty.jetty.servlet.Dispatcher.shared_session") != null) + if (getAttribute("Dispatcher.shared_session") != null) _xSession= super.getSession(create); else { @@ -709,7 +709,7 @@ public RequestDispatcher getRequestDispatcher(String url) public String getMethod() { if (this._filterType==Dispatcher.__ERROR) - return org.browsermob.proxy.jetty.http.HttpRequest.__GET; + return net.lightbody.bmp.proxy.jetty.http.HttpRequest.__GET; return super.getMethod(); } } diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/FilterHolder.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/FilterHolder.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/FilterHolder.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/FilterHolder.java index 7b75744d0..c4f30fdd6 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/FilterHolder.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/FilterHolder.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.http.HttpHandler; +import net.lightbody.bmp.proxy.jetty.http.HttpHandler; import javax.servlet.Filter; import javax.servlet.FilterConfig; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/FormAuthenticator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/FormAuthenticator.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/FormAuthenticator.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/FormAuthenticator.java index 2d4341e00..24b89d1aa 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/FormAuthenticator.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/FormAuthenticator.java @@ -13,14 +13,19 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.Authenticator; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.SSORealm; +import net.lightbody.bmp.proxy.jetty.http.SecurityConstraint; +import net.lightbody.bmp.proxy.jetty.http.UserRealm; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Credential; +import net.lightbody.bmp.proxy.jetty.util.Password; +import net.lightbody.bmp.proxy.jetty.util.URI; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Credential; -import org.browsermob.proxy.jetty.util.Password; -import org.browsermob.proxy.jetty.util.URI; import javax.servlet.http.*; import java.io.IOException; @@ -42,8 +47,8 @@ public class FormAuthenticator implements Authenticator static Log log = LogFactory.getLog(FormAuthenticator.class); /* ------------------------------------------------------------ */ - public final static String __J_URI="org.browsermob.proxy.jetty.jetty.URI"; - public final static String __J_AUTHENTICATED="org.browsermob.proxy.jetty.jetty.Auth"; + public final static String __J_URI="net.lightbody.bmp.proxy.jetty.jetty.URI"; + public final static String __J_AUTHENTICATED="net.lightbody.bmp.proxy.jetty.jetty.Auth"; public final static String __J_SECURITY_CHECK="j_security_check"; public final static String __J_USERNAME="j_username"; public final static String __J_PASSWORD="j_password"; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/HashSessionManager.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/HashSessionManager.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/HashSessionManager.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/HashSessionManager.java index 931aac322..99a7518ad 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/HashSessionManager.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/HashSessionManager.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Holder.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Holder.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Holder.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Holder.java index 07e333bdb..10052a90d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Holder.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Holder.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpHandler; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpHandler; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LifeCycle; import java.io.Serializable; import java.util.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Invoker.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Invoker.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Invoker.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Invoker.java index 2908ac41d..c12b5d9d9 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/Invoker.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/Invoker.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.URI; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.URI; import javax.servlet.ServletContext; import javax.servlet.ServletException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/JSR154Filter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/JSR154Filter.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/JSR154Filter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/JSR154Filter.java index 68f951666..c3ecaef38 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/JSR154Filter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/JSR154Filter.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.util.LazyList; +import net.lightbody.bmp.proxy.jetty.util.LazyList; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -34,7 +34,7 @@ * tell the jetty lists what they are and why you can't use a normal filter/wrapper for * this? *
  • SRV.6.2.2 Dispatachers where the container cannot wrap the request or - * response. See http://jetty.mortbay.org/browsermob/proxy/jetty/doc/servlet24.html#d0e711 + * response. See xxx * to find out why this is stupid.
  • * * diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/JettyWebConfiguration.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/JettyWebConfiguration.java similarity index 80% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/JettyWebConfiguration.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/JettyWebConfiguration.java index 33e3b02a5..baa4ad9e3 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/JettyWebConfiguration.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/JettyWebConfiguration.java @@ -13,13 +13,13 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Resource; +import net.lightbody.bmp.proxy.jetty.xml.XmlConfiguration; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Resource; -import org.browsermob.proxy.jetty.xml.XmlConfiguration; /** @@ -37,7 +37,7 @@ public class JettyWebConfiguration implements Configuration /** - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#setWebApplicationContext(org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext) + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#setWebApplicationContext(net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext) */ public void setWebApplicationContext (WebApplicationContext context) { @@ -51,7 +51,7 @@ public WebApplicationContext getWebApplicationContext () /** configureClassPath * Not used. - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureClassPath() + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureClassPath() */ public void configureClassPath () throws Exception { @@ -59,7 +59,7 @@ public void configureClassPath () throws Exception /** configureDefaults * Not used. - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureDefaults() + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureDefaults() */ public void configureDefaults () throws Exception { @@ -67,7 +67,7 @@ public void configureDefaults () throws Exception /** configureWebApp * Apply web-jetty.xml configuration - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureWebApp() + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureWebApp() */ public void configureWebApp () throws Exception { @@ -95,7 +95,7 @@ public void configureWebApp () throws Exception // Give permission to see Jetty classes String[] old_server_classes = _context.getServerClasses(); String[] server_classes = new String[1+(old_server_classes==null?0:old_server_classes.length)]; - server_classes[0]="-org.browsermob.proxy.jetty."; + server_classes[0]="-net.lightbody.bmp.proxy.jetty."; if (server_classes!=null) System.arraycopy(old_server_classes, 0, server_classes, 1, old_server_classes.length); diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java index 1ac48eba5..220af438d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHandler.java @@ -13,13 +13,21 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; - - +package net.lightbody.bmp.proxy.jetty.jetty.servlet; + + +import net.lightbody.bmp.proxy.jetty.http.EOFException; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpHandler; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.PathMap; +import net.lightbody.bmp.proxy.jetty.http.Version; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -47,7 +55,7 @@ * initialized, then a HashSessionManager with a standard * java.util.Random generator is created. *

    - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationHandler + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationHandler * @version $Id: ServletHandler.java,v 1.133 2006/03/15 14:43:00 gregwilkins Exp $ * @author Greg Wilkins */ @@ -376,7 +384,7 @@ protected synchronized void doStart() if (isStarted()) return; - _contextLog = LogFactory.getLog("org.browsermob.proxy.jetty.jetty.context."+getHttpContext().getHttpContextName()); + _contextLog = LogFactory.getLog("net.lightbody.bmp.proxy.jetty.jetty.context."+getHttpContext().getHttpContextName()); if (_contextLog==null) _contextLog=log; @@ -1023,7 +1031,7 @@ public ServletContext getContext(String uri) { ServletHandler handler= (ServletHandler) getHttpContext().getHttpServer() - .findHandler(org.browsermob.proxy.jetty.jetty.servlet.ServletHandler.class, + .findHandler(net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHandler.class, uri, getHttpContext().getVirtualHosts()); if (handler!=null) diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHolder.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHolder.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHolder.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHolder.java index 2776c5677..e7d95c3f5 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHolder.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHolder.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.UserRealm; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.UserRealm; -import org.browsermob.proxy.jetty.log.LogFactory; import javax.servlet.*; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpContext.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpContext.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpContext.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpContext.java index 3aca0f68a..04baa1726 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpContext.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpContext.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.http.HttpRequest; -import org.browsermob.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; @@ -202,7 +202,7 @@ public void destroy() /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.http.HttpContext#enterContextScope(org.browsermob.proxy.jetty.http.HttpRequest, org.browsermob.proxy.jetty.http.HttpResponse) + * @see HttpContext#enterContextScope(HttpRequest, HttpResponse) */ public Object enterContextScope(HttpRequest request, HttpResponse response) { @@ -223,7 +223,7 @@ public Object enterContextScope(HttpRequest request, HttpResponse response) /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.util.Container#doStop() + * @see Container#doStop() */ protected void doStop() throws Exception { diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpRequest.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpRequest.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java index c158268c0..97b2f45aa 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpRequest.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpRequest.java @@ -14,12 +14,16 @@ // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; - +package net.lightbody.bmp.proxy.jetty.jetty.servlet; + +import net.lightbody.bmp.proxy.jetty.http.HttpConnection; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpInputStream; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.SecurityConstraint; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.RequestDispatcher; import javax.servlet.ServletInputStream; @@ -369,7 +373,7 @@ public String getQueryString() public String getAuthType() { String at= _httpRequest.getAuthType(); - if (at==SecurityConstraint.__BASIC_AUTH) + if (at== SecurityConstraint.__BASIC_AUTH) return HttpServletRequest.BASIC_AUTH; if (at==SecurityConstraint.__FORM_AUTH) return HttpServletRequest.FORM_AUTH; @@ -614,7 +618,7 @@ public ServletInputStream getInputStream() if (_inputState!=0 && _inputState!=1) throw new IllegalStateException(); if (_in==null) - _in = new ServletIn((HttpInputStream)_httpRequest.getInputStream()); + _in = new ServletIn((HttpInputStream)_httpRequest.getInputStream()); _inputState=1; _reader=null; return _in; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpResponse.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpResponse.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java index 21633ace3..2acb69aeb 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletHttpResponse.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletHttpResponse.java @@ -13,15 +13,15 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; - +package net.lightbody.bmp.proxy.jetty.jetty.servlet; + +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpOutputStream; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.http.HttpOutputStream; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.*; import javax.servlet.http.Cookie; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletIn.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletIn.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java index 54f1e3ada..a475eb2ae 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletIn.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletIn.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.http.HttpInputStream; +import net.lightbody.bmp.proxy.jetty.http.HttpInputStream; import javax.servlet.ServletInputStream; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletOut.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletOut.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java index 714d88f05..277b233c9 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletOut.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletOut.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.IO; import javax.servlet.ServletOutputStream; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletSSL.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletSSL.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletSSL.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletSSL.java index c90388850..c53686968 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletSSL.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletSSL.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; /* --------------------------------------------------------------------- */ /** Jetty Servlet SSL support utilities. diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletWriter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletWriter.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletWriter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletWriter.java index 13b5346d9..613be3d34 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/ServletWriter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/ServletWriter.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpOutputStream; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpOutputStream; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/SessionContext.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/SessionContext.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/SessionContext.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/SessionContext.java index 34bfa48ea..a15c60646 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/SessionContext.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/SessionContext.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionContext; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/SessionManager.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/SessionManager.java similarity index 91% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/SessionManager.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/SessionManager.java index 847ef19f1..868ad0d58 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/SessionManager.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/SessionManager.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; -import org.browsermob.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -39,7 +39,7 @@ public interface SessionManager extends LifeCycle, Serializable * org.mortbay.jetty.servlet.SessionCookie system property. */ public final static String __SessionCookie= - System.getProperty("org.browsermob.proxy.jetty.jetty.servlet.SessionCookie","JSESSIONID"); + System.getProperty("net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionCookie","JSESSIONID"); /* ------------------------------------------------------------ */ /** Session URL parameter name. @@ -47,7 +47,7 @@ public interface SessionManager extends LifeCycle, Serializable * org.mortbay.jetty.servlet.SessionURL system property. */ public final static String __SessionURL = - System.getProperty("org.browsermob.proxy.jetty.jetty.servlet.SessionURL","jsessionid"); + System.getProperty("net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionURL","jsessionid"); final static String __SessionUrlPrefix=";"+__SessionURL+"="; @@ -58,7 +58,7 @@ public interface SessionManager extends LifeCycle, Serializable * no domain is specified for the session cookie. */ public final static String __SessionDomain= - "org.browsermob.proxy.jetty.jetty.servlet.SessionDomain"; + "net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionDomain"; /* ------------------------------------------------------------ */ /** Session Path. @@ -67,7 +67,7 @@ public interface SessionManager extends LifeCycle, Serializable * the context path is used as the path for the cookie. */ public final static String __SessionPath= - "org.browsermob.proxy.jetty.jetty.servlet.SessionPath"; + "net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionPath"; /* ------------------------------------------------------------ */ /** Session Max Age. @@ -76,7 +76,7 @@ public interface SessionManager extends LifeCycle, Serializable * a max age of -1 is used. */ public final static String __MaxAge= - "org.browsermob.proxy.jetty.jetty.servlet.MaxAge"; + "net.lightbody.bmp.proxy.jetty.jetty.servlet.MaxAge"; /* ------------------------------------------------------------ */ public void initialize(ServletHandler handler); diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/TagLibConfiguration.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/TagLibConfiguration.java similarity index 89% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/TagLibConfiguration.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/TagLibConfiguration.java index 31727779f..18edf9e98 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/TagLibConfiguration.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/TagLibConfiguration.java @@ -13,13 +13,13 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Resource; +import net.lightbody.bmp.proxy.jetty.xml.XmlParser; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Resource; -import org.browsermob.proxy.jetty.xml.XmlParser; import java.util.EventListener; import java.util.HashSet; @@ -59,7 +59,7 @@ public TagLibConfiguration() /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#setWebApplicationContext(org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext) + * @see WebApplicationContext.Configuration#setWebApplicationContext(WebApplicationContext) */ public void setWebApplicationContext(WebApplicationContext context) { @@ -68,7 +68,7 @@ public void setWebApplicationContext(WebApplicationContext context) /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#getWebApplicationContext() + * @see WebApplicationContext.Configuration#getWebApplicationContext() */ public WebApplicationContext getWebApplicationContext() { @@ -77,7 +77,7 @@ public WebApplicationContext getWebApplicationContext() /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureClassPath() + * @see WebApplicationContext.Configuration#configureClassPath() */ public void configureClassPath() throws Exception { @@ -85,7 +85,7 @@ public void configureClassPath() throws Exception /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureDefaults() + * @see WebApplicationContext.Configuration#configureDefaults() */ public void configureDefaults() throws Exception { @@ -93,7 +93,7 @@ public void configureDefaults() throws Exception /* ------------------------------------------------------------ */ /* - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration#configureWebApp() + * @see WebApplicationContext.Configuration#configureWebApp() */ public void configureWebApp() throws Exception { diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/WebApplicationContext.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/WebApplicationContext.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/WebApplicationContext.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/WebApplicationContext.java index aca48a48d..9ec213e6a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/WebApplicationContext.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/WebApplicationContext.java @@ -13,13 +13,17 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; - +package net.lightbody.bmp.proxy.jetty.jetty.servlet; + +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpHandler; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.UserRealm; +import net.lightbody.bmp.proxy.jetty.jetty.Server; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.jetty.Server; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -47,7 +51,7 @@ * A single WebApplicationHandler instance is used to provide * security, filter, sevlet and resource handling. * - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationHandler + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationHandler * @version $Id: WebApplicationContext.java,v 1.136 2005/10/26 08:11:04 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ @@ -56,7 +60,7 @@ public class WebApplicationContext extends ServletHttpContext implements Externa private static Log log= LogFactory.getLog(WebApplicationContext.class); /* ------------------------------------------------------------ */ - private String _defaultsDescriptor= "org/browsermob/proxy/jetty/jetty/servlet/webdefault.xml"; + private String _defaultsDescriptor= "net/lightbody/bmp/proxy/jetty/jetty/servlet/webdefault.xml"; private String _war; private boolean _extract; private boolean _ignorewebjetty; @@ -670,7 +674,7 @@ public void setDisplayName(String name) /** Set the defaults web.xml file. * The default web.xml is used to configure all webapplications * before the WEB-INF/web.xml file is applied. By default the - * org/browsermob/proxy/jetty/jetty/servlet/webdefault.xml resource from the + * net/lightbody/bmp/proxy/jetty/jetty/servlet/webdefault.xml resource from the * org.mortbay.jetty.jar is used. * @param defaults File, Resource, URL or null. */ diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/WebApplicationHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/WebApplicationHandler.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/WebApplicationHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/WebApplicationHandler.java index cc1ed71cd..923fa2035 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/WebApplicationHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/WebApplicationHandler.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.PathMap; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpResponse; -import org.browsermob.proxy.jetty.http.PathMap; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import javax.servlet.*; import javax.servlet.http.*; @@ -33,7 +33,7 @@ * capabilities to provide full J2EE web container support. *

    * @since Jetty 4.1 - * @see org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext + * @see net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext * @version $Id: WebApplicationHandler.java,v 1.62 2006/01/04 13:55:31 gregwilkins Exp $ * @author Greg Wilkins */ @@ -350,14 +350,14 @@ protected synchronized void doStop() throws Exception public String getErrorPage(int status, ServletHttpRequest request) { String error_page= null; - Class exClass= (Class)request.getAttribute(ServletHandler.__J_S_ERROR_EXCEPTION_TYPE); + Class exClass= (Class)request.getAttribute(__J_S_ERROR_EXCEPTION_TYPE); if (ServletException.class.equals(exClass)) { error_page= _webApplicationContext.getErrorPage(exClass.getName()); if (error_page == null) { - Throwable th= (Throwable)request.getAttribute(ServletHandler.__J_S_ERROR_EXCEPTION); + Throwable th= (Throwable)request.getAttribute(__J_S_ERROR_EXCEPTION); while (th instanceof ServletException) th= ((ServletException)th).getRootCause(); if (th != null) @@ -622,7 +622,7 @@ public void setFilterChainsCached(boolean filterChainsCached) /* ------------------------------------------------------------ */ /** - * @see org.browsermob.proxy.jetty.util.Container#addComponent(java.lang.Object) + * @see net.lightbody.bmp.proxy.jetty.util.Container#addComponent(java.lang.Object) */ protected void addComponent(Object o) { @@ -640,7 +640,7 @@ protected void addComponent(Object o) /* ------------------------------------------------------------ */ /** - * @see org.browsermob.proxy.jetty.util.Container#removeComponent(java.lang.Object) + * @see net.lightbody.bmp.proxy.jetty.util.Container#removeComponent(java.lang.Object) */ protected void removeComponent(Object o) { diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/XMLConfiguration.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/XMLConfiguration.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/XMLConfiguration.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/XMLConfiguration.java index e92864f31..a1c770960 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/XMLConfiguration.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/XMLConfiguration.java @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.Authenticator; +import net.lightbody.bmp.proxy.jetty.http.ClientCertAuthenticator; +import net.lightbody.bmp.proxy.jetty.http.SecurityConstraint; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.Resource; +import net.lightbody.bmp.proxy.jetty.xml.XmlParser; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.Authenticator; -import org.browsermob.proxy.jetty.http.ClientCertAuthenticator; -import org.browsermob.proxy.jetty.http.SecurityConstraint; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.Resource; -import org.browsermob.proxy.jetty.xml.XmlParser; import javax.servlet.UnavailableException; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/AbstractSessionManagerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/AbstractSessionManagerMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/AbstractSessionManagerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/AbstractSessionManagerMBean.java index 45fc47540..b8c61a0cc 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/AbstractSessionManagerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/AbstractSessionManagerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; -import org.browsermob.proxy.jetty.jetty.servlet.SessionManager; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionManager; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ConfigurationMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ConfigurationMBean.java similarity index 83% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ConfigurationMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ConfigurationMBean.java index f38130b85..842627115 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ConfigurationMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ConfigurationMBean.java @@ -13,13 +13,13 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.jmx.ModelMBeanImpl; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext.Configuration; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.jmx.ModelMBeanImpl; import javax.management.MBeanException; import javax.management.MBeanServer; @@ -48,7 +48,7 @@ public ConfigurationMBean() /**defineManagedResource * Grab the object which this mbean is proxying for, which in * this case is an org.mortbay.jetty.servlet.WebApplicationContext.Configuration - * @see org.browsermob.proxy.jetty.util.jmx.ModelMBeanImpl#defineManagedResource() + * @see net.lightbody.bmp.proxy.jetty.util.jmx.ModelMBeanImpl#defineManagedResource() */ protected void defineManagedResource() { @@ -73,7 +73,7 @@ public String getName () /**uniqueObjectName * Make a unique jmx name for this configuration object - * @see org.browsermob.proxy.jetty.util.jmx.ModelMBeanImpl#uniqueObjectName(javax.management.MBeanServer, java.lang.String) + * @see net.lightbody.bmp.proxy.jetty.util.jmx.ModelMBeanImpl#uniqueObjectName(javax.management.MBeanServer, java.lang.String) */ public synchronized ObjectName uniqueObjectName(MBeanServer server, String on) { diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/FilterHolderMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/FilterHolderMBean.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/FilterHolderMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/FilterHolderMBean.java index 45d106f86..16863a5a7 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/FilterHolderMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/FilterHolderMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; -import org.browsermob.proxy.jetty.jetty.servlet.FilterHolder; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.FilterHolder; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/HolderMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/HolderMBean.java similarity index 90% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/HolderMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/HolderMBean.java index a4cee364c..0d9c061fa 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/HolderMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/HolderMBean.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.Holder; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.jmx.LifeCycleMBean; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.Holder; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.jmx.LifeCycleMBean; import javax.management.MBeanException; import javax.management.MBeanServer; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/JettyWebConfigurationMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/JettyWebConfigurationMBean.java similarity index 87% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/JettyWebConfigurationMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/JettyWebConfigurationMBean.java index 9a50c6d8c..b24f88bd9 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/JettyWebConfigurationMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/JettyWebConfigurationMBean.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; import javax.management.MBeanException; @@ -26,7 +26,7 @@ * @version $Revision: 1.1 $ $Date: 2004/09/27 14:33:58 $ * */ -public class JettyWebConfigurationMBean extends org.browsermob.proxy.jetty.jetty.servlet.jmx.ConfigurationMBean +public class JettyWebConfigurationMBean extends net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx.ConfigurationMBean { public JettyWebConfigurationMBean () throws MBeanException diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHandlerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHandlerMBean.java similarity index 90% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHandlerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHandlerMBean.java index 6dc56b637..6a706159f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHandlerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHandlerMBean.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; +import net.lightbody.bmp.proxy.jetty.http.jmx.HttpHandlerMBean; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHandler; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionManager; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.jmx.HttpHandlerMBean; -import org.browsermob.proxy.jetty.jetty.servlet.ServletHandler; -import org.browsermob.proxy.jetty.jetty.servlet.SessionManager; -import org.browsermob.proxy.jetty.log.LogFactory; import javax.management.MBeanException; import javax.management.ObjectName; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHolderMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHolderMBean.java similarity index 92% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHolderMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHolderMBean.java index 154e79e45..3dd78b645 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHolderMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHolderMBean.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; -import org.browsermob.proxy.jetty.jetty.servlet.ServletHandler; -import org.browsermob.proxy.jetty.jetty.servlet.ServletHolder; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHandler; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHolder; import javax.management.MBeanException; import java.util.ArrayList; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHttpContextMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHttpContextMBean.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHttpContextMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHttpContextMBean.java index 2c288d40b..0440dc585 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/ServletHttpContextMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/ServletHttpContextMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; -import org.browsermob.proxy.jetty.http.jmx.HttpContextMBean; +import net.lightbody.bmp.proxy.jetty.http.jmx.HttpContextMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/SessionManagerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/SessionManagerMBean.java similarity index 88% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/SessionManagerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/SessionManagerMBean.java index 4ffcf95b1..ab5b5043c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/SessionManagerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/SessionManagerMBean.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; -import org.browsermob.proxy.jetty.jetty.servlet.SessionManager; -import org.browsermob.proxy.jetty.util.jmx.LifeCycleMBean; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.SessionManager; +import net.lightbody.bmp.proxy.jetty.util.jmx.LifeCycleMBean; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/WebApplicationContextMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/WebApplicationContextMBean.java similarity index 92% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/WebApplicationContextMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/WebApplicationContextMBean.java index 932a40dea..193640ead 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/WebApplicationContextMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/WebApplicationContextMBean.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationContext; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LifeCycleEvent; +import net.lightbody.bmp.proxy.jetty.util.LifeCycleListener; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.WebApplicationContext; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LifeCycleEvent; -import org.browsermob.proxy.jetty.util.LifeCycleListener; -import org.browsermob.proxy.jetty.util.LogSupport; import javax.management.MBeanException; import javax.management.MBeanServer; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/WebApplicationHandlerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/WebApplicationHandlerMBean.java similarity index 93% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/WebApplicationHandlerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/WebApplicationHandlerMBean.java index 5843bd46e..505480fc8 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/WebApplicationHandlerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/WebApplicationHandlerMBean.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.WebApplicationHandler; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.jetty.servlet.WebApplicationHandler; -import org.browsermob.proxy.jetty.log.LogFactory; import javax.management.MBeanException; import javax.management.ObjectName; diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/XMLConfigurationMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/XMLConfigurationMBean.java similarity index 87% rename from src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/XMLConfigurationMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/XMLConfigurationMBean.java index 2b1593f24..6a0ff82c0 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/servlet/jmx/XMLConfigurationMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/servlet/jmx/XMLConfigurationMBean.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.jetty.servlet.jmx; +package net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx; import javax.management.MBeanException; @@ -26,7 +26,7 @@ * @version $Revision: 1.1 $ $Date: 2004/09/27 14:33:59 $ * */ -public class XMLConfigurationMBean extends org.browsermob.proxy.jetty.jetty.servlet.jmx.ConfigurationMBean +public class XMLConfigurationMBean extends net.lightbody.bmp.proxy.jetty.jetty.servlet.jmx.ConfigurationMBean { public XMLConfigurationMBean() throws MBeanException diff --git a/src/main/java/org/browsermob/proxy/jetty/jetty/win32/Service.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/win32/Service.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/jetty/win32/Service.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/win32/Service.java index a1da209a5..10b782196 100644 --- a/src/main/java/org/browsermob/proxy/jetty/jetty/win32/Service.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/jetty/win32/Service.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.jetty.win32; +package net.lightbody.bmp.proxy.jetty.jetty.win32; +import net.lightbody.bmp.proxy.jetty.http.HttpServer; +import net.lightbody.bmp.proxy.jetty.jetty.Server; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpServer; -import org.browsermob.proxy.jetty.jetty.Server; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; import java.io.File; import java.io.FileOutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/log/Factory.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/Factory.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/log/Factory.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/Factory.java index 1aa99b61c..34999a2c3 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/Factory.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/Factory.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogConfigurationException; diff --git a/src/main/java/org/browsermob/proxy/jetty/log/Frame.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/Frame.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/log/Frame.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/Frame.java index dfd8b72db..44f003b07 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/Frame.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/Frame.java @@ -3,7 +3,7 @@ // $Id: Frame.java,v 1.1 2004/06/04 21:37:20 gregwilkins Exp $ // ======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; diff --git a/src/main/java/org/browsermob/proxy/jetty/log/LogFactory.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogFactory.java similarity index 90% rename from src/main/java/org/browsermob/proxy/jetty/log/LogFactory.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogFactory.java index 1b7bb8d3e..fa1e308e6 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/LogFactory.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogFactory.java @@ -4,7 +4,7 @@ * To change the template for this generated file go to * Window - Preferences - Java - Code Generation - Code and Comments */ -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; import org.apache.commons.logging.Log; @@ -20,7 +20,7 @@ * @author gregw */ public class LogFactory { - static boolean noDiscovery = Boolean.getBoolean("org.browsermob.proxy.jetty.log.LogFactory.noDiscovery"); + static boolean noDiscovery = Boolean.getBoolean("LogFactory.noDiscovery"); static org.apache.commons.logging.LogFactory factory=noDiscovery?new Factory():org.apache.commons.logging.LogFactory.getFactory(); public static Log getLog(Class logClass) diff --git a/src/main/java/org/browsermob/proxy/jetty/log/LogImpl.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogImpl.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/log/LogImpl.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogImpl.java index ddae46289..541c437a2 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/LogImpl.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogImpl.java @@ -3,9 +3,9 @@ // $Id: LogImpl.java,v 1.6 2005/08/13 00:01:27 gregwilkins Exp $ // ======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; -import org.browsermob.proxy.jetty.util.Loader; +import net.lightbody.bmp.proxy.jetty.util.Loader; import java.util.ArrayList; import java.util.StringTokenizer; @@ -24,7 +24,7 @@ * * This logger can be configured with the org.mortbay.log.Factory * - * @see org.browsermob.proxy.jetty.log.LogFactory + * @see net.lightbody.bmp.proxy.jetty.log.LogFactory */ public class LogImpl implements org.apache.commons.logging.Log { @@ -120,7 +120,7 @@ public synchronized void add(String logSinkClass) try { if (logSinkClass==null || logSinkClass.length()==0) - logSinkClass="org.browsermob.proxy.jetty.log.OutputStreamLogSink"; + logSinkClass="OutputStreamLogSink"; Class sinkClass = Loader.loadClass(this.getClass(),logSinkClass); LogSink sink=(LogSink)sinkClass.newInstance(); add(sink); @@ -176,7 +176,7 @@ private synchronized void defaultInit() if (!_initialized) { _initialized = true; - String sinkClasses = System.getProperty("LOG_SINKS","org.browsermob.proxy.jetty.log.OutputStreamLogSink"); + String sinkClasses = System.getProperty("LOG_SINKS","OutputStreamLogSink"); StringTokenizer sinkTokens = new StringTokenizer(sinkClasses, ";, "); LogSink sink= null; @@ -187,14 +187,14 @@ private synchronized void defaultInit() try { Class sinkClass = Loader.loadClass(this.getClass(),sinkClassName); - if (org.browsermob.proxy.jetty.log.LogSink.class.isAssignableFrom(sinkClass)) { + if (net.lightbody.bmp.proxy.jetty.log.LogSink.class.isAssignableFrom(sinkClass)) { sink = (LogSink)sinkClass.newInstance(); sink.start(); add(sink); } else // Can't use Code.fail here, that's what we're setting up - System.err.println(sinkClass+" is not a org.browsermob.proxy.jetty.log.LogSink"); + System.err.println(sinkClass+" is not a LogSink"); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/org/browsermob/proxy/jetty/log/LogSink.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogSink.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/log/LogSink.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogSink.java index 073d1197d..b9e5b26ab 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/LogSink.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogSink.java @@ -3,9 +3,9 @@ // $Id: LogSink.java,v 1.1 2004/06/04 21:37:20 gregwilkins Exp $ // ======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; -import org.browsermob.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import java.io.Serializable; diff --git a/src/main/java/org/browsermob/proxy/jetty/log/LogStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogStream.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/log/LogStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogStream.java index c8ac57d72..d0b045874 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/LogStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/LogStream.java @@ -13,10 +13,10 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; +import net.lightbody.bmp.proxy.jetty.util.ByteArrayOutputStream2; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.util.ByteArrayOutputStream2; import java.io.PrintStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/log/NullLogSink.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/NullLogSink.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/log/NullLogSink.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/NullLogSink.java index 7741752a0..497b2411c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/NullLogSink.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/NullLogSink.java @@ -3,7 +3,7 @@ // $Id: NullLogSink.java,v 1.1 2004/06/04 21:37:20 gregwilkins Exp $ // ======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; public class NullLogSink implements LogSink { diff --git a/src/main/java/org/browsermob/proxy/jetty/log/OutputStreamLogSink.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/OutputStreamLogSink.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/log/OutputStreamLogSink.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/OutputStreamLogSink.java index ba01f6221..b230e20bb 100644 --- a/src/main/java/org/browsermob/proxy/jetty/log/OutputStreamLogSink.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/OutputStreamLogSink.java @@ -3,9 +3,9 @@ // $Id: OutputStreamLogSink.java,v 1.4 2004/09/19 08:04:57 gregwilkins Exp $ // ======================================================================== -package org.browsermob.proxy.jetty.log; +package net.lightbody.bmp.proxy.jetty.log; -import org.browsermob.proxy.jetty.util.*; +import net.lightbody.bmp.proxy.jetty.util.*; import java.io.IOException; import java.io.OutputStream; @@ -39,7 +39,7 @@ *

    If LOG_TIMEZONE is set, it is used to set the timezone of the log date * format, otherwise GMT is used. * - * @see org.browsermob.proxy.jetty.util.Log + * @see net.lightbody.bmp.proxy.jetty.util.Log * @version $Id: OutputStreamLogSink.java,v 1.4 2004/09/19 08:04:57 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ @@ -484,7 +484,7 @@ public boolean isStarted() } /* (non-Javadoc) - * @see org.browsermob.proxy.jetty.log.LogSink#setLogImpl(org.browsermob.proxy.jetty.log.LogImpl) + * @see LogSink#setLogImpl(LogImpl) */ public void setLogImpl(LogImpl impl) { diff --git a/src/main/java/org/browsermob/proxy/jetty/log/services/org.apache.commons.logging.LogFactory b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/services/org.apache.commons.logging.LogFactory similarity index 100% rename from src/main/java/org/browsermob/proxy/jetty/log/services/org.apache.commons.logging.LogFactory rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/log/services/org.apache.commons.logging.LogFactory diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/AdminServlet.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/AdminServlet.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/servlet/AdminServlet.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/AdminServlet.java index d64ec4d9d..56e6e5da6 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/AdminServlet.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/AdminServlet.java @@ -13,17 +13,23 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.html.*; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpHandler; +import net.lightbody.bmp.proxy.jetty.http.HttpListener; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.HttpServer; +import net.lightbody.bmp.proxy.jetty.http.PathMap; +import net.lightbody.bmp.proxy.jetty.jetty.servlet.ServletHandler; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.URI; +import net.lightbody.bmp.proxy.jetty.util.UrlEncoded; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.html.*; -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.jetty.servlet.ServletHandler; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LifeCycle; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.URI; -import org.browsermob.proxy.jetty.util.UrlEncoded; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -58,7 +64,7 @@ public void init(ServletConfig config) throws ServletException { super.init(config); - _servers =HttpServer.getHttpServers(); + _servers = HttpServer.getHttpServers(); } /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/CGI.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/CGI.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/servlet/CGI.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/CGI.java index 71d420fc8..87c008cdc 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/CGI.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/CGI.java @@ -12,15 +12,15 @@ // - exceptions should report to client via sendError() // - tidy up -package org.browsermob.proxy.jetty.servlet; - +package net.lightbody.bmp.proxy.jetty.servlet; + +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.StringUtil; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/Debug.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Debug.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/servlet/Debug.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Debug.java index daae16585..4cd47579a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/Debug.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Debug.java @@ -3,14 +3,14 @@ // $Id: Debug.java,v 1.10 2005/08/13 00:01:28 gregwilkins Exp $ // --------------------------------------------------------------------------- -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.html.*; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.log.LogImpl; +import net.lightbody.bmp.proxy.jetty.log.LogSink; +import net.lightbody.bmp.proxy.jetty.log.OutputStreamLogSink; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.html.*; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.log.LogImpl; -import org.browsermob.proxy.jetty.log.LogSink; -import org.browsermob.proxy.jetty.log.OutputStreamLogSink; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -85,7 +85,7 @@ public void doGet(HttpServletRequest request, tf.table().newRow().addCell(Break.rule).cell().attribute("COLSPAN","2"); - tf.addTextField("LSC","Add LogSink Class",40,"org.browsermob.proxy.jetty.log.OutputStreamLogSink"); + tf.addTextField("LSC","Add LogSink Class",40,"OutputStreamLogSink"); tf.addButtonArea(); tf.addButton("Action","Set Options"); diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/Dump.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Dump.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/servlet/Dump.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Dump.java index fec1bd369..602f2a2a2 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/Dump.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Dump.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.html.*; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.Loader; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.html.*; -import org.browsermob.proxy.jetty.http.HttpException; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.Loader; -import org.browsermob.proxy.jetty.util.LogSupport; import javax.servlet.*; import javax.servlet.http.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/Forward.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Forward.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/servlet/Forward.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Forward.java index 5201c56e8..2f66c25bd 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/Forward.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/Forward.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartFilter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartFilter.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartFilter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartFilter.java index f46c12b8a..c38f28eef 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartFilter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartFilter.java @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.MultiMap; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.MultiMap; -import org.browsermob.proxy.jetty.util.StringUtil; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartRequest.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartRequest.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartRequest.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartRequest.java index c121b9cea..66978deea 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartRequest.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartRequest.java @@ -13,14 +13,14 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LineInput; +import net.lightbody.bmp.proxy.jetty.util.MultiMap; +import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpFields; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LineInput; -import org.browsermob.proxy.jetty.util.MultiMap; -import org.browsermob.proxy.jetty.util.StringUtil; import javax.servlet.http.HttpServletRequest; import java.io.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartResponse.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartResponse.java similarity index 92% rename from src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartResponse.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartResponse.java index 9465fa017..06c66e936 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/MultiPartResponse.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/MultiPartResponse.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -28,7 +28,7 @@ * @author Greg Wilkins * @author Jim Crossley */ -public class MultiPartResponse extends org.browsermob.proxy.jetty.http.MultiPartResponse +public class MultiPartResponse extends net.lightbody.bmp.proxy.jetty.http.MultiPartResponse { /* ------------------------------------------------------------ */ /** MultiPartResponse constructor. diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/NotFoundServlet.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/NotFoundServlet.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/servlet/NotFoundServlet.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/NotFoundServlet.java index 086706d59..867dc9880 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/NotFoundServlet.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/NotFoundServlet.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/PostFileFilter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/PostFileFilter.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/servlet/PostFileFilter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/PostFileFilter.java index ccbe44df0..0b40d9068 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/PostFileFilter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/PostFileFilter.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/ProxyServlet.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/ProxyServlet.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/servlet/ProxyServlet.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/ProxyServlet.java index 4f25a2c22..9edf25d9d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/ProxyServlet.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/ProxyServlet.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/SendRedirect.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/SendRedirect.java similarity index 89% rename from src/main/java/org/browsermob/proxy/jetty/servlet/SendRedirect.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/SendRedirect.java index dac236395..8db63021d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/SendRedirect.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/SendRedirect.java @@ -13,15 +13,15 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.html.Heading; +import net.lightbody.bmp.proxy.jetty.html.Page; +import net.lightbody.bmp.proxy.jetty.html.TableForm; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.URI; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.html.Heading; -import org.browsermob.proxy.jetty.html.Page; -import org.browsermob.proxy.jetty.html.TableForm; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.URI; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/SessionDump.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/SessionDump.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/servlet/SessionDump.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/SessionDump.java index 6af46090a..a38a2b1a9 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/SessionDump.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/SessionDump.java @@ -13,13 +13,13 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; +import net.lightbody.bmp.proxy.jetty.html.Page; +import net.lightbody.bmp.proxy.jetty.html.TableForm; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.html.Page; -import org.browsermob.proxy.jetty.html.TableForm; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; import javax.servlet.ServletConfig; import javax.servlet.ServletException; diff --git a/src/main/java/org/browsermob/proxy/jetty/servlet/WelcomeFilter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/WelcomeFilter.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/servlet/WelcomeFilter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/WelcomeFilter.java index 3ebd477f3..bde9a2c05 100644 --- a/src/main/java/org/browsermob/proxy/jetty/servlet/WelcomeFilter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/servlet/WelcomeFilter.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.servlet; +package net.lightbody.bmp.proxy.jetty.servlet; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; diff --git a/src/main/java/org/browsermob/proxy/jetty/start/Classpath.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Classpath.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/start/Classpath.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Classpath.java index 5840e9116..364b9132d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/start/Classpath.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Classpath.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.start; +package net.lightbody.bmp.proxy.jetty.start; import java.io.File; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/start/Main.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Main.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/start/Main.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Main.java index 6eb81974d..758c6bf38 100644 --- a/src/main/java/org/browsermob/proxy/jetty/start/Main.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Main.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.start; +package net.lightbody.bmp.proxy.jetty.start; import java.io.*; import java.lang.reflect.InvocationTargetException; @@ -26,7 +26,7 @@ /** * Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the * start.jar archive. It allows an application to be started with the command "java -jar - * start.jar". The behaviour of Main is controlled by the "org/browsermob/proxy/jetty/start/start.config" file + * start.jar". The behaviour of Main is controlled by the "net/lightbody/bmp/proxy/jetty/start/start.config" file * obtained as a resource or file. This can be overridden with the START system property. The * format of each line in this file is: * @@ -78,7 +78,7 @@ public class Main static boolean _debug=System.getProperty("DEBUG",null)!=null; private String _classname=null; private Classpath _classpath=new Classpath(); - private String _config=System.getProperty("START","org/browsermob/proxy/jetty/start/start.config"); + private String _config=System.getProperty("START", "net/lightbody/bmp/proxy/jetty/start/start.config"); private ArrayList _xml=new ArrayList(); public static void main(String[] args) diff --git a/src/main/java/org/browsermob/proxy/jetty/start/Monitor.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Monitor.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/start/Monitor.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Monitor.java index 1e7ea8352..cc538c9ae 100644 --- a/src/main/java/org/browsermob/proxy/jetty/start/Monitor.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Monitor.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.start; +package net.lightbody.bmp.proxy.jetty.start; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.net.InetAddress; diff --git a/src/main/java/org/browsermob/proxy/jetty/start/README.txt b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/README.txt similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/start/README.txt rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/README.txt index 936b04586..a980da96e 100644 --- a/src/main/java/org/browsermob/proxy/jetty/start/README.txt +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/README.txt @@ -22,7 +22,7 @@ built-in in JDK 1.4 etc.) - Looks for Sun's JAVAC (required for Jasper JSP engine) and puts it in classpath. For this to work, launcher has to be started with JDK, not JRE! -- After classpath has been configured, it invokes org.browsermob.proxy.jetty.jetty.Server +- After classpath has been configured, it invokes Server with any command line arguments it received. - When there are no commandline args, launcher starts Jetty Demo (using configuration files etc/demo.xml and etc/admin.xml) and on Windows diff --git a/src/main/java/org/browsermob/proxy/jetty/start/Version.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Version.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/start/Version.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Version.java index 779074428..c929c6818 100644 --- a/src/main/java/org/browsermob/proxy/jetty/start/Version.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/Version.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.start; +package net.lightbody.bmp.proxy.jetty.start; /** * Utility class for parsing and comparing version strings. diff --git a/src/main/java/org/browsermob/proxy/jetty/start/start.config b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/start.config similarity index 100% rename from src/main/java/org/browsermob/proxy/jetty/start/start.config rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/start/start.config diff --git a/src/main/java/org/browsermob/proxy/jetty/stop/Main.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/stop/Main.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/stop/Main.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/stop/Main.java index 81184e7ee..810a1f0ac 100644 --- a/src/main/java/org/browsermob/proxy/jetty/stop/Main.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/stop/Main.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.stop; +package net.lightbody.bmp.proxy.jetty.stop; import java.io.OutputStream; import java.net.InetAddress; @@ -40,7 +40,7 @@ public class Main { private boolean _debug = System.getProperty("DEBUG",null)!=null; - private String _config = System.getProperty("START","org/browsermob/proxy/jetty/start/start.config"); + private String _config = System.getProperty("START", "net/lightbody/bmp/proxy/jetty/start/start.config"); private int _port = Integer.getInteger("STOP.PORT",8079).intValue(); private String _key = System.getProperty("STOP.KEY","jetty"); diff --git a/src/main/java/org/browsermob/proxy/jetty/util/B64Code.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/B64Code.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/B64Code.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/B64Code.java index 7303a2794..1ca18f2f2 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/B64Code.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/B64Code.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.UnsupportedEncodingException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/BadResource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/BadResource.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/BadResource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/BadResource.java index 4fbfba1bd..4e78c5e22 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/BadResource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/BadResource.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.*; import java.net.URL; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/BlockingQueue.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/BlockingQueue.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/BlockingQueue.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/BlockingQueue.java index cbcbdd7f6..e2cd2c690 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/BlockingQueue.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/BlockingQueue.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; /* ------------------------------------------------------------ */ /** Blocking queue. diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ByteArrayISO8859Writer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayISO8859Writer.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/ByteArrayISO8859Writer.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayISO8859Writer.java index 4b89c7c8f..15609f6dd 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ByteArrayISO8859Writer.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayISO8859Writer.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ByteArrayOutputStream2.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayOutputStream2.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/ByteArrayOutputStream2.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayOutputStream2.java index 1cefe301d..ce52f8e87 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ByteArrayOutputStream2.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayOutputStream2.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.ByteArrayOutputStream; /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ByteArrayPool.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayPool.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/ByteArrayPool.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayPool.java index 3a5144de2..6e3f1ef69 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ByteArrayPool.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteArrayPool.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; /* ------------------------------------------------------------ */ @@ -26,7 +26,7 @@ public class ByteArrayPool { public static final int __POOL_SIZE= - Integer.getInteger("org.browsermob.proxy.jetty.util.ByteArrayPool.pool_size",8).intValue(); + Integer.getInteger("ByteArrayPool.pool_size",8).intValue(); public static final ThreadLocal __pools=new BAThreadLocal(); public static int __slot; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ByteBufferOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteBufferOutputStream.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/ByteBufferOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteBufferOutputStream.java index b4c0effbb..d49edb910 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ByteBufferOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ByteBufferOutputStream.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/CachedResource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/CachedResource.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/CachedResource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/CachedResource.java index d526bc9bf..9112e227c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/CachedResource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/CachedResource.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.*; import java.net.MalformedURLException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/CodeException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/CodeException.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/util/CodeException.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/CodeException.java index 033c357ed..18d34328a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/CodeException.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/CodeException.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; /* ------------------------------------------------------------ */ /** Code Exception. diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ComponentEvent.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ComponentEvent.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/ComponentEvent.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ComponentEvent.java index 3ec84463b..dd3fdbddf 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ComponentEvent.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ComponentEvent.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.EventObject; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ComponentListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ComponentListener.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/ComponentListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ComponentListener.java index d7e6c13ea..000da092f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ComponentListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ComponentListener.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.EventListener; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Container.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/Container.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java index e81028744..1c2782f22 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Container.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Container.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.http.HttpContext; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; import java.io.Serializable; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Credential.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Credential.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/Credential.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Credential.java index 27e95dee7..ffd57b95d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Credential.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Credential.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.security.MessageDigest; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/DateCache.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/DateCache.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/DateCache.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/DateCache.java index 2fe05b8a6..f6e908086 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/DateCache.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/DateCache.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/EventProvider.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/EventProvider.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/util/EventProvider.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/EventProvider.java index f86e94b79..e117afcd4 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/EventProvider.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/EventProvider.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.EventListener; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/FileResource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/FileResource.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/util/FileResource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/FileResource.java index 90fecfb1f..6b54cffd5 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/FileResource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/FileResource.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.*; import java.net.*; @@ -46,7 +46,7 @@ public class FileResource extends URLResource { __checkAliases= "true".equalsIgnoreCase - (System.getProperty("org.browsermob.proxy.jetty.util.FileResource.checkAliases","true")); + (System.getProperty("FileResource.checkAliases","true")); if (__checkAliases) log.info("Checking Resource aliases"); @@ -94,7 +94,7 @@ public static boolean getCheckAliases() // Assume that File.toURL produced unencoded chars. So try // encoding them. String urls= - "file:"+ org.browsermob.proxy.jetty.util.URI.encodePath(url.toString().substring(5)); + "file:"+ net.lightbody.bmp.proxy.jetty.util.URI.encodePath(url.toString().substring(5)); _file =new File(new URI(urls)); } catch (Exception e2) @@ -133,7 +133,7 @@ public Resource addPath(String path) } else { - path = org.browsermob.proxy.jetty.util.URI.canonicalPath(path); + path = net.lightbody.bmp.proxy.jetty.util.URI.canonicalPath(path); // treat all paths being added as relative String rel=path; @@ -144,7 +144,7 @@ public Resource addPath(String path) r=new FileResource(newFile.toURI().toURL(),null,newFile); } - String encoded= org.browsermob.proxy.jetty.util.URI.encodePath(path); + String encoded= net.lightbody.bmp.proxy.jetty.util.URI.encodePath(path); int expected=r._urlString.length()-encoded.length(); int index = r._urlString.lastIndexOf(encoded, expected); diff --git a/src/main/java/org/browsermob/proxy/jetty/util/IO.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/IO.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/util/IO.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/IO.java index 0135fcc1f..866688a7d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/IO.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/IO.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.*; @@ -38,7 +38,7 @@ public class IO extends ThreadPool CRLF_BYTES = {(byte)'\015',(byte)'\012'}; /* ------------------------------------------------------------------- */ - public static int bufferSize = Integer.getInteger("org.browsermob.proxy.jetty.util.IO.bufferSize", 8192).intValue(); + public static int bufferSize = Integer.getInteger("IO.bufferSize", 8192).intValue(); /* ------------------------------------------------------------------- */ private static class Singleton { diff --git a/src/main/java/org/browsermob/proxy/jetty/util/InetAddrPort.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/InetAddrPort.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/InetAddrPort.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/InetAddrPort.java index 3f17d5a78..344c450d0 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/InetAddrPort.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/InetAddrPort.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.Serializable; import java.net.InetAddress; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/JarFileResource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/JarFileResource.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/JarFileResource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/JarFileResource.java index 27e192505..45acf7657 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/JarFileResource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/JarFileResource.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.File; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/JarResource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/JarResource.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/JarResource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/JarResource.java index 87d9bbf8d..16e2653df 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/JarResource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/JarResource.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.File; import java.io.FileOutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/KeyPairTool.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/KeyPairTool.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/KeyPairTool.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/KeyPairTool.java index 8580d6f99..5fda69f48 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/KeyPairTool.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/KeyPairTool.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.*; import java.security.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LazyList.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LazyList.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/LazyList.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LazyList.java index 942068f2b..4cb2edc18 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LazyList.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LazyList.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycle.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycle.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/util/LifeCycle.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycle.java index 1515aa481..761984fc7 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycle.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycle.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util;/* ------------------------------------------------------------ */ +package net.lightbody.bmp.proxy.jetty.util;/* ------------------------------------------------------------ */ /** A component LifeCycle. * Represents the life cycle interface for an abstract * software component. diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycleEvent.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleEvent.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/LifeCycleEvent.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleEvent.java index 9eaca1ce5..af68f20ce 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycleEvent.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleEvent.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.EventObject; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycleListener.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleListener.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/LifeCycleListener.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleListener.java index b8258f690..32feea9e0 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycleListener.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleListener.java @@ -13,7 +13,7 @@ //limitations under the License. //======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.EventListener; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycleThread.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleThread.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/util/LifeCycleThread.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleThread.java index 29868d9ff..7b843f1e3 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LifeCycleThread.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LifeCycleThread.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.InterruptedIOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LineInput.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LineInput.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/LineInput.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LineInput.java index a9a56f4dd..51ada856f 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LineInput.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LineInput.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Loader.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Loader.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/Loader.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Loader.java index a1870471f..21601f9ee 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Loader.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Loader.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; /* ------------------------------------------------------------ */ /** ClassLoader Helper. diff --git a/src/main/java/org/browsermob/proxy/jetty/util/LogSupport.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LogSupport.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/util/LogSupport.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LogSupport.java index 76a25791f..53292da14 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/LogSupport.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/LogSupport.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import org.apache.commons.logging.Log; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/MultiException.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/MultiException.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/MultiException.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/MultiException.java index 36909b38f..29f533ce5 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/MultiException.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/MultiException.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.PrintStream; import java.io.PrintWriter; import java.util.List; @@ -106,9 +106,9 @@ public void ifExceptionThrowMulti() public String toString() { if (LazyList.size(nested)>0) - return "org.browsermob.proxy.jetty.util.MultiException"+ + return "MultiException"+ LazyList.getList(nested); - return "org.browsermob.proxy.jetty.util.MultiException[]"; + return "MultiException[]"; } /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/util/MultiMap.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/MultiMap.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/MultiMap.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/MultiMap.java index cb7acc151..5168627fe 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/MultiMap.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/MultiMap.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Observed.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Observed.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/Observed.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Observed.java index 877fc2e81..2b4839352 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Observed.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Observed.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.Observable; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/OutputObserver.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/OutputObserver.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/util/OutputObserver.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/OutputObserver.java index 082816b10..8c53c2e3d 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/OutputObserver.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/OutputObserver.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.IOException; import java.io.OutputStream; @@ -21,7 +21,7 @@ /* ------------------------------------------------------------ */ /** Observer output events. * - * @see org.browsermob.proxy.jetty.http.HttpOutputStream + * @see net.lightbody.bmp.proxy.jetty.http.HttpOutputStream * @version $Id: OutputObserver.java,v 1.3 2004/05/09 20:32:49 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ diff --git a/src/main/java/org/browsermob/proxy/jetty/util/PKCS12Import.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/PKCS12Import.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/PKCS12Import.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/PKCS12Import.java index d88f1b587..566b17aee 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/PKCS12Import.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/PKCS12Import.java @@ -4,7 +4,7 @@ // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.*; import java.security.Key; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Password.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Password.java similarity index 97% rename from src/main/java/org/browsermob/proxy/jetty/util/Password.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Password.java index 1dbd0b1d9..1235389f2 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Password.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Password.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; @@ -219,7 +219,7 @@ public static void main(String[] arg) { if (arg.length!=1 && arg.length!=2 ) { - System.err.println("Usage - java org.browsermob.proxy.jetty.util.Password [] "); + System.err.println("Usage - java Password [] "); System.err.println("If the password is ?, the user will be prompted for the password"); System.exit(1); } diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Pool.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Pool.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/Pool.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Pool.java index 929e8d156..fde52e2ba 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Pool.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Pool.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.Serializable; import java.util.HashMap; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Primitive.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Primitive.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/Primitive.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Primitive.java index c685a965b..22f428e38 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Primitive.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Primitive.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/QuotedStringTokenizer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/QuotedStringTokenizer.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/QuotedStringTokenizer.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/QuotedStringTokenizer.java index 0cd65a052..4046bc0bf 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/QuotedStringTokenizer.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/QuotedStringTokenizer.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.NoSuchElementException; import java.util.StringTokenizer; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/Resource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Resource.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/Resource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Resource.java index 8fb6fcee5..7b2258933 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/Resource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/Resource.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.*; import java.net.MalformedURLException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/RolloverFileOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/RolloverFileOutputStream.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/RolloverFileOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/RolloverFileOutputStream.java index ca0f15d82..31485d315 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/RolloverFileOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/RolloverFileOutputStream.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.*; import java.lang.ref.WeakReference; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/SingletonList.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/SingletonList.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/SingletonList.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/SingletonList.java index ebf7c5174..33834aff8 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/SingletonList.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/SingletonList.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.util.AbstractList; import java.util.Iterator; import java.util.ListIterator; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/StringBufferWriter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringBufferWriter.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/StringBufferWriter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringBufferWriter.java index c44fa8d3a..cb3d5eaef 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/StringBufferWriter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringBufferWriter.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/StringMap.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringMap.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/StringMap.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringMap.java index 758d3fa49..ede78d7d2 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/StringMap.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringMap.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.Externalizable; import java.util.*; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/StringUtil.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringUtil.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/StringUtil.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringUtil.java index 4231d1518..b24b1b246 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/StringUtil.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/StringUtil.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; // ==================================================================== /** Fast String Utilities. diff --git a/src/main/java/org/browsermob/proxy/jetty/util/TempByteHolder.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TempByteHolder.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/TempByteHolder.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TempByteHolder.java index 18d87ec03..54f7ef6a4 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/TempByteHolder.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TempByteHolder.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.File; import java.io.IOException; @@ -322,7 +322,7 @@ public void readFrom(java.io.InputStream is) throws IOException { * Create tempfile if it does not already exist */ private void createTempFile() throws IOException { - _tempfilef = File.createTempFile("org.browsermob.proxy.jetty.util.TempByteHolder-",".tmp",_temp_directory).getCanonicalFile(); + _tempfilef = File.createTempFile("TempByteHolder-",".tmp",_temp_directory).getCanonicalFile(); _tempfilef.deleteOnExit(); _tempfile = new RandomAccessFile(_tempfilef,"rw"); } diff --git a/src/main/java/org/browsermob/proxy/jetty/util/TestCase.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TestCase.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/TestCase.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TestCase.java index ac1e18c53..db5830257 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/TestCase.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TestCase.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.InputStream; import java.util.Enumeration; @@ -44,7 +44,7 @@ * Test.report(); * * - * @see org.browsermob.proxy.jetty.util.Code + * @see net.lightbody.bmp.proxy.jetty.util.Code * @version $Id: TestCase.java,v 1.8 2005/08/13 00:01:28 gregwilkins Exp $ * @author Greg Wilkins */ @@ -58,7 +58,7 @@ public class TestCase private static final char[] spaces = " ".toCharArray(); static final String SelfFailTest = - "org.browsermob.proxy.jetty.util.TestCase all fail"; + "TestCase all fail"; /*-------------------------------------------------------------------*/ private String testCase; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ThreadPool.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ThreadPool.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/ThreadPool.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ThreadPool.java index 5dcae17a4..ccd4dce76 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ThreadPool.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ThreadPool.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.Serializable; /* ------------------------------------------------------------ */ @@ -39,8 +39,8 @@ public class ThreadPool implements LifeCycle,Serializable { static Log log=LogFactory.getLog(ThreadPool.class); static private int __pool=0; - public static final String __DAEMON="org.browsermob.proxy.jetty.util.ThreadPool.daemon"; - public static final String __PRIORITY="org.browsermob.proxy.jetty.util.ThreadPool.priority"; + public static final String __DAEMON="ThreadPool.daemon"; + public static final String __PRIORITY="ThreadPool.priority"; /* ------------------------------------------------------------------- */ private Pool _pool; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/ThreadedServer.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ThreadedServer.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/ThreadedServer.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ThreadedServer.java index 182055bc5..f2ec4f886 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/ThreadedServer.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/ThreadedServer.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/TypeUtil.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TypeUtil.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/TypeUtil.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TypeUtil.java index 47451026a..9bccdc5e7 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/TypeUtil.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/TypeUtil.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -160,7 +160,7 @@ public class TypeUtil /* ------------------------------------------------------------ */ private static int intCacheSize= - Integer.getInteger("org.browsermob.proxy.jetty.util.TypeUtil.IntegerCacheSize",600).intValue(); + Integer.getInteger("TypeUtil.IntegerCacheSize",600).intValue(); private static Integer[] integerCache = new Integer[intCacheSize]; private static String[] integerStrCache = new String[intCacheSize]; private static Integer minusOne = new Integer(-1); diff --git a/src/main/java/org/browsermob/proxy/jetty/util/URI.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/URI.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/URI.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/URI.java index de1184c93..da08e896a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/URI.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/URI.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.UnsupportedEncodingException; import java.util.Collections; @@ -40,7 +40,7 @@ public class URI { private static Log log = LogFactory.getLog(URI.class); - public static final String __CHARSET=System.getProperty("org.browsermob.proxy.jetty.util.URI.charset",StringUtil.__UTF_8); + public static final String __CHARSET=System.getProperty("URI.charset",StringUtil.__UTF_8); public static final boolean __CHARSET_IS_DEFAULT=__CHARSET.equals(StringUtil.__UTF_8); /* ------------------------------------------------------------ */ diff --git a/src/main/java/org/browsermob/proxy/jetty/util/URLResource.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/URLResource.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/URLResource.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/URLResource.java index b24b70a5e..02f194515 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/URLResource.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/URLResource.java @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.File; import java.io.IOException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/UnixCrypt.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/UnixCrypt.java similarity index 96% rename from src/main/java/org/browsermob/proxy/jetty/util/UnixCrypt.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/UnixCrypt.java index fb4db3bc3..65519076a 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/UnixCrypt.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/UnixCrypt.java @@ -1,474 +1,474 @@ -/* - * @(#)UnixCrypt.java 0.9 96/11/25 - * - * Copyright (c) 1996 Aki Yoshida. All rights reserved. - * - * Permission to use, copy, modify and distribute this software - * for non-commercial or commercial purposes and without fee is - * hereby granted provided that this copyright notice appears in - * all copies. - */ - -/** - * Unix crypt(3C) utility - * - * @version 0.9, 11/25/96 - * @author Aki Yoshida - */ - -/** - * modified April 2001 - * by Iris Van den Broeke, Daniel Deville - */ - -package org.browsermob.proxy.jetty.util; - -/* ------------------------------------------------------------ */ -/** Unix Crypt. - * Implements the one way cryptography used by Unix systems for - * simple password protection. - * @version $Id: UnixCrypt.java,v 1.5 2004/10/11 00:28:41 gregwilkins Exp $ - * @author Greg Wilkins (gregw) - */ -public class UnixCrypt extends Object -{ - - /* (mostly) Standard DES Tables from Tom Truscott */ - private static final byte[] IP = { /* initial permutation */ - 58, 50, 42, 34, 26, 18, 10, 2, - 60, 52, 44, 36, 28, 20, 12, 4, - 62, 54, 46, 38, 30, 22, 14, 6, - 64, 56, 48, 40, 32, 24, 16, 8, - 57, 49, 41, 33, 25, 17, 9, 1, - 59, 51, 43, 35, 27, 19, 11, 3, - 61, 53, 45, 37, 29, 21, 13, 5, - 63, 55, 47, 39, 31, 23, 15, 7}; - - /* The final permutation is the inverse of IP - no table is necessary */ - private static final byte[] ExpandTr = { /* expansion operation */ - 32, 1, 2, 3, 4, 5, - 4, 5, 6, 7, 8, 9, - 8, 9, 10, 11, 12, 13, - 12, 13, 14, 15, 16, 17, - 16, 17, 18, 19, 20, 21, - 20, 21, 22, 23, 24, 25, - 24, 25, 26, 27, 28, 29, - 28, 29, 30, 31, 32, 1}; - - private static final byte[] PC1 = { /* permuted choice table 1 */ - 57, 49, 41, 33, 25, 17, 9, - 1, 58, 50, 42, 34, 26, 18, - 10, 2, 59, 51, 43, 35, 27, - 19, 11, 3, 60, 52, 44, 36, - - 63, 55, 47, 39, 31, 23, 15, - 7, 62, 54, 46, 38, 30, 22, - 14, 6, 61, 53, 45, 37, 29, - 21, 13, 5, 28, 20, 12, 4}; - - private static final byte[] Rotates = { /* PC1 rotation schedule */ - 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; - - - private static final byte[] PC2 = { /* permuted choice table 2 */ - 9, 18, 14, 17, 11, 24, 1, 5, - 22, 25, 3, 28, 15, 6, 21, 10, - 35, 38, 23, 19, 12, 4, 26, 8, - 43, 54, 16, 7, 27, 20, 13, 2, - - 0, 0, 41, 52, 31, 37, 47, 55, - 0, 0, 30, 40, 51, 45, 33, 48, - 0, 0, 44, 49, 39, 56, 34, 53, - 0, 0, 46, 42, 50, 36, 29, 32}; - - private static final byte[][] S = { /* 48->32 bit substitution tables */ - /* S[1] */ - {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, - 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, - 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, - 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, - /* S[2] */ - {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, - 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, - 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, - 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, - /* S[3] */ - {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, - 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, - 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, - 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, - /* S[4] */ - {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, - 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, - 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, - 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, - /* S[5] */ - {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, - 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, - 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, - 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, - /* S[6] */ - {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, - 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, - 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, - 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, - /* S[7] */ - {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, - 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, - 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, - 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, - /* S[8] */ - {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, - 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, - 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, - 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}; - - private static final byte[] P32Tr = { /* 32-bit permutation function */ - 16, 7, 20, 21, - 29, 12, 28, 17, - 1, 15, 23, 26, - 5, 18, 31, 10, - 2, 8, 24, 14, - 32, 27, 3, 9, - 19, 13, 30, 6, - 22, 11, 4, 25}; - - private static final byte[] CIFP = { /* compressed/interleaved permutation */ - 1, 2, 3, 4, 17, 18, 19, 20, - 5, 6, 7, 8, 21, 22, 23, 24, - 9, 10, 11, 12, 25, 26, 27, 28, - 13, 14, 15, 16, 29, 30, 31, 32, - - 33, 34, 35, 36, 49, 50, 51, 52, - 37, 38, 39, 40, 53, 54, 55, 56, - 41, 42, 43, 44, 57, 58, 59, 60, - 45, 46, 47, 48, 61, 62, 63, 64}; - - private static final byte[] ITOA64 = { /* 0..63 => ascii-64 */ - (byte)'.',(byte) '/',(byte) '0',(byte) '1',(byte) '2',(byte) '3',(byte) '4',(byte) '5', - (byte)'6',(byte) '7',(byte) '8',(byte) '9',(byte) 'A',(byte) 'B',(byte) 'C',(byte) 'D', - (byte)'E',(byte) 'F',(byte) 'G',(byte) 'H',(byte) 'I',(byte) 'J',(byte) 'K',(byte) 'L', - (byte)'M',(byte) 'N',(byte) 'O',(byte) 'P',(byte) 'Q',(byte) 'R',(byte) 'S',(byte) 'T', - (byte)'U',(byte) 'V',(byte) 'W',(byte) 'X',(byte) 'Y',(byte) 'Z',(byte) 'a',(byte) 'b', - (byte)'c',(byte) 'd',(byte) 'e',(byte) 'f',(byte) 'g',(byte) 'h',(byte) 'i',(byte) 'j', - (byte)'k',(byte) 'l',(byte) 'm',(byte) 'n',(byte) 'o',(byte) 'p',(byte) 'q',(byte) 'r', - (byte)'s',(byte) 't',(byte) 'u',(byte) 'v',(byte) 'w',(byte) 'x',(byte) 'y',(byte) 'z'}; - - /* ===== Tables that are initialized at run time ==================== */ - - private static byte[] A64TOI = new byte[128]; /* ascii-64 => 0..63 */ - - /* Initial key schedule permutation */ - private static long[][] PC1ROT = new long[16][16]; - - /* Subsequent key schedule rotation permutations */ - private static long[][][] PC2ROT = new long[2][16][16]; - - /* Initial permutation/expansion table */ - private static long[][] IE3264 = new long[8][16]; - - /* Table that combines the S, P, and E operations. */ - private static long[][] SPE = new long[8][64]; - - /* compressed/interleaved => final permutation table */ - private static long[][] CF6464 = new long[16][16]; - - - /* ==================================== */ - - static { - byte[] perm = new byte[64]; - byte[] temp = new byte[64]; - - // inverse table. - for (int i=0; i<64; i++) A64TOI[ITOA64[i]] = (byte)i; - - // PC1ROT - bit reverse, then PC1, then Rotate, then PC2 - for (int i=0; i<64; i++) perm[i] = (byte)0;; - for (int i=0; i<64; i++) { - int k; - if ((k = (int)PC2[i]) == 0) continue; - k += Rotates[0]-1; - if ((k%28) < Rotates[0]) k -= 28; - k = (int)PC1[k]; - if (k > 0) { - k--; - k = (k|0x07) - (k&0x07); - k++; - } - perm[i] = (byte)k; - } - init_perm(PC1ROT, perm, 8); - - // PC2ROT - PC2 inverse, then Rotate, then PC2 - for (int j=0; j<2; j++) { - int k; - for (int i=0; i<64; i++) perm[i] = temp[i] = 0; - for (int i=0; i<64; i++) { - if ((k = (int)PC2[i]) == 0) continue; - temp[k-1] = (byte)(i+1); - } - for (int i=0; i<64; i++) { - if ((k = (int)PC2[i]) == 0) continue; - k += j; - if ((k%28) <= j) k -= 28; - perm[i] = temp[k]; - } - - init_perm(PC2ROT[j], perm, 8); - } - - // Bit reverse, intial permupation, expantion - for (int i=0; i<8; i++) { - for (int j=0; j<8; j++) { - int k = (j < 2)? 0: IP[ExpandTr[i*6+j-2]-1]; - if (k > 32) k -= 32; - else if (k > 0) k--; - if (k > 0) { - k--; - k = (k|0x07) - (k&0x07); - k++; - } - perm[i*8+j] = (byte)k; - } - } - - init_perm(IE3264, perm, 8); - - // Compression, final permutation, bit reverse - for (int i=0; i<64; i++) { - int k = IP[CIFP[i]-1]; - if (k > 0) { - k--; - k = (k|0x07) - (k&0x07); - k++; - } - perm[k-1] = (byte)(i+1); - } - - init_perm(CF6464, perm, 8); - - // SPE table - for (int i=0; i<48; i++) - perm[i] = P32Tr[ExpandTr[i]-1]; - for (int t=0; t<8; t++) { - for (int j=0; j<64; j++) { - int k = (((j >> 0) & 0x01) << 5) | (((j >> 1) & 0x01) << 3) | - (((j >> 2) & 0x01) << 2) | (((j >> 3) & 0x01) << 1) | - (((j >> 4) & 0x01) << 0) | (((j >> 5) & 0x01) << 4); - k = S[t][k]; - k = (((k >> 3) & 0x01) << 0) | (((k >> 2) & 0x01) << 1) | - (((k >> 1) & 0x01) << 2) | (((k >> 0) & 0x01) << 3); - for (int i=0; i<32; i++) temp[i] = 0; - for (int i=0; i<4; i++) temp[4*t+i] = (byte)((k >> i) & 0x01); - long kk = 0; - for (int i=24; --i>=0; ) kk = ((kk<<1) | - ((long)temp[perm[i]-1])<<32 | - ((long)temp[perm[i+24]-1])); - - SPE[t][j] = to_six_bit(kk); - } - } - } - - /** - * You can't call the constructer. - */ - private UnixCrypt() { } - - /** - * Returns the transposed and split code of a 24-bit code - * into a 4-byte code, each having 6 bits. - */ - private static int to_six_bit(int num) { - return (((num << 26) & 0xfc000000) | ((num << 12) & 0xfc0000) | - ((num >> 2) & 0xfc00) | ((num >> 16) & 0xfc)); - } - - /** - * Returns the transposed and split code of two 24-bit code - * into two 4-byte code, each having 6 bits. - */ - private static long to_six_bit(long num) { - return (((num << 26) & 0xfc000000fc000000L) | ((num << 12) & 0xfc000000fc0000L) | - ((num >> 2) & 0xfc000000fc00L) | ((num >> 16) & 0xfc000000fcL)); - } - - /** - * Returns the permutation of the given 64-bit code with - * the specified permutataion table. - */ - private static long perm6464(long c, long[][]p) { - long out = 0L; - for (int i=8; --i>=0; ) { - int t = (int)(0x00ff & c); - c >>= 8; - long tp = p[i<<1][t&0x0f]; - out |= tp; - tp = p[(i<<1)+1][t>>4]; - out |= tp; - } - return out; - } - - /** - * Returns the permutation of the given 32-bit code with - * the specified permutataion table. - */ - private static long perm3264(int c, long[][]p) { - long out = 0L; - for (int i=4; --i>=0; ) { - int t = (0x00ff & c); - c >>= 8; - long tp = p[i<<1][t&0x0f]; - out |= tp; - tp = p[(i<<1)+1][t>>4]; - out |= tp; - } - return out; - } - - /** - * Returns the key schedule for the given key. - */ - private static long[] des_setkey(long keyword) { - long K = perm6464(keyword, PC1ROT); - long[] KS = new long[16]; - KS[0] = K&~0x0303030300000000L; - - for (int i=1; i<16; i++) { - KS[i] = K; - K = perm6464(K, PC2ROT[Rotates[i]-1]); - - KS[i] = K&~0x0303030300000000L; - } - return KS; - } - - /** - * Returns the DES encrypted code of the given word with the specified - * environment. - */ - private static long des_cipher(long in, int salt, int num_iter, long[] KS) { - salt = to_six_bit(salt); - long L = in; - long R = L; - L &= 0x5555555555555555L; - R = (R & 0xaaaaaaaa00000000L) | ((R >> 1) & 0x0000000055555555L); - L = ((((L << 1) | (L << 32)) & 0xffffffff00000000L) | - ((R | (R >> 32)) & 0x00000000ffffffffL)); - - L = perm3264((int)(L>>32), IE3264); - R = perm3264((int)(L&0xffffffff), IE3264); - - while (--num_iter >= 0) { - for (int loop_count=0; loop_count<8; loop_count++) { - long kp; - long B; - long k; - - kp = KS[(loop_count<<1)]; - k = ((R>>32) ^ R) & salt & 0xffffffffL; - k |= (k<<32); - B = (k ^ R ^ kp); - - L ^= (SPE[0][(int)((B>>58)&0x3f)] ^ SPE[1][(int)((B>>50)&0x3f)] ^ - SPE[2][(int)((B>>42)&0x3f)] ^ SPE[3][(int)((B>>34)&0x3f)] ^ - SPE[4][(int)((B>>26)&0x3f)] ^ SPE[5][(int)((B>>18)&0x3f)] ^ - SPE[6][(int)((B>>10)&0x3f)] ^ SPE[7][(int)((B>>2)&0x3f)]); - - kp = KS[(loop_count<<1)+1]; - k = ((L>>32) ^ L) & salt & 0xffffffffL; - k |= (k<<32); - B = (k ^ L ^ kp); - - R ^= (SPE[0][(int)((B>>58)&0x3f)] ^ SPE[1][(int)((B>>50)&0x3f)] ^ - SPE[2][(int)((B>>42)&0x3f)] ^ SPE[3][(int)((B>>34)&0x3f)] ^ - SPE[4][(int)((B>>26)&0x3f)] ^ SPE[5][(int)((B>>18)&0x3f)] ^ - SPE[6][(int)((B>>10)&0x3f)] ^ SPE[7][(int)((B>>2)&0x3f)]); - } - // swap L and R - L ^= R; - R ^= L; - L ^= R; - } - L = ((((L>>35) & 0x0f0f0f0fL) | (((L&0xffffffff)<<1) & 0xf0f0f0f0L))<<32 | - (((R>>35) & 0x0f0f0f0fL) | (((R&0xffffffff)<<1) & 0xf0f0f0f0L))); - - L = perm6464(L, CF6464); - - return L; - } - - /** - * Initializes the given permutation table with the mapping table. - */ - private static void init_perm(long[][] perm, byte[] p,int chars_out) { - for (int k=0; k>2; - l = 1<<(l&0x03); - for (int j=0; j<16; j++) { - int s = ((k&0x07)+((7-(k>>3))<<3)); - if ((j & l) != 0x00) perm[i][j] |= (1L<=0;) { - char c = (i < setting.length())? setting.charAt(i): '.'; - cryptresult[i] = (byte)c; - salt = (salt<<6) | (0x00ff&A64TOI[c]); - } - - long rsltblock = des_cipher(constdatablock, salt, 25, KS); - - cryptresult[12] = ITOA64[(((int)rsltblock)<<2)&0x3f]; - rsltblock >>= 4; - for (int i=12; --i>=2; ) { - cryptresult[i] = ITOA64[((int)rsltblock)&0x3f]; - rsltblock >>= 6; - } - - return new String(cryptresult, 0x00, 0, 13); - } - - public static void main(String[] arg) - { - if (arg.length!=2) - { - System.err.println("Usage - java org.browsermob.proxy.jetty.util.UnixCrypt "); - System.exit(1); - } - - System.err.println("Crypt="+crypt(arg[0],arg[1])); - } - -} - +/* + * @(#)UnixCrypt.java 0.9 96/11/25 + * + * Copyright (c) 1996 Aki Yoshida. All rights reserved. + * + * Permission to use, copy, modify and distribute this software + * for non-commercial or commercial purposes and without fee is + * hereby granted provided that this copyright notice appears in + * all copies. + */ + +/** + * Unix crypt(3C) utility + * + * @version 0.9, 11/25/96 + * @author Aki Yoshida + */ + +/** + * modified April 2001 + * by Iris Van den Broeke, Daniel Deville + */ + +package net.lightbody.bmp.proxy.jetty.util; + +/* ------------------------------------------------------------ */ +/** Unix Crypt. + * Implements the one way cryptography used by Unix systems for + * simple password protection. + * @version $Id: UnixCrypt.java,v 1.5 2004/10/11 00:28:41 gregwilkins Exp $ + * @author Greg Wilkins (gregw) + */ +public class UnixCrypt extends Object +{ + + /* (mostly) Standard DES Tables from Tom Truscott */ + private static final byte[] IP = { /* initial permutation */ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7}; + + /* The final permutation is the inverse of IP - no table is necessary */ + private static final byte[] ExpandTr = { /* expansion operation */ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1}; + + private static final byte[] PC1 = { /* permuted choice table 1 */ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4}; + + private static final byte[] Rotates = { /* PC1 rotation schedule */ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; + + + private static final byte[] PC2 = { /* permuted choice table 2 */ + 9, 18, 14, 17, 11, 24, 1, 5, + 22, 25, 3, 28, 15, 6, 21, 10, + 35, 38, 23, 19, 12, 4, 26, 8, + 43, 54, 16, 7, 27, 20, 13, 2, + + 0, 0, 41, 52, 31, 37, 47, 55, + 0, 0, 30, 40, 51, 45, 33, 48, + 0, 0, 44, 49, 39, 56, 34, 53, + 0, 0, 46, 42, 50, 36, 29, 32}; + + private static final byte[][] S = { /* 48->32 bit substitution tables */ + /* S[1] */ + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + /* S[2] */ + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + /* S[3] */ + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + /* S[4] */ + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + /* S[5] */ + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + /* S[6] */ + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + /* S[7] */ + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + /* S[8] */ + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}; + + private static final byte[] P32Tr = { /* 32-bit permutation function */ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25}; + + private static final byte[] CIFP = { /* compressed/interleaved permutation */ + 1, 2, 3, 4, 17, 18, 19, 20, + 5, 6, 7, 8, 21, 22, 23, 24, + 9, 10, 11, 12, 25, 26, 27, 28, + 13, 14, 15, 16, 29, 30, 31, 32, + + 33, 34, 35, 36, 49, 50, 51, 52, + 37, 38, 39, 40, 53, 54, 55, 56, + 41, 42, 43, 44, 57, 58, 59, 60, + 45, 46, 47, 48, 61, 62, 63, 64}; + + private static final byte[] ITOA64 = { /* 0..63 => ascii-64 */ + (byte)'.',(byte) '/',(byte) '0',(byte) '1',(byte) '2',(byte) '3',(byte) '4',(byte) '5', + (byte)'6',(byte) '7',(byte) '8',(byte) '9',(byte) 'A',(byte) 'B',(byte) 'C',(byte) 'D', + (byte)'E',(byte) 'F',(byte) 'G',(byte) 'H',(byte) 'I',(byte) 'J',(byte) 'K',(byte) 'L', + (byte)'M',(byte) 'N',(byte) 'O',(byte) 'P',(byte) 'Q',(byte) 'R',(byte) 'S',(byte) 'T', + (byte)'U',(byte) 'V',(byte) 'W',(byte) 'X',(byte) 'Y',(byte) 'Z',(byte) 'a',(byte) 'b', + (byte)'c',(byte) 'd',(byte) 'e',(byte) 'f',(byte) 'g',(byte) 'h',(byte) 'i',(byte) 'j', + (byte)'k',(byte) 'l',(byte) 'm',(byte) 'n',(byte) 'o',(byte) 'p',(byte) 'q',(byte) 'r', + (byte)'s',(byte) 't',(byte) 'u',(byte) 'v',(byte) 'w',(byte) 'x',(byte) 'y',(byte) 'z'}; + + /* ===== Tables that are initialized at run time ==================== */ + + private static byte[] A64TOI = new byte[128]; /* ascii-64 => 0..63 */ + + /* Initial key schedule permutation */ + private static long[][] PC1ROT = new long[16][16]; + + /* Subsequent key schedule rotation permutations */ + private static long[][][] PC2ROT = new long[2][16][16]; + + /* Initial permutation/expansion table */ + private static long[][] IE3264 = new long[8][16]; + + /* Table that combines the S, P, and E operations. */ + private static long[][] SPE = new long[8][64]; + + /* compressed/interleaved => final permutation table */ + private static long[][] CF6464 = new long[16][16]; + + + /* ==================================== */ + + static { + byte[] perm = new byte[64]; + byte[] temp = new byte[64]; + + // inverse table. + for (int i=0; i<64; i++) A64TOI[ITOA64[i]] = (byte)i; + + // PC1ROT - bit reverse, then PC1, then Rotate, then PC2 + for (int i=0; i<64; i++) perm[i] = (byte)0;; + for (int i=0; i<64; i++) { + int k; + if ((k = (int)PC2[i]) == 0) continue; + k += Rotates[0]-1; + if ((k%28) < Rotates[0]) k -= 28; + k = (int)PC1[k]; + if (k > 0) { + k--; + k = (k|0x07) - (k&0x07); + k++; + } + perm[i] = (byte)k; + } + init_perm(PC1ROT, perm, 8); + + // PC2ROT - PC2 inverse, then Rotate, then PC2 + for (int j=0; j<2; j++) { + int k; + for (int i=0; i<64; i++) perm[i] = temp[i] = 0; + for (int i=0; i<64; i++) { + if ((k = (int)PC2[i]) == 0) continue; + temp[k-1] = (byte)(i+1); + } + for (int i=0; i<64; i++) { + if ((k = (int)PC2[i]) == 0) continue; + k += j; + if ((k%28) <= j) k -= 28; + perm[i] = temp[k]; + } + + init_perm(PC2ROT[j], perm, 8); + } + + // Bit reverse, intial permupation, expantion + for (int i=0; i<8; i++) { + for (int j=0; j<8; j++) { + int k = (j < 2)? 0: IP[ExpandTr[i*6+j-2]-1]; + if (k > 32) k -= 32; + else if (k > 0) k--; + if (k > 0) { + k--; + k = (k|0x07) - (k&0x07); + k++; + } + perm[i*8+j] = (byte)k; + } + } + + init_perm(IE3264, perm, 8); + + // Compression, final permutation, bit reverse + for (int i=0; i<64; i++) { + int k = IP[CIFP[i]-1]; + if (k > 0) { + k--; + k = (k|0x07) - (k&0x07); + k++; + } + perm[k-1] = (byte)(i+1); + } + + init_perm(CF6464, perm, 8); + + // SPE table + for (int i=0; i<48; i++) + perm[i] = P32Tr[ExpandTr[i]-1]; + for (int t=0; t<8; t++) { + for (int j=0; j<64; j++) { + int k = (((j >> 0) & 0x01) << 5) | (((j >> 1) & 0x01) << 3) | + (((j >> 2) & 0x01) << 2) | (((j >> 3) & 0x01) << 1) | + (((j >> 4) & 0x01) << 0) | (((j >> 5) & 0x01) << 4); + k = S[t][k]; + k = (((k >> 3) & 0x01) << 0) | (((k >> 2) & 0x01) << 1) | + (((k >> 1) & 0x01) << 2) | (((k >> 0) & 0x01) << 3); + for (int i=0; i<32; i++) temp[i] = 0; + for (int i=0; i<4; i++) temp[4*t+i] = (byte)((k >> i) & 0x01); + long kk = 0; + for (int i=24; --i>=0; ) kk = ((kk<<1) | + ((long)temp[perm[i]-1])<<32 | + ((long)temp[perm[i+24]-1])); + + SPE[t][j] = to_six_bit(kk); + } + } + } + + /** + * You can't call the constructer. + */ + private UnixCrypt() { } + + /** + * Returns the transposed and split code of a 24-bit code + * into a 4-byte code, each having 6 bits. + */ + private static int to_six_bit(int num) { + return (((num << 26) & 0xfc000000) | ((num << 12) & 0xfc0000) | + ((num >> 2) & 0xfc00) | ((num >> 16) & 0xfc)); + } + + /** + * Returns the transposed and split code of two 24-bit code + * into two 4-byte code, each having 6 bits. + */ + private static long to_six_bit(long num) { + return (((num << 26) & 0xfc000000fc000000L) | ((num << 12) & 0xfc000000fc0000L) | + ((num >> 2) & 0xfc000000fc00L) | ((num >> 16) & 0xfc000000fcL)); + } + + /** + * Returns the permutation of the given 64-bit code with + * the specified permutataion table. + */ + private static long perm6464(long c, long[][]p) { + long out = 0L; + for (int i=8; --i>=0; ) { + int t = (int)(0x00ff & c); + c >>= 8; + long tp = p[i<<1][t&0x0f]; + out |= tp; + tp = p[(i<<1)+1][t>>4]; + out |= tp; + } + return out; + } + + /** + * Returns the permutation of the given 32-bit code with + * the specified permutataion table. + */ + private static long perm3264(int c, long[][]p) { + long out = 0L; + for (int i=4; --i>=0; ) { + int t = (0x00ff & c); + c >>= 8; + long tp = p[i<<1][t&0x0f]; + out |= tp; + tp = p[(i<<1)+1][t>>4]; + out |= tp; + } + return out; + } + + /** + * Returns the key schedule for the given key. + */ + private static long[] des_setkey(long keyword) { + long K = perm6464(keyword, PC1ROT); + long[] KS = new long[16]; + KS[0] = K&~0x0303030300000000L; + + for (int i=1; i<16; i++) { + KS[i] = K; + K = perm6464(K, PC2ROT[Rotates[i]-1]); + + KS[i] = K&~0x0303030300000000L; + } + return KS; + } + + /** + * Returns the DES encrypted code of the given word with the specified + * environment. + */ + private static long des_cipher(long in, int salt, int num_iter, long[] KS) { + salt = to_six_bit(salt); + long L = in; + long R = L; + L &= 0x5555555555555555L; + R = (R & 0xaaaaaaaa00000000L) | ((R >> 1) & 0x0000000055555555L); + L = ((((L << 1) | (L << 32)) & 0xffffffff00000000L) | + ((R | (R >> 32)) & 0x00000000ffffffffL)); + + L = perm3264((int)(L>>32), IE3264); + R = perm3264((int)(L&0xffffffff), IE3264); + + while (--num_iter >= 0) { + for (int loop_count=0; loop_count<8; loop_count++) { + long kp; + long B; + long k; + + kp = KS[(loop_count<<1)]; + k = ((R>>32) ^ R) & salt & 0xffffffffL; + k |= (k<<32); + B = (k ^ R ^ kp); + + L ^= (SPE[0][(int)((B>>58)&0x3f)] ^ SPE[1][(int)((B>>50)&0x3f)] ^ + SPE[2][(int)((B>>42)&0x3f)] ^ SPE[3][(int)((B>>34)&0x3f)] ^ + SPE[4][(int)((B>>26)&0x3f)] ^ SPE[5][(int)((B>>18)&0x3f)] ^ + SPE[6][(int)((B>>10)&0x3f)] ^ SPE[7][(int)((B>>2)&0x3f)]); + + kp = KS[(loop_count<<1)+1]; + k = ((L>>32) ^ L) & salt & 0xffffffffL; + k |= (k<<32); + B = (k ^ L ^ kp); + + R ^= (SPE[0][(int)((B>>58)&0x3f)] ^ SPE[1][(int)((B>>50)&0x3f)] ^ + SPE[2][(int)((B>>42)&0x3f)] ^ SPE[3][(int)((B>>34)&0x3f)] ^ + SPE[4][(int)((B>>26)&0x3f)] ^ SPE[5][(int)((B>>18)&0x3f)] ^ + SPE[6][(int)((B>>10)&0x3f)] ^ SPE[7][(int)((B>>2)&0x3f)]); + } + // swap L and R + L ^= R; + R ^= L; + L ^= R; + } + L = ((((L>>35) & 0x0f0f0f0fL) | (((L&0xffffffff)<<1) & 0xf0f0f0f0L))<<32 | + (((R>>35) & 0x0f0f0f0fL) | (((R&0xffffffff)<<1) & 0xf0f0f0f0L))); + + L = perm6464(L, CF6464); + + return L; + } + + /** + * Initializes the given permutation table with the mapping table. + */ + private static void init_perm(long[][] perm, byte[] p,int chars_out) { + for (int k=0; k>2; + l = 1<<(l&0x03); + for (int j=0; j<16; j++) { + int s = ((k&0x07)+((7-(k>>3))<<3)); + if ((j & l) != 0x00) perm[i][j] |= (1L<=0;) { + char c = (i < setting.length())? setting.charAt(i): '.'; + cryptresult[i] = (byte)c; + salt = (salt<<6) | (0x00ff&A64TOI[c]); + } + + long rsltblock = des_cipher(constdatablock, salt, 25, KS); + + cryptresult[12] = ITOA64[(((int)rsltblock)<<2)&0x3f]; + rsltblock >>= 4; + for (int i=12; --i>=2; ) { + cryptresult[i] = ITOA64[((int)rsltblock)&0x3f]; + rsltblock >>= 6; + } + + return new String(cryptresult, 0x00, 0, 13); + } + + public static void main(String[] arg) + { + if (arg.length!=2) + { + System.err.println("Usage - java UnixCrypt "); + System.exit(1); + } + + System.err.println("Crypt="+crypt(arg[0],arg[1])); + } + +} + diff --git a/src/main/java/org/browsermob/proxy/jetty/util/UrlEncoded.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/UrlEncoded.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/UrlEncoded.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/UrlEncoded.java index 080e8d514..0d9b76d76 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/UrlEncoded.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/UrlEncoded.java @@ -13,10 +13,10 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; import java.io.UnsupportedEncodingException; import java.util.Iterator; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/WriterOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/WriterOutputStream.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/util/WriterOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/WriterOutputStream.java index 7e7ca608b..6e0c1d43b 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/WriterOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/WriterOutputStream.java @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util; +package net.lightbody.bmp.proxy.jetty.util; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/jmx/LifeCycleMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/LifeCycleMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/util/jmx/LifeCycleMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/LifeCycleMBean.java index 2203e2e39..a49c1fb94 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/jmx/LifeCycleMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/LifeCycleMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util.jmx; +package net.lightbody.bmp.proxy.jetty.util.jmx; -import org.browsermob.proxy.jetty.util.LifeCycle; +import net.lightbody.bmp.proxy.jetty.util.LifeCycle; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/jmx/ModelMBeanImpl.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ModelMBeanImpl.java similarity index 99% rename from src/main/java/org/browsermob/proxy/jetty/util/jmx/ModelMBeanImpl.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ModelMBeanImpl.java index 08ceb242b..fa7c70ffc 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/jmx/ModelMBeanImpl.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ModelMBeanImpl.java @@ -13,12 +13,12 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util.jmx; +package net.lightbody.bmp.proxy.jetty.util.jmx; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; +import net.lightbody.bmp.proxy.jetty.util.TypeUtil; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LogSupport; -import org.browsermob.proxy.jetty.util.TypeUtil; import javax.management.*; import javax.management.modelmbean.*; @@ -78,7 +78,7 @@ public class ModelMBeanImpl private static HashMap __objectId = new HashMap(); - private static String __defaultDomain="org.browsermob.proxy.jetty"; + private static String __defaultDomain="net.lightbody.bmp.proxy.jetty"; protected ModelMBeanInfoSupport _beanInfo; private MBeanServer _mBeanServer; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/jmx/ThreadPoolMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ThreadPoolMBean.java similarity index 95% rename from src/main/java/org/browsermob/proxy/jetty/util/jmx/ThreadPoolMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ThreadPoolMBean.java index dfe185e6e..97e162549 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/jmx/ThreadPoolMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ThreadPoolMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util.jmx; +package net.lightbody.bmp.proxy.jetty.util.jmx; -import org.browsermob.proxy.jetty.util.ThreadPool; +import net.lightbody.bmp.proxy.jetty.util.ThreadPool; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/util/jmx/ThreadedServerMBean.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ThreadedServerMBean.java similarity index 94% rename from src/main/java/org/browsermob/proxy/jetty/util/jmx/ThreadedServerMBean.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ThreadedServerMBean.java index d2771d3f7..7e9128530 100644 --- a/src/main/java/org/browsermob/proxy/jetty/util/jmx/ThreadedServerMBean.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/util/jmx/ThreadedServerMBean.java @@ -13,9 +13,9 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.util.jmx; +package net.lightbody.bmp.proxy.jetty.util.jmx; -import org.browsermob.proxy.jetty.util.ThreadedServer; +import net.lightbody.bmp.proxy.jetty.util.ThreadedServer; import javax.management.MBeanException; diff --git a/src/main/java/org/browsermob/proxy/jetty/xml/XmlConfiguration.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/xml/XmlConfiguration.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/xml/XmlConfiguration.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/xml/XmlConfiguration.java index 38dd124f1..1e6b9222c 100644 --- a/src/main/java/org/browsermob/proxy/jetty/xml/XmlConfiguration.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/xml/XmlConfiguration.java @@ -13,11 +13,11 @@ // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.xml; +package net.lightbody.bmp.proxy.jetty.xml; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.*; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -62,7 +62,7 @@ private synchronized static void initParser() throws IOException __parser = new XmlParser(); URL config13URL = XmlConfiguration.class.getClassLoader().getResource( - "org/browsermob/proxy/jetty/xml/configure_1_3.dtd"); + "net/lightbody/bmp/proxy/jetty/xml/configure_1_3.dtd"); __parser.redirectEntity("configure.dtd", config13URL); __parser.redirectEntity("configure_1_3.dtd", config13URL); __parser.redirectEntity("http://jetty.jetty.org/configure_1_3.dtd", config13URL); @@ -71,19 +71,19 @@ private synchronized static void initParser() throws IOException __parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", config13URL); URL config12URL = XmlConfiguration.class.getClassLoader().getResource( - "org/browsermob/proxy/jetty/xml/configure_1_2.dtd"); + "net/lightbody/bmp/proxy/jetty/xml/configure_1_2.dtd"); __parser.redirectEntity("configure_1_2.dtd", config12URL); __parser.redirectEntity("http://jetty.jetty.org/configure_1_2.dtd", config12URL); __parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.2//EN", config12URL); URL config11URL = XmlConfiguration.class.getClassLoader().getResource( - "org/browsermob/proxy/jetty/xml/configure_1_1.dtd"); + "net/lightbody/bmp/proxy/jetty/xml/configure_1_1.dtd"); __parser.redirectEntity("configure_1_1.dtd", config11URL); __parser.redirectEntity("http://jetty.jetty.org/configure_1_1.dtd", config11URL); __parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.1//EN", config11URL); URL config10URL = XmlConfiguration.class.getClassLoader().getResource( - "org/browsermob/proxy/jetty/xml/configure_1_0.dtd"); + "net/lightbody/bmp/proxy/jetty/xml/configure_1_0.dtd"); __parser.redirectEntity("configure_1_0.dtd", config10URL); __parser.redirectEntity("http://jetty.jetty.org/configure_1_0.dtd", config10URL); __parser.redirectEntity("-//Mort Bay Consulting//DTD Configure 1.0//EN", config10URL); @@ -665,7 +665,7 @@ else if ("URL".equals(type)) else if ("InetAddress".equals(type)) aClass = java.net.InetAddress.class; else if ("InetAddrPort".equals(type)) - aClass = org.browsermob.proxy.jetty.util.InetAddrPort.class; + aClass = net.lightbody.bmp.proxy.jetty.util.InetAddrPort.class; else aClass = Loader.loadClass(XmlConfiguration.class, type); } @@ -814,7 +814,7 @@ private Object value(Object obj, XmlParser.Node node) throws NoSuchMethodExcepti } } - if ("InetAddrPort".equals(type) || "org.browsermob.proxy.jetty.util.InetAddrPort".equals(type)) + if ("InetAddrPort".equals(type) || "InetAddrPort".equals(type)) { if (value instanceof InetAddrPort) return value; try diff --git a/src/main/java/org/browsermob/proxy/jetty/xml/XmlParser.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/xml/XmlParser.java similarity index 98% rename from src/main/java/org/browsermob/proxy/jetty/xml/XmlParser.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/xml/XmlParser.java index 40c3b09c7..7b3a61405 100644 --- a/src/main/java/org/browsermob/proxy/jetty/xml/XmlParser.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/jetty/xml/XmlParser.java @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package org.browsermob.proxy.jetty.xml; +package net.lightbody.bmp.proxy.jetty.xml; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; +import net.lightbody.bmp.proxy.jetty.util.LazyList; +import net.lightbody.bmp.proxy.jetty.util.LogSupport; import org.apache.commons.logging.Log; -import org.browsermob.proxy.jetty.log.LogFactory; -import org.browsermob.proxy.jetty.util.LazyList; -import org.browsermob.proxy.jetty.util.LogSupport; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; @@ -57,7 +57,7 @@ public XmlParser() try { SAXParserFactory factory=SAXParserFactory.newInstance(); - boolean notValidating=Boolean.getBoolean("org.browsermob.proxy.jetty.xml.XmlParser.NotValidating"); + boolean notValidating=Boolean.getBoolean("XmlParser.NotValidating"); factory.setValidating(!notValidating); _parser=factory.newSAXParser(); try diff --git a/src/main/java/org/browsermob/proxy/selenium/ExtendedKeyUsageConstants.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ExtendedKeyUsageConstants.java similarity index 96% rename from src/main/java/org/browsermob/proxy/selenium/ExtendedKeyUsageConstants.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ExtendedKeyUsageConstants.java index eb7889e8c..494736bbc 100644 --- a/src/main/java/org/browsermob/proxy/selenium/ExtendedKeyUsageConstants.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ExtendedKeyUsageConstants.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.selenium; +package net.lightbody.bmp.proxy.selenium; /** * This interface stores commonly used OIDs for X.509v3 certificates. diff --git a/src/main/java/org/browsermob/proxy/selenium/KeyStoreManager.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/KeyStoreManager.java similarity index 66% rename from src/main/java/org/browsermob/proxy/selenium/KeyStoreManager.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/KeyStoreManager.java index 2f2d59293..06871d5bf 100644 --- a/src/main/java/org/browsermob/proxy/selenium/KeyStoreManager.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/KeyStoreManager.java @@ -1,16 +1,39 @@ -package org.browsermob.proxy.selenium; +package net.lightbody.bmp.proxy.selenium; +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator; +import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.browsermob.proxy.jetty.log.LogFactory; -import java.io.*; -import java.security.*; -import java.security.cert.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.UnrecoverableEntryException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; import java.util.HashMap; /** - * This is the main entry point into the Cybervillains CA. + * This is the main entry point into the impersonated CA. * * This class handles generation, storage and the persistent * mapping of input to duplicated certificates and mapped public @@ -37,13 +60,10 @@ public class KeyStoreManager { private final String CERTMAP_SER_FILE = "certmap.ser"; private final String SUBJMAP_SER_FILE = "subjmap.ser"; - private final String EXPORTED_CERT_NAME = "cybervillainsCA.cer"; - private final char[] _keypassword = "password".toCharArray(); private final char[] _keystorepass = "password".toCharArray(); - private final String _caPrivateKeystore = "cybervillainsCA.jks"; - private final String _caCertAlias = "signingCert"; - public static final String _caPrivKeyAlias = "signingCertPrivKey"; + private final String _caPrivateKeystore = "ca-keystore-rsa.p12"; + public static final String _caPrivKeyAlias = "key"; X509Certificate _caCert; PrivateKey _caPrivKey; @@ -52,20 +72,13 @@ public class KeyStoreManager { private HashMap _rememberedPrivateKeys; private HashMap _mappedPublicKeys; private HashMap _certMap; - private HashMap _subjectMap; + private HashMap hostnameThumbprintMap; private final String KEYMAP_SER_FILE = "keymap.ser"; private final String PUB_KEYMAP_SER_FILE = "pubkeymap.ser"; public final String RSA_KEYGEN_ALGO = "RSA"; public final String DSA_KEYGEN_ALGO = "DSA"; - public final KeyPairGenerator _rsaKpg; - public final KeyPairGenerator _dsaKpg; - - private SecureRandom _sr; - - - private boolean persistImmediately = true; private File root; @@ -74,20 +87,6 @@ public class KeyStoreManager { public KeyStoreManager(File root) { this.root = root; - Security.insertProviderAt(new BouncyCastleProvider(), 2); - - _sr = new SecureRandom(); - - try - { - _rsaKpg = KeyPairGenerator.getInstance(RSA_KEYGEN_ALGO); - _dsaKpg = KeyPairGenerator.getInstance(DSA_KEYGEN_ALGO); - } - catch(Throwable t) - { - throw new Error(t); - } - try { File privKeys = new File(root, KEYMAP_SER_FILE); @@ -135,15 +134,9 @@ public KeyStoreManager(File root) { throw new Error(e); } - - - _rsaKpg.initialize(1024, _sr); - _dsaKpg.initialize(1024, _sr); - - try { - _ks = KeyStore.getInstance("JKS"); + _ks = KeyStore.getInstance("PKCS12"); reloadKeystore(); } @@ -200,13 +193,13 @@ public KeyStoreManager(File root) { if(!file.exists()) { - _subjectMap = new HashMap(); + hostnameThumbprintMap = new HashMap(); } else { ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); // Deserialize the object - _subjectMap = (HashMap)in.readObject(); + hostnameThumbprintMap = (HashMap)in.readObject(); in.close(); } @@ -226,73 +219,23 @@ public KeyStoreManager(File root) { } - private void reloadKeystore() throws FileNotFoundException, IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableKeyException { + private void reloadKeystore() throws FileNotFoundException, IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableEntryException { InputStream is = new FileInputStream(new File(root, _caPrivateKeystore)); - if (is != null) { - _ks.load(is, _keystorepass); - _caCert = (X509Certificate)_ks.getCertificate(_caCertAlias); - _caPrivKey = (PrivateKey)_ks.getKey(_caPrivKeyAlias, _keypassword); - } + _ks.load(is, _keystorepass); + + KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) _ks.getEntry(_caPrivKeyAlias, new KeyStore.PasswordProtection(_keypassword)); + _caCert = (X509Certificate) privateKeyEntry.getCertificate(); + _caPrivKey = privateKeyEntry.getPrivateKey(); } /** * Creates, writes and loads a new keystore and CA root certificate. */ protected void createKeystore() { - - java.security.cert.Certificate signingCert = null; - PrivateKey caPrivKey = null; - if(_caCert == null || _caPrivKey == null) { - try - { - log.debug("Keystore or signing cert & keypair not found. Generating..."); - - KeyPair caKeypair = getRSAKeyPair(); - caPrivKey = caKeypair.getPrivate(); - signingCert = CertificateCreator.createTypicalMasterCert(caKeypair); - - log.debug("Done generating signing cert"); - log.debug(signingCert); - - _ks.load(null, _keystorepass); - - _ks.setCertificateEntry(_caCertAlias, signingCert); - _ks.setKeyEntry(_caPrivKeyAlias, caPrivKey, _keypassword, new java.security.cert.Certificate[] {signingCert}); - - File caKsFile = new File(root, _caPrivateKeystore); - - OutputStream os = new FileOutputStream(caKsFile); - _ks.store(os, _keystorepass); - - log.debug("Wrote JKS keystore to: " + - caKsFile.getAbsolutePath()); - - // also export a .cer that can be imported as a trusted root - // to disable all warning dialogs for interception - - File signingCertFile = new File(root, EXPORTED_CERT_NAME); - - FileOutputStream cerOut = new FileOutputStream(signingCertFile); - - byte[] buf = signingCert.getEncoded(); - - log.debug("Wrote signing cert to: " + signingCertFile.getAbsolutePath()); - - cerOut.write(buf); - cerOut.flush(); - cerOut.close(); - - _caCert = (X509Certificate)signingCert; - _caPrivKey = caPrivKey; - } - catch(Exception e) - { - log.error("Fatal error creating/storing keystore or signing cert.", e); - throw new Error(e); - } + throw new RuntimeException("Legacy ProxyServer implementation does not support dynamic CA generation"); } else { @@ -314,12 +257,13 @@ protected void createKeystore() { public synchronized void addCertAndPrivateKey(String hostname, final X509Certificate cert, final PrivateKey privKey) throws KeyStoreException, CertificateException, NoSuchAlgorithmException { -// String alias = ThumbprintUtil.getThumbprint(cert); - - _ks.deleteEntry(hostname); + try { + _ks.deleteEntry(hostname); + } catch (KeyStoreException e) { + // ignore errors deleting the existing entry + } - _ks.setCertificateEntry(hostname, cert); - _ks.setKeyEntry(hostname, privKey, _keypassword, new java.security.cert.Certificate[] {cert}); + _ks.setKeyEntry(hostname, privKey, _keypassword, new java.security.cert.Certificate[] {cert, getSigningCert()}); if(persistImmediately) { @@ -366,7 +310,6 @@ public synchronized X509Certificate getCertificateByAlias(final String alias) th /** * Returns the aliased certificate. Certificates are aliased by their hostname. * @see ThumbprintUtil - * @param alias * @return * @throws KeyStoreException * @throws UnrecoverableKeyException @@ -381,7 +324,7 @@ public synchronized X509Certificate getCertificateByAlias(final String alias) th */ public synchronized X509Certificate getCertificateByHostname(final String hostname) throws KeyStoreException, CertificateParsingException, InvalidKeyException, CertificateExpiredException, CertificateNotYetValidException, SignatureException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException, UnrecoverableKeyException{ - String alias = _subjectMap.get(getSubjectForHostname(hostname)); + String alias = hostnameThumbprintMap.get(hostname); if(alias != null) { return (X509Certificate)_ks.getCertificate(alias); @@ -427,99 +370,6 @@ public void setPersistImmediately(final boolean persistImmediately) { this.persistImmediately = persistImmediately; } - /** - * This method returns the duplicated certificate mapped to the passed in cert, or - * creates and returns one if no mapping has yet been performed. If a naked public - * key has already been mapped that matches the key in the cert, the already mapped - * keypair will be reused for the mapped cert. - * @param cert - * @return - * @throws CertificateEncodingException - * @throws InvalidKeyException - * @throws CertificateException - * @throws CertificateNotYetValidException - * @throws NoSuchAlgorithmException - * @throws NoSuchProviderException - * @throws SignatureException - * @throws KeyStoreException - * @throws UnrecoverableKeyException - */ - public synchronized X509Certificate getMappedCertificate(final X509Certificate cert) - throws CertificateEncodingException, - InvalidKeyException, - CertificateException, - CertificateNotYetValidException, - NoSuchAlgorithmException, - NoSuchProviderException, - SignatureException, - KeyStoreException, - UnrecoverableKeyException - { - - String thumbprint = ThumbprintUtil.getThumbprint(cert); - - String mappedCertThumbprint = _certMap.get(thumbprint); - - if(mappedCertThumbprint == null) - { - - // Check if we've already mapped this public key from a KeyValue - PublicKey mappedPk = getMappedPublicKey(cert.getPublicKey()); - PrivateKey privKey; - - if(mappedPk == null) - { - PublicKey pk = cert.getPublicKey(); - - String algo = pk.getAlgorithm(); - - KeyPair kp; - - if(algo.equals("RSA")) { - kp = getRSAKeyPair(); - } - else if(algo.equals("DSA")) { - kp = getDSAKeyPair(); - } - else - { - throw new InvalidKeyException("Key algorithm " + algo + " not supported."); - } - mappedPk = kp.getPublic(); - privKey = kp.getPrivate(); - - mapPublicKeys(cert.getPublicKey(), mappedPk); - } - else - { - privKey = getPrivateKey(mappedPk); - } - - - X509Certificate replacementCert = - CertificateCreator.mitmDuplicateCertificate( - cert, - mappedPk, - getSigningCert(), - getSigningPrivateKey()); - - addCertAndPrivateKey(null, replacementCert, privKey); - - mappedCertThumbprint = ThumbprintUtil.getThumbprint(replacementCert); - - _certMap.put(thumbprint, mappedCertThumbprint); - _certMap.put(mappedCertThumbprint, thumbprint); - _subjectMap.put(replacementCert.getSubjectX500Principal().getName(), thumbprint); - - if(persistImmediately) { - persist(); - } - return replacementCert; - } - return getCertificateByAlias(mappedCertThumbprint); - - } - /** * This method returns the mapped certificate for a hostname, or generates a "standard" * SSL server certificate issued by the CA to the supplied subject if no mapping has been @@ -541,24 +391,22 @@ else if(algo.equals("DSA")) { */ public X509Certificate getMappedCertificateForHostname(String hostname) throws CertificateParsingException, InvalidKeyException, CertificateExpiredException, CertificateNotYetValidException, SignatureException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException, UnrecoverableKeyException { - String subject = getSubjectForHostname(hostname); - - String thumbprint = _subjectMap.get(subject); + String thumbprint = hostnameThumbprintMap.get(hostname); if(thumbprint == null) { - KeyPair kp = getRSAKeyPair(); + KeyPair kp = new RSAKeyGenerator().generate(); - X509Certificate newCert = CertificateCreator.generateStdSSLServerCertificate(kp.getPublic(), + X509Certificate newCert = ServerCertificateCreator.generateStdSSLServerCertificate(kp, getSigningCert(), getSigningPrivateKey(), - subject); + hostname); addCertAndPrivateKey(hostname, newCert, kp.getPrivate()); thumbprint = ThumbprintUtil.getThumbprint(newCert); - _subjectMap.put(subject, thumbprint); + hostnameThumbprintMap.put(hostname, thumbprint); if(persistImmediately) { persist(); @@ -572,12 +420,6 @@ public X509Certificate getMappedCertificateForHostname(String hostname) throws C } - private String getSubjectForHostname(String hostname) { - //String subject = "C=USA, ST=WA, L=Seattle, O=Cybervillains, OU=CertificationAutority, CN=" + hostname + ", EmailAddress=evilRoot@cybervillains.com"; - String subject = "CN=" + hostname + ", OU=Test, O=CyberVillainsCA, L=Seattle, S=Washington, C=US"; - return subject; - } - private synchronized void persistCertMap() { try { ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(root, CERTMAP_SER_FILE))); @@ -599,7 +441,7 @@ private synchronized void persistCertMap() { private synchronized void persistSubjectMap() { try { ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(root, SUBJMAP_SER_FILE))); - out.writeObject(_subjectMap); + out.writeObject(hostnameThumbprintMap); out.flush(); out.close(); } catch (FileNotFoundException e) { @@ -631,31 +473,6 @@ public synchronized PrivateKey getPrivateKeyForLocalCert(final X509Certificate c return (PrivateKey)_ks.getKey(thumbprint, _keypassword); } - - /** - * Generate an RSA Key Pair - * @return - */ - public KeyPair getRSAKeyPair() - { - KeyPair kp = _rsaKpg.generateKeyPair(); - rememberKeyPair(kp); - return kp; - - } - - /** - * Generate a DSA Key Pair - * @return - */ - public KeyPair getDSAKeyPair() - { - KeyPair kp = _dsaKpg.generateKeyPair(); - rememberKeyPair(kp); - return kp; - } - - private synchronized void persistPublicKeyMap() { try { ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(root, PUB_KEYMAP_SER_FILE))); @@ -710,7 +527,6 @@ public synchronized void mapPublicKeys(final PublicKey original, final PublicKey * later see an X509Data with the same public key, we shouldn't split this * in our MITM impl. So when creating a new cert, we should check if we've already * assigned a substitute key and re-use it, and vice-versa. - * @param pk * @return */ public synchronized PublicKey getMappedPublicKey(final PublicKey original) diff --git a/src/main/java/org/browsermob/proxy/selenium/SeleniumProxyHandler.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/SeleniumProxyHandler.java old mode 100644 new mode 100755 similarity index 88% rename from src/main/java/org/browsermob/proxy/selenium/SeleniumProxyHandler.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/SeleniumProxyHandler.java index a7d267f61..f5601cc1b --- a/src/main/java/org/browsermob/proxy/selenium/SeleniumProxyHandler.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/SeleniumProxyHandler.java @@ -1,21 +1,53 @@ -package org.browsermob.proxy.selenium; - -import org.browsermob.proxy.jetty.http.*; -import org.browsermob.proxy.jetty.http.handler.AbstractHttpHandler; -import org.browsermob.proxy.jetty.util.IO; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.StringMap; -import org.browsermob.proxy.jetty.util.URI; -import org.browsermob.proxy.util.ResourceExtractor; -import org.browsermob.proxy.util.TrustEverythingSSLTrustManager; +package net.lightbody.bmp.proxy.selenium; + +import net.lightbody.bmp.proxy.jetty.http.HttpConnection; +import net.lightbody.bmp.proxy.jetty.http.HttpException; +import net.lightbody.bmp.proxy.jetty.http.HttpFields; +import net.lightbody.bmp.proxy.jetty.http.HttpMessage; +import net.lightbody.bmp.proxy.jetty.http.HttpRequest; +import net.lightbody.bmp.proxy.jetty.http.HttpResponse; +import net.lightbody.bmp.proxy.jetty.http.HttpServer; +import net.lightbody.bmp.proxy.jetty.http.HttpTunnel; +import net.lightbody.bmp.proxy.jetty.http.SslListener; +import net.lightbody.bmp.proxy.jetty.http.handler.AbstractHttpHandler; +import net.lightbody.bmp.proxy.jetty.util.IO; +import net.lightbody.bmp.proxy.jetty.util.InetAddrPort; +import net.lightbody.bmp.proxy.jetty.util.StringMap; +import net.lightbody.bmp.proxy.jetty.util.URI; +import net.lightbody.bmp.proxy.util.TrustEverythingSSLTrustManager; +import net.lightbody.bmp.util.DeleteDirectoryTask; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.Socket; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; /* ------------------------------------------------------------ */ @@ -30,7 +62,7 @@ * @version $Id: ProxyHandler.java,v 1.34 2005/10/05 13:32:59 gregwilkins Exp $ */ public class SeleniumProxyHandler extends AbstractHttpHandler { - private static Logger log = Logger.getLogger(SeleniumProxyHandler.class.getName()); + private static final Logger log = LoggerFactory.getLogger(SeleniumProxyHandler.class); protected Set _proxyHostsWhiteList; protected Set _proxyHostsBlackList; @@ -40,13 +72,14 @@ public class SeleniumProxyHandler extends AbstractHttpHandler { private final Map _sslMap = new LinkedHashMap(); @SuppressWarnings("unused") private String sslKeystorePath; - private boolean useCyberVillains = true; + private boolean useImpersonatingCA = true; private boolean trustAllSSLCertificates = false; private final String dontInjectRegex; private final String debugURL; private final boolean proxyInjectionMode; private final boolean forceProxyChain; private boolean fakeCertsGenerated; + private final List deleteDirectoryTasks = Collections.synchronizedList(new ArrayList()); // see docs for the lock object on SeleniumServer for information on this and why it is IMPORTANT! private Object shutdownLock; @@ -233,7 +266,7 @@ public void handle(String pathInContext, String pathParams, HttpRequest request, response.getOutputStream().close(); } catch (Exception e) { - log.log(Level.FINE, "Could not proxy " + uri, e); + log.warn("Could not proxy " + uri, e); if (!response.isCommitted()) response.sendError(HttpResponse.__400_Bad_Request, "Could not proxy " + uri + "\n" + e); } @@ -262,7 +295,7 @@ private boolean isSeleniumUrl(String url) { } protected long proxyPlainTextRequest(URL url, String pathInContext, String pathParams, HttpRequest request, HttpResponse response) throws IOException { - log.fine("PROXY URL=" + url); + log.debug("PROXY URL=" + url); URLConnection connection = url.openConnection(); if(System.getProperty("http.proxyHost") != null && @@ -381,7 +414,7 @@ protected long proxyPlainTextRequest(URL url, String pathInContext, String pathP response.setReason(http.getResponseMessage()); String contentType = http.getContentType(); - log.fine("Content-Type is: " + contentType); + log.debug("Content-Type is: " + contentType); } if (proxy_in == null) { @@ -420,7 +453,7 @@ protected long proxyPlainTextRequest(URL url, String pathInContext, String pathP long bytesCopied = -1; request.setHandled(true); if (proxy_in != null) { - bytesCopied = ModifiedIO.copy(proxy_in, response.getOutputStream()); + bytesCopied = IOUtils.copyLarge(proxy_in, response.getOutputStream()); } return bytesCopied; @@ -445,7 +478,7 @@ public void handleConnect(String pathInContext, String pathParams, HttpRequest r URI uri = request.getURI(); try { - log.fine("CONNECT: " + uri); + log.debug("CONNECT: " + uri); InetAddrPort addrPort; // When logging, we'll attempt to send messages to hosts that don't exist if (uri.toString().endsWith(".selenium.doesnotexist:443")) { @@ -498,7 +531,7 @@ public void handleConnect(String pathInContext, String pathParams, HttpRequest r } } catch (Exception e) { - log.log(Level.FINE, "error during handleConnect", e); + log.error("error during handleConnect", e); response.sendError(HttpResponse.__500_Internal_Server_Error, e.toString()); } } @@ -514,8 +547,8 @@ protected SslRelay getSslRelayOrCreateNew(URI uri, InetAddrPort addrPort, HttpSe listener = new SslRelay(addrPort); - if (useCyberVillains) { - wireUpSslWithCyberVilliansCA(host, listener); + if (useImpersonatingCA) { + wireUpSslWithImpersonationCA(host, listener); } else { wireUpSslWithRemoteService(host, listener); } @@ -570,27 +603,47 @@ protected void wireUpSslWithRemoteService(String host, SslRelay listener) throws listener.setNukeDirOrFile(keystore); } - protected void wireUpSslWithCyberVilliansCA(String host, SslRelay listener) { + protected void wireUpSslWithImpersonationCA(String host, SslRelay listener) { try { - File root = File.createTempFile("seleniumSslSupport", host); - root.delete(); - root.mkdirs(); + // see https://github.com/webmetrics/browsermob-proxy/issues/105 + String escapedHost = host.replace('*', '_'); + + Path tempDir = Files.createTempDirectory("seleniumSslSupport" + escapedHost); + final File root = tempDir.toFile(); - ResourceExtractor.extractResourcePath(getClass(), "/sslSupport", root); + // delete the temp directory when the VM stops or aborts + DeleteDirectoryTask deleteDirectoryTask = new DeleteDirectoryTask(tempDir); + deleteDirectoryTasks.add(deleteDirectoryTask); + Runtime.getRuntime().addShutdownHook(new Thread(deleteDirectoryTask)); + // copy the CA keystore to the temp directory from the classpath + Path rsaKeystorePath = tempDir.resolve("ca-keystore-rsa.p12"); + + Files.copy(getClass().getResourceAsStream("/sslSupport/ca-keystore-rsa.p12"), rsaKeystorePath); KeyStoreManager mgr = new KeyStoreManager(root); mgr.getCertificateByHostname(host); mgr.getKeyStore().deleteEntry(KeyStoreManager._caPrivKeyAlias); mgr.persist(); - listener.setKeystore(new File(root, "cybervillainsCA.jks").getAbsolutePath()); + listener.setKeystore(rsaKeystorePath.toFile().getAbsolutePath()); listener.setNukeDirOrFile(root); } catch (Exception e) { + log.error("Error occurred wiring CA", e); throw new RuntimeException(e); } } + public void cleanSslWithCyberVilliansCA(){ + synchronized (deleteDirectoryTasks) { + if (!deleteDirectoryTasks.isEmpty()) { + for (DeleteDirectoryTask task : deleteDirectoryTasks) { + task.run(); + } + } + } + } + /* ------------------------------------------------------------ */ protected HttpTunnel newHttpTunnel(HttpRequest request, HttpResponse response, InetAddress iaddr, int port, int timeoutMS) throws IOException { try { @@ -600,7 +653,7 @@ protected HttpTunnel newHttpTunnel(HttpRequest request, HttpResponse response, I return new HttpTunnel(socket, null, null); } catch (IOException e) { - log.log(Level.FINE, "Exception thrown", e); + log.warn("Exception creating new HTTP tunnel", e); response.sendError(HttpResponse.__400_Bad_Request); return null; } @@ -752,7 +805,6 @@ protected void customizeRequest(Socket socket, HttpRequest request) { super.customizeRequest(socket,request); URI uri=request.getURI(); - // Convert the URI to a proxy URL // // NOTE: Don't just add a host + port to the request URI, since this causes the URI to @@ -765,13 +817,8 @@ protected void customizeRequest(Socket socket, HttpRequest request) public void stop() throws InterruptedException { super.stop(); - if (nukeDirOrFile != null) { - if (nukeDirOrFile.isDirectory()) { - LauncherUtils.recursivelyDeleteDir(nukeDirOrFile); - } else { - nukeDirOrFile.delete(); - } - } + FileUtils.deleteQuietly(nukeDirOrFile); } } + } diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ServerCertificateCreator.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ServerCertificateCreator.java new file mode 100644 index 000000000..de5dd0358 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ServerCertificateCreator.java @@ -0,0 +1,46 @@ +package net.lightbody.bmp.proxy.selenium; + +import net.lightbody.bmp.mitm.CertificateAndKey; +import net.lightbody.bmp.mitm.CertificateInfo; +import net.lightbody.bmp.mitm.CertificateInfoGenerator; +import net.lightbody.bmp.mitm.HostnameCertificateInfoGenerator; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.SecurityProviderTool; +import net.lightbody.bmp.mitm.util.MitmConstants; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Collections; + +/** + * Utility to create server certificates for legacy {@link net.lightbody.bmp.proxy.ProxyServer} MITM. + */ +public class ServerCertificateCreator { + /** + * Use the default hostname-impersonating certificate info generator that the MITM module provides. + */ + private static final CertificateInfoGenerator CERT_INFO_GENERATOR = new HostnameCertificateInfoGenerator(); + + /** + * Use the default (JDK where available, otherwise BC) security provider to generate certificates. + */ + private static final SecurityProviderTool SECURITY_PROVIDER = new DefaultSecurityProviderTool(); + + public static X509Certificate generateStdSSLServerCertificate( + KeyPair newPublicAndPrivateKey, + X509Certificate caCert, + PrivateKey caPrivateKey, + String hostname) { + CertificateInfo certificateInfo = CERT_INFO_GENERATOR.generate(Collections.singletonList(hostname), null); + + CertificateAndKey newServerCert = SECURITY_PROVIDER.createServerCertificate( + certificateInfo, + caCert, + caPrivateKey, + newPublicAndPrivateKey, + MitmConstants.DEFAULT_MESSAGE_DIGEST); + + return newServerCert.getCertificate(); + } +} diff --git a/src/main/java/org/browsermob/proxy/selenium/ThumbprintUtil.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ThumbprintUtil.java similarity index 96% rename from src/main/java/org/browsermob/proxy/selenium/ThumbprintUtil.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ThumbprintUtil.java index 04c5f2514..afb959a9c 100644 --- a/src/main/java/org/browsermob/proxy/selenium/ThumbprintUtil.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/selenium/ThumbprintUtil.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.selenium; +package net.lightbody.bmp.proxy.selenium; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.util.encoders.Base64; diff --git a/src/main/java/org/browsermob/proxy/util/CappedByteArrayOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/CappedByteArrayOutputStream.java similarity index 96% rename from src/main/java/org/browsermob/proxy/util/CappedByteArrayOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/CappedByteArrayOutputStream.java index f2165add2..e6855d33f 100644 --- a/src/main/java/org/browsermob/proxy/util/CappedByteArrayOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/CappedByteArrayOutputStream.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.util; +package net.lightbody.bmp.proxy.util; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/org/browsermob/proxy/util/ChainableWriter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ChainableWriter.java similarity index 97% rename from src/main/java/org/browsermob/proxy/util/ChainableWriter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ChainableWriter.java index 3883f2978..44396a6b9 100644 --- a/src/main/java/org/browsermob/proxy/util/ChainableWriter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ChainableWriter.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.util; +package net.lightbody.bmp.proxy.util; import java.io.IOException; import java.io.Writer; diff --git a/src/main/java/org/browsermob/proxy/util/ClonedInputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ClonedInputStream.java similarity index 71% rename from src/main/java/org/browsermob/proxy/util/ClonedInputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ClonedInputStream.java index 6d14a2223..a0503d992 100644 --- a/src/main/java/org/browsermob/proxy/util/ClonedInputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ClonedInputStream.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.util; +package net.lightbody.bmp.proxy.util; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -13,24 +13,30 @@ public ClonedInputStream(InputStream is) { } public int read() throws IOException { - int resp = is.read(); - os.write(resp); + int byteRead = is.read(); + if (byteRead > -1) { + os.write(byteRead); + } - return resp; + return byteRead; } public int read(byte[] b) throws IOException { - int resp = is.read(b); - os.write(b); + int respLen = is.read(b); + if (respLen > 0) { + os.write(b, 0, respLen); + } - return resp; + return respLen; } public int read(byte[] b, int off, int len) throws IOException { - int resp = is.read(b, off, len); - os.write(b, off, len); + int respLen = is.read(b, off, len); + if (respLen > 0) { + os.write(b, off, respLen); + } - return resp; + return respLen; } public long skip(long n) throws IOException { diff --git a/src/main/java/org/browsermob/proxy/util/ClonedOutputStream.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ClonedOutputStream.java similarity index 94% rename from src/main/java/org/browsermob/proxy/util/ClonedOutputStream.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ClonedOutputStream.java index 3dd418b22..285cc7a32 100644 --- a/src/main/java/org/browsermob/proxy/util/ClonedOutputStream.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/ClonedOutputStream.java @@ -1,8 +1,7 @@ -package org.browsermob.proxy.util; +package net.lightbody.bmp.proxy.util; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; public class ClonedOutputStream extends OutputStream { diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/IOUtils.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/IOUtils.java new file mode 100644 index 000000000..c658e889a --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/IOUtils.java @@ -0,0 +1,41 @@ +package net.lightbody.bmp.proxy.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class IOUtils { + /** + * Copies the input stream to the output stream and closes both streams. Both streams are guaranteed to be closed, even if the copy + * operation throws an exception. The copy operation may throw IOException, but closing either stream will not throw IOException. + * + * @param in InputStream to read and close + * @param out OutputStream to read and close + * @throws IOException if an error occurs reading or writing to/from the streams + */ + public static void copyAndClose(InputStream in, OutputStream out) throws IOException { + try { + org.apache.commons.io.IOUtils.copy(in, out); + } finally { + org.apache.commons.io.IOUtils.closeQuietly(in); + org.apache.commons.io.IOUtils.closeQuietly(out); + } + } + + /** + * Reads and closes the input stream, converting it to a String using the UTF-8 charset. The input stream is guaranteed to be closed, even + * if the reading/conversion throws an exception. + * + * @param in UTF-8-encoded InputStream to read + * @return String of InputStream's contents + * @throws IOException if an error occurs reading from the stream + */ + public static String toStringAndClose(InputStream in) throws IOException { + try { + return org.apache.commons.io.IOUtils.toString(in, StandardCharsets.UTF_8); + } finally { + org.apache.commons.io.IOUtils.closeQuietly(in); + } + } +} diff --git a/src/main/java/org/browsermob/proxy/util/LockingChainingWriter.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/LockingChainingWriter.java similarity index 95% rename from src/main/java/org/browsermob/proxy/util/LockingChainingWriter.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/LockingChainingWriter.java index a6655737e..99333e404 100644 --- a/src/main/java/org/browsermob/proxy/util/LockingChainingWriter.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/LockingChainingWriter.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.util; +package net.lightbody.bmp.proxy.util; import java.io.File; import java.io.FileWriter; diff --git a/src/main/java/org/browsermob/proxy/util/TrustEverythingSSLTrustManager.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/TrustEverythingSSLTrustManager.java similarity index 98% rename from src/main/java/org/browsermob/proxy/util/TrustEverythingSSLTrustManager.java rename to browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/TrustEverythingSSLTrustManager.java index 0a4b44df5..39cef989a 100644 --- a/src/main/java/org/browsermob/proxy/util/TrustEverythingSSLTrustManager.java +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/proxy/util/TrustEverythingSSLTrustManager.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.util; +package net.lightbody.bmp.proxy.util; import javax.net.ssl.*; import java.security.GeneralSecurityException; diff --git a/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java b/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java new file mode 100644 index 000000000..dffe0ade9 --- /dev/null +++ b/browsermob-legacy/src/main/java/net/lightbody/bmp/util/DeleteDirectoryTask.java @@ -0,0 +1,60 @@ +package net.lightbody.bmp.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +/** + * A Runnable that deletes the specified directory. Useful as a shutdown hook. + */ +public class DeleteDirectoryTask implements Runnable { + // the static final logger is in this static inner class to allow the logger initialization code to use DeleteDirectoryTask + // without prematurely initializing the logger when loading this class. since the 'log' field is in a static inner class, it will + // only be initialized when it is actually used, instead of when the classloader loads the DeleteDirectoryTask class. + private static class LogHolder { + private static final Logger log = LoggerFactory.getLogger(DeleteDirectoryTask.class); + } + + private final Path directory; + + public DeleteDirectoryTask(Path directory) { + this.directory = directory; + } + + @Override + public void run() { + try { + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + try { + Files.delete(file); + } catch (IOException e) { + LogHolder.log.warn("Unable to delete file or directory", e); + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + try { + Files.delete(dir); + } catch (IOException e) { + LogHolder.log.warn("Unable to delete file or directory", e); + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + LogHolder.log.warn("Unable to delete file or directory", e); + } + } +} diff --git a/src/main/java/org/java_bandwidthlimiter/BandwidthLimiter.java b/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/BandwidthLimiter.java similarity index 94% rename from src/main/java/org/java_bandwidthlimiter/BandwidthLimiter.java rename to browsermob-legacy/src/main/java/org/java_bandwidthlimiter/BandwidthLimiter.java index cc1c0de78..e1a27c331 100644 --- a/src/main/java/org/java_bandwidthlimiter/BandwidthLimiter.java +++ b/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/BandwidthLimiter.java @@ -36,6 +36,10 @@ public interface BandwidthLimiter { public void setDownstreamKbps(long downstreamKbps); public void setUpstreamKbps(long upstreamKbps); + + public void setDownstreamMaxKB(long downstreamMaxKB); + + public void setUpstreamMaxKB(long upstreamMaxKB); public void setLatency(long latency); } diff --git a/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/MaximumTransferExceededException.java b/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/MaximumTransferExceededException.java new file mode 100644 index 000000000..3b00e68ca --- /dev/null +++ b/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/MaximumTransferExceededException.java @@ -0,0 +1,22 @@ +package org.java_bandwidthlimiter; + +import java.io.IOException; + +public class MaximumTransferExceededException extends IOException { + private final boolean isUpstream; + private final long limit; + + public boolean isUpstream() { + return isUpstream; + } + + public long getLimit() { + return limit; + } + + public MaximumTransferExceededException(long limit, boolean isUpstream) { + super("Maximum " + (isUpstream? "upstream" : "downstream") + " transfer allowance of " + limit + " KB exceeded."); + this.isUpstream = isUpstream; + this.limit = limit; + } +} diff --git a/src/main/java/org/java_bandwidthlimiter/StreamManager.java b/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/StreamManager.java similarity index 85% rename from src/main/java/org/java_bandwidthlimiter/StreamManager.java rename to browsermob-legacy/src/main/java/org/java_bandwidthlimiter/StreamManager.java index dcf88f26f..94f2a8bd5 100644 --- a/src/main/java/org/java_bandwidthlimiter/StreamManager.java +++ b/browsermob-legacy/src/main/java/org/java_bandwidthlimiter/StreamManager.java @@ -61,6 +61,8 @@ private class StreamParams { public long remainingBps; public long nextResetTimestamp; public long nextResetSubIntervals; + public long maxBytes; + public long remainingBytes; private long timeToNextReset() { return nextResetTimestamp - System.currentTimeMillis(); @@ -83,11 +85,11 @@ private void adjustBytes(long bytesNumber) { //even calls to setDownstreamKbps and setUpstreamKbps will be forced to honor this upperbound. private long maxBytesPerSecond; - private StreamParams downStream = new StreamParams(); - private StreamParams upStream = new StreamParams(); + private final StreamParams downStream = new StreamParams(); + private final StreamParams upStream = new StreamParams(); private long latency = 0; - private Random randomGenerator = new Random(); + private final Random randomGenerator = new Random(); /** * Create an instance of StreamManager. @@ -101,11 +103,13 @@ private void adjustBytes(long bytesNumber) { * */ public StreamManager(long maxBitsPerSecond) { - setMaxBitsPerSecondThreshold( maxBitsPerSecond ); - setDownstreamKbps(maxBitsPerSecond / 1000); - setUpstreamKbps(maxBitsPerSecond / 1000); - setPayloadPercentage(95); - disable(); + this.maxBytesPerSecond = maxBitsPerSecond/8; + setMaxBps(this.downStream, this.maxBytesPerSecond); + setMaxBps(this.upStream, this.maxBytesPerSecond); + this.actualPayloadPercentage = 0.95; + setMaxBytes(this.downStream, 0); + setMaxBytes(this.upStream, 0); + this.enabled = false; } @@ -155,6 +159,30 @@ public void setLatency(long latency) { this.latency = latency; } + /** + * Specifies how many kilobytes in total the client is allowed to download. + * When the limit is used up, MaximumTransferExceededException is thrown + * @param downstreamMaxKB + */ + @Override + public void setDownstreamMaxKB(long downstreamMaxKB) { + setMaxBytes(this.downStream, downstreamMaxKB * 1000); + } + + /** + * Specifies how many kilobytes in total the client is allowed to upload. + * When the limit is used up, MaximumTransferExceededException is thrown + * @param upstreamMaxKB + */ + @Override + public void setUpstreamMaxKB(long upstreamMaxKB) { + setMaxBytes(this.upStream, upstreamMaxKB * 1000); + } + + public long getLatency() { + return latency; + } + /** * To take into account overhead due to underlying protocols (e.g. TCP/IP) * @param payloadPercentage a ] 0 , 100] value. where 100 means that the required @@ -188,7 +216,7 @@ public InputStream registerStream(InputStream in) { * Register an output stream. * A client would then use the returned OutputStream, which will throttle * the one passed as parameter. - * * @param in The OutputStream that will be throttled + * @param out The OutputStream that will be throttled * @return a new throttled OutputStream (wrapping the one given as parameter) * */ @@ -211,6 +239,22 @@ public void setMaxBitsPerSecondThreshold(long maxBitsPerSecond) { setMaxBps(this.downStream, this.downStream.maxBps); setMaxBps(this.upStream, this.upStream.maxBps); } + + public long getMaxUpstreamKB(){ + return this.upStream.maxBytes/1000; + } + + public long getRemainingUpstreamKB(){ + return this.upStream.remainingBytes/1000; + } + + public long getMaxDownstreamKB(){ + return this.downStream.maxBytes/1000; + } + + public long getRemainingDownstreamKB(){ + return this.downStream.remainingBytes/1000; + } private void setMaxBps( StreamParams direction, long maxBps ) { synchronized (direction) { @@ -222,6 +266,13 @@ private void setMaxBps( StreamParams direction, long maxBps ) { direction.reset(); } } + + private void setMaxBytes( StreamParams direction, long maxBytes ) { + synchronized (direction) { + direction.maxBytes = maxBytes; + direction.remainingBytes = maxBytes; + } + } private long timeToNextReset(StreamParams direction) { synchronized (direction) { @@ -251,7 +302,7 @@ private int getAllowedBytesWrite(ManagedOutputStream stream, int bufferLength) { private int getAllowedBytesUnFair(StreamParams direction, int bufferLength) { //this is an unfair allocation of bytes/second because it gives as many //bytes as possible to anyone who ask for them - int allowed = 0; + int allowed; synchronized(direction) { resetCounterIfNecessary(direction); //this stream desires to read up to bufferLength bytes @@ -283,18 +334,19 @@ private int manageRead(ManagedInputStream stream, byte[] b, int off, int len) th int allowed = getAllowedBytesRead(stream, len); if(allowed > 0) { // Read a maximum of "allowed" bytes - long start = System.currentTimeMillis(); int bytesRead = stream.doRead(b, off, allowed); - long end = System.currentTimeMillis(); + + // check if we exceeded the transfer limit + if(this.downStream.maxBytes > 0 && (this.downStream.remainingBytes -= bytesRead) < 0){ + throw new MaximumTransferExceededException(getMaxDownstreamKB(), false); + } // If less than the "allowed" bytes were read, adjust how many we can still read for this period of time adjustBytes(this.downStream, allowed - bytesRead); //apply latency if it's the case, we only apply it if the last activity //happened more than the latency itself ago. - long latency = (start - stream.getLastActivity()) > this.latency ? this.latency : 0; - // sleep for the amount of time it should have taken to read the amount of bytes read - StreamManager.simulate(this.downStream.adjustedMaxBps, latency, bytesRead, end - start, stream.getRoundUp()); +// long latency = (start - stream.getLastActivity()) > this.latency ? this.latency : 0; return bytesRead; } else { long sleepTime = timeToNextReset(this.downStream); @@ -318,16 +370,19 @@ private void manageWrite(ManagedOutputStream stream, byte[] b, int off, int len) assert maxBytesPerSecond > 0; int bytesWritten = 0; - int allowed = 0; + int allowed; // we need a while loop since the write doesn't return a "written bytes" count, // rather it expects that all of them are written // hence we loop here until all of them have been written - long start = System.currentTimeMillis(); while(bytesWritten < len) { allowed = getAllowedBytesWrite(stream, len); if(allowed > 0) { stream.doWrite(b, off, allowed); - bytesWritten += allowed; + bytesWritten += allowed; + // check if we exceeded the transfer limit + if(this.upStream.maxBytes > 0 && (this.upStream.remainingBytes -= allowed) < 0){ + throw new MaximumTransferExceededException(this.getMaxUpstreamKB(), true); + } } else { long sleepTime = timeToNextReset(this.upStream); if( sleepTime > 0 ) { @@ -340,39 +395,16 @@ private void manageWrite(ManagedOutputStream stream, byte[] b, int off, int len) } } } - long end = System.currentTimeMillis(); - //sleep for the amount of time it should have taken to write the amount of bytes written - long latency = (start - stream.getLastActivity()) > this.latency ? this.latency : 0; - StreamManager.simulate(this.upStream.adjustedMaxBps, latency, bytesWritten, end - start, stream.getRoundUp()); - } - } - - // this function emulates the time a stream should take to read/write a @bytesPerSecond - // it takes a parameter @timeTaken which is the time it actually took so we can subtract it - // to adjust the waiting time - private static long simulate(long bytesPerSecond, long latency, int bytes, long timeTaken, boolean roundUp) { - if ( bytesPerSecond <= 0) { //< defensive code - return 0; - } - - // workout how much we should have waited to read this amount of bytes - double d = ((double) bytes / bytesPerSecond) * 1000; - - long expectedTime = (long) (roundUp ? Math.ceil(d) : Math.floor(d)) + latency; - long diff = expectedTime - timeTaken; //< subtract the amount of time ALREADY taken to get those bytes - - if (diff > 0) { - StreamManager.threadSleep(diff); +// long latency = (start - stream.getLastActivity()) > this.latency ? this.latency : 0; } - return diff; } private static void threadSleep(long sleepTime) { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { - Thread.interrupted(); + Thread.currentThread().interrupt(); } } @@ -385,7 +417,7 @@ private class ManagedOutputStream extends OutputStream { long lastActivity; boolean roundUp; //just an helper buffer so we don't allocate it all the time when calling void write(int b) which writes ONE byte! - private byte[] oneByteBuff = new byte[1]; + private final byte[] oneByteBuff = new byte[1]; public ManagedOutputStream(OutputStream stream, StreamManager manager) { assert manager != null; @@ -431,10 +463,12 @@ public void doWrite(byte[] b, int offset, int length) throws IOException { stream.write(b, offset, length); } + @Override public void flush() throws IOException { stream.flush(); } + @Override public void close() throws IOException { stream.close(); } @@ -446,7 +480,7 @@ private class ManagedInputStream extends InputStream { long lastActivity; boolean roundUp; //just an helper buffer so we don't allocate it all the time when calling int read() which reads ONE byte! - private byte[] oneByteBuff = new byte[1]; + private final byte[] oneByteBuff = new byte[1]; public ManagedInputStream(InputStream stream, StreamManager manager) { assert manager != null; @@ -469,17 +503,20 @@ public boolean getRoundUp() { return roundUp; } + @Override public int read() throws IOException { read(oneByteBuff, 0, 1); return oneByteBuff[0]; } + @Override public int read(byte[] b) throws IOException { int length = b.length; int bytesRead = read(b, 0, length); return bytesRead; } + @Override public int read(byte[] b, int off, int len) throws IOException { int readBytes = manager.manageRead(this, b, off, len); if( readBytes > 0 ) { @@ -496,26 +533,32 @@ public int doRead(byte[] b, int offset, int length) throws IOException { return stream.read(b, offset, length); } + @Override public long skip(long n) throws IOException { return stream.skip(n); } + @Override public int available() throws IOException { return stream.available(); } + @Override public void close() throws IOException { stream.close(); } + @Override public void mark(int readLimit) { stream.mark(readLimit); } + @Override public void reset() throws IOException { stream.reset(); } + @Override public boolean markSupported() { return stream.markSupported(); } diff --git a/browsermob-legacy/src/main/resources/net/lightbody/bmp/html/error.html b/browsermob-legacy/src/main/resources/net/lightbody/bmp/html/error.html new file mode 100644 index 000000000..14db50c22 --- /dev/null +++ b/browsermob-legacy/src/main/resources/net/lightbody/bmp/html/error.html @@ -0,0 +1,21 @@ + +%s + + + + + +

    +
    +

    %s

    +
    + +
    + +

    %s

    + +
    %s
    +
    +
    + + \ No newline at end of file diff --git a/browsermob-legacy/src/main/resources/net/lightbody/bmp/l10n/messages.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/l10n/messages.properties new file mode 100644 index 000000000..34ae81daa --- /dev/null +++ b/browsermob-legacy/src/main/resources/net/lightbody/bmp/l10n/messages.properties @@ -0,0 +1,30 @@ +# This file contains localized strings for BrowserMob Proxy messages that are displayed to users or returned to clients + +# response.* messages are sent back to the client when the proxy is unable to properly proxy a request. For example, when the proxy cannot connect to the +# remote server, or when the proxy encounters some exception while processing the server's response. +response.conn_failure.title=Unable to connect +response.conn_failure.short=Cannot establish a connection to the server at %s +response.dns_not_found.title=Server not found +response.dns_not_found.short=Cannot find the server at %s. +response.dns_not_found.long=
    • Check the address for typing errors such as ww.example.com instead of www.example.com
    • \ +
    • If you are unable to load any pages, check your computer's network connection.
    • \ +
    • If your computer or network is protected by a firewall or proxy, make sure that it is permitted to access the Web.
    +response.generic.title=An unknown error occurred +response.generic.short=An unknown error occurred while loading %s +response.generic.long=

    An unknown error occurred while loading the page.

    +response.malformed_uri.title=The address isn't valid +response.malformed_uri.short=The URL is not valid and cannot be loaded. +response.malformed_uri.long=
    • Web addresses are usually written like http://www.example.com/
    • \ +
    • Make sure that you're using forward slashes (i.e. /).
    +response.net_interrupt.title=The connection was interrupted +response.net_interrupt.short=The connection to %s was interrupted while the page was loading. +response.net_reset.title=The connection was reset +response.net_reset.short=The connection to the server was reset while the page was loading. +response.net_timeout.title=The connection has timed out +response.net_timeout.short=The server at %s is taking too long to respond. +# common page title for all errors +response.common_error.pagetitle=Problem loading page +# long description shared by several errors +response.common_error.long=
    • The site could be temporarily unavailable or too busy. Try again in a few moments.
    • \ +
    • If you are unable to load any pages, check your computer's network connection.
    • \ +
    • If your computer or network is protected by a firewall or proxy, make sure it is permitted to access the Web.
    diff --git a/src/main/resources/org/browsermob/proxy/jetty/http/ajp/jmx/mbean_en.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/ajp/jmx/mbean_en.properties similarity index 100% rename from src/main/resources/org/browsermob/proxy/jetty/http/ajp/jmx/mbean_en.properties rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/ajp/jmx/mbean_en.properties diff --git a/src/main/resources/org/browsermob/proxy/jetty/http/encoding.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/encoding.properties similarity index 100% rename from src/main/resources/org/browsermob/proxy/jetty/http/encoding.properties rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/encoding.properties diff --git a/src/main/resources/org/browsermob/proxy/jetty/http/handler/jmx/mbean_en.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/handler/jmx/mbean_en.properties similarity index 100% rename from src/main/resources/org/browsermob/proxy/jetty/http/handler/jmx/mbean_en.properties rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/handler/jmx/mbean_en.properties diff --git a/src/main/resources/org/browsermob/proxy/jetty/http/jmx/mbean_en.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/jmx/mbean_en.properties similarity index 89% rename from src/main/resources/org/browsermob/proxy/jetty/http/jmx/mbean_en.properties rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/jmx/mbean_en.properties index 281cabf76..bcc38a803 100644 --- a/src/main/resources/org/browsermob/proxy/jetty/http/jmx/mbean_en.properties +++ b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/jmx/mbean_en.properties @@ -17,17 +17,17 @@ HttpServer.destroy() = Destroy the Jetty Server and all registered listeners, HttpServer.addListener(java.lang.String) = Create and add a new HttpListener. HttpServer.addListener(java.lang.String)[0] = addrPort:The IP:port to listen on -HttpServer.addListener(org.browsermob.proxy.jetty.util.InetAddrPort) = Create and add a new HttpListener. -HttpServer.addListener(org.browsermob.proxy.jetty.util.InetAddrPort)[0] = addrPort:The IP:port to listen on -HttpServer.addListener(org.browsermob.proxy.jetty.http.HttpListener) = Add a HttpListener. -HttpServer.addListener(org.browsermob.proxy.jetty.http.HttpListener)[0] = listener:A HttpListener instance. -HttpServer.removeListener(org.browsermob.proxy.jetty.http.HttpListener) = Add a HttpListener. -HttpServer.removeListener(org.browsermob.proxy.jetty.http.HttpListener)[0] = listener:A HttpListener instance. - -HttpServer.addContext(org.browsermob.proxy.jetty.http.HttpContext) = Add a HttpContext. -HttpServer.addContext(org.browsermob.proxy.jetty.http.HttpContext)[0] =context:A HttpContext with ContextPath set. -HttpServer.removeContext(org.browsermob.proxy.jetty.http.HttpContext) = Add a HttpContext. -HttpServer.removeContext(org.browsermob.proxy.jetty.http.HttpContext)[0] =context:A HttpContext with ContextPath set. +HttpServer.addListener(net.lightbody.bmp.proxy.jetty.util.InetAddrPort) = Create and add a new HttpListener. +HttpServer.addListener(net.lightbody.bmp.proxy.jetty.util.InetAddrPort)[0] = addrPort:The IP:port to listen on +HttpServer.addListener(net.lightbody.bmp.proxy.jetty.http.HttpListener) = Add a HttpListener. +HttpServer.addListener(net.lightbody.bmp.proxy.jetty.http.HttpListener)[0] = listener:A HttpListener instance. +HttpServer.removeListener(net.lightbody.bmp.proxy.jetty.http.HttpListener) = Add a HttpListener. +HttpServer.removeListener(net.lightbody.bmp.proxy.jetty.http.HttpListener)[0] = listener:A HttpListener instance. + +HttpServer.addContext(net.lightbody.bmp.proxy.jetty.http.HttpContext) = Add a HttpContext. +HttpServer.addContext(net.lightbody.bmp.proxy.jetty.http.HttpContext)[0] =context:A HttpContext with ContextPath set. +HttpServer.removeContext(net.lightbody.bmp.proxy.jetty.http.HttpContext) = Add a HttpContext. +HttpServer.removeContext(net.lightbody.bmp.proxy.jetty.http.HttpContext)[0] =context:A HttpContext with ContextPath set. HttpServer.addContext(java.lang.String,java.lang.String) = Create and add a new context. HttpServer.addContext(java.lang.String,java.lang.String)[0] = virtualHost:Virtual host name. null for all hosts. HttpServer.addContext(java.lang.String,java.lang.String)[1] = contextPath:The path specification for the context. @@ -156,11 +156,11 @@ HttpContext.getAttribute(java.lang.String)[0]=name:The attribute name. HttpContext.getAttributeNames()=Get a list of attribute names. HttpContext.removeAttribute(java.lang.String) =Remove a context attribute. HttpContext.removeAttribute(java.lang.String)[0]=name:The attribute name. -HttpContext.addHandler(org.browsermob.proxy.jetty.http.HttpHandler) = Add a HttpHandler instance to the context. When a request is serviced by the context, each handler is called in order until it is handled. -HttpContext.addHandler(org.browsermob.proxy.jetty.http.HttpHandler)[0] = handler:The HttpHandler instance to add. -HttpContext.addHandler(org.browsermob.proxy.jetty.http.HttpHandler) = Add a HttpHandler instance to the context. When a request is serviced by the context, each handler is called in order until it is handled. -HttpContext.addHandler(int,org.browsermob.proxy.jetty.http.HttpHandler)[0] = index:The index within the context to insert the handler at. -HttpContext.addHandler(int,org.browsermob.proxy.jetty.http.HttpHandler)[1] = handler:The HttpHandler instance to add. +HttpContext.addHandler(net.lightbody.bmp.proxy.jetty.http.HttpHandler) = Add a HttpHandler instance to the context. When a request is serviced by the context, each handler is called in order until it is handled. +HttpContext.addHandler(net.lightbody.bmp.proxy.jetty.http.HttpHandler)[0] = handler:The HttpHandler instance to add. +HttpContext.addHandler(net.lightbody.bmp.proxy.jetty.http.HttpHandler) = Add a HttpHandler instance to the context. When a request is serviced by the context, each handler is called in order until it is handled. +HttpContext.addHandler(int,net.lightbody.bmp.proxy.jetty.http.HttpHandler)[0] = index:The index within the context to insert the handler at. +HttpContext.addHandler(int,net.lightbody.bmp.proxy.jetty.http.HttpHandler)[1] = handler:The HttpHandler instance to add. HttpContext.getHandler(int) = Get a context HttpHandler. HttpContext.getHandler(int)[0] = index:The index of the handler within the context. HttpContext.removeHandler(int) = Remove a context HttpHandler. diff --git a/src/main/resources/org/browsermob/proxy/jetty/http/mime.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/mime.properties similarity index 100% rename from src/main/resources/org/browsermob/proxy/jetty/http/mime.properties rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/http/mime.properties diff --git a/src/main/resources/org/browsermob/proxy/jetty/jetty/jmx/mbean_en.properties b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/jetty/jmx/mbean_en.properties similarity index 100% rename from src/main/resources/org/browsermob/proxy/jetty/jetty/jmx/mbean_en.properties rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/jetty/jmx/mbean_en.properties diff --git a/src/main/resources/org/browsermob/proxy/jetty/jetty/servlet/webdefault.xml b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/jetty/servlet/webdefault.xml similarity index 96% rename from src/main/resources/org/browsermob/proxy/jetty/jetty/servlet/webdefault.xml rename to browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/jetty/servlet/webdefault.xml index be214c74b..1dace3fbb 100644 --- a/src/main/resources/org/browsermob/proxy/jetty/jetty/servlet/webdefault.xml +++ b/browsermob-legacy/src/main/resources/net/lightbody/bmp/proxy/jetty/jetty/servlet/webdefault.xml @@ -14,7 +14,7 @@ - + @@ -37,17 +37,17 @@ @@ -89,7 +89,7 @@ default - org.browsermob.proxy.jetty.jetty.servlet.Default + net.lightbody.bmp.proxy.jetty.jetty.servlet.Default acceptRanges true @@ -249,7 +249,7 @@ - + - + + + io.netty + netty-codec-socks + + + io.netty + netty-buffer + + + io.netty + netty-codec + + + io.netty + netty-codec-http + + + io.netty + netty-common + + + io.netty + netty-handler + + + io.netty + netty-transport + + + + + + org.codehaus.groovy.modules.http-builder + http-builder + 0.7.1 + test + + + + org.hamcrest + hamcrest-library + test + + + + + \ No newline at end of file diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/exception/JavascriptCompilationException.java b/browsermob-rest/src/main/java/net/lightbody/bmp/exception/JavascriptCompilationException.java new file mode 100644 index 000000000..85ff9a08d --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/exception/JavascriptCompilationException.java @@ -0,0 +1,27 @@ +package net.lightbody.bmp.exception; + +import com.google.sitebricks.headless.Request; +import net.lightbody.bmp.filters.JavascriptRequestResponseFilter; + +/** + * Indicates that an error occurred when compiling javascript in {@link JavascriptRequestResponseFilter}, + * for use by {@link net.lightbody.bmp.proxy.bricks.ProxyResource#addRequestFilter(int, Request)} + * or {@link net.lightbody.bmp.proxy.bricks.ProxyResource#addResponseFilter(int, Request)}. + */ +public class JavascriptCompilationException extends RuntimeException { + public JavascriptCompilationException() { + super(); + } + + public JavascriptCompilationException(String message) { + super(message); + } + + public JavascriptCompilationException(String message, Throwable cause) { + super(message, cause); + } + + public JavascriptCompilationException(Throwable cause) { + super(cause); + } +} diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/exception/ProxyExistsException.java b/browsermob-rest/src/main/java/net/lightbody/bmp/exception/ProxyExistsException.java new file mode 100644 index 000000000..a49c2aa88 --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/exception/ProxyExistsException.java @@ -0,0 +1,31 @@ +package net.lightbody.bmp.exception; + +public class ProxyExistsException extends RuntimeException { + private static final long serialVersionUID = -5515796684778166504L; + + private final int port; + + public ProxyExistsException(int port) { + this.port = port; + } + + public ProxyExistsException(String message, int port) { + super(message); + this.port = port; + } + + public ProxyExistsException(String message, Throwable cause, int port) { + super(message, cause); + this.port = port; + } + + public ProxyExistsException(Throwable cause, int port) { + super(cause); + this.port = port; + } + + public int getPort() { + return port; + } + +} diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/exception/ProxyPortsExhaustedException.java b/browsermob-rest/src/main/java/net/lightbody/bmp/exception/ProxyPortsExhaustedException.java new file mode 100644 index 000000000..9889ad7d0 --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/exception/ProxyPortsExhaustedException.java @@ -0,0 +1,21 @@ +package net.lightbody.bmp.exception; + +public class ProxyPortsExhaustedException extends RuntimeException { + private static final long serialVersionUID = -6801448612785792233L; + + public ProxyPortsExhaustedException() { + super(); + } + + public ProxyPortsExhaustedException(String message, Throwable cause) { + super(message, cause); + } + + public ProxyPortsExhaustedException(String message) { + super(message); + } + + public ProxyPortsExhaustedException(Throwable cause) { + super(cause); + } +} diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/filters/JavascriptRequestResponseFilter.java b/browsermob-rest/src/main/java/net/lightbody/bmp/filters/JavascriptRequestResponseFilter.java new file mode 100644 index 000000000..a7e017f33 --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/filters/JavascriptRequestResponseFilter.java @@ -0,0 +1,91 @@ +package net.lightbody.bmp.filters; + +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.exception.JavascriptCompilationException; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +/** + * Convenience class that executes arbitrary javascript code as a {@link RequestFilter} or {@link ResponseFilter}. + */ +public class JavascriptRequestResponseFilter implements RequestFilter, ResponseFilter { + private static final Logger log = LoggerFactory.getLogger(JavascriptRequestResponseFilter.class); + + private static final ScriptEngine JAVASCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("JavaScript"); + + private CompiledScript compiledRequestFilterScript; + private CompiledScript compiledResponseFilterScript; + + public void setRequestFilterScript(String script) { + Compilable compilable = (Compilable) JAVASCRIPT_ENGINE; + try { + compiledRequestFilterScript = compilable.compile(script); + } catch (ScriptException e) { + throw new JavascriptCompilationException("Unable to compile javascript. Script in error:\n" + script, e); + } + } + + public void setResponseFilterScript(String script) { + Compilable compilable = (Compilable) JAVASCRIPT_ENGINE; + try { + compiledResponseFilterScript = compilable.compile(script); + } catch (ScriptException e) { + throw new JavascriptCompilationException("Unable to compile javascript. Script in error:\n" + script, e); + } + } + + @Override + public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (compiledRequestFilterScript == null) { + return null; + } + + Bindings bindings = JAVASCRIPT_ENGINE.createBindings(); + bindings.put("request", request); + bindings.put("contents", contents); + bindings.put("messageInfo", messageInfo); + bindings.put("log", log); + + try { + Object retVal = compiledRequestFilterScript.eval(bindings); + // avoid implicit javascript returns + if (retVal instanceof HttpResponse) { + return (HttpResponse) retVal; + } else { + return null; + } + } catch (ScriptException e) { + log.error("Could not invoke filterRequest using supplied javascript", e); + + return null; + } + } + + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + if (compiledResponseFilterScript == null) { + return; + } + + Bindings bindings = JAVASCRIPT_ENGINE.createBindings(); + bindings.put("response", response); + bindings.put("contents", contents); + bindings.put("messageInfo", messageInfo); + bindings.put("log", log); + try { + compiledResponseFilterScript.eval(bindings); + } catch (ScriptException e) { + log.error("Could not invoke filterResponse using supplied javascript", e); + } + } +} diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java new file mode 100644 index 000000000..6830f8196 --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/ProxyManager.java @@ -0,0 +1,289 @@ +package net.lightbody.bmp.proxy; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.name.Named; +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.BrowserMobProxyServer; +import net.lightbody.bmp.exception.ProxyExistsException; +import net.lightbody.bmp.exception.ProxyPortsExhaustedException; +import net.lightbody.bmp.proxy.auth.AuthType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.ref.WeakReference; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +@Singleton +public class ProxyManager { + private static final Logger LOG = LoggerFactory.getLogger(ProxyManager.class); + + private int lastPort; + private final int minPort; + private final int maxPort; + private final Provider proxyServerProvider; + // retain a reference to the Cache to allow the ProxyCleanupTask to .cleanUp(), since asMap() is just a view into the cache. + // it would seem to make sense to pass the newly-built Cache directly to the ProxyCleanupTask and have it retain a WeakReference to it, and + // only maintain a reference to the .asMap() result in this class. puzzlingly, however, the Cache can actually get garbage collected + // before the .asMap() view of it does. + private final Cache proxyCache; + private final ConcurrentMap proxies; + + /** + * Interval at which expired proxy checks will actively clean up expired proxies. Proxies may still be cleaned up when accessing the + * proxies map. + */ + private static final int EXPIRED_PROXY_CLEANUP_INTERVAL_SECONDS = 60; + + // Initialize-on-demand a single thread executor that will create a daemon thread to clean up expired proxies. Since the resulting executor + // is a singleton, there will at most one thread to service all ProxyManager instances. + private static class ScheduledExecutorHolder { + private static final ScheduledExecutorService expiredProxyCleanupExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("expired-proxy-cleanup-thread"); + thread.setDaemon(true); + return thread; + } + }); + } + + // static inner class to prevent leaking ProxyManager instances to the cleanup task + private static class ProxyCleanupTask implements Runnable { + // using a WeakReference that will indicate to us when the Cache (and thus its ProxyManager) has been garbage + // collected, allowing this cleanup task to kill itself + private final WeakReference> proxyCache; + + public ProxyCleanupTask(Cache cache) { + this.proxyCache = new WeakReference>(cache); + } + + @Override + public void run() { + Cache cache = proxyCache.get(); + if (cache != null) { + try { + cache.cleanUp(); + } catch (RuntimeException e) { + LOG.warn("Error occurred while attempting to clean up expired proxies", e); + } + } else { + // the cache instance was garbage collected, so it no longer needs to be cleaned up. throw an exception + // to prevent the scheduled executor from re-scheduling this cleanup + LOG.info("Proxy Cache was garbage collected. No longer cleaning up expired proxies for unused ProxyManager."); + + throw new RuntimeException("Exiting ProxyCleanupTask"); + } + } + } + + @Inject + public ProxyManager(Provider proxyServerProvider, @Named("minPort") Integer minPort, @Named("maxPort") Integer maxPort, final @Named("ttl") Integer ttl) { + this.proxyServerProvider = proxyServerProvider; + this.minPort = minPort; + this.maxPort = maxPort; + this.lastPort = maxPort; + if (ttl > 0) { + // proxies should be evicted after the specified ttl, so set up an evicting cache and a listener to stop the proxies when they're evicted + RemovalListener removalListener = new RemovalListener() { + public void onRemoval(RemovalNotification removal) { + try { + LegacyProxyServer proxy = removal.getValue(); + if (proxy != null) { + LOG.info("Expiring ProxyServer on port {} after {} seconds without activity", proxy.getPort(), ttl); + proxy.stop(); + } + } catch (Exception ex) { + LOG.warn("Error while stopping an expired proxy on port " + removal.getKey(), ex); + } + } + }; + + this.proxyCache = CacheBuilder.newBuilder() + .expireAfterAccess(ttl, TimeUnit.SECONDS) + .removalListener(removalListener) + .build(); + + this.proxies = proxyCache.asMap(); + + // schedule the asynchronous proxy cleanup task + ScheduledExecutorHolder.expiredProxyCleanupExecutor.scheduleWithFixedDelay(new ProxyCleanupTask(proxyCache), + EXPIRED_PROXY_CLEANUP_INTERVAL_SECONDS, EXPIRED_PROXY_CLEANUP_INTERVAL_SECONDS, TimeUnit.SECONDS); + } else { + this.proxies = new ConcurrentHashMap(); + // nothing to timeout, so no Cache + this.proxyCache = null; + } + } + + public LegacyProxyServer create(Map options, Integer port, String bindAddr, String serverBindAddr, boolean useEcc, boolean trustAllServers) { + LOG.debug("Instantiate ProxyServer..."); + LegacyProxyServer proxy = proxyServerProvider.get(); + + if (useEcc) { + if (proxy instanceof BrowserMobProxyServer) { + LOG.info("Using Elliptic Curve Cryptography for certificate impersonation"); + + ((BrowserMobProxyServer) proxy).setUseEcc(true); + } else { + LOG.warn("Cannot use Eliiptic Curve Cryptography with legacy ProxyServer implementation. Using default RSA certificates."); + } + } + + if (trustAllServers) { + if (proxy instanceof BrowserMobProxyServer) { + ((BrowserMobProxyServer) proxy).setTrustAllServers(true); + } + } + + if (options != null) { + // this is a short-term work-around for Proxy Auth in the REST API until the upcoming REST API refactor + String proxyUsername = options.remove("proxyUsername"); + String proxyPassword = options.remove("proxyPassword"); + if (proxyUsername != null && proxyPassword != null) { + ((BrowserMobProxy) proxy).chainedProxyAuthorization(proxyUsername, proxyPassword, AuthType.BASIC); + } + + LOG.debug("Apply options `{}` to new ProxyServer...", options); + proxy.setOptions(options); + } + + if (bindAddr != null) { + LOG.debug("Bind ProxyServer to `{}`...", bindAddr); + InetAddress inetAddress; + try { + inetAddress = InetAddress.getByName(bindAddr); + } catch (UnknownHostException e) { + LOG.error("Unable to bind proxy to address: " + bindAddr + "; proxy will not be created.", e); + + throw new RuntimeException("Unable to bind proxy to address: ", e); + } + + proxy.setLocalHost(inetAddress); + } + + InetAddress serverInetAddress = null; + if (serverBindAddr != null) { + LOG.debug("Bind ProxyServer serverAddress to `{}`...", serverBindAddr); + try { + serverInetAddress = InetAddress.getByName(serverBindAddr); + } catch (UnknownHostException e) { + LOG.error("Unable to bind proxy to server address: " + serverBindAddr + "; proxy will not be created.", e); + + throw new RuntimeException("Unable to bind proxy to server address: ", e); + } + } + + if (port != null) { + return startProxy(proxy, port, serverInetAddress); + } + + while (proxies.size() <= maxPort - minPort) { + LOG.debug("Use next available port for new ProxyServer..."); + port = nextPort(); + try { + return startProxy(proxy, port, serverInetAddress); + } catch (ProxyExistsException ex) { + LOG.debug("Proxy already exists at port {}", port); + } + } + throw new ProxyPortsExhaustedException(); + } + + public LegacyProxyServer create(Map options, Integer port, String bindAddr, boolean useEcc, boolean trustAllServers) { + return create(options, port, null, null, false, false); + } + + public LegacyProxyServer create(Map options, Integer port) { + return create(options, port, null, null, false, false); + } + + public LegacyProxyServer create(Map options) { + return create(options, null, null, null, false, false); + } + + public LegacyProxyServer create() { + return create(null, null, null, null, false, false); + } + + public LegacyProxyServer create(int port) { + return create(null, port, null, null, false, false); + } + + public LegacyProxyServer get(int port) { + return proxies.get(port); + } + + private LegacyProxyServer startProxy(LegacyProxyServer proxy, int port, InetAddress serverBindAddr) { + if (port != 0) { + proxy.setPort(port); + LegacyProxyServer old = proxies.putIfAbsent(port, proxy); + if (old != null) { + LOG.info("Proxy already exists at port {}", port); + throw new ProxyExistsException(port); + } + } + + try { + if (serverBindAddr != null && proxy instanceof BrowserMobProxyServer) { + BrowserMobProxyServer bProxy = (BrowserMobProxyServer) proxy; + bProxy.start(port, null, serverBindAddr); + } else { + proxy.start(); + } + + if (port == 0) { + int realPort = proxy.getPort(); + proxies.put(realPort, proxy); + } + + return proxy; + } catch (Exception ex) { + if (port != 0) { + proxies.remove(port); + } + try { + proxy.stop(); + } catch (Exception ex2) { + ex.addSuppressed(ex2); + } + throw ex; + } + } + + private synchronized int nextPort() { + return lastPort < maxPort ? ++lastPort : (lastPort = minPort); + } + + public Collection get() { + return proxies.values(); + } + + public void delete(int port) { + LegacyProxyServer proxy = proxies.remove(port); + if (proxy == null) { + return; + } + + // temporary: to avoid stopping an already-stopped BrowserMobProxyServer instance, see if it's stopped before re-stopping it + if (proxy instanceof ProxyServer || !((BrowserMobProxyServer) proxy).isStopped()) { + proxy.stop(); + } + } + +} diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java new file mode 100644 index 000000000..07654db32 --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/bricks/ProxyResource.java @@ -0,0 +1,775 @@ +package net.lightbody.bmp.proxy.bricks; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import com.google.sitebricks.At; +import com.google.sitebricks.client.transport.Json; +import com.google.sitebricks.client.transport.Text; +import com.google.sitebricks.headless.Reply; +import com.google.sitebricks.headless.Request; +import com.google.sitebricks.headless.Service; +import com.google.sitebricks.http.Delete; +import com.google.sitebricks.http.Get; +import com.google.sitebricks.http.Post; +import com.google.sitebricks.http.Put; +import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.BrowserMobProxyServer; +import net.lightbody.bmp.core.har.Har; +import net.lightbody.bmp.exception.ProxyExistsException; +import net.lightbody.bmp.exception.ProxyPortsExhaustedException; +import net.lightbody.bmp.exception.UnsupportedCharsetException; +import net.lightbody.bmp.filters.JavascriptRequestResponseFilter; +import net.lightbody.bmp.proxy.CaptureType; +import net.lightbody.bmp.proxy.LegacyProxyServer; +import net.lightbody.bmp.proxy.ProxyManager; +import net.lightbody.bmp.proxy.ProxyServer; +import net.lightbody.bmp.proxy.http.BrowserMobHttpRequest; +import net.lightbody.bmp.proxy.http.BrowserMobHttpResponse; +import net.lightbody.bmp.proxy.http.RequestInterceptor; +import net.lightbody.bmp.proxy.http.ResponseInterceptor; +import net.lightbody.bmp.util.BrowserMobHttpUtil; +import org.java_bandwidthlimiter.StreamManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +@At("/proxy") +@Service +public class ProxyResource { + private static final Logger LOG = LoggerFactory.getLogger(ProxyResource.class); + + private final ProxyManager proxyManager; + + @Inject + public ProxyResource(ProxyManager proxyManager) { + this.proxyManager = proxyManager; + } + + @Get + public Reply getProxies() { + Collection proxyList = new ArrayList(); + for (LegacyProxyServer proxy : proxyManager.get()) { + proxyList.add(new ProxyDescriptor(proxy.getPort())); + } + return Reply.with(new ProxyListDescriptor(proxyList)).as(Json.class); + } + + @Post + public Reply newProxy(Request request) { + String systemProxyHost = System.getProperty("http.proxyHost"); + String systemProxyPort = System.getProperty("http.proxyPort"); + String httpProxy = request.param("httpProxy"); + String proxyUsername = request.param("proxyUsername"); + String proxyPassword = request.param("proxyPassword"); + + Hashtable options = new Hashtable(); + + // If the upstream proxy is specified via query params that should override any default system level proxy. + if (httpProxy != null) { + options.put("httpProxy", httpProxy); + } else if ((systemProxyHost != null) && (systemProxyPort != null)) { + options.put("httpProxy", String.format("%s:%s", systemProxyHost, systemProxyPort)); + } + + // this is a short-term work-around for Proxy Auth in the REST API until the upcoming REST API refactor + if (proxyUsername != null && proxyPassword != null) { + options.put("proxyUsername", proxyUsername); + options.put("proxyPassword", proxyPassword); + } + + String paramBindAddr = request.param("bindAddress"); + String paramServerBindAddr = request.param("serverBindAddress"); + Integer paramPort = request.param("port") == null ? null : Integer.parseInt(request.param("port")); + + String useEccString = request.param("useEcc"); + boolean useEcc = Boolean.parseBoolean(useEccString); + + String trustAllServersString = request.param("trustAllServers"); + boolean trustAllServers = Boolean.parseBoolean(trustAllServersString); + + LOG.debug("POST proxy instance on bindAddress `{}` & port `{}` & serverBindAddress `{}`", + paramBindAddr, paramPort, paramServerBindAddr); + LegacyProxyServer proxy; + try { + proxy = proxyManager.create(options, paramPort, paramBindAddr, paramServerBindAddr, useEcc, trustAllServers); + } catch (ProxyExistsException ex) { + return Reply.with(new ProxyDescriptor(ex.getPort())).status(455).as(Json.class); + } catch (ProxyPortsExhaustedException ex) { + return Reply.saying().status(456); + } catch (Exception ex) { + StringWriter s = new StringWriter(); + ex.printStackTrace(new PrintWriter(s)); + return Reply.with(s).as(Text.class).status(550); + } + return Reply.with(new ProxyDescriptor(proxy.getPort())).as(Json.class); + } + + @Get + @At("/:port/har") + public Reply getHar(@Named("port") int port) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + Har har = proxy.getHar(); + + return Reply.with(har).as(Json.class); + } + + @Put + @At("/:port/har") + public Reply newHar(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String initialPageRef = request.param("initialPageRef"); + String initialPageTitle = request.param("initialPageTitle"); + Har oldHar = proxy.newHar(initialPageRef, initialPageTitle); + + String captureHeaders = request.param("captureHeaders"); + String captureContent = request.param("captureContent"); + String captureBinaryContent = request.param("captureBinaryContent"); + proxy.setCaptureHeaders(Boolean.parseBoolean(captureHeaders)); + proxy.setCaptureContent(Boolean.parseBoolean(captureContent)); + proxy.setCaptureBinaryContent(Boolean.parseBoolean(captureBinaryContent)); + + String captureCookies = request.param("captureCookies"); + if (proxy instanceof BrowserMobProxyServer && Boolean.parseBoolean(captureCookies)) { + BrowserMobProxyServer browserMobProxyServer = (BrowserMobProxyServer) proxy; + browserMobProxyServer.enableHarCaptureTypes(CaptureType.getCookieCaptureTypes()); + } + + if (oldHar != null) { + return Reply.with(oldHar).as(Json.class); + } else { + return Reply.saying().noContent(); + } + } + + @Put + @At("/:port/har/pageRef") + public Reply setPage(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String pageRef = request.param("pageRef"); + String pageTitle = request.param("pageTitle"); + proxy.newPage(pageRef, pageTitle); + + return Reply.saying().ok(); + } + + @Get + @At("/:port/blacklist") + public Reply getBlacklist(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + return Reply.with(proxy.getBlacklistedUrls()).as(Json.class); + } + + @Put + @At("/:port/blacklist") + public Reply blacklist(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String blacklist = request.param("regex"); + int responseCode = parseResponseCode(request.param("status")); + String method = request.param("method"); + proxy.blacklistRequests(blacklist, responseCode, method); + + return Reply.saying().ok(); + } + + @Delete + @At("/:port/blacklist") + public Reply clearBlacklist(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + proxy.clearBlacklist(); + return Reply.saying().ok(); + } + + @Get + @At("/:port/whitelist") + public Reply getWhitelist(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + return Reply.with(proxy.getWhitelistUrls()).as(Json.class); + } + + @Put + @At("/:port/whitelist") + public Reply whitelist(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String regex = request.param("regex"); + int responseCode = parseResponseCode(request.param("status")); + proxy.whitelistRequests(regex.split(","), responseCode); + + return Reply.saying().ok(); + } + + @Delete + @At("/:port/whitelist") + public Reply clearWhitelist(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + proxy.clearWhitelist(); + return Reply.saying().ok(); + } + + @Post + @At("/:port/auth/basic/:domain") + public Reply autoBasicAuth(@Named("port") int port, @Named("domain") String domain, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + Map credentials = request.read(HashMap.class).as(Json.class); + proxy.autoBasicAuthorization(domain, credentials.get("username"), credentials.get("password")); + + return Reply.saying().ok(); + } + + @Post + @At("/:port/headers") + public Reply updateHeaders(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + Map headers = request.read(Map.class).as(Json.class); + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + proxy.addHeader(key, value); + } + return Reply.saying().ok(); + } + + @Post + @At("/:port/interceptor/response") + public Reply addResponseInterceptor(@Named("port") int port, Request request) throws IOException, ScriptException { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + if (!(proxy instanceof ProxyServer)) { + return Reply.saying().badRequest(); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + request.readTo(baos); + + ScriptEngineManager mgr = new ScriptEngineManager(); + final ScriptEngine engine = mgr.getEngineByName("JavaScript"); + Compilable compilable = (Compilable) engine; + final CompiledScript script = compilable.compile(baos.toString()); + + proxy.addResponseInterceptor(new ResponseInterceptor() { + @Override + public void process(BrowserMobHttpResponse response, Har har) { + Bindings bindings = engine.createBindings(); + bindings.put("response", response); + bindings.put("har", har); + bindings.put("log", LOG); + try { + script.eval(bindings); + } catch (ScriptException e) { + LOG.error("Could not execute JS-based response interceptor", e); + } + } + }); + + return Reply.saying().ok(); + } + + @Post + @At("/:port/interceptor/request") + public Reply addRequestInterceptor(@Named("port") int port, Request request) throws IOException, ScriptException { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + if (!(proxy instanceof ProxyServer)) { + return Reply.saying().badRequest(); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + request.readTo(baos); + + ScriptEngineManager mgr = new ScriptEngineManager(); + final ScriptEngine engine = mgr.getEngineByName("JavaScript"); + Compilable compilable = (Compilable) engine; + final CompiledScript script = compilable.compile(baos.toString()); + + proxy.addRequestInterceptor(new RequestInterceptor() { + @Override + public void process(BrowserMobHttpRequest request, Har har) { + Bindings bindings = engine.createBindings(); + bindings.put("request", request); + bindings.put("har", har); + bindings.put("log", LOG); + try { + script.eval(bindings); + } catch (ScriptException e) { + LOG.error("Could not execute JS-based response interceptor", e); + } + } + }); + + return Reply.saying().ok(); + } + + @Post + @At("/:port/filter/request") + public Reply addRequestFilter(@Named("port") int port, Request request) throws IOException, ScriptException { + LegacyProxyServer legacyProxy = proxyManager.get(port); + if (legacyProxy == null) { + return Reply.saying().notFound(); + } + + if (!(legacyProxy instanceof BrowserMobProxyServer)) { + return Reply.saying().badRequest(); + } + + BrowserMobProxy proxy = (BrowserMobProxy) legacyProxy; + + JavascriptRequestResponseFilter requestResponseFilter = new JavascriptRequestResponseFilter(); + + String script = getEntityBodyFromRequest(request); + requestResponseFilter.setRequestFilterScript(script); + + proxy.addRequestFilter(requestResponseFilter); + + return Reply.saying().ok(); + } + + @Post + @At("/:port/filter/response") + public Reply addResponseFilter(@Named("port") int port, Request request) throws IOException, ScriptException { + LegacyProxyServer legacyProxy = proxyManager.get(port); + if (legacyProxy == null) { + return Reply.saying().notFound(); + } + + if (!(legacyProxy instanceof BrowserMobProxyServer)) { + return Reply.saying().badRequest(); + } + + BrowserMobProxy proxy = (BrowserMobProxy) legacyProxy; + + JavascriptRequestResponseFilter requestResponseFilter = new JavascriptRequestResponseFilter(); + + String script = getEntityBodyFromRequest(request); + requestResponseFilter.setResponseFilterScript(script); + + proxy.addResponseFilter(requestResponseFilter); + + return Reply.saying().ok(); + } + + @Put + @At("/:port/limit") + public Reply limit(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + StreamManager streamManager = proxy.getStreamManager(); + String upstreamKbps = request.param("upstreamKbps"); + if (upstreamKbps != null) { + try { + streamManager.setUpstreamKbps(Integer.parseInt(upstreamKbps)); + streamManager.enable(); + } catch (NumberFormatException e) { + } + } + + String upstreamBps = request.param("upstreamBps"); + if (upstreamBps != null) { + try { + ((BrowserMobProxy) proxy).setWriteBandwidthLimit(Integer.parseInt(upstreamBps)); + } catch (NumberFormatException e) { + } + } + + String downstreamKbps = request.param("downstreamKbps"); + if (downstreamKbps != null) { + try { + streamManager.setDownstreamKbps(Integer.parseInt(downstreamKbps)); + streamManager.enable(); + } catch (NumberFormatException e) { + } + } + + String downstreamBps = request.param("downstreamBps"); + if (downstreamBps != null) { + try { + ((BrowserMobProxy) proxy).setReadBandwidthLimit(Integer.parseInt(downstreamBps)); + } catch (NumberFormatException e) { + } + } + + String upstreamMaxKB = request.param("upstreamMaxKB"); + if (upstreamMaxKB != null) { + try { + streamManager.setUpstreamMaxKB(Integer.parseInt(upstreamMaxKB)); + streamManager.enable(); + } catch (NumberFormatException e) { + } + } + String downstreamMaxKB = request.param("downstreamMaxKB"); + if (downstreamMaxKB != null) { + try { + streamManager.setDownstreamMaxKB(Integer.parseInt(downstreamMaxKB)); + streamManager.enable(); + } catch (NumberFormatException e) { + } + } + String latency = request.param("latency"); + if (latency != null) { + try { + streamManager.setLatency(Integer.parseInt(latency)); + streamManager.enable(); + } catch (NumberFormatException e) { + } + } + String payloadPercentage = request.param("payloadPercentage"); + if (payloadPercentage != null) { + try { + streamManager.setPayloadPercentage(Integer.parseInt(payloadPercentage)); + } catch (NumberFormatException e) { + } + } + String maxBitsPerSecond = request.param("maxBitsPerSecond"); + if (maxBitsPerSecond != null) { + try { + streamManager.setMaxBitsPerSecondThreshold(Integer.parseInt(maxBitsPerSecond)); + } catch (NumberFormatException e) { + } + } + String enable = request.param("enable"); + if (enable != null) { + if (Boolean.parseBoolean(enable)) { + streamManager.enable(); + } else { + streamManager.disable(); + } + } + return Reply.saying().ok(); + } + + @Get + @At("/:port/limit") + public Reply getLimits(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + return Reply.with(new BandwidthLimitDescriptor(proxy.getStreamManager())).as(Json.class); + } + + @Put + @At("/:port/timeout") + public Reply timeout(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String requestTimeout = request.param("requestTimeout"); + if (requestTimeout != null) { + try { + proxy.setRequestTimeout(Integer.parseInt(requestTimeout)); + } catch (NumberFormatException e) { + } + } + String readTimeout = request.param("readTimeout"); + if (readTimeout != null) { + try { + proxy.setSocketOperationTimeout(Integer.parseInt(readTimeout)); + } catch (NumberFormatException e) { + } + } + String connectionTimeout = request.param("connectionTimeout"); + if (connectionTimeout != null) { + try { + proxy.setConnectionTimeout(Integer.parseInt(connectionTimeout)); + } catch (NumberFormatException e) { + } + } + String dnsCacheTimeout = request.param("dnsCacheTimeout"); + if (dnsCacheTimeout != null) { + try { + proxy.setDNSCacheTimeout(Integer.parseInt(dnsCacheTimeout)); + } catch (NumberFormatException e) { + } + } + return Reply.saying().ok(); + } + + @Delete + @At("/:port") + public Reply delete(@Named("port") int port) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + proxyManager.delete(port); + return Reply.saying().ok(); + } + + @Post + @At("/:port/hosts") + public Reply remapHosts(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + @SuppressWarnings("unchecked") Map headers = request.read(Map.class).as(Json.class); + + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + proxy.remapHost(key, value); + proxy.setDNSCacheTimeout(0); + proxy.clearDNSCache(); + } + + return Reply.saying().ok(); + } + + + @Put + @At("/:port/wait") + public Reply wait(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String quietPeriodInMs = request.param("quietPeriodInMs"); + String timeoutInMs = request.param("timeoutInMs"); + proxy.waitForNetworkTrafficToStop(Integer.parseInt(quietPeriodInMs), Integer.parseInt(timeoutInMs)); + return Reply.saying().ok(); + } + + @Delete + @At("/:port/dns/cache") + public Reply clearDnsCache(@Named("port") int port) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + proxy.clearDNSCache(); + return Reply.saying().ok(); + } + + @Put + @At("/:port/rewrite") + public Reply rewriteUrl(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String match = request.param("matchRegex"); + String replace = request.param("replace"); + proxy.rewriteUrl(match, replace); + return Reply.saying().ok(); + } + + @Delete + @At("/:port/rewrite") + public Reply clearRewriteRules(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + proxy.clearRewriteRules(); + return Reply.saying().ok(); + } + + @Put + @At("/:port/retry") + public Reply retryCount(@Named("port") int port, Request request) { + LegacyProxyServer proxy = proxyManager.get(port); + if (proxy == null) { + return Reply.saying().notFound(); + } + + String count = request.param("retrycount"); + proxy.setRetryCount(Integer.parseInt(count)); + return Reply.saying().ok(); + } + + private int parseResponseCode(String response) { + int responseCode = 200; + if (response != null) { + try { + responseCode = Integer.parseInt(response); + } catch (NumberFormatException e) { + } + } + return responseCode; + } + + public static class ProxyDescriptor { + private int port; + + public ProxyDescriptor() { + } + + public ProxyDescriptor(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + } + + public static class ProxyListDescriptor { + private Collection proxyList; + + public ProxyListDescriptor() { + } + + public ProxyListDescriptor(Collection proxyList) { + this.proxyList = proxyList; + } + + public Collection getProxyList() { + return proxyList; + } + + public void setProxyList(Collection proxyList) { + this.proxyList = proxyList; + } + } + + public static class BandwidthLimitDescriptor { + private long maxUpstreamKB; + private long remainingUpstreamKB; + private long maxDownstreamKB; + private long remainingDownstreamKB; + + public BandwidthLimitDescriptor() { + } + + public BandwidthLimitDescriptor(StreamManager manager) { + this.maxDownstreamKB = manager.getMaxDownstreamKB(); + this.remainingDownstreamKB = manager.getRemainingDownstreamKB(); + this.maxUpstreamKB = manager.getMaxUpstreamKB(); + this.remainingUpstreamKB = manager.getRemainingUpstreamKB(); + } + + public long getMaxUpstreamKB() { + return maxUpstreamKB; + } + + public void setMaxUpstreamKB(long maxUpstreamKB) { + this.maxUpstreamKB = maxUpstreamKB; + } + + public long getRemainingUpstreamKB() { + return remainingUpstreamKB; + } + + public void setRemainingUpstreamKB(long remainingUpstreamKB) { + this.remainingUpstreamKB = remainingUpstreamKB; + } + + public long getMaxDownstreamKB() { + return maxDownstreamKB; + } + + public void setMaxDownstreamKB(long maxDownstreamKB) { + this.maxDownstreamKB = maxDownstreamKB; + } + + public long getRemainingDownstreamKB() { + return remainingDownstreamKB; + } + + public void setRemainingDownstreamKB(long remainingDownstreamKB) { + this.remainingDownstreamKB = remainingDownstreamKB; + } + } + + private String getEntityBodyFromRequest(Request request) throws IOException { + String contentTypeHeader = request.header("Content-Type"); + Charset charset = null; + try { + charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); + } catch (UnsupportedCharsetException e) { + java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause(); + LOG.error("Character set declared in Content-Type header is not supported. Content-Type header: {}", contentTypeHeader, cause); + + throw cause; + } + + if (charset == null) { + charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + } + + ByteArrayOutputStream entityBodyBytes = new ByteArrayOutputStream(); + request.readTo(entityBodyBytes); + + return new String(entityBodyBytes.toByteArray(), charset); + } + +} diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java new file mode 100644 index 000000000..bf20e31b2 --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/ConfigModule.java @@ -0,0 +1,121 @@ +package net.lightbody.bmp.proxy.guice; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Binder; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.Provider; +import joptsimple.ArgumentAcceptingOptionSpec; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import net.lightbody.bmp.proxy.LegacyProxyServer; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.TimeZone; + +public class ConfigModule implements Module { + private String[] args; + + public ConfigModule(String[] args) { + this.args = args; + } + + @Override + public void configure(Binder binder) { + OptionParser parser = new OptionParser(); + + ArgumentAcceptingOptionSpec portSpec = + parser.accepts("port", "The port to listen on") + .withOptionalArg().ofType(Integer.class).defaultsTo(8080); + + ArgumentAcceptingOptionSpec addressSpec = + parser.accepts("address", "The address to bind to") + .withOptionalArg() + .ofType(String.class) + .defaultsTo("0.0.0.0"); + + ArgumentAcceptingOptionSpec proxyPortRange = + parser.accepts("proxyPortRange", "The range of ports to use for proxies") + .withOptionalArg() + .ofType(Integer.class) + .defaultsTo(8081, 8581) + .withValuesSeparatedBy('-'); + + ArgumentAcceptingOptionSpec ttlSpec = + parser.accepts("ttl", "Time in seconds until an unused proxy is deleted") + .withOptionalArg() + .ofType(Integer.class) + .defaultsTo(0); + + ArgumentAcceptingOptionSpec useLittleProxy = + parser.accepts("use-littleproxy", "Use the littleproxy backend instead of the legacy Jetty 5-based implementation") + .withOptionalArg() + .ofType(Boolean.class) + .defaultsTo(true); + + parser.acceptsAll(Arrays.asList("help", "?"), "This help text"); + + OptionSet options = parser.parse(args); + + if (options.has("?")) { + try { + parser.printHelpOn(System.out); + System.exit(0); + } catch (IOException e) { + // should never happen, but... + e.printStackTrace(); + } + return; + } + + // temporary, until REST API is replaced + LegacyProxyServerProvider.useLittleProxy = useLittleProxy.value(options); + if (LegacyProxyServerProvider.useLittleProxy) { + System.out.println("Running BrowserMob Proxy using LittleProxy implementation. To revert to the legacy implementation, run the proxy with the command-line option '--use-littleproxy false'."); + } else { + System.out.println("Running BrowserMob Proxy using legacy implementation."); + } + + List ports = options.valuesOf(proxyPortRange); + if(ports.size() < 2){ + throw new IllegalArgumentException(); + } + Integer minPort; + Integer maxPort; + if(ports.get(1) > ports.get(0)){ + minPort = ports.get(0); + maxPort = ports.get(1); + }else{ + minPort = ports.get(1); + maxPort = ports.get(0); + } + Integer port = portSpec.value(options); + if(port >= minPort && port <= maxPort){ + int num = maxPort - minPort; + minPort = port + 1; + maxPort = minPort + num; + } + + binder.bind(Key.get(Integer.class, new NamedImpl("port"))).toInstance(port); + binder.bind(Key.get(String.class, new NamedImpl("address"))).toInstance(addressSpec.value(options)); + binder.bind(Key.get(Integer.class, new NamedImpl("minPort"))).toInstance(minPort); + binder.bind(Key.get(Integer.class, new NamedImpl("maxPort"))).toInstance(maxPort); + binder.bind(Key.get(Integer.class, new NamedImpl("ttl"))).toInstance(ttlSpec.value(options)); + + binder.bind(LegacyProxyServer.class).toProvider(LegacyProxyServerProvider.class); + + // bind an ObjectMapper provider that uses the system time zone instead of UTC by default + binder.bind(ObjectMapper.class).toProvider(new Provider() { + @Override + public ObjectMapper get() { + ObjectMapper objectMapper = new ObjectMapper(); + + objectMapper.setTimeZone(TimeZone.getDefault()); + + return objectMapper; + } + }); + } +} diff --git a/src/main/java/org/browsermob/proxy/guice/JettyModule.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyModule.java similarity index 88% rename from src/main/java/org/browsermob/proxy/guice/JettyModule.java rename to browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyModule.java index f14a3d587..72735f3e5 100644 --- a/src/main/java/org/browsermob/proxy/guice/JettyModule.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyModule.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.guice; +package net.lightbody.bmp.proxy.guice; import com.google.inject.Binder; import com.google.inject.Module; diff --git a/src/main/java/org/browsermob/proxy/guice/JettyServerProvider.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java similarity index 68% rename from src/main/java/org/browsermob/proxy/guice/JettyServerProvider.java rename to browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java index b979c40dd..4584620ca 100644 --- a/src/main/java/org/browsermob/proxy/guice/JettyServerProvider.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/JettyServerProvider.java @@ -1,9 +1,12 @@ -package org.browsermob.proxy.guice; +package net.lightbody.bmp.proxy.guice; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; import com.google.inject.servlet.GuiceFilter; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; @@ -13,8 +16,8 @@ public class JettyServerProvider implements Provider { private Server server; @Inject - public JettyServerProvider(@Named("port") int port) { - server = new Server(port); + public JettyServerProvider(@Named("port") int port, @Named("address") String address) throws UnknownHostException { + server = new Server(new InetSocketAddress(InetAddress.getByName(address), port)); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); diff --git a/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/LegacyProxyServerProvider.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/LegacyProxyServerProvider.java new file mode 100644 index 000000000..f7e592a0c --- /dev/null +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/LegacyProxyServerProvider.java @@ -0,0 +1,20 @@ +package net.lightbody.bmp.proxy.guice; + +import com.google.inject.Provider; +import net.lightbody.bmp.BrowserMobProxyServerLegacyAdapter; +import net.lightbody.bmp.proxy.LegacyProxyServer; +import net.lightbody.bmp.proxy.ProxyServer; + +public class LegacyProxyServerProvider implements Provider { + // temporary, until REST API is replaced + public static volatile boolean useLittleProxy = false; + + @Override + public LegacyProxyServer get() { + if (useLittleProxy || Boolean.getBoolean("bmp.use.littleproxy")) { + return new BrowserMobProxyServerLegacyAdapter(); + } else { + return new ProxyServer(); + } + } +} diff --git a/src/main/java/org/browsermob/proxy/guice/NamedImpl.java b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/NamedImpl.java similarity index 95% rename from src/main/java/org/browsermob/proxy/guice/NamedImpl.java rename to browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/NamedImpl.java index 9daa69f58..487b94f27 100644 --- a/src/main/java/org/browsermob/proxy/guice/NamedImpl.java +++ b/browsermob-rest/src/main/java/net/lightbody/bmp/proxy/guice/NamedImpl.java @@ -1,4 +1,4 @@ -package org.browsermob.proxy.guice; +package net.lightbody.bmp.proxy.guice; import com.google.inject.name.Named; diff --git a/browsermob-rest/src/test/groovy/net/lightbody/bmp/proxy/FilterTest.groovy b/browsermob-rest/src/test/groovy/net/lightbody/bmp/proxy/FilterTest.groovy new file mode 100644 index 000000000..7c99c0212 --- /dev/null +++ b/browsermob-rest/src/test/groovy/net/lightbody/bmp/proxy/FilterTest.groovy @@ -0,0 +1,290 @@ +package net.lightbody.bmp.proxy + +import com.google.sitebricks.headless.Request +import groovyx.net.http.HTTPBuilder +import groovyx.net.http.Method +import net.lightbody.bmp.BrowserMobProxyServerLegacyAdapter +import net.lightbody.bmp.filters.RequestFilter +import net.lightbody.bmp.filters.ResponseFilter +import net.lightbody.bmp.proxy.bricks.ProxyResource +import net.lightbody.bmp.proxy.test.util.ProxyResourceTest +import org.apache.http.entity.ContentType +import org.junit.Test +import org.mockserver.matchers.Times +import org.mockserver.model.Header + +import static org.hamcrest.Matchers.endsWith +import static org.hamcrest.Matchers.greaterThanOrEqualTo +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertThat +import static org.junit.Assert.assertTrue +import static org.junit.Assert.fail +import static org.junit.Assume.assumeThat +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.never +import static org.mockito.Mockito.verify +import static org.mockito.Mockito.when +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response + +class FilterTest extends ProxyResourceTest { + @Test + void testCanModifyRequestHeadersWithJavascript() { + final String requestFilterJavaScript = + ''' + request.headers().remove('User-Agent'); + request.headers().add('User-Agent', 'My-Custom-User-Agent-String 1.0'); + ''' + + Request mockRestRequest = createMockRestRequestWithEntity(requestFilterJavaScript) + + proxyResource.addRequestFilter(proxyPort, mockRestRequest) + + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyuseragent") + .withHeader(new Header("User-Agent", "My-Custom-User-Agent-String 1.0")), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header("Content-Type", "text/plain")) + .withBody("success")); + + HTTPBuilder http = getHttpBuilder() + + http.request(Method.GET, ContentType.TEXT_PLAIN) { req -> + uri.path = "/modifyuseragent" + + response.success = { resp, reader -> + assertEquals("Javascript interceptor did not modify the user agent string", "success", reader.text) + } + } + } + + + @Test + void testCanModifyRequestContentsWithJavascript() { + final String requestFilterJavaScript = + ''' + if (request.getUri().endsWith('/modifyrequest') && contents.isText()) { + // using == instead of === since under Java 7 the js engine treats js strings as 'string' but Java Strings as 'object' + if (contents.getTextContents() == 'original request text') { + contents.setTextContents('modified request text'); + } + } + ''' + + Request mockRestRequest = createMockRestRequestWithEntity(requestFilterJavaScript) + + proxyResource.addRequestFilter(proxyPort, mockRestRequest) + + mockServer.when(request() + .withMethod("PUT") + .withPath("/modifyrequest") + .withBody("modified request text"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header("Content-Type", "text/plain; charset=UTF-8")) + .withBody("success")); + + HTTPBuilder http = getHttpBuilder() + + http.request(Method.PUT, ContentType.TEXT_PLAIN) { req -> + uri.path = "/modifyrequest" + body = "original request text" + + response.success = { resp, reader -> + assertEquals("Javascript interceptor did not modify request body", "success", reader.text) + } + } + } + + @Test + void testCanModifyResponseWithJavascript() { + final String responseFilterJavaScript = + ''' + if (contents.isText()) { + // using == instead of === since under Java 7 the js engine treats js strings as 'string' but Java Strings as 'object' + if (contents.getTextContents() == 'original response text') { + contents.setTextContents('modified response text'); + } + } + ''' + + Request mockRestRequest = createMockRestRequestWithEntity(responseFilterJavaScript) + + proxyResource.addResponseFilter(proxyPort, mockRestRequest) + + mockServer.when(request() + .withMethod("GET") + .withPath("/modifyresponse"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header("Content-Type", "text/plain; charset=UTF-8")) + .withBody("original response text")); + + HTTPBuilder http = getHttpBuilder() + + http.request(Method.GET, ContentType.TEXT_PLAIN) { req -> + uri.path = "/modifyresponse" + + response.success = { resp, reader -> + assertEquals("Javascript interceptor did not modify response text", "modified response text", reader.text) + } + } + } + + @Test + void testCanAccessOriginalRequestWithJavascript() { + final String requestFilterJavaScript = + ''' + if (request.getUri().endsWith('/originalrequest')) { + request.setUri(request.getUri().replaceAll('originalrequest', 'modifiedrequest')); + } + ''' + + Request mockRestAddReqFilterRequest = createMockRestRequestWithEntity(requestFilterJavaScript) + proxyResource.addRequestFilter(proxyPort, mockRestAddReqFilterRequest) + + final String responseFilterJavaScript = + ''' + contents.setTextContents(messageInfo.getOriginalRequest().getUri()); + ''' + Request mockRestAddRespFilterRequest = createMockRestRequestWithEntity(responseFilterJavaScript) + proxyResource.addResponseFilter(proxyPort, mockRestAddRespFilterRequest) + + mockServer.when(request() + .withMethod("GET") + .withPath("/modifiedrequest"), + Times.exactly(1)) + .respond(response() + .withStatusCode(200) + .withHeader(new Header("Content-Type", "text/plain; charset=UTF-8")) + .withBody("should-be-replaced")); + + HTTPBuilder http = getHttpBuilder() + + http.request(Method.GET, ContentType.TEXT_PLAIN) { req -> + uri.path = "/originalrequest" + + response.success = { resp, reader -> + assertThat("Javascript interceptor did not read messageData.originalRequest variable successfully", reader.text, endsWith("originalrequest")) + } + } + } + + @Test + void testRequestFilterNotAddedIfJavascriptDoesNotCompile() { + final String requestFilterJavaScript = + ''' + this javascript won't compile! + ''' + + Request mockRestAddReqFilterRequest = createMockRestRequestWithEntity(requestFilterJavaScript) + + // mock the proxy so we can verify the addRequestFilter() method is never called + def mockProxy = mock(BrowserMobProxyServerLegacyAdapter) + + // mock the ProxyManager to return the mocked proxy + ProxyManager mockProxyManager = mock(ProxyManager) + when(mockProxyManager.get(proxyPort)).thenReturn(mockProxy) + + // not using the local ProxyResource, since we need to mock out the dependencies + ProxyResource proxyResource = new ProxyResource(mockProxyManager) + + boolean javascriptExceptionOccurred = false; + + try { + proxyResource.addRequestFilter(proxyPort, mockRestAddReqFilterRequest) + } catch (JavascriptCompilationException) { + javascriptExceptionOccurred = true; + } + + assertTrue("Expected javascript to fail to compile", javascriptExceptionOccurred) + + verify(mockProxy, never()).addRequestFilter(any(RequestFilter)) + } + + @Test + void testResponseFilterNotAddedIfJavascriptDoesNotCompile() { + final String responseFilterJavaScript = + ''' + this javascript won't compile! + ''' + + Request mockRestAddRespFilterRequest = createMockRestRequestWithEntity(responseFilterJavaScript) + + // mock the proxy so we can verify the addResponseFilter() method is never called + def mockProxy = mock(BrowserMobProxyServerLegacyAdapter) + + // mock the ProxyManager to return the mocked proxy + ProxyManager mockProxyManager = mock(ProxyManager) + when(mockProxyManager.get(proxyPort)).thenReturn(mockProxy) + + // not using the local ProxyResource, since we need to mock out the dependencies + ProxyResource proxyResource = new ProxyResource(mockProxyManager) + + boolean javascriptExceptionOccurred = false; + + try { + proxyResource.addResponseFilter(proxyPort, mockRestAddRespFilterRequest) + } catch (JavascriptCompilationException) { + javascriptExceptionOccurred = true; + } + + assertTrue("Expected javascript to fail to compile", javascriptExceptionOccurred) + + verify(mockProxy, never()).addResponseFilter(any(ResponseFilter)) + } + + @Test + void testCanShortCircuitRequestWithJavascript() { + def javaVersion = System.getProperty("java.specification.version") as double + assumeThat("Skipping Nashorn-dependent test on Java 1.7", javaVersion, greaterThanOrEqualTo(1.8d)) + + final String requestFilterJavaScript = + ''' + // "import" classes + var DefaultFullHttpResponse = Java.type('io.netty.handler.codec.http.DefaultFullHttpResponse'); + var HttpResponseStatus = Java.type('io.netty.handler.codec.http.HttpResponseStatus'); + var HttpObjectUtil = Java.type('net.lightbody.bmp.util.HttpObjectUtil'); + + // create a new DefaultFullHttpResponse that will short-circuit the request + var shortCircuitRequest = new DefaultFullHttpResponse(request.getProtocolVersion(), HttpResponseStatus.PAYMENT_REQUIRED); + + // use the convenient HttpObjectUtil.replaceTextHttpEntityBody() method to set the entity body + var responseBody = 'You have to pay the troll toll to get into this Proxy\\'s soul'; + HttpObjectUtil.replaceTextHttpEntityBody(shortCircuitRequest, responseBody); + + // return the short-circuit FullHttpResponse + shortCircuitRequest; + ''' + + Request mockRestRequest = createMockRestRequestWithEntity(requestFilterJavaScript) + + proxyResource.addRequestFilter(proxyPort, mockRestRequest) + + HTTPBuilder http = getHttpBuilder() + + http.request(Method.GET, ContentType.TEXT_PLAIN) { req -> + uri.path = "/testShortCircuit" + + response.success = { resp, reader -> + fail("Expected short-circuit response to return an HTTP 402 Payment Required") + } + + response.failure = { resp, reader -> + assertEquals("Expected short-circuit response to return an HTTP 402 Payment Required", 402, resp.status) + assertEquals("Expected short-circuit response to contain body text set in Javascript", "You have to pay the troll toll to get into this Proxy's soul", reader.text) + } + } + } + + @Override + String[] getArgs() { + return ["--use-littleproxy", "true"] + } +} diff --git a/browsermob-rest/src/test/groovy/net/lightbody/bmp/proxy/test/util/ProxyResourceTest.groovy b/browsermob-rest/src/test/groovy/net/lightbody/bmp/proxy/test/util/ProxyResourceTest.groovy new file mode 100644 index 000000000..866ab5d7c --- /dev/null +++ b/browsermob-rest/src/test/groovy/net/lightbody/bmp/proxy/test/util/ProxyResourceTest.groovy @@ -0,0 +1,70 @@ +package net.lightbody.bmp.proxy.test.util + +import com.google.sitebricks.headless.Request +import groovyx.net.http.HTTPBuilder +import net.lightbody.bmp.proxy.LegacyProxyServer +import net.lightbody.bmp.proxy.bricks.ProxyResource +import org.junit.After +import org.junit.Before +import org.mockito.invocation.InvocationOnMock +import org.mockito.stubbing.Answer +import org.mockserver.integration.ClientAndServer + +import java.nio.charset.StandardCharsets + +import static org.mockito.Matchers.any +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +abstract class ProxyResourceTest extends ProxyManagerTest { + ProxyResource proxyResource + int proxyPort + protected ClientAndServer mockServer; + protected int mockServerPort; + + @Before + public void setUpMockServer() { + mockServer = new ClientAndServer(0); + mockServerPort = mockServer.getPort(); + } + + @After + public void tearDownMockServer() { + if (mockServer != null) { + mockServer.stop(); + } + } + + @Before + void setUpProxyResource() { + LegacyProxyServer proxy = proxyManager.create(0) + proxyPort = proxy.port + + proxyResource = new ProxyResource(proxyManager) + } + + public HTTPBuilder getHttpBuilder() { + def http = new HTTPBuilder("http://localhost:${mockServerPort}") + http.setProxy("localhost", proxyPort, "http") + + return http + } + + /** + * Creates a mock sitebricks REST request with the specified entity body. + */ + public static Request createMockRestRequestWithEntity(String entityBody) { + Request mockRestRequest = mock(Request) + when(mockRestRequest.header("Content-Type")).thenReturn("text/plain; charset=utf-8") + when(mockRestRequest.readTo(any(OutputStream))).then(new Answer() { + @Override + Object answer(InvocationOnMock invocationOnMock) throws Throwable { + OutputStream os = invocationOnMock.getArguments()[0] + os.write(entityBody.getBytes(StandardCharsets.UTF_8)) + + return null + } + }) + mockRestRequest + } +} diff --git a/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java new file mode 100644 index 000000000..85eacc06c --- /dev/null +++ b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ExpiringProxyTest.java @@ -0,0 +1,67 @@ +package net.lightbody.bmp.proxy; + +import net.lightbody.bmp.proxy.guice.LegacyProxyServerProvider; +import org.junit.Test; + +import java.util.Collections; +import java.util.Random; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ExpiringProxyTest { + @Test + public void testExpiredProxyStops() throws InterruptedException { + int minPort = new Random().nextInt(50000) + 10000; + + ProxyManager proxyManager = new ProxyManager(new LegacyProxyServerProvider(), + minPort, + minPort + 100, + 2); + + LegacyProxyServer proxy = proxyManager.create(Collections.emptyMap()); + int port = proxy.getPort(); + + LegacyProxyServer retrievedProxy = proxyManager.get(port); + + assertEquals("ProxyManager did not return the expected proxy instance", proxy, retrievedProxy); + + Thread.sleep(2500); + + // explicitly create a new proxy to cause a write to the cache. cleanups happen on "every" write and "occasional" reads, so force a cleanup by writing. + int newPort = proxyManager.create(Collections.emptyMap()).getPort(); + proxyManager.delete(newPort); + + LegacyProxyServer expiredProxy = proxyManager.get(port); + + assertNull("ProxyManager did not expire proxy as expected", expiredProxy); + } + + @Test + public void testZeroTtlProxyDoesNotExpire() throws InterruptedException { + int minPort = new Random().nextInt(50000) + 10000; + + ProxyManager proxyManager = new ProxyManager(new LegacyProxyServerProvider(), + minPort, + minPort + 100, + 0); + + LegacyProxyServer proxy = proxyManager.create(Collections.emptyMap()); + int port = proxy.getPort(); + + LegacyProxyServer retrievedProxy = proxyManager.get(port); + + assertEquals("ProxyManager did not return the expected proxy instance", proxy, retrievedProxy); + + Thread.sleep(2500); + + // explicitly create a new proxy to cause a write to the cache. cleanups happen on "every" write and "occasional" reads, so force a cleanup by writing. + int newPort = proxyManager.create(Collections.emptyMap()).getPort(); + proxyManager.delete(newPort); + + LegacyProxyServer nonExpiredProxy = proxyManager.get(port); + + assertEquals("ProxyManager did not return the expected proxy instance", proxy, nonExpiredProxy); + } + +} diff --git a/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ProxyPortAssignmentTest.java b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ProxyPortAssignmentTest.java new file mode 100644 index 000000000..5cf77152d --- /dev/null +++ b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/ProxyPortAssignmentTest.java @@ -0,0 +1,57 @@ +package net.lightbody.bmp.proxy; + +import java.util.HashMap; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import net.lightbody.bmp.exception.ProxyExistsException; +import net.lightbody.bmp.exception.ProxyPortsExhaustedException; +import net.lightbody.bmp.proxy.test.util.ProxyManagerTest; +import org.junit.Test; + +public class ProxyPortAssignmentTest extends ProxyManagerTest { + + @Override + public String[] getArgs() { + return new String[]{"--proxyPortRange", "9091-9093"}; + } + + @Test + public void testAutoAssignment() throws Exception { + int[] ports = {9091, 9092, 9093}; + LegacyProxyServer p; + for(int port : ports){ + p = proxyManager.create(new HashMap()); + assertEquals(port, p.getPort()); + } + try{ + proxyManager.create(new HashMap()); + fail(); + }catch(ProxyPortsExhaustedException e){ + proxyManager.delete(9093); + p = proxyManager.create(new HashMap()); + assertEquals(9093, p.getPort()); + + proxyManager.delete(9091); + p = proxyManager.create(new HashMap()); + assertEquals(9091, p.getPort()); + + for(int port : ports){ + proxyManager.delete(port); + } + } + } + + @Test + public void testManualAssignment() throws Exception { + LegacyProxyServer p = proxyManager.create(new HashMap(), 9094); + assertEquals(9094, p.getPort()); + try{ + proxyManager.create(new HashMap(), 9094); + fail(); + }catch(ProxyExistsException e){ + assertEquals(9094, e.getPort()); + proxyManager.delete(9094); + } + } +} diff --git a/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/test/util/ProxyManagerTest.java b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/test/util/ProxyManagerTest.java new file mode 100644 index 000000000..44a5f8099 --- /dev/null +++ b/browsermob-rest/src/test/java/net/lightbody/bmp/proxy/test/util/ProxyManagerTest.java @@ -0,0 +1,31 @@ +package net.lightbody.bmp.proxy.test.util; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import net.lightbody.bmp.proxy.LegacyProxyServer; +import net.lightbody.bmp.proxy.ProxyManager; +import net.lightbody.bmp.proxy.guice.ConfigModule; +import org.junit.After; +import org.junit.Before; + +public abstract class ProxyManagerTest { + protected ProxyManager proxyManager; + + public abstract String[] getArgs(); + + @Before + public void setUp() throws Exception { + Injector injector = Guice.createInjector(new ConfigModule(getArgs())); + proxyManager = injector.getInstance(ProxyManager.class); + } + + @After + public void tearDown() throws Exception { + for(LegacyProxyServer p : proxyManager.get()){ + try{ + proxyManager.delete(p.getPort()); + }catch(Exception e){ } + } + } + +} diff --git a/browsermob-rest/src/test/resources/log4j2-test.json b/browsermob-rest/src/test/resources/log4j2-test.json new file mode 100644 index 000000000..f3e5e72ec --- /dev/null +++ b/browsermob-rest/src/test/resources/log4j2-test.json @@ -0,0 +1,23 @@ +{ + "configuration" : { + "name": "test", + "appenders": { + "Console": { + "name": "console", + "target": "SYSTEM_OUT", + "PatternLayout": { + "pattern": "%-7r %date %level [%thread] %logger - %msg%n" + } + } + }, + + "loggers": { + "root": { + "level": "info", + "appender-ref": { + "ref": "console" + } + } + } + } +} \ No newline at end of file diff --git a/mitm/README.md b/mitm/README.md new file mode 100644 index 000000000..d37ad8da3 --- /dev/null +++ b/mitm/README.md @@ -0,0 +1,164 @@ +# MITM with LittleProxy +The MITM module is a LittleProxy-compatible module that enables man-in-the-middle interception of HTTPS requests. MITM allows you to: +- [Generate both RSA and EC private keys](#improving-performance-with-elliptic-curve-ec-cryptography) (EC provides a significant performance boost, ~50x faster than RSA) +- [Use a custom Certificate Authority](#using-a-custom-certificate-authority) (e.g. a corporate CA) to sign impersonated certificates, or generate (and optionally save) a new CA on-the-fly +- [Specify a custom trust store](#trusted-root-certificates-and-custom-trust-stores) on proxy-to-server connections, allowing the proxy to trust personal or corporate CAs +- [Use OpenSSL](#openssl-support), improving performance over Java's built-in TLS implementation + +Though MITM is developed and distributed with BrowserMob Proxy, it has no dependency on BMP and can be used in a LittleProxy-only environment. The only additional dependency is the Bouncy Castle encryption library. + +## Quick start +### LittleProxy (without BrowserMob Proxy) + +To use MITM with standalone LittleProxy, add a dependency on the `mitm` module in your pom: + +```xml + + + org.littleshoot + littleproxy + 1.1.2 + + + + + net.lightbody.bmp + mitm + 2.1.4 + +``` + +When creating your LittleProxy server, set the MitmManager to an instance of `net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager`: + +```java + HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(ImpersonatingMitmManager.builder().build()); +``` + +The default implementation of `ImpersonatingMitmManager` will generate a new CA Root Certificate when the first request is made to the proxy. See below for instructions on saving the generated root certificate, or using your own root certificate and private key. + +### BrowserMob Proxy +The MITM module is enabled by default with BrowserMob Proxy. No additional steps are required to enable MITM with BrowserMob Proxy. + +By default, BrowserMob Proxy will use the `ca-keystore-rsa.p12` file to load its CA Root Certificate and Private Key. The corresponding certificate file is `ca-certificate-rsa.cer`, which can be installed as a trusted Certificate Authority in browsers or other HTTP clients to avoid HTTPS warnings when using BrowserMob Proxy. + +## Examples +Several examples are available to help you get started: + +Example File | Configuration +-------------|-------------- +[LittleProxyDefaultConfigExample.java](src/test/java/net/lightbody/bmp/mitm/example/CustomCAKeyStoreExample.java) | Default configuration with LittleProxy +[SaveGeneratedCAExample.java](src/test/java/net/lightbody/bmp/mitm/example/SaveGeneratedCAExample.java) | Save a dynamically-generated CA root certificate for installation in a browser +[CustomCAKeyStoreExample.java](src/test/java/net/lightbody/bmp/mitm/example/CustomCAKeyStoreExample.java) and [CustomCAPemFileExample.java](src/test/java/net/lightbody/bmp/mitm/example/CustomCAPemFileExample.java) | Use an existing CA certificate and private key +[EllipticCurveCAandServerExample.java](src/test/java/net/lightbody/bmp/mitm/example/EllipticCurveCAandServerExample.java) | Use EC cryptography when generating the CA private key and when impersonating server certificates + +## Generating and Saving Root Certificates +By default, when using the MITM module with LittleProxy, the CA Root Certificate and Private Key are generated dynamically. The dynamically generated Root Certificate and Private Key can be saved for installation in a browser or later reuse by using the methods on the `RootCertificateGenerator` class. For example: + +```java + // create a CA Root Certificate using default settings + RootCertificateGenerator rootCertificateGenerator = RootCertificateGenerator.builder().build(); + + // save the newly-generated Root Certificate and Private Key -- the .cer file can be imported + // directly into a browser + rootCertificateGenerator.saveRootCertificateAsPemFile(new File("/tmp/certificate.cer")); + rootCertificateGenerator.savePrivateKeyAsPemFile(new File("/tmp/private-key.pem"), "password"); + + // or save the certificate and private key as a PKCS12 keystore, for later use + rootCertificateGenerator.saveRootCertificateAndKey("PKCS12", new File("/tmp/keystore.p12"), + "privateKeyAlias", "password"); + + // tell the ImpersonatingMitmManager use the RootCertificateGenerator we just configured + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(rootCertificateGenerator) + .build(); + + // tell LittleProxy to use the ImpersonatingMitmManager when MITMing + HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager); +``` + +## Using a Custom Certificate Authority +Whether you are using the MITM module with LittleProxy or BrowserMob Proxy, you can provide your own root certificate and private key to use when signing impersonated server certificates. To use a root certificate and private key from a key store (PKCS12 or JKS), use the `KeyStoreFileCertificateSource` class: + +```java + CertificateAndKeySource existingCertificateSource = + new KeyStoreFileCertificateSource("PKCS12", new File("/path/to/keystore.p12", "privateKeyAlias", "password"); + + // configure the MitmManager to use the custom KeyStore source + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(existingCertificateSource) + .build(); + + // when using LittleProxy, use the .withManInTheMiddle method on the bootstrap: + HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager); + + // when using BrowserMob Proxy, use .setMitmManager() on the BrowserMobProxy object: + BrowserMobProxy proxyServer = new BrowserMobProxyServer(); + proxyServer.setMitmManager(mitmManager); +``` + +You can also load the root certificate and private key from separate PEM-encoded files using the `PemFileCertificateSource` class, or create an implementation of `CertificateAndKeySource` that loads the certificate and private key from another source. + +## Trusted Root Certificates and Custom Trust Stores +The MITM module trusts the Certificate Authorities in the JVM's default trust store, as well as a default list of trusted CAs derived from NSS/Firefox's list of trusted CAs (courtesy of the cURL team: https://curl.haxx.se/ca/cacert.pem). + +To add your own CA to the list of root CAs trusted by the MITM module, use the `add()` methods in the `net.lightbody.bmp.mitm.TrustSource` class. Alternatively, it is possible to disable upstream server validation, but this is only recommended when testing. Examples: +```java + // your root CA certificate(s) may be in a Java KeyStore, a PEM-encoded File or String, or an X509Certificate + File pemEncodedCAFile = ...; + TrustSource trustSource = TrustSource.defaultTrustSource().add(pemEncodedCAFile); + + // when using MITM+LittleProxy, use the trustAllServers() method, or set the TrustSource on the ImpersonatingMitmManager: + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .trustSource(trustSource) // use an explicit trust source, or: + .trustAllServers(true) // do not validate servers' certificates + .build(); + + // when using BrowserMob Proxy, use the .setTrustSource() method: + BrowserMobProxy proxyServer = new BrowserMobProxyServer(); + proxyServer.setTrustSource(trustSource); + // or disable server certificate validation: + proxyServer.setTrustAllServers(true); +``` + +## Improving Performance with Elliptic Curve (EC) Cryptography +By default, the certificates generated by the MITM module use RSA private keys for both impersonated server certificates and for generated CA root certificates. However, all modern browsers support Elliptic Curve Cryptography, which uses smaller key sizes. As a result, impersonated EC server certificates can be generated significantly faster (approximately 50x faster is common, typically <10ms per impersonated certificate). + +The MITM module's RootCertificateGenerator can be configured to generate an EC root certificate for use with EC server certificates. If you are using your own CA root certificate +and private key, make sure to generate an EC private key if you intend to use impersonated EC server certificates. (Though it is possible to generate "hybrid" +server certificates with an EC key signed by an RSA CA, they are uncommon, and not all clients support them. In particular, Java clients and servers [before 8u92 do not support hybrid certificates.](https://bugs.openjdk.java.net/browse/JDK-8136442)) + +To generate EC certificates for impersonated servers, set the `serverKeyGenerator` to `ECKeyGenerator` in ImpersonatingMitmManager. To generate an EC root certificate and private key, set the `keyGenerator` to `ECKeyGenerator` in RootCertificateGenerator: + +```java + // create a RootCertificateGenerator that generates EC Certificate Authorities; you may also load your + // own EC certificate and private key using any other CertificateAndKeySource implementation + // (KeyStoreFileCertificateSource, PemFileCertificateSource, etc.). + CertificateAndKeySource rootCertificateGenerator = RootCertificateGenerator.builder() + .keyGenerator(new ECKeyGenerator()) + .build(); + + // tell the ImpersonatingMitmManager to generate EC keys and to use the EC RootCertificateGenerator + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(rootCertificateGenerator) + .serverKeyGenerator(new ECKeyGenerator()) + .build(); + + // when using LittleProxy: + HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager); + + // when using BrowserMob Proxy: + BrowserMobProxy proxy = new BrowserMobProxyServer(); + proxy.setMitmManager(mitmManager); +``` + +## OpenSSL support +The MITM module takes advantage of Netty's support for OpenSSL, allowing you to use OpenSSL instead of Java's built-in TLS implementation, which may provide +significant performance benefits. The MITM module itself requires no additional configuration to use OpenSSL: all you need is an OpenSSL installation and a dependency on the `netty-tcnative` library for your platform. +See Netty's OpenSSL instructions for details: http://netty.io/wiki/requirements-for-4.x.html#tls-with-openssl + +## Acknowledgements +The MITM module would not have been possible without the efforts of Frank Ganske, the Zed Attack Proxy, and Brad Hill. Thank you for all your excellent work! diff --git a/mitm/pom.xml b/mitm/pom.xml new file mode 100644 index 000000000..ca0f5ecdb --- /dev/null +++ b/mitm/pom.xml @@ -0,0 +1,115 @@ + + + + browsermob-proxy + net.lightbody.bmp + 2.1.6-SNAPSHOT + + 4.0.0 + + LittleProxy MITM Module + + mitm + + + + + net.lightbody.bmp + littleproxy + 1.1.0-beta-bmp-17 + true + + + com.barchart.udt + barchart-udt-bundle + + + commons-cli + commons-cli + + + + + + org.bouncycastle + bcprov-jdk15on + + + + org.bouncycastle + bcpkix-jdk15on + + + + junit + junit + test + + + + org.mockito + mockito-core + test + + + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + + com.fasterxml.jackson.core + jackson-core + test + + + + com.fasterxml.jackson.core + jackson-databind + test + + + + com.fasterxml.jackson.core + jackson-annotations + test + + + + org.hamcrest + hamcrest-library + test + + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + test + + + + org.slf4j + jcl-over-slf4j + test + + + + + \ No newline at end of file diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateAndKey.java b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateAndKey.java new file mode 100644 index 000000000..8c5b1696d --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateAndKey.java @@ -0,0 +1,25 @@ +package net.lightbody.bmp.mitm; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * A simple container for an X.509 certificate and its corresponding private key. + */ +public class CertificateAndKey { + private final X509Certificate certificate; + private final PrivateKey privateKey; + + public CertificateAndKey(X509Certificate certificate, PrivateKey privateKey) { + this.certificate = certificate; + this.privateKey = privateKey; + } + + public X509Certificate getCertificate() { + return certificate; + } + + public PrivateKey getPrivateKey() { + return privateKey; + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateAndKeySource.java b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateAndKeySource.java new file mode 100644 index 000000000..04e901d0a --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateAndKeySource.java @@ -0,0 +1,16 @@ +package net.lightbody.bmp.mitm; + +/** + * A CertificateAndKeySource generates {@link CertificateAndKey}s, i.e. the root certificate and private key used + * to sign impersonated certificates of upstream servers. Implementations of this interface load impersonation materials + * from various sources, including Java KeyStores, JKS files, etc., or generate them on-the-fly. + */ +public interface CertificateAndKeySource { + /** + * Loads a certificate and its corresponding private key. Every time this method is called, it should return the same + * certificate and private key (although it may be a different {@link CertificateAndKey} instance). + * + * @return certificate and its corresponding private key + */ + CertificateAndKey load(); +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateInfo.java b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateInfo.java new file mode 100644 index 000000000..288e86e35 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateInfo.java @@ -0,0 +1,114 @@ +package net.lightbody.bmp.mitm; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * Container for X.509 Certificate information. + */ +public class CertificateInfo { + private String commonName; + private String organization; + private String organizationalUnit; + + private String email; + private String locality; + private String state; + private String countryCode; + + private Date notBefore; + private Date notAfter; + + private List subjectAlternativeNames = Collections.emptyList(); + + public String getCommonName() { + return commonName; + } + + public String getOrganization() { + return organization; + } + + public String getOrganizationalUnit() { + return organizationalUnit; + } + + public Date getNotBefore() { + return notBefore; + } + + public Date getNotAfter() { + return notAfter; + } + + public String getEmail() { + return email; + } + + public String getLocality() { + return locality; + } + + public String getState() { + return state; + } + + public String getCountryCode() { + return countryCode; + } + + public List getSubjectAlternativeNames() { + return subjectAlternativeNames; + } + + public CertificateInfo commonName(String commonName) { + this.commonName = commonName; + return this; + } + + public CertificateInfo organization(String organization) { + this.organization = organization; + return this; + } + + public CertificateInfo organizationalUnit(String organizationalUnit) { + this.organizationalUnit = organizationalUnit; + return this; + } + + public CertificateInfo notBefore(Date notBefore) { + this.notBefore = notBefore; + return this; + } + + public CertificateInfo notAfter(Date notAfter) { + this.notAfter = notAfter; + return this; + } + + public CertificateInfo email(String email) { + this.email = email; + return this; + } + + public CertificateInfo locality(String locality) { + this.locality = locality; + return this; + } + + public CertificateInfo state(String state) { + this.state = state; + return this; + } + + public CertificateInfo countryCode(String countryCode) { + this.countryCode = countryCode; + return this; + } + + public CertificateInfo subjectAlternativeNames(List subjectAlternativeNames) { + this.subjectAlternativeNames = subjectAlternativeNames; + return this; + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateInfoGenerator.java b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateInfoGenerator.java new file mode 100644 index 000000000..21c064f89 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/CertificateInfoGenerator.java @@ -0,0 +1,19 @@ +package net.lightbody.bmp.mitm; + +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * A functional interface to allow customization of the certificates generated by the + * {@link net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager}. + */ +public interface CertificateInfoGenerator { + /** + * Generate a certificate for the specified hostnames, optionally using parameters from the originalCertificate. + * + * @param hostnames the hostnames to generate the certificate for, which may include wildcards + * @param originalCertificate original X.509 certificate sent by the upstream server, which may be null + * @return CertificateInfo to be used to create an X509Certificate for the specified hostnames + */ + CertificateInfo generate(List hostnames, X509Certificate originalCertificate); +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/ExistingCertificateSource.java b/mitm/src/main/java/net/lightbody/bmp/mitm/ExistingCertificateSource.java new file mode 100644 index 000000000..9ffbba28b --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/ExistingCertificateSource.java @@ -0,0 +1,31 @@ +package net.lightbody.bmp.mitm; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * A simple adapter that produces a {@link CertificateAndKey} from existing {@link X509Certificate} and {@link PrivateKey} + * java objects. + */ +public class ExistingCertificateSource implements CertificateAndKeySource { + private final X509Certificate rootCertificate; + private final PrivateKey privateKey; + + public ExistingCertificateSource(X509Certificate rootCertificate, PrivateKey privateKey) { + if (rootCertificate == null) { + throw new IllegalArgumentException("CA root certificate cannot be null"); + } + + if (privateKey == null) { + throw new IllegalArgumentException("Private key cannot be null"); + } + + this.rootCertificate = rootCertificate; + this.privateKey = privateKey; + } + + @Override + public CertificateAndKey load() { + return new CertificateAndKey(rootCertificate, privateKey); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/HostnameCertificateInfoGenerator.java b/mitm/src/main/java/net/lightbody/bmp/mitm/HostnameCertificateInfoGenerator.java new file mode 100644 index 000000000..e5ac4a35a --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/HostnameCertificateInfoGenerator.java @@ -0,0 +1,54 @@ +package net.lightbody.bmp.mitm; + +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.List; + +/** + * A {@link CertificateInfoGenerator} that uses only a hostname to populate a new {@link CertificateInfo}. The + * values in the upstream server's original X.509 certificate will be ignored. + */ +public class HostnameCertificateInfoGenerator implements CertificateInfoGenerator { + /** + * The 'O' to use for the impersonated server certificate when doing "simple" certificate impersonation (i.e. + * not copying values from actual server certificate). + */ + private static final String DEFAULT_IMPERSONATED_CERT_ORG = "Impersonated Certificate"; + + /** + * The 'O' to use for the impersonated server certificate when doing "simple" certificate impersonation. + */ + private static final String DEFAULT_IMPERSONATED_CERT_ORG_UNIT = "LittleProxy MITM"; + + @Override + public CertificateInfo generate(List hostnames, X509Certificate originalCertificate) { + if (hostnames == null || hostnames.size() < 1) { + throw new IllegalArgumentException("Cannot create X.509 certificate without server hostname"); + } + + // take the first entry as the CN + String commonName = hostnames.get(0); + + return new CertificateInfo() + .commonName(commonName) + .organization(DEFAULT_IMPERSONATED_CERT_ORG) + .organizationalUnit(DEFAULT_IMPERSONATED_CERT_ORG_UNIT) + .notBefore(getNotBefore()) + .notAfter(getNotAfter()) + .subjectAlternativeNames(hostnames); + } + + /** + * Returns the default Not Before date for impersonated certificates. Defaults to the current date minus 1 year. + */ + protected Date getNotBefore() { + return new Date(System.currentTimeMillis() - 365L * 24L * 60L * 60L * 1000L); + } + + /** + * Returns the default Not After date for impersonated certificates. Defaults to the current date plus 1 year. + */ + protected Date getNotAfter() { + return new Date(System.currentTimeMillis() + 365L * 24L * 60L * 60L * 1000L); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/KeyStoreCertificateSource.java b/mitm/src/main/java/net/lightbody/bmp/mitm/KeyStoreCertificateSource.java new file mode 100644 index 000000000..f92a4f4ec --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/KeyStoreCertificateSource.java @@ -0,0 +1,77 @@ +package net.lightbody.bmp.mitm; + +import net.lightbody.bmp.mitm.exception.CertificateSourceException; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableEntryException; +import java.security.cert.X509Certificate; + +/** + * A {@link CertificateAndKeySource} that loads the root certificate and private key from a Java KeyStore. The + * KeyStore must contain a certificate and a private key, specified by the privateKeyAlias value. The KeyStore must + * already be loaded and initialized; to load the KeyStore from a file or classpath resource, use + * {@link KeyStoreFileCertificateSource}, {@link PemFileCertificateSource}, or a custom + * implementation of {@link CertificateAndKeySource}. + */ +public class KeyStoreCertificateSource implements CertificateAndKeySource { + private final KeyStore keyStore; + private final String keyStorePassword; + private final String privateKeyAlias; + + public KeyStoreCertificateSource(KeyStore keyStore, String privateKeyAlias, String keyStorePassword) { + if (keyStore == null) { + throw new IllegalArgumentException("KeyStore cannot be null"); + } + + if (privateKeyAlias == null) { + throw new IllegalArgumentException("Private key alias cannot be null"); + } + + if (keyStorePassword == null) { + throw new IllegalArgumentException("KeyStore password cannot be null"); + } + + this.keyStore = keyStore; + this.keyStorePassword = keyStorePassword; + this.privateKeyAlias = privateKeyAlias; + } + + @Override + public CertificateAndKey load() { + try { + KeyStore.Entry entry; + try { + entry = keyStore.getEntry(privateKeyAlias, new KeyStore.PasswordProtection(keyStorePassword.toCharArray())); + } catch (UnrecoverableEntryException e) { + throw new CertificateSourceException("Unable to load private key with alias " + privateKeyAlias + " from KeyStore. Verify the KeyStore password is correct.", e); + } + + if (entry == null) { + throw new CertificateSourceException("Unable to find entry in keystore with alias: " + privateKeyAlias); + } + + if (!(entry instanceof KeyStore.PrivateKeyEntry)) { + throw new CertificateSourceException("Entry in KeyStore with alias " + privateKeyAlias + " did not contain a private key entry"); + } + + KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry; + + PrivateKey privateKey = privateKeyEntry.getPrivateKey(); + + if (!(privateKeyEntry.getCertificate() instanceof X509Certificate)) { + throw new CertificateSourceException("Certificate for private key in KeyStore was not an X509Certificate. Private key alias: " + privateKeyAlias + + ". Certificate type: " + (privateKeyEntry.getCertificate() != null ? privateKeyEntry.getCertificate().getClass().getName() : null)); + } + + X509Certificate x509Certificate = (X509Certificate) privateKeyEntry.getCertificate(); + + return new CertificateAndKey(x509Certificate, privateKey); + } catch (KeyStoreException | NoSuchAlgorithmException e) { + throw new CertificateSourceException("Error accessing keyStore", e); + } + } + +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/KeyStoreFileCertificateSource.java b/mitm/src/main/java/net/lightbody/bmp/mitm/KeyStoreFileCertificateSource.java new file mode 100644 index 000000000..57717230f --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/KeyStoreFileCertificateSource.java @@ -0,0 +1,146 @@ +package net.lightbody.bmp.mitm; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import net.lightbody.bmp.mitm.exception.CertificateSourceException; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.SecurityProviderTool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.security.KeyStore; + +/** + * Loads a KeyStore from a file or classpath resource. If configured with a File object, attempts to load the KeyStore + * from the specified file. Otherwise, attempts to load the KeyStore from a classpath resource. + */ +public class KeyStoreFileCertificateSource implements CertificateAndKeySource { + private static final Logger log = LoggerFactory.getLogger(KeyStoreFileCertificateSource.class); + + private final String keyStoreClasspathResource; + private final File keyStoreFile; + + private final String keyStoreType; + + private final String keyStorePassword; + private final String privateKeyAlias; + + private SecurityProviderTool securityProviderTool = new DefaultSecurityProviderTool(); + + private final Supplier certificateAndKey = Suppliers.memoize(new Supplier() { + @Override + public CertificateAndKey get() { + return loadKeyStore(); + } + }); + + /** + * Creates a {@link CertificateAndKeySource} that loads an existing {@link KeyStore} from a classpath resource. + * @param keyStoreType the KeyStore type, such as PKCS12 or JKS + * @param keyStoreClasspathResource classpath resource to load (for example, "/keystore.jks") + * @param privateKeyAlias the alias of the private key in the KeyStore + * @param keyStorePassword te KeyStore password + */ + public KeyStoreFileCertificateSource(String keyStoreType, String keyStoreClasspathResource, String privateKeyAlias, String keyStorePassword) { + if (keyStoreClasspathResource == null) { + throw new IllegalArgumentException("The classpath location of the KeyStore cannot be null"); + } + + if (keyStoreType == null) { + throw new IllegalArgumentException("KeyStore type cannot be null"); + } + + if (privateKeyAlias == null) { + throw new IllegalArgumentException("Alias of the private key in the KeyStore cannot be null"); + } + + this.keyStoreClasspathResource = keyStoreClasspathResource; + this.keyStoreFile = null; + + this.keyStoreType = keyStoreType; + this.keyStorePassword = keyStorePassword; + this.privateKeyAlias = privateKeyAlias; + } + + /** + * Creates a {@link CertificateAndKeySource} that loads an existing {@link KeyStore} from a classpath resource. + * @param keyStoreType the KeyStore type, such as PKCS12 or JKS + * @param keyStoreFile KeyStore file to load + * @param privateKeyAlias the alias of the private key in the KeyStore + * @param keyStorePassword te KeyStore password + */ + public KeyStoreFileCertificateSource(String keyStoreType, File keyStoreFile, String privateKeyAlias, String keyStorePassword) { + if (keyStoreFile == null) { + throw new IllegalArgumentException("The KeyStore file cannot be null"); + } + + if (keyStoreType == null) { + throw new IllegalArgumentException("KeyStore type cannot be null"); + } + + if (privateKeyAlias == null) { + throw new IllegalArgumentException("Alias of the private key in the KeyStore cannot be null"); + } + + this.keyStoreFile = keyStoreFile; + this.keyStoreClasspathResource = null; + + this.keyStoreType = keyStoreType; + this.keyStorePassword = keyStorePassword; + this.privateKeyAlias = privateKeyAlias; + } + + /** + * Override the default {@link SecurityProviderTool} used to load the KeyStore. + */ + public KeyStoreFileCertificateSource certificateTool(SecurityProviderTool securityProviderTool) { + this.securityProviderTool = securityProviderTool; + return this; + } + + @Override + public CertificateAndKey load() { + return certificateAndKey.get(); + + } + + /** + * Loads the {@link CertificateAndKey} from the KeyStore using the {@link SecurityProviderTool}. + */ + private CertificateAndKey loadKeyStore() { + // load the KeyStore from the file or classpath resource, then delegate to a KeyStoreCertificateSource + KeyStore keyStore; + if (keyStoreFile != null) { + keyStore = securityProviderTool.loadKeyStore(keyStoreFile, keyStoreType, keyStorePassword); + } else { + // copy the classpath resource to a temporary file and load the keystore from that temp file + Path tempKeyStoreFile = null; + try (InputStream keystoreAsStream = KeyStoreFileCertificateSource.class.getResourceAsStream(keyStoreClasspathResource)) { + tempKeyStoreFile = Files.createTempFile("keystore", "temp"); + Files.copy(keystoreAsStream, tempKeyStoreFile, StandardCopyOption.REPLACE_EXISTING); + + keyStore = securityProviderTool.loadKeyStore(tempKeyStoreFile.toFile(), keyStoreType, keyStorePassword); + } catch (IOException e) { + throw new CertificateSourceException("Unable to open KeyStore classpath resource: " + keyStoreClasspathResource, e); + } finally { + if (tempKeyStoreFile != null) { + try { + Files.deleteIfExists(tempKeyStoreFile); + } catch (IOException e) { + log.warn("Unable to delete temporary KeyStore file: {}.", tempKeyStoreFile.toAbsolutePath()); + } + } + } + } + + KeyStoreCertificateSource keyStoreCertificateSource = new KeyStoreCertificateSource(keyStore, privateKeyAlias, keyStorePassword); + + return keyStoreCertificateSource.load(); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/PemFileCertificateSource.java b/mitm/src/main/java/net/lightbody/bmp/mitm/PemFileCertificateSource.java new file mode 100644 index 000000000..6bf5253a5 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/PemFileCertificateSource.java @@ -0,0 +1,83 @@ +package net.lightbody.bmp.mitm; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.SecurityProviderTool; +import net.lightbody.bmp.mitm.util.EncryptionUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.StringReader; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * Loads impersonation materials from two separate, PEM-encoded files: a CA root certificate and its corresponding + * private key. + */ +public class PemFileCertificateSource implements CertificateAndKeySource { + private static final Logger log = LoggerFactory.getLogger(PemFileCertificateSource.class); + + private final File certificateFile; + private final File privateKeyFile; + private final String privateKeyPassword; + + private SecurityProviderTool securityProviderTool = new DefaultSecurityProviderTool(); + + private final Supplier certificateAndKey = Suppliers.memoize(new Supplier() { + @Override + public CertificateAndKey get() { + return loadCertificateAndKeyFiles(); + } + }); + + /** + * Creates a {@link CertificateAndKeySource} that loads the certificate and private key from PEM files. + * + * @param certificateFile PEM-encoded file containing the root certificate + * @param privateKeyFile PEM-encoded file continaing the certificate's private key + * @param privateKeyPassword password for the private key + */ + public PemFileCertificateSource(File certificateFile, File privateKeyFile, String privateKeyPassword) { + this.certificateFile = certificateFile; + this.privateKeyFile = privateKeyFile; + this.privateKeyPassword = privateKeyPassword; + } + + /** + * Override the default {@link SecurityProviderTool} used to load the PEM files. + */ + public PemFileCertificateSource certificateTool(SecurityProviderTool securityProviderTool) { + this.securityProviderTool = securityProviderTool; + return this; + } + + @Override + public CertificateAndKey load() { + return certificateAndKey.get(); + } + + private CertificateAndKey loadCertificateAndKeyFiles() { + if (certificateFile == null) { + throw new IllegalArgumentException("PEM root certificate file cannot be null"); + } + + if (privateKeyFile == null) { + throw new IllegalArgumentException("PEM private key file cannot be null"); + } + + if (privateKeyPassword == null) { + log.warn("Attempting to load private key from file without password. Private keys should be password-protected."); + } + + String pemEncodedCertificate = EncryptionUtil.readPemStringFromFile(certificateFile); + X509Certificate certificate = securityProviderTool.decodePemEncodedCertificate(new StringReader(pemEncodedCertificate)); + + String pemEncodedPrivateKey = EncryptionUtil.readPemStringFromFile(privateKeyFile); + PrivateKey privateKey = securityProviderTool.decodePemEncodedPrivateKey(new StringReader(pemEncodedPrivateKey), privateKeyPassword); + + return new CertificateAndKey(certificate, privateKey); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/RootCertificateGenerator.java b/mitm/src/main/java/net/lightbody/bmp/mitm/RootCertificateGenerator.java new file mode 100644 index 000000000..ac4c7a0b7 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/RootCertificateGenerator.java @@ -0,0 +1,259 @@ +package net.lightbody.bmp.mitm; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import net.lightbody.bmp.mitm.keys.KeyGenerator; +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.SecurityProviderTool; +import net.lightbody.bmp.mitm.util.EncryptionUtil; +import net.lightbody.bmp.mitm.util.MitmConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.KeyPair; +import java.security.KeyStore; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * A {@link CertificateAndKeySource} that dynamically generates a CA root certificate and private key. The certificate + * and key will only be generated once; all subsequent calls to {@link #load()} will return the same materials. To save + * the generated certificate and/or private key for installation in a browser or other client, use one of the encode + * or save methods: + *
      + *
    • {@link #encodeRootCertificateAsPem()}
    • + *
    • {@link #encodePrivateKeyAsPem(String)}
    • + *
    • {@link #saveRootCertificateAsPemFile(File)}
    • + *
    • {@link #savePrivateKeyAsPemFile(File, String)}
    • + *
    • {@link #saveRootCertificateAndKey(String, File, String, String)}
    • + *
    + */ +public class RootCertificateGenerator implements CertificateAndKeySource { + private static final Logger log = LoggerFactory.getLogger(RootCertificateGenerator.class); + + private final CertificateInfo rootCertificateInfo; + + private final String messageDigest; + + private final KeyGenerator keyGenerator; + + private final SecurityProviderTool securityProviderTool; + + /** + * The default algorithm to use when encrypting objects in PEM files (such as private keys). + */ + private static final String DEFAULT_PEM_ENCRYPTION_ALGORITHM = "AES-128-CBC"; + + /** + * The new root certificate and private key are generated only once, even across multiple calls to {@link #load()}}, + * to allow users to save the new generated root certificate for use in browsers/other HTTP clients. + */ + private final Supplier generatedCertificateAndKey = Suppliers.memoize(new Supplier() { + @Override + public CertificateAndKey get() { + return generateRootCertificate(); + } + }); + + public RootCertificateGenerator(CertificateInfo rootCertificateInfo, + String messageDigest, + KeyGenerator keyGenerator, + SecurityProviderTool securityProviderTool) { + if (rootCertificateInfo == null) { + throw new IllegalArgumentException("CA root certificate cannot be null"); + } + + if (messageDigest == null) { + throw new IllegalArgumentException("Message digest cannot be null"); + } + + if (keyGenerator == null) { + throw new IllegalArgumentException("Key generator cannot be null"); + } + + if (securityProviderTool == null) { + throw new IllegalArgumentException("Certificate tool cannot be null"); + } + + this.rootCertificateInfo = rootCertificateInfo; + this.messageDigest = messageDigest; + this.keyGenerator = keyGenerator; + this.securityProviderTool = securityProviderTool; + } + + @Override + public CertificateAndKey load() { + // only generate the materials once, so they can can be saved if desired + return generatedCertificateAndKey.get(); + } + + /** + * Generates a new CA root certificate and private key. + * + * @return new root certificate and private key + */ + private CertificateAndKey generateRootCertificate() { + long generationStart = System.currentTimeMillis(); + + // create the public and private key pair that will be used to sign the generated certificate + KeyPair caKeyPair = keyGenerator.generate(); + + // delegate the creation and signing of the X.509 certificate to the certificate tool + CertificateAndKey certificateAndKey = securityProviderTool.createCARootCertificate( + rootCertificateInfo, + caKeyPair, + messageDigest); + + long generationFinished = System.currentTimeMillis(); + + log.info("Generated CA root certificate and private key in {}ms. Key generator: {}. Signature algorithm: {}.", + generationFinished - generationStart, keyGenerator, messageDigest); + + return certificateAndKey; + } + + /** + * Returns the generated root certificate as a PEM-encoded String. + */ + public String encodeRootCertificateAsPem() { + return securityProviderTool.encodeCertificateAsPem(generatedCertificateAndKey.get().getCertificate()); + } + + /** + * Returns the generated private key as a PEM-encoded String, encrypted using the specified password and the + * {@link #DEFAULT_PEM_ENCRYPTION_ALGORITHM}. + * + * @param privateKeyPassword password to use to encrypt the private key + */ + public String encodePrivateKeyAsPem(String privateKeyPassword) { + return securityProviderTool.encodePrivateKeyAsPem(generatedCertificateAndKey.get().getPrivateKey(), privateKeyPassword, DEFAULT_PEM_ENCRYPTION_ALGORITHM); + } + + /** + * Saves the root certificate as PEM-encoded data to the specified file. + */ + public void saveRootCertificateAsPemFile(File file) { + String pemEncodedCertificate = securityProviderTool.encodeCertificateAsPem(generatedCertificateAndKey.get().getCertificate()); + + EncryptionUtil.writePemStringToFile(file, pemEncodedCertificate); + } + + /** + * Saves the private key as PEM-encoded data to a file, using the specified password to encrypt the private key and + * the {@link #DEFAULT_PEM_ENCRYPTION_ALGORITHM}. If the password is null, the private key will be stored unencrypted. + * In general, private keys should not be stored unencrypted. + * + * @param file file to save the private key to + * @param passwordForPrivateKey password to protect the private key + */ + public void savePrivateKeyAsPemFile(File file, String passwordForPrivateKey) { + String pemEncodedPrivateKey = securityProviderTool.encodePrivateKeyAsPem(generatedCertificateAndKey.get().getPrivateKey(), passwordForPrivateKey, DEFAULT_PEM_ENCRYPTION_ALGORITHM); + + EncryptionUtil.writePemStringToFile(file, pemEncodedPrivateKey); + } + + /** + * Saves the generated certificate and private key as a file, using the specified password to protect the key store. + * + * @param keyStoreType the KeyStore type, such as PKCS12 or JKS + * @param file file to export the root certificate and private key to + * @param privateKeyAlias alias for the private key in the KeyStore + * @param password password for the private key and the KeyStore + */ + public void saveRootCertificateAndKey(String keyStoreType, + File file, + String privateKeyAlias, + String password) { + CertificateAndKey certificateAndKey = generatedCertificateAndKey.get(); + + KeyStore keyStore = securityProviderTool.createRootCertificateKeyStore(keyStoreType, certificateAndKey, privateKeyAlias, password); + + securityProviderTool.saveKeyStore(file, keyStore, password); + } + + /** + * Convenience method to return a new {@link Builder} instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A Builder for {@link RootCertificateGenerator}s. Initialized with suitable default values suitable for most purposes. + */ + public static class Builder { + private CertificateInfo certificateInfo = new CertificateInfo() + .commonName(getDefaultCommonName()) + .organization("CA dynamically generated by LittleProxy") + .notBefore(new Date(System.currentTimeMillis() - 365L * 24L * 60L * 60L * 1000L)) + .notAfter(new Date(System.currentTimeMillis() + 365L * 24L * 60L * 60L * 1000L)); + + private KeyGenerator keyGenerator = new RSAKeyGenerator(); + + private String messageDigest = MitmConstants.DEFAULT_MESSAGE_DIGEST; + + private SecurityProviderTool securityProviderTool = new DefaultSecurityProviderTool(); + + /** + * Certificate info to use to generate the root certificate. Reasonable default values will be used if certificate + * info is not supplied. + */ + public Builder certificateInfo(CertificateInfo certificateInfo) { + this.certificateInfo = certificateInfo; + return this; + } + + /** + * The {@link KeyGenerator} that will be used to generate the root certificate's public and private keys. + */ + public Builder keyGenerator(KeyGenerator keyGenerator) { + this.keyGenerator = keyGenerator; + return this; + } + + /** + * The message digest that will be used when self-signing the root certificates. + */ + public Builder messageDigest(String messageDigest) { + this.messageDigest = messageDigest; + return this; + } + + /** + * The {@link SecurityProviderTool} implementation that will be used to generate certificates. + */ + public Builder certificateTool(SecurityProviderTool securityProviderTool) { + this.securityProviderTool = securityProviderTool; + return this; + } + + public RootCertificateGenerator build() { + return new RootCertificateGenerator(certificateInfo, messageDigest, keyGenerator, securityProviderTool); + } + } + + /** + * Creates a default CN field for a certificate, using the hostname of this machine and the current time. + */ + private static String getDefaultCommonName() { + String hostname; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + hostname = "localhost"; + } + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz"); + + String currentDateTime = dateFormat.format(new Date()); + + String defaultCN = "Generated CA (" + hostname + ") " + currentDateTime; + + // CN fields can only be 64 characters + return defaultCN.length() <= 64 ? defaultCN : defaultCN.substring(0, 63); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/TrustSource.java b/mitm/src/main/java/net/lightbody/bmp/mitm/TrustSource.java new file mode 100644 index 000000000..0aa3ebf53 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/TrustSource.java @@ -0,0 +1,199 @@ +package net.lightbody.bmp.mitm; + +import com.google.common.collect.ObjectArrays; +import com.google.common.io.Files; +import net.lightbody.bmp.mitm.exception.UncheckedIOException; +import net.lightbody.bmp.mitm.util.TrustUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * A source of trusted root certificate authorities. Provides static methods to obtain default trust sources: + *
      + *
    • {@link #defaultTrustSource()}- both the built-in and JVM-trusted CAs
    • + *
    • {@link #javaTrustSource()} - only default CAs trusted by the JVM
    • + *
    • {@link #builtinTrustSource()} - only built-in trusted CAs (ultimately derived from Firefox's trust list)
    • + *
    + * + * Custom TrustSources can be built by starting with {@link #empty()}, then calling the various add() methods to add + * PEM-encoded files and Strings, KeyStores, and X509Certificates to the TrustSource. For example: + *

    + * + * TrustSource customTrustSource = TrustSource.empty() + * .add(myX509Certificate) + * .add(pemFileContainingMyCA) + * .add(javaKeyStore); + * + *

    + * Note: This class is immutable, so calls to add() will return a new instance, rather than modifying the existing instance. + */ +public class TrustSource { + /** + * The default TrustSource. To obtain this TrustSource, use {@link #defaultTrustSource()}. + */ + private static final TrustSource DEFAULT_TRUST_SOURCE = TrustSource.javaTrustSource().add(TrustSource.builtinTrustSource()); + + /** + * The root CAs this TrustSource trusts. + */ + private final X509Certificate[] trustedCAs; + + /** + * Creates a TrustSource that contains no trusted certificates. For public use, see {@link #empty()}. + */ + protected TrustSource() { + this(TrustUtil.EMPTY_CERTIFICATE_ARRAY); + } + + /** + * Creates a TrustSource that considers only the specified certificates as "trusted". For public use, + * use {@link #empty()} followed by {@link #add(X509Certificate...)}. + * + * @param trustedCAs root CAs to trust + */ + protected TrustSource(X509Certificate... trustedCAs) { + if (trustedCAs == null) { + this.trustedCAs = TrustUtil.EMPTY_CERTIFICATE_ARRAY; + } else { + this.trustedCAs = trustedCAs; + } + } + + /** + * Returns the X509 certificates considered "trusted" by this TrustSource. This method will not return null, but + * may return an empty array. + */ + public X509Certificate[] getTrustedCAs() { + return trustedCAs; + } + + /** + * Returns a TrustSource that contains no trusted CAs. Can be used in conjunction with the add() methods to build + * a TrustSource containing custom CAs from a variety of sources (PEM files, KeyStores, etc.). + */ + public static TrustSource empty() { + return new TrustSource(); + } + + /** + * Returns a TrustSource containing the default trusted CAs. By default, contains both the JVM's trusted CAs and the + * built-in trusted CAs (Firefox's trusted CAs). + */ + public static TrustSource defaultTrustSource() { + return DEFAULT_TRUST_SOURCE; + } + + /** + * Returns a TrustSource containing only the builtin trusted CAs and does not include the JVM's trusted CAs. + * See {@link TrustUtil#getBuiltinTrustedCAs()}. + */ + public static TrustSource builtinTrustSource() { + return new TrustSource(TrustUtil.getBuiltinTrustedCAs()); + } + + /** + * Returns a TrustSource containing the default CAs trusted by this JVM. See {@link TrustUtil#getJavaTrustedCAs()}. + */ + public static TrustSource javaTrustSource() { + return new TrustSource(TrustUtil.getJavaTrustedCAs()); + } + + /** + * Returns a new TrustSource containing the same trusted CAs as this TrustSource, plus zero or more CAs contained in + * the PEM-encoded String. The String may contain multiple certificates and may contain comments or other non-PEM-encoded + * text, as long as the PEM-encoded certificates are delimited by appropriate BEGIN_CERTIFICATE and END_CERTIFICATE + * text blocks. + * + * @param trustedPemEncodedCAs String containing PEM-encoded certificates to trust + * @return a new TrustSource containing this TrustSource's trusted CAs plus the CAs in the specified String + */ + public TrustSource add(String trustedPemEncodedCAs) { + if (trustedPemEncodedCAs == null) { + throw new IllegalArgumentException("PEM-encoded trusted CA String cannot be null"); + } + + X509Certificate[] trustedCertificates = TrustUtil.readX509CertificatesFromPem(trustedPemEncodedCAs); + + return add(trustedCertificates); + } + + /** + * Returns a new TrustSource containing the same trusted CAs as this TrustSource, plus zero or more additional + * trusted X509Certificates. If trustedCertificates is null or empty, returns this same TrustSource. + * + * @param trustedCertificates X509Certificates of CAs to trust + * @return a new TrustSource containing this TrustSource's trusted CAs plus the specified CAs + */ + public TrustSource add(X509Certificate... trustedCertificates) { + if (trustedCertificates == null || trustedCertificates.length == 0) { + return this; + } + + X509Certificate[] newTrustedCAs = ObjectArrays.concat(trustedCAs, trustedCertificates, X509Certificate.class); + + return new TrustSource(newTrustedCAs); + } + + /** + * Returns a new TrustSource containing the same trusted CAs as this TrustSource, plus all trusted certificate + * entries from the specified trustStore. This method will only add trusted certificate entries from the specified + * KeyStore (i.e. entries of type {@link java.security.KeyStore.TrustedCertificateEntry}; private keys will be + * ignored. The trustStore may be in JKS or PKCS12 format. + * + * @param trustStore keystore containing trusted certificate entries + * @return a new TrustSource containing this TrustSource's trusted CAs plus trusted certificate entries from the keystore + */ + public TrustSource add(KeyStore trustStore) { + if (trustStore == null) { + throw new IllegalArgumentException("Trust store cannot be null"); + } + + List trustedCertificates = TrustUtil.extractTrustedCertificateEntries(trustStore); + + return add(trustedCertificates.toArray(new X509Certificate[0])); + } + + /** + * Returns a new TrustSource containing the same trusted CAs as this TrustSource, plus zero or more CAs contained in + * the PEM-encoded File. The File may contain multiple certificates and may contain comments or other non-PEM-encoded + * text, as long as the PEM-encoded certificates are delimited by appropriate BEGIN_CERTIFICATE and END_CERTIFICATE + * text blocks. The file may contain UTF-8 characters, but the PEM-encoded certificate data itself must be US-ASCII. + * + * @param trustedCAPemFile File containing PEM-encoded certificates + * @return a new TrustSource containing this TrustSource's trusted CAs plus the CAs in the specified String + */ + public TrustSource add(File trustedCAPemFile) { + if (trustedCAPemFile == null) { + throw new IllegalArgumentException("Trusted CA file cannot be null"); + } + + String pemFileContents; + try { + pemFileContents = Files.asCharSource(trustedCAPemFile, StandardCharsets.UTF_8).read(); + } catch (IOException e) { + throw new UncheckedIOException("Unable to read file containing PEM-encoded trusted CAs: " + trustedCAPemFile.getAbsolutePath(), e); + } + + return add(pemFileContents); + } + + /** + * Returns a new TrustSource containing the same trusted CAs as this TrustSource, plus the trusted CAs in the specified + * TrustSource. + * + * @param trustSource TrustSource to combine with this TrustSource + * @return a new TrustSource containing both TrustSources' trusted CAs + */ + public TrustSource add(TrustSource trustSource) { + if (trustSource == null) { + throw new IllegalArgumentException("TrustSource cannot be null"); + } + + return add(trustSource.getTrustedCAs()); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/CertificateCreationException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/CertificateCreationException.java new file mode 100644 index 000000000..3dc7740c3 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/CertificateCreationException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates a problem creating a certificate (server or CA). + */ +public class CertificateCreationException extends RuntimeException { + private static final long serialVersionUID = 592999944486567944L; + + public CertificateCreationException() { + } + + public CertificateCreationException(String message) { + super(message); + } + + public CertificateCreationException(String message, Throwable cause) { + super(message, cause); + } + + public CertificateCreationException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/CertificateSourceException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/CertificateSourceException.java new file mode 100644 index 000000000..e136d3710 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/CertificateSourceException.java @@ -0,0 +1,24 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates that a {@link net.lightbody.bmp.mitm.CertificateAndKeySource} encountered an error while loading a + * certificate and/or private key from a KeyStore, PEM file, or other source. + */ +public class CertificateSourceException extends RuntimeException { + private static final long serialVersionUID = 6195838041376082083L; + + public CertificateSourceException() { + } + + public CertificateSourceException(String message) { + super(message); + } + + public CertificateSourceException(String message, Throwable cause) { + super(message, cause); + } + + public CertificateSourceException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/ExportException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/ExportException.java new file mode 100644 index 000000000..37998c0ab --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/ExportException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates an error occurred while exporting/serializing a certificate, private key, KeyStore, etc. + */ +public class ExportException extends RuntimeException { + private static final long serialVersionUID = -3505301862887355206L; + + public ExportException() { + } + + public ExportException(String message) { + super(message); + } + + public ExportException(String message, Throwable cause) { + super(message, cause); + } + + public ExportException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/ImportException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/ImportException.java new file mode 100644 index 000000000..d3f3d480b --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/ImportException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates that an error occurred while importing a certificate, private key, or KeyStore. + */ +public class ImportException extends RuntimeException { + private static final long serialVersionUID = 584414535648926010L; + + public ImportException() { + } + + public ImportException(String message) { + super(message); + } + + public ImportException(String message, Throwable cause) { + super(message, cause); + } + + public ImportException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/KeyGeneratorException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/KeyGeneratorException.java new file mode 100644 index 000000000..9b97f8056 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/KeyGeneratorException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates an exception occurred while generating a key pair. + */ +public class KeyGeneratorException extends RuntimeException { + private static final long serialVersionUID = 7607159769324427808L; + + public KeyGeneratorException() { + } + + public KeyGeneratorException(String message) { + super(message); + } + + public KeyGeneratorException(String message, Throwable cause) { + super(message, cause); + } + + public KeyGeneratorException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/KeyStoreAccessException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/KeyStoreAccessException.java new file mode 100644 index 000000000..17070e77b --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/KeyStoreAccessException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates an error occurred while accessing a java KeyStore. + */ +public class KeyStoreAccessException extends RuntimeException { + private static final long serialVersionUID = -5560417886988154298L; + + public KeyStoreAccessException() { + } + + public KeyStoreAccessException(String message) { + super(message); + } + + public KeyStoreAccessException(String message, Throwable cause) { + super(message, cause); + } + + public KeyStoreAccessException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/MitmException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/MitmException.java new file mode 100644 index 000000000..8c8d2712c --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/MitmException.java @@ -0,0 +1,24 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates a general problem occurred while attempting to man-in-the-middle communications between the client and the + * upstream server. + */ +public class MitmException extends RuntimeException { + private static final long serialVersionUID = -1960691906515767537L; + + public MitmException() { + } + + public MitmException(String message) { + super(message); + } + + public MitmException(String message, Throwable cause) { + super(message, cause); + } + + public MitmException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/SslContextInitializationException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/SslContextInitializationException.java new file mode 100644 index 000000000..65ae9b9dd --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/SslContextInitializationException.java @@ -0,0 +1,23 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates an error occurred while attempting to create a new {@link javax.net.ssl.SSLContext}. + */ +public class SslContextInitializationException extends RuntimeException { + private static final long serialVersionUID = 6744059714710316821L; + + public SslContextInitializationException() { + } + + public SslContextInitializationException(String message) { + super(message); + } + + public SslContextInitializationException(String message, Throwable cause) { + super(message, cause); + } + + public SslContextInitializationException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/TrustSourceException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/TrustSourceException.java new file mode 100644 index 000000000..2f9c6ed81 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/TrustSourceException.java @@ -0,0 +1,21 @@ +package net.lightbody.bmp.mitm.exception; + +/** + * Indicates that an error occurred while attempting to create or populate a {@link net.lightbody.bmp.mitm.TrustSource}. + */ +public class TrustSourceException extends RuntimeException { + public TrustSourceException() { + } + + public TrustSourceException(String message) { + super(message); + } + + public TrustSourceException(String message, Throwable cause) { + super(message, cause); + } + + public TrustSourceException(Throwable cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/exception/UncheckedIOException.java b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/UncheckedIOException.java new file mode 100644 index 000000000..d4603a485 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/exception/UncheckedIOException.java @@ -0,0 +1,17 @@ +package net.lightbody.bmp.mitm.exception; + +import java.io.IOException; + +/** + * A convenience exception that wraps checked {@link IOException}s. (The built-in java.io.UncheckedIOException is only + * available on Java 8.) + */ +public class UncheckedIOException extends RuntimeException { + public UncheckedIOException(String message, IOException cause) { + super(message, cause); + } + + public UncheckedIOException(IOException cause) { + super(cause); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/keys/ECKeyGenerator.java b/mitm/src/main/java/net/lightbody/bmp/mitm/keys/ECKeyGenerator.java new file mode 100644 index 000000000..1a52c4b9f --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/keys/ECKeyGenerator.java @@ -0,0 +1,55 @@ +package net.lightbody.bmp.mitm.keys; + +import net.lightbody.bmp.mitm.exception.KeyGeneratorException; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.spec.ECGenParameterSpec; + +/** + * A {@link KeyGenerator} that creates Elliptic Curve key pairs. + */ +public class ECKeyGenerator implements KeyGenerator { + private static final String EC_KEY_GEN_ALGORITHM = "EC"; + + private static final String DEFAULT_NAMED_CURVE = "secp256r1"; + + private final String namedCurve; + + /** + * Create a {@link KeyGenerator} that will create EC key pairs using the secp256r1 named curve (NIST P-256) + * supported by modern web browsers. + */ + public ECKeyGenerator() { + this.namedCurve = DEFAULT_NAMED_CURVE; + } + + /** + * Create a {@link KeyGenerator} that will create EC key pairs using the specified named curve. + */ + public ECKeyGenerator(String namedCurve) { + this.namedCurve = namedCurve; + } + + @Override + public KeyPair generate() { + // obtain an EC key pair generator for the specified named curve + KeyPairGenerator generator; + try { + generator = java.security.KeyPairGenerator.getInstance(EC_KEY_GEN_ALGORITHM); + ECGenParameterSpec ecName = new ECGenParameterSpec(namedCurve); + generator.initialize(ecName); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new KeyGeneratorException("Unable to generate EC public/private key pair using named curve: " + namedCurve, e); + } + + return generator.generateKeyPair(); + } + + @Override + public String toString() { + return "EC (" + namedCurve + ")"; + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/keys/KeyGenerator.java b/mitm/src/main/java/net/lightbody/bmp/mitm/keys/KeyGenerator.java new file mode 100644 index 000000000..fcc203757 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/keys/KeyGenerator.java @@ -0,0 +1,15 @@ +package net.lightbody.bmp.mitm.keys; + +import java.security.KeyPair; + +/** + * A functional interface for key pair generators. + */ +public interface KeyGenerator { + /** + * Generates a new public/private key pair. This method should not cache or reuse any previously-generated key pairs. + * + * @return a new public/private key pair + */ + KeyPair generate(); +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/keys/RSAKeyGenerator.java b/mitm/src/main/java/net/lightbody/bmp/mitm/keys/RSAKeyGenerator.java new file mode 100644 index 000000000..a8c8aaf03 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/keys/RSAKeyGenerator.java @@ -0,0 +1,56 @@ +package net.lightbody.bmp.mitm.keys; + +import net.lightbody.bmp.mitm.exception.KeyGeneratorException; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +/** + * A {@link KeyGenerator} that creates RSA key pairs. + */ +public class RSAKeyGenerator implements KeyGenerator { + private static final String RSA_KEY_GEN_ALGORITHM = "RSA"; + + /** + * Use a default RSA key size of 2048, since Chrome, Firefox, and possibly other browsers have begun to distrust + * certificates signed with 1024-bit RSA keys. + */ + private static final int DEFAULT_KEY_SIZE = 2048; + + private final int keySize; + + /** + * Create a {@link KeyGenerator} that will create a 2048-bit RSA key pair. + */ + public RSAKeyGenerator() { + this.keySize = DEFAULT_KEY_SIZE; + } + + /** + * Create a {@link KeyGenerator} that will create an RSA key pair of the specified keySize. + */ + public RSAKeyGenerator(int keySize) { + this.keySize = keySize; + } + + @Override + public KeyPair generate() { + // obtain an RSA key pair generator for the specified key size + KeyPairGenerator generator; + try { + generator = KeyPairGenerator.getInstance(RSA_KEY_GEN_ALGORITHM); + generator.initialize(keySize); + } catch (NoSuchAlgorithmException e) { + throw new KeyGeneratorException("Unable to generate " + keySize + "-bit RSA public/private key pair", e); + } + + return generator.generateKeyPair(); + } + + @Override + public String toString() { + return "RSA (" + keySize + ")"; + } +} + diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java b/mitm/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java new file mode 100644 index 000000000..58b0a9554 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java @@ -0,0 +1,518 @@ +package net.lightbody.bmp.mitm.manager; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableList; +import io.netty.buffer.ByteBufAllocator; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SupportedCipherSuiteFilter; +import net.lightbody.bmp.mitm.CertificateAndKey; +import net.lightbody.bmp.mitm.CertificateAndKeySource; +import net.lightbody.bmp.mitm.CertificateInfo; +import net.lightbody.bmp.mitm.CertificateInfoGenerator; +import net.lightbody.bmp.mitm.HostnameCertificateInfoGenerator; +import net.lightbody.bmp.mitm.RootCertificateGenerator; +import net.lightbody.bmp.mitm.TrustSource; +import net.lightbody.bmp.mitm.exception.MitmException; +import net.lightbody.bmp.mitm.exception.SslContextInitializationException; +import net.lightbody.bmp.mitm.keys.ECKeyGenerator; +import net.lightbody.bmp.mitm.keys.KeyGenerator; +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator; +import net.lightbody.bmp.mitm.stats.CertificateGenerationStatistics; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.SecurityProviderTool; +import net.lightbody.bmp.mitm.util.EncryptionUtil; +import net.lightbody.bmp.mitm.util.MitmConstants; +import net.lightbody.bmp.mitm.util.SslUtil; +import net.lightbody.bmp.util.HttpUtil; +import org.littleshoot.proxy.MitmManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * An {@link MitmManager} that will create SSLEngines for clients that present impersonated certificates for upstream servers. The impersonated + * certificates will be signed using the certificate and private key specified in an {@link #rootCertificateSource}. The impersonated server + * certificates will be created by the {@link #securityProviderTool} based on the {@link CertificateInfo} returned by the {@link #certificateInfoGenerator}. + */ +public class ImpersonatingMitmManager implements MitmManager { + private static final Logger log = LoggerFactory.getLogger(ImpersonatingMitmManager.class); + + /** + * Cipher suites allowed on proxy connections to upstream servers. + */ + private final List serverCipherSuites; + + /** + * Cipher suites allowed on client connections to the proxy. + */ + private final List clientCipherSuites; + + /** + * The SSLContext that will be used for communications with all upstream servers. This can be reused, so store it as a lazily-loaded singleton. + */ + private final Supplier upstreamServerSslContext = Suppliers.memoize(new Supplier() { + @Override + public SslContext get() { + return SslUtil.getUpstreamServerSslContext(serverCipherSuites, trustSource); + } + }); + + /** + * Cache for impersonating netty SslContexts. SslContexts can be safely reused, so caching the impersonating contexts avoids + * repeatedly re-impersonating upstream servers. + */ + private final Cache sslContextCache; + + /** + * Generator used to create public and private keys for the server certificates. + */ + private final KeyGenerator serverKeyGenerator; + + /** + * The source of the CA's {@link CertificateAndKey} that will be used to sign generated server certificates. + */ + private final CertificateAndKeySource rootCertificateSource; + + /** + * The message digest used to sign the server certificate, such as SHA512. + * See https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest for information + * on supported message digests. + */ + private final String serverCertificateMessageDigest; + + /** + * The source of trusted root CAs. May be null, which disables all upstream certificate validation. Disabling upstream + * certificate validation allows attackers to intercept communciations and should only be used during testing. + */ + private final TrustSource trustSource; + + /** + * Utility used to generate {@link CertificateInfo} objects when impersonating an upstream server. + */ + private final CertificateInfoGenerator certificateInfoGenerator; + + /** + * Tool implementation that is used to generate, sign, and otherwise manipulate server certificates. + */ + private final SecurityProviderTool securityProviderTool; + + /** + * The CA root root certificate used to sign generated server certificates. {@link CertificateAndKeySource#load()} + * is only called once to retrieve the CA root certificate, which will be used to impersonate all server certificates. + */ + private Supplier rootCertificate = Suppliers.memoize(new Supplier() { + @Override + public CertificateAndKey get() { + return rootCertificateSource.load(); + } + }); + + /** + * Simple server certificate generation statistics. + */ + private final CertificateGenerationStatistics statistics = new CertificateGenerationStatistics(); + + /** + * Creates a new ImpersonatingMitmManager. In general, use {@link ImpersonatingMitmManager.Builder} + * to construct new instances. + */ + public ImpersonatingMitmManager(CertificateAndKeySource rootCertificateSource, + KeyGenerator serverKeyGenerator, + String serverMessageDigest, + TrustSource trustSource, + int sslContextCacheConcurrencyLevel, + long cacheExpirationIntervalMs, + SecurityProviderTool securityProviderTool, + CertificateInfoGenerator certificateInfoGenerator, + Collection serverCipherSuites, + Collection clientCipherSuites) { + if (rootCertificateSource == null) { + throw new IllegalArgumentException("CA root certificate source cannot be null"); + } + + if (serverKeyGenerator == null) { + throw new IllegalArgumentException("Server key generator cannot be null"); + } + + if (serverMessageDigest == null) { + throw new IllegalArgumentException("Server certificate message digest cannot be null"); + } + + if (securityProviderTool == null) { + throw new IllegalArgumentException("The certificate tool implementation cannot be null"); + } + + if (certificateInfoGenerator == null) { + throw new IllegalArgumentException("Certificate info generator cannot be null"); + } + + this.rootCertificateSource = rootCertificateSource; + + this.trustSource = trustSource; + + this.serverCertificateMessageDigest = serverMessageDigest; + + this.serverKeyGenerator = serverKeyGenerator; + + this.sslContextCache = CacheBuilder.newBuilder() + .concurrencyLevel(sslContextCacheConcurrencyLevel) + .expireAfterAccess(cacheExpirationIntervalMs, TimeUnit.MILLISECONDS) + .build(); + + this.securityProviderTool = securityProviderTool; + + this.certificateInfoGenerator = certificateInfoGenerator; + + this.serverCipherSuites = ImmutableList.copyOf(serverCipherSuites); + log.debug("Allowed ciphers for proxy connections to upstream servers (some ciphers may not be available): {}", serverCipherSuites); + + this.clientCipherSuites = ImmutableList.copyOf(clientCipherSuites); + log.debug("Allowed ciphers for client connections to proxy (some ciphers may not be available): {}", clientCipherSuites); + } + + @Override + public SSLEngine serverSslEngine() { + try { + SSLEngine sslEngine = upstreamServerSslContext.get().newEngine(ByteBufAllocator.DEFAULT); + + return sslEngine; + } catch (RuntimeException e) { + throw new MitmException("Error creating SSLEngine for connection to upstream server", e); + } + } + + @Override + public SSLEngine serverSslEngine(String peerHost, int peerPort) { + try { + SSLEngine sslEngine = upstreamServerSslContext.get().newEngine(ByteBufAllocator.DEFAULT, peerHost, peerPort); + + // support SNI by setting the endpoint identification algorithm. this requires Java 7+. + SSLParameters sslParams = new SSLParameters(); + sslParams.setEndpointIdentificationAlgorithm("HTTPS"); + sslEngine.setSSLParameters(sslParams); + + return sslEngine; + } catch (RuntimeException e) { + throw new MitmException("Error creating SSLEngine for connection to upstream server: " + peerHost + ":" + peerPort, e); + } + } + + @Override + public SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession sslSession) { + String requestedHostname = HttpUtil.getHostFromRequest(httpRequest); + + try { + SslContext ctx = getHostnameImpersonatingSslContext(requestedHostname, sslSession); + + return ctx.newEngine(ByteBufAllocator.DEFAULT); + } catch (RuntimeException e) { + throw new MitmException("Error creating SSLEngine for connection to client to impersonate upstream host: " + requestedHostname, e); + } + } + + /** + * Retrieves an SSLContext that impersonates the specified hostname. If an impersonating SSLContext has already been + * created for this hostname and is stored in the cache, it will be reused. Otherwise, a certificate will be created + * which impersonates the specified hostname. + * + * @param hostnameToImpersonate the hostname for which the impersonated SSLContext is being requested + * @param sslSession the upstream server SSLSession + * @return SSLContext which will present an impersonated certificate + */ + private SslContext getHostnameImpersonatingSslContext(final String hostnameToImpersonate, final SSLSession sslSession) { + try { + return sslContextCache.get(hostnameToImpersonate, new Callable() { + @Override + public SslContext call() throws Exception { + return createImpersonatingSslContext(sslSession, hostnameToImpersonate); + } + }); + } catch (ExecutionException e) { + throw new SslContextInitializationException("An error occurred while impersonating the remote host: " + hostnameToImpersonate, e); + } + + //TODO: generate wildcard certificates, rather than one certificate per host, to reduce the number of certs generated + } + + /** + * Creates an SSLContext that will present an impersonated certificate for the specified hostname to the client. + * This is a convenience method for {@link #createImpersonatingSslContext(CertificateInfo)} that generates the + * {@link CertificateInfo} from the specified hostname using the {@link #certificateInfoGenerator}. + * + * @param sslSession sslSession between the proxy and the upstream server + * @param hostnameToImpersonate hostname (supplied by the client's HTTP CONNECT) that will be impersonated + * @return an SSLContext presenting a certificate matching the hostnameToImpersonate + */ + private SslContext createImpersonatingSslContext(SSLSession sslSession, String hostnameToImpersonate) { + // get the upstream server's certificate so the certificateInfoGenerator can (optionally) use it to construct a forged certificate + X509Certificate originalCertificate = SslUtil.getServerCertificate(sslSession); + + // get the CertificateInfo that will be used to populate the impersonated X509Certificate + CertificateInfo certificateInfo = certificateInfoGenerator.generate(Collections.singletonList(hostnameToImpersonate), originalCertificate); + + SslContext sslContext = createImpersonatingSslContext(certificateInfo); + + return sslContext; + } + + /** + * Generates an {@link SslContext} using an impersonated certificate containing the information in the specified + * certificateInfo. + * + * @param certificateInfo certificate information to impersonate + * @return an SslContext that will present the impersonated certificate to the client + */ + private SslContext createImpersonatingSslContext(CertificateInfo certificateInfo) { + long impersonationStart = System.currentTimeMillis(); + + // generate a public and private key pair for the forged certificate. the SslContext will send the impersonated certificate to clients + // to impersonate the real upstream server, and will use the private key to encrypt the channel. + KeyPair serverKeyPair = serverKeyGenerator.generate(); + + // get the CA root certificate and private key that will be used to sign the forged certificate + X509Certificate caRootCertificate = rootCertificate.get().getCertificate(); + PrivateKey caPrivateKey = rootCertificate.get().getPrivateKey(); + if (caRootCertificate == null || caPrivateKey == null) { + throw new IllegalStateException("A CA root certificate and private key are required to sign a server certificate. Root certificate was: " + + caRootCertificate + ". Private key was: " + caPrivateKey); + } + + // determine if the server private key was signed with an RSA private key. though TLS no longer requires the server + // certificate to use the same private key type as the root certificate, Java bug JDK-8136442 prevents Java from creating a opening an SSL socket + // if the CA and server certificates are not of the same type. see https://bugs.openjdk.java.net/browse/JDK-8136442 + // note this only applies to RSA CAs signing EC server certificates; Java seems to properly handle EC CAs signing + // RSA server certificates. + if (EncryptionUtil.isEcKey(serverKeyPair.getPrivate()) && EncryptionUtil.isRsaKey(caPrivateKey)) { + log.warn("CA private key is an RSA key and impersonated server private key is an Elliptic Curve key. JDK bug 8136442 may prevent the proxy server from creating connections to clients due to 'no cipher suites in common'."); + } + + // create the forged server certificate and sign it with the root certificate and private key + CertificateAndKey impersonatedCertificateAndKey = securityProviderTool.createServerCertificate( + certificateInfo, + caRootCertificate, + caPrivateKey, + serverKeyPair, + serverCertificateMessageDigest); + + X509Certificate[] certChain = {impersonatedCertificateAndKey.getCertificate(), caRootCertificate}; + SslContext sslContext; + try { + sslContext = SslContextBuilder.forServer(impersonatedCertificateAndKey.getPrivateKey(), certChain) + .ciphers(clientCipherSuites, SupportedCipherSuiteFilter.INSTANCE) + .build(); + + } catch (SSLException e) { + throw new MitmException("Error creating SslContext for connection to client using impersonated certificate and private key", e); + } + + long impersonationFinish = System.currentTimeMillis(); + + statistics.certificateCreated(impersonationStart, impersonationFinish); + + log.debug("Impersonated certificate for {} in {}ms", certificateInfo.getCommonName(), impersonationFinish - impersonationStart); + + return sslContext; + } + + /** + * Returns basic certificate generation statistics for this MitmManager. + */ + public CertificateGenerationStatistics getStatistics() { + return this.statistics; + } + + /** + * Convenience method to return a new {@link Builder} instance default default values: a {@link RootCertificateGenerator} + * that dynamically generates an RSA root certificate and RSA server certificates. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Convenience method to return a new {@link Builder} instance that will dynamically create EC root certificates and + * EC server certificates, but otherwise uses default values. + */ + public static Builder builderWithECC() { + return new Builder() + .serverKeyGenerator(new ECKeyGenerator()) + .rootCertificateSource(RootCertificateGenerator.builder() + .keyGenerator(new ECKeyGenerator()) + .build()); + } + + /** + * A Builder for {@link ImpersonatingMitmManager}s. Initialized with suitable default values suitable for most purposes. + */ + public static class Builder { + private CertificateAndKeySource rootCertificateSource = RootCertificateGenerator.builder().build(); + + private KeyGenerator serverKeyGenerator = new RSAKeyGenerator(); + + private TrustSource trustSource = TrustSource.defaultTrustSource(); + + private int cacheConcurrencyLevel = 8; + private long cacheExpirationIntervalMs = TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES); + + private String serverMessageDigest = MitmConstants.DEFAULT_MESSAGE_DIGEST; + + private SecurityProviderTool securityProviderTool = new DefaultSecurityProviderTool(); + + private CertificateInfoGenerator certificateInfoGenerator = new HostnameCertificateInfoGenerator(); + + private Collection serverCiphers; + + private Collection clientCiphers; + + /** + * The source of the CA root certificate that will be used to sign the impersonated server certificates. Custom + * certificates can be used by supplying an implementation of {@link CertificateAndKeySource}, such as + * {@link net.lightbody.bmp.mitm.PemFileCertificateSource}. Alternatively, a new root certificate can be generated + * and saved (for later import into browsers) using {@link RootCertificateGenerator}. + * + * @param certificateAndKeySource impersonation materials source to use + */ + public Builder rootCertificateSource(CertificateAndKeySource certificateAndKeySource) { + this.rootCertificateSource = certificateAndKeySource; + return this; + } + + /** + * The message digest that will be used when signing server certificates with the root certificate's private key. + */ + public Builder serverMessageDigest(String serverMessageDigest) { + this.serverMessageDigest = serverMessageDigest; + return this; + } + + /** + * When true, no upstream certificate verification will be performed. This will make it possible for + * attackers to MITM communications with the upstream server, so use trustAllServers only when testing. + * Calling this method with 'true' will remove any trustSource set with {@link #trustSource(TrustSource)}. + * Calling this method with 'false' has no effect unless trustAllServers was previously called with 'true'. + * To set a specific TrustSource, use {@link #trustSource(TrustSource)}. + */ + public Builder trustAllServers(boolean trustAllServers) { + if (trustAllServers) { + this.trustSource = null; + } else { + // if the TrustSource was previously removed, restore it to the default. otherwise keep the existing TrustSource. + if (this.trustSource == null) { + this.trustSource = TrustSource.defaultTrustSource(); + } + } + + return this; + } + + /** + * The TrustSource that supplies the trusted root CAs used to validate upstream servers' certificates. + */ + public Builder trustSource(TrustSource trustSource) { + this.trustSource = trustSource; + return this; + } + + /** + * The {@link KeyGenerator} that will be used to generate the server public and private keys. + */ + public Builder serverKeyGenerator(KeyGenerator serverKeyGenerator) { + this.serverKeyGenerator = serverKeyGenerator; + return this; + } + + /** + * The concurrency level for the SSLContext cache. Increase this beyond the default value for high-volume proxy servers. + */ + public Builder cacheConcurrencyLevel(int cacheConcurrencyLevel) { + this.cacheConcurrencyLevel = cacheConcurrencyLevel; + return this; + } + + /** + * The length of time SSLContexts with forged certificates will be kept in the cache. + */ + public Builder cacheExpirationInterval(long cacheExpirationInterval, TimeUnit timeUnit) { + this.cacheExpirationIntervalMs = TimeUnit.MILLISECONDS.convert(cacheExpirationInterval, timeUnit); + return this; + } + + /** + * The {@link CertificateInfoGenerator} that will populate {@link CertificateInfo} objects containing certificate data for + * forced X509Certificates. + */ + public Builder certificateInfoGenerator(CertificateInfoGenerator certificateInfoGenerator) { + this.certificateInfoGenerator = certificateInfoGenerator; + return this; + } + + /** + * The cipher suites allowed on connections to upstream servers. Cipher suite names should be specified in Java + * format, rather than OpenSSL format (e.g., TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), even when using OpenSSL. + * Ciphers will be preferred in the order they are returned by the collection's iterator. + */ + public Builder serverCiphers(Collection serverCiphers) { + this.serverCiphers = serverCiphers; + return this; + } + + /** + * The cipher suites allowed on client connections to the proxy. Cipher suite names should be specified in Java + * format, rather than OpenSSL format (e.g., TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), even when using OpenSSL. + * Ciphers will be preferred in the order they are returned by the collection's iterator. + */ + public Builder clientCiphers(Collection clientCiphers) { + this.clientCiphers = clientCiphers; + return this; + } + + /** + * The {@link SecurityProviderTool} implementation that will be used to generate certificates. + */ + public Builder certificateTool(SecurityProviderTool securityProviderTool) { + this.securityProviderTool = securityProviderTool; + return this; + } + + public ImpersonatingMitmManager build() { + if (clientCiphers == null) { + clientCiphers = SslUtil.getDefaultCipherList(); + } + + if (serverCiphers == null) { + serverCiphers = SslUtil.getDefaultCipherList(); + } + + return new ImpersonatingMitmManager( + rootCertificateSource, + serverKeyGenerator, + serverMessageDigest, + trustSource, + cacheConcurrencyLevel, + cacheExpirationIntervalMs, + securityProviderTool, + certificateInfoGenerator, + serverCiphers, + clientCiphers + ); + } + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/stats/CertificateGenerationStatistics.java b/mitm/src/main/java/net/lightbody/bmp/mitm/stats/CertificateGenerationStatistics.java new file mode 100644 index 000000000..bb3b99e60 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/stats/CertificateGenerationStatistics.java @@ -0,0 +1,57 @@ +package net.lightbody.bmp.mitm.stats; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Tracks basic certificate generation statistics. + */ +public class CertificateGenerationStatistics { + private AtomicLong certificateGenerationTimeMs = new AtomicLong(); + private AtomicInteger certificatesGenerated = new AtomicInteger(); + + private AtomicLong firstCertificateGeneratedTimestamp = new AtomicLong(); + + /** + * Records a certificate generation that started at startTimeMs and completed at finishTimeMs. + */ + public void certificateCreated(long startTimeMs, long finishTimeMs) { + certificatesGenerated.incrementAndGet(); + certificateGenerationTimeMs.addAndGet(finishTimeMs - startTimeMs); + + // record the timestamp of the first certificate generation + firstCertificateGeneratedTimestamp.compareAndSet(0L, System.currentTimeMillis()); + } + + /** + * Returns the total number of certificates created. + */ + public int getCertificatesGenerated() { + return certificatesGenerated.get(); + } + + /** + * Returns the total number of ms spent generating all certificates. + */ + public long getTotalCertificateGenerationTimeMs() { + return certificateGenerationTimeMs.get(); + } + + /** + * Returns the average number of ms per certificate generated. + */ + public long getAvgCertificateGenerationTimeMs() { + if (certificatesGenerated.get() > 0) { + return certificateGenerationTimeMs.get() / certificatesGenerated.get(); + } else { + return 0L; + } + } + + /** + * Returns the timestamp (ms since epoch) when the first certificate was generated, or 0 if none have been generated. + */ + public long firstCertificateGeneratedTimestamp() { + return firstCertificateGeneratedTimestamp.get(); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java b/mitm/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java new file mode 100644 index 000000000..ae27cf55c --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java @@ -0,0 +1,387 @@ +package net.lightbody.bmp.mitm.tools; + +import com.google.common.net.InetAddresses; +import net.lightbody.bmp.mitm.CertificateAndKey; +import net.lightbody.bmp.mitm.CertificateInfo; +import net.lightbody.bmp.mitm.exception.CertificateCreationException; +import net.lightbody.bmp.mitm.exception.ExportException; +import net.lightbody.bmp.mitm.exception.ImportException; +import net.lightbody.bmp.mitm.util.EncryptionUtil; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMEncryptor; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; +import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +import javax.net.ssl.KeyManager; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringWriter; +import java.math.BigInteger; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +public class BouncyCastleSecurityProviderTool implements SecurityProviderTool { + static { + Security.addProvider(new BouncyCastleProvider()); + } + + /** + * The size of certificate serial numbers, in bits. + */ + private static final int CERTIFICATE_SERIAL_NUMBER_SIZE = 160; + + @Override + public CertificateAndKey createServerCertificate(CertificateInfo certificateInfo, + X509Certificate caRootCertificate, + PrivateKey caPrivateKey, + KeyPair serverKeyPair, + String messageDigest) { + // make sure certificateInfo contains all fields necessary to generate the certificate + if (certificateInfo.getCommonName() == null) { + throw new IllegalArgumentException("Must specify CN for server certificate"); + } + + if (certificateInfo.getNotBefore() == null) { + throw new IllegalArgumentException("Must specify Not Before for server certificate"); + } + + if (certificateInfo.getNotAfter() == null) { + throw new IllegalArgumentException("Must specify Not After for server certificate"); + } + + // create the subject for the new server certificate. when impersonating an upstream server, this should contain + // the hostname of the server we are trying to impersonate in the CN field + X500Name serverCertificateSubject = createX500NameForCertificate(certificateInfo); + + // get the algorithm that will be used to sign the new certificate, which is a combination of the message digest + // and the digital signature from the CA's private key + String signatureAlgorithm = EncryptionUtil.getSignatureAlgorithm(messageDigest, caPrivateKey); + + // get a ContentSigner with our CA private key that will be used to sign the new server certificate + ContentSigner signer = getCertificateSigner(caPrivateKey, signatureAlgorithm); + + // generate a serial number for the new certificate. serial numbers only need to be unique within our + // certification authority; a large random integer will satisfy that requirement. + BigInteger serialNumber = EncryptionUtil.getRandomBigInteger(CERTIFICATE_SERIAL_NUMBER_SIZE); + + // create the X509Certificate using Bouncy Castle. the BC X509CertificateHolder can be converted to a JCA X509Certificate. + X509CertificateHolder certificateHolder; + try { + certificateHolder = new JcaX509v3CertificateBuilder(caRootCertificate, + serialNumber, + certificateInfo.getNotBefore(), + certificateInfo.getNotAfter(), + serverCertificateSubject, + serverKeyPair.getPublic()) + .addExtension(Extension.subjectAlternativeName, false, getDomainNameSANsAsASN1Encodable(certificateInfo.getSubjectAlternativeNames())) + .addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(serverKeyPair.getPublic())) + .addExtension(Extension.basicConstraints, false, new BasicConstraints(false)) + .build(signer); + } catch (CertIOException e) { + throw new CertificateCreationException("Error creating new server certificate", e); + } + + // convert the Bouncy Castle certificate holder into a JCA X509Certificate + X509Certificate serverCertificate = convertToJcaCertificate(certificateHolder); + + return new CertificateAndKey(serverCertificate, serverKeyPair.getPrivate()); + } + + @Override + public KeyStore createServerKeyStore(String keyStoreType, CertificateAndKey serverCertificateAndKey, X509Certificate rootCertificate, String privateKeyAlias, String password) { + throw new UnsupportedOperationException("BouncyCastle implementation does not implement this method"); + } + + @Override + public KeyStore createRootCertificateKeyStore(String keyStoreType, CertificateAndKey rootCertificateAndKey, String privateKeyAlias, String password) { + throw new UnsupportedOperationException("BouncyCastle implementation does not implement this method"); + } + + @Override + public CertificateAndKey createCARootCertificate(CertificateInfo certificateInfo, + KeyPair keyPair, + String messageDigest) { + if (certificateInfo.getNotBefore() == null) { + throw new IllegalArgumentException("Must specify Not Before for server certificate"); + } + + if (certificateInfo.getNotAfter() == null) { + throw new IllegalArgumentException("Must specify Not After for server certificate"); + } + + // create the X500Name that will be both the issuer and the subject of the new root certificate + X500Name issuer = createX500NameForCertificate(certificateInfo); + + BigInteger serial = EncryptionUtil.getRandomBigInteger(CERTIFICATE_SERIAL_NUMBER_SIZE); + + PublicKey rootCertificatePublicKey = keyPair.getPublic(); + + String signatureAlgorithm = EncryptionUtil.getSignatureAlgorithm(messageDigest, keyPair.getPrivate()); + + // this is a CA root certificate, so it is self-signed + ContentSigner selfSigner = getCertificateSigner(keyPair.getPrivate(), signatureAlgorithm); + + ASN1EncodableVector extendedKeyUsages = new ASN1EncodableVector(); + extendedKeyUsages.add(KeyPurposeId.id_kp_serverAuth); + extendedKeyUsages.add(KeyPurposeId.id_kp_clientAuth); + extendedKeyUsages.add(KeyPurposeId.anyExtendedKeyUsage); + + X509CertificateHolder certificateHolder; + try { + certificateHolder = new JcaX509v3CertificateBuilder( + issuer, + serial, + certificateInfo.getNotBefore(), + certificateInfo.getNotAfter(), + issuer, + rootCertificatePublicKey) + .addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(rootCertificatePublicKey)) + .addExtension(Extension.basicConstraints, true, new BasicConstraints(true)) + .addExtension(Extension.keyUsage, false, new KeyUsage( + KeyUsage.keyCertSign + | KeyUsage.digitalSignature + | KeyUsage.keyEncipherment + | KeyUsage.dataEncipherment + | KeyUsage.cRLSign)) + .addExtension(Extension.extendedKeyUsage, false, new DERSequence(extendedKeyUsages)) + .build(selfSigner); + } catch (CertIOException e) { + throw new CertificateCreationException("Error creating root certificate", e); + } + + // convert the Bouncy Castle X590CertificateHolder to a JCA cert + X509Certificate cert = convertToJcaCertificate(certificateHolder); + + return new CertificateAndKey(cert, keyPair.getPrivate()); + } + + @Override + public String encodePrivateKeyAsPem(PrivateKey privateKey, String passwordForPrivateKey, String encryptionAlgorithm) { + if (passwordForPrivateKey == null) { + throw new IllegalArgumentException("You must specify a password when serializing a private key"); + } + + PEMEncryptor encryptor = new JcePEMEncryptorBuilder(encryptionAlgorithm) + .build(passwordForPrivateKey.toCharArray()); + + return encodeObjectAsPemString(privateKey, encryptor); + } + + @Override + public String encodeCertificateAsPem(Certificate certificate) { + return encodeObjectAsPemString(certificate, null); + } + + @Override + public PrivateKey decodePemEncodedPrivateKey(Reader privateKeyReader, String password) { + try (PEMParser pemParser = new PEMParser(privateKeyReader)) { + Object keyPair = pemParser.readObject(); + + // retrieve the PrivateKeyInfo from the returned keyPair object. if the key is encrypted, it needs to be + // decrypted using the specified password first. + PrivateKeyInfo keyInfo; + if (keyPair instanceof PEMEncryptedKeyPair) { + if (password == null) { + throw new ImportException("Unable to import private key. Key is encrypted, but no password was provided."); + } + + PEMDecryptorProvider decryptor = new JcePEMDecryptorProviderBuilder().build(password.toCharArray()); + + PEMKeyPair decryptedKeyPair = ((PEMEncryptedKeyPair) keyPair).decryptKeyPair(decryptor); + + keyInfo = decryptedKeyPair.getPrivateKeyInfo(); + } else { + keyInfo = ((PEMKeyPair) keyPair).getPrivateKeyInfo(); + } + + return new JcaPEMKeyConverter().getPrivateKey(keyInfo); + } catch (IOException e) { + throw new ImportException("Unable to read PEM-encoded PrivateKey", e); + } + } + + @Override + public X509Certificate decodePemEncodedCertificate(Reader certificateReader) { + // JCA provides this functionality already, but it can be easily implemented using BC as well + throw new UnsupportedOperationException("BouncyCastle implementation does not implement this method"); + } + + @Override + public KeyStore loadKeyStore(File file, String keyStoreType, String password) { + throw new UnsupportedOperationException("BouncyCastle implementation does not implement this method"); + } + + @Override + public void saveKeyStore(File file, KeyStore keyStore, String keystorePassword) { + throw new UnsupportedOperationException("BouncyCastle implementation does not implement this method"); + } + + @Override + public KeyManager[] getKeyManagers(KeyStore keyStore, String keyStorePassword) { + return new KeyManager[0]; + } + + + /** + * Creates an X500Name based on the specified certificateInfo. + * + * @param certificateInfo information to populate the X500Name with + * @return a new X500Name object for use as a subject or issuer + */ + private static X500Name createX500NameForCertificate(CertificateInfo certificateInfo) { + X500NameBuilder x500NameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + + if (certificateInfo.getCommonName() != null) { + x500NameBuilder.addRDN(BCStyle.CN, certificateInfo.getCommonName()); + } + + if (certificateInfo.getOrganization() != null) { + x500NameBuilder.addRDN(BCStyle.O, certificateInfo.getOrganization()); + } + + if (certificateInfo.getOrganizationalUnit() != null) { + x500NameBuilder.addRDN(BCStyle.OU, certificateInfo.getOrganizationalUnit()); + } + + if (certificateInfo.getEmail() != null) { + x500NameBuilder.addRDN(BCStyle.E, certificateInfo.getEmail()); + } + + if (certificateInfo.getLocality() != null) { + x500NameBuilder.addRDN(BCStyle.L, certificateInfo.getLocality()); + } + + if (certificateInfo.getState() != null) { + x500NameBuilder.addRDN(BCStyle.ST, certificateInfo.getState()); + } + + if (certificateInfo.getCountryCode() != null) { + x500NameBuilder.addRDN(BCStyle.C, certificateInfo.getCountryCode()); + } + + // TODO: Add more X.509 certificate fields as needed + + return x500NameBuilder.build(); + } + + /** + * Converts a list of domain name Subject Alternative Names into ASN1Encodable GeneralNames objects, for use with + * the Bouncy Castle certificate builder. + * + * @param subjectAlternativeNames domain name SANs to convert + * @return a GeneralNames instance that includes the specifie dsubjectAlternativeNames as DNS name fields + */ + private static GeneralNames getDomainNameSANsAsASN1Encodable(List subjectAlternativeNames) { + List encodedSANs = new ArrayList<>(subjectAlternativeNames.size()); + for (String subjectAlternativeName : subjectAlternativeNames) { + // IP addresses use the IP Address tag instead of the DNS Name tag in the SAN list + boolean isIpAddress = InetAddresses.isInetAddress(subjectAlternativeName); + GeneralName generalName = new GeneralName(isIpAddress ? GeneralName.iPAddress : GeneralName.dNSName, subjectAlternativeName); + encodedSANs.add(generalName); + } + + return new GeneralNames(encodedSANs.toArray(new GeneralName[encodedSANs.size()])); + } + + /** + * Creates a ContentSigner that can be used to sign certificates with the given private key and signature algorithm. + * + * @param certAuthorityPrivateKey the private key to use to sign certificates + * @param signatureAlgorithm the algorithm to use to sign certificates + * @return a ContentSigner + */ + private static ContentSigner getCertificateSigner(PrivateKey certAuthorityPrivateKey, String signatureAlgorithm) { + try { + return new JcaContentSignerBuilder(signatureAlgorithm) + .build(certAuthorityPrivateKey); + } catch (OperatorCreationException e) { + throw new CertificateCreationException("Unable to create ContentSigner using signature algorithm: " + signatureAlgorithm, e); + } + } + + /** + * Converts a Bouncy Castle X509CertificateHolder into a JCA X590Certificate. + * + * @param bouncyCastleCertificate BC X509CertificateHolder + * @return JCA X509Certificate + */ + private static X509Certificate convertToJcaCertificate(X509CertificateHolder bouncyCastleCertificate) { + try { + return new JcaX509CertificateConverter() + .getCertificate(bouncyCastleCertificate); + } catch (CertificateException e) { + throw new CertificateCreationException("Unable to convert X590CertificateHolder to JCA X590Certificate", e); + } + } + + /** + * Creates the SubjectKeyIdentifier for a Bouncy Castle X590CertificateHolder. + * + * @param key public key to identify + * @return SubjectKeyIdentifier for the specified key + */ + private static SubjectKeyIdentifier createSubjectKeyIdentifier(Key key) { + SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(key.getEncoded()); + + return new BcX509ExtensionUtils().createSubjectKeyIdentifier(publicKeyInfo); + } + + /** + * Encodes the specified security object in PEM format, using the specified encryptor. If the encryptor is null, + * the object will not be encrypted in the generated String. + * + * @param object object to encrypt (certificate, private key, etc.) + * @param encryptor engine to encrypt the resulting PEM String, or null if no encryption should be used + * @return a PEM-encoded String + */ + private static String encodeObjectAsPemString(Object object, PEMEncryptor encryptor) { + StringWriter stringWriter = new StringWriter(); + + try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { + pemWriter.writeObject(object, encryptor); + pemWriter.flush(); + } catch (IOException e) { + throw new ExportException("Unable to generate PEM string representing object", e); + } + + return stringWriter.toString(); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java b/mitm/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java new file mode 100644 index 000000000..501f5a6c0 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java @@ -0,0 +1,166 @@ +package net.lightbody.bmp.mitm.tools; + +import com.google.common.io.CharStreams; +import net.lightbody.bmp.mitm.CertificateAndKey; +import net.lightbody.bmp.mitm.CertificateInfo; +import net.lightbody.bmp.mitm.exception.ImportException; +import net.lightbody.bmp.mitm.exception.KeyStoreAccessException; +import net.lightbody.bmp.mitm.util.KeyStoreUtil; + +import javax.net.ssl.KeyManager; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +/** + * A {@link SecurityProviderTool} implementation that uses the default system Security provider where possible, but uses the + * Bouncy Castle provider for operations that the JCA does not provide or implement (e.g. certificate generation and signing). + */ +public class DefaultSecurityProviderTool implements SecurityProviderTool { + private final SecurityProviderTool bouncyCastle = new BouncyCastleSecurityProviderTool(); + + @Override + public CertificateAndKey createCARootCertificate(CertificateInfo certificateInfo, KeyPair keyPair, String messageDigest) { + return bouncyCastle.createCARootCertificate(certificateInfo, keyPair, messageDigest); + } + + @Override + public CertificateAndKey createServerCertificate(CertificateInfo certificateInfo, X509Certificate caRootCertificate, PrivateKey caPrivateKey, KeyPair serverKeyPair, String messageDigest) { + return bouncyCastle.createServerCertificate(certificateInfo, caRootCertificate, caPrivateKey, serverKeyPair, messageDigest); + } + + @Override + public KeyStore createServerKeyStore(String keyStoreType, + CertificateAndKey serverCertificateAndKey, + X509Certificate rootCertificate, + String privateKeyAlias, + String password) { + if (password == null) { + throw new IllegalArgumentException("KeyStore password cannot be null"); + } + + if (privateKeyAlias == null) { + throw new IllegalArgumentException("Private key alias cannot be null"); + } + + // create a KeyStore containing the impersonated certificate's private key and a certificate chain with the + // impersonated cert and our root certificate + KeyStore impersonatedCertificateKeyStore = KeyStoreUtil.createEmptyKeyStore(keyStoreType, null); + + // create the certificate chain back for the impersonated certificate back to the root certificate + Certificate[] chain = {serverCertificateAndKey.getCertificate(), rootCertificate}; + + try { + // place the impersonated certificate and its private key in the KeyStore + impersonatedCertificateKeyStore.setKeyEntry(privateKeyAlias, serverCertificateAndKey.getPrivateKey(), password.toCharArray(), chain); + } catch (KeyStoreException e) { + throw new KeyStoreAccessException("Error storing impersonated certificate and private key in KeyStore", e); + } + + return impersonatedCertificateKeyStore; + } + + @Override + public KeyStore createRootCertificateKeyStore(String keyStoreType, CertificateAndKey rootCertificateAndKey, String privateKeyAlias, String password) { + return KeyStoreUtil.createRootCertificateKeyStore(keyStoreType, rootCertificateAndKey.getCertificate(), privateKeyAlias, rootCertificateAndKey.getPrivateKey(), password, null); + } + + @Override + public String encodePrivateKeyAsPem(PrivateKey privateKey, String passwordForPrivateKey, String encryptionAlgorithm) { + return bouncyCastle.encodePrivateKeyAsPem(privateKey, passwordForPrivateKey, encryptionAlgorithm); + } + + @Override + public String encodeCertificateAsPem(Certificate certificate) { + return bouncyCastle.encodeCertificateAsPem(certificate); + } + + @Override + public PrivateKey decodePemEncodedPrivateKey(Reader privateKeyReader, String password) { + return bouncyCastle.decodePemEncodedPrivateKey(privateKeyReader, password); + } + + @Override + public X509Certificate decodePemEncodedCertificate(Reader certificateReader) { + // JCA supports reading PEM-encoded X509Certificates fairly easily, so there is no need to use BC to read the cert + Certificate certificate; + + // the JCA CertificateFactory takes an InputStream, so convert the reader to a stream first. converting to a String first + // is not ideal, but is relatively straightforward. (PEM certificates should only contain US_ASCII-compatible characters.) + try (InputStream certificateAsStream = new ByteArrayInputStream(CharStreams.toString(certificateReader).getBytes(StandardCharsets.US_ASCII))) { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + certificate = certificateFactory.generateCertificate(certificateAsStream); + } catch (CertificateException | IOException e) { + throw new ImportException("Unable to read PEM-encoded X509Certificate", e); + } + + if (!(certificate instanceof X509Certificate)) { + throw new ImportException("Attempted to import non-X.509 certificate as X.509 certificate"); + } + + return (X509Certificate) certificate; + } + + /** + * Loads the KeyStore from the specified InputStream. The InputStream is not closed after the KeyStore has been read. + * + * @param file file containing a KeyStore + * @param keyStoreType KeyStore type, such as "JKS" or "PKCS12" + * @param password password of the KeyStore + * @return KeyStore loaded from the input stream + */ + @Override + public KeyStore loadKeyStore(File file, String keyStoreType, String password) { + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance(keyStoreType); + } catch (KeyStoreException e) { + throw new KeyStoreAccessException("Unable to get KeyStore instance of type: " + keyStoreType, e); + } + + try (InputStream keystoreAsStream = new FileInputStream(file)) { + keyStore.load(keystoreAsStream, password.toCharArray()); + } catch (IOException e) { + throw new ImportException("Unable to read KeyStore from file: " + file.getName(), e); + } catch (CertificateException | NoSuchAlgorithmException e) { + throw new ImportException("Error while reading KeyStore", e); + } + + return keyStore; + } + + /** + * Exports the keyStore to the specified file. + * + * @param file file to save the KeyStore to + * @param keyStore KeyStore to export + * @param keystorePassword the password for the KeyStore + */ + @Override + public void saveKeyStore(File file, KeyStore keyStore, String keystorePassword) { + try (FileOutputStream fos = new FileOutputStream(file)) { + keyStore.store(fos, keystorePassword.toCharArray()); + } catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException e) { + throw new KeyStoreAccessException("Unable to save KeyStore to file: " + file.getName(), e); + } + } + + @Override + public KeyManager[] getKeyManagers(KeyStore keyStore, String keyStorePassword) { + return KeyStoreUtil.getKeyManagers(keyStore, keyStorePassword, null, null); + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/tools/SecurityProviderTool.java b/mitm/src/main/java/net/lightbody/bmp/mitm/tools/SecurityProviderTool.java new file mode 100644 index 000000000..8b6df2a87 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/tools/SecurityProviderTool.java @@ -0,0 +1,142 @@ +package net.lightbody.bmp.mitm.tools; + +import net.lightbody.bmp.mitm.CertificateAndKey; +import net.lightbody.bmp.mitm.CertificateInfo; + +import javax.net.ssl.KeyManager; +import java.io.File; +import java.io.Reader; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +/** + * Generic interface for functionality provided by a Security Provider. + */ +public interface SecurityProviderTool { + /** + * Creates a new self-signed CA root certificate, suitable for use signing new server certificates. + * + * @param certificateInfo certificate info to populate in the new root cert + * @param keyPair root certificate's public and private keys + * @param messageDigest digest to use when signing the new root certificate, such as SHA512 + * @return a new root certificate and private key + */ + CertificateAndKey createCARootCertificate(CertificateInfo certificateInfo, + KeyPair keyPair, + String messageDigest); + + /** + * Creates a new server X.509 certificate using the serverKeyPair. The new certificate will be populated with + * information from the specified certificateInfo and will be signed using the specified caPrivateKey and messageDigest. + * + * @param certificateInfo basic X.509 certificate info that will be used to create the server certificate + * @param caRootCertificate root certificate that will be used to populate the issuer field of the server certificate + * @param serverKeyPair server's public and private keys + * @param messageDigest message digest to use when signing the server certificate, such as SHA512 + * @param caPrivateKey root certificate private key that will be used to sign the server certificate + * @return a new server certificate and its private key + */ + CertificateAndKey createServerCertificate(CertificateInfo certificateInfo, + X509Certificate caRootCertificate, + PrivateKey caPrivateKey, + KeyPair serverKeyPair, + String messageDigest); + + /** + * Assembles a Java KeyStore containing a server's certificate, private key, and the certificate authority's certificate, + * which can be used to create an {@link javax.net.ssl.SSLContext}. + * + * @param keyStoreType the KeyStore type, such as JKS or PKCS12 + * @param serverCertificateAndKey certificate and private key for the server, which will be placed in the KeyStore + * @param rootCertificate CA root certificate of the private key that signed the server certificate + * @param privateKeyAlias alias to assign the private key (with accompanying certificate chain) to in the KeyStore + * @param password password for the new KeyStore and private key + * @return a new KeyStore with the server's certificate and password-protected private key + */ + KeyStore createServerKeyStore(String keyStoreType, + CertificateAndKey serverCertificateAndKey, + X509Certificate rootCertificate, + String privateKeyAlias, + String password); + + /** + * Assembles a Java KeyStore containing a CA root certificate and its private key. + * + * @param keyStoreType the KeyStore type, such as JKS or PKCS12 + * @param rootCertificateAndKey certification authority's root certificate and private key, which will be placed in the KeyStore + * @param privateKeyAlias alias to assign the private key (with accompanying certificate chain) to in the KeyStore + * @param password password for the new KeyStore and private key + * @return a new KeyStore with the root certificate and password-protected private key + */ + KeyStore createRootCertificateKeyStore(String keyStoreType, + CertificateAndKey rootCertificateAndKey, + String privateKeyAlias, + String password); + + /** + * Encodes a private key in PEM format, encrypting it with the specified password. The private key will be encrypted + * using the specified algorithm. + * + * @param privateKey private key to encode + * @param passwordForPrivateKey password to protect the private key + * @param encryptionAlgorithm algorithm to use to encrypt the private key + * @return PEM-encoded private key as a String + */ + String encodePrivateKeyAsPem(PrivateKey privateKey, String passwordForPrivateKey, String encryptionAlgorithm); + + /** + * Encodes a certificate in PEM format. + * + * @param certificate certificate to encode + * @return PEM-encoded certificate as a String + */ + String encodeCertificateAsPem(Certificate certificate); + + /** + * Decodes a PEM-encoded private key into a {@link PrivateKey}. The password may be null if the PEM-encoded private key + * is not password-encrypted. + * + * @param privateKeyReader a reader for a PEM-encoded private key + * @param password password protecting the private key @return the decoded private key + */ + PrivateKey decodePemEncodedPrivateKey(Reader privateKeyReader, String password); + + /** + * Decodes a PEM-encoded X.509 Certificate into a {@link X509Certificate}. + * + * @param certificateReader a reader for a PEM-encoded certificate + * @return the decoded X.509 certificate + */ + X509Certificate decodePemEncodedCertificate(Reader certificateReader); + + /** + * Loads a Java KeyStore object from a file. + * + * @param file KeyStore file to load + * @param keyStoreType KeyStore type (PKCS12, JKS, etc.) + * @param password the KeyStore password + * @return an initialized Java KeyStore object + */ + KeyStore loadKeyStore(File file, String keyStoreType, String password); + + /** + * Saves a Java KeyStore to a file, protecting it with the specified password. + * + * @param file file to save the KeyStore to + * @param keyStore KeyStore to save + * @param keystorePassword password for the KeyStore + */ + void saveKeyStore(File file, KeyStore keyStore, String keystorePassword); + + /** + * Retrieve the KeyManagers for the specified KeyStore. + * + * @param keyStore the KeyStore to retrieve KeyManagers from + * @param keyStorePassword the KeyStore password + * @return KeyManagers for the specified KeyStore + */ + KeyManager[] getKeyManagers(KeyStore keyStore, String keyStorePassword); +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java b/mitm/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java new file mode 100644 index 000000000..04bc36a35 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java @@ -0,0 +1,149 @@ +package net.lightbody.bmp.mitm.trustmanager; + +import io.netty.util.internal.EmptyArrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; +import java.net.Socket; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * An {@link X509ExtendedTrustManager} and {@link javax.net.ssl.X509TrustManager} that will accept all server and client + * certificates. Before accepting a certificate, the InsecureExtendedTrustManager uses the default X509ExtendedTrustManager + * to determine if the certificate would otherwise be trusted, and logs a debug-level message if it is not trusted. + */ +public class InsecureExtendedTrustManager extends X509ExtendedTrustManager { + private static final Logger log = LoggerFactory.getLogger(InsecureExtendedTrustManager.class); + + /** + * An {@link X509ExtendedTrustManager} that does no certificate validation whatsoever. + */ + private static final X509ExtendedTrustManager NOOP_EXTENDED_TRUST_MANAGER = new X509ExtendedTrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + }; + + /** + * The default extended trust manager, which will be used to determine if certificates would otherwise be trusted. + */ + protected static final X509ExtendedTrustManager DEFAULT_EXTENDED_TRUST_MANAGER = getDefaultExtendedTrustManager(); + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { + try { + DEFAULT_EXTENDED_TRUST_MANAGER.checkClientTrusted(x509Certificates, s, socket); + } catch (CertificateException e) { + log.debug("Accepting an untrusted client certificate: {}", x509Certificates[0].getSubjectDN(), e); + } + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { + try { + DEFAULT_EXTENDED_TRUST_MANAGER.checkServerTrusted(x509Certificates, s, socket); + } catch (CertificateException e) { + log.debug("Accepting an untrusted server certificate: {}", x509Certificates[0].getSubjectDN(), e); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { + try { + DEFAULT_EXTENDED_TRUST_MANAGER.checkClientTrusted(x509Certificates, s, sslEngine); + } catch (CertificateException e) { + log.debug("Accepting an untrusted client certificate: {}", x509Certificates[0].getSubjectDN(), e); + } + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { + try { + DEFAULT_EXTENDED_TRUST_MANAGER.checkServerTrusted(x509Certificates, s, sslEngine); + } catch (CertificateException e) { + log.debug("Accepting an untrusted server certificate: {}", x509Certificates[0].getSubjectDN(), e); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + try { + DEFAULT_EXTENDED_TRUST_MANAGER.checkClientTrusted(x509Certificates, s); + } catch (CertificateException e) { + log.debug("Accepting an untrusted client certificate: {}", x509Certificates[0].getSubjectDN(), e); + } + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + try { + DEFAULT_EXTENDED_TRUST_MANAGER.checkServerTrusted(x509Certificates, s); + } catch (CertificateException e) { + log.debug("Accepting an untrusted server certificate: {}", x509Certificates[0].getSubjectDN(), e); + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + + /** + * Returns the JDK's default X509ExtendedTrustManager, or a no-op trust manager if the default cannot be found. + */ + private static X509ExtendedTrustManager getDefaultExtendedTrustManager() { + TrustManagerFactory trustManagerFactory; + try { + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + // initialize the TrustManagerFactory with the default KeyStore + trustManagerFactory.init((KeyStore) null); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + log.debug("Unable to initialize default TrustManagerFactory. Using no-op X509ExtendedTrustManager.", e); + return NOOP_EXTENDED_TRUST_MANAGER; + } + + // find the X509ExtendedTrustManager in the list of registered trust managers + for (TrustManager tm : trustManagerFactory.getTrustManagers()) { + if (tm instanceof X509ExtendedTrustManager) { + return (X509ExtendedTrustManager) tm; + } + } + + // no default X509ExtendedTrustManager found, so return a no-op + log.debug("No default X509ExtendedTrustManager found. Using no-op."); + return NOOP_EXTENDED_TRUST_MANAGER; + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java b/mitm/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java new file mode 100644 index 000000000..6d9cfd040 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014 The Netty Project, + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package net.lightbody.bmp.mitm.trustmanager; + +import io.netty.handler.ssl.util.SimpleTrustManagerFactory; + +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; +import java.security.KeyStore; + +/** + * Note: This is a modified version of {@link io.netty.handler.ssl.util.InsecureTrustManagerFactory} from Netty + * 4.0.36. Unlike the netty version, this class returns an {@link X509ExtendedTrustManager} instead of an + * {@link javax.net.ssl.X509TrustManager} instance, which allows us to bypass additional certificate validations. + *

    + * An insecure {@link TrustManagerFactory} that trusts all X.509 certificates without any verification. + *

    + * NOTE: + * Never use this {@link TrustManagerFactory} in production. + * It is purely for testing purposes, and thus it is very insecure. + *

    + */ +public class InsecureTrustManagerFactory extends SimpleTrustManagerFactory { + + public static final TrustManagerFactory INSTANCE = new InsecureTrustManagerFactory(); + + public static final X509ExtendedTrustManager tm = new InsecureExtendedTrustManager(); + + @Override + protected void engineInit(KeyStore keyStore) throws Exception { + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return new TrustManager[]{tm}; + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java b/mitm/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java new file mode 100644 index 000000000..037f37c23 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java @@ -0,0 +1,127 @@ +package net.lightbody.bmp.mitm.util; + +import net.lightbody.bmp.mitm.exception.ExportException; +import net.lightbody.bmp.mitm.exception.ImportException; + +import javax.crypto.Cipher; +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.DSAKey; +import java.security.interfaces.ECKey; +import java.security.interfaces.RSAKey; +import java.util.Random; + +/** + * A collection of simple JCA-related utilities. + */ +public class EncryptionUtil { + /** + * Creates a signature algorithm string using the specified message digest and the encryption type corresponding + * to the supplied signingKey. Useful when generating the signature algorithm to be used to sign server certificates + * using the CA root certificate's signingKey. + *

    + * For example, if the root certificate has an RSA private key, and you + * wish to use the SHA256 message digest, this method will return the string "SHA256withRSA". See the + * "Signature Algorithms" section of http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html + * for a list of JSSE-supported signature algorithms. + * + * @param messageDigest digest to use to sign the certificate, such as SHA512 + * @param signingKey private key that will be used to sign the certificate + * @return a JCA-compatible signature algorithm + */ + public static String getSignatureAlgorithm(String messageDigest, Key signingKey) { + return messageDigest + "with" + getDigitalSignatureType(signingKey); + } + + /** + * Returns the type of digital signature used with the specified signing key. + * + * @param signingKey private key that will be used to sign a certificate (or something else) + * @return a string representing the digital signature type (ECDSA, RSA, etc.) + */ + public static String getDigitalSignatureType(Key signingKey) { + if (signingKey instanceof ECKey) { + return "ECDSA"; + } else if (signingKey instanceof RSAKey) { + return "RSA"; + } else if (signingKey instanceof DSAKey) { + return "DSA"; + } else { + throw new IllegalArgumentException("Cannot determine digital signature encryption type for unknown key type: " + signingKey.getClass().getCanonicalName()); + } + + } + + /** + * Creates a random BigInteger greater than 0 with the specified number of bits. + * + * @param bits number of bits to generate + * @return random BigInteger + */ + public static BigInteger getRandomBigInteger(int bits) { + return new BigInteger(bits, new Random()); + } + + /** + * Returns true if the key is an RSA public or private key. + */ + public static boolean isRsaKey(Key key) { + return "RSA".equals(key.getAlgorithm()); + } + + /** + * Returns true if the key is an elliptic curve public or private key. + */ + public static boolean isEcKey(Key key) { + return "EC".equals(key.getAlgorithm()); + } + + /** + * Convenience method to write PEM data to a file. The file will be encoded in the US_ASCII character set. + * + * @param file file to write to + * @param pemDataToWrite PEM data to write to the file + */ + public static void writePemStringToFile(File file, String pemDataToWrite) { + try { + Files.write(file.toPath(), pemDataToWrite.getBytes(StandardCharsets.US_ASCII)); + } catch (IOException e) { + throw new ExportException("Unable to write PEM string to file: " + file.getName(), e); + } + } + + /** + * Convenience method to read PEM data from a file. The file encoding must be US_ASCII. + * + * @param file file to read from + * @return PEM data from file + */ + public static String readPemStringFromFile(File file) { + try { + byte[] fileContents = Files.readAllBytes(file.toPath()); + return new String(fileContents, StandardCharsets.US_ASCII); + } catch (IOException e) { + throw new ImportException("Unable to read PEM-encoded data from file: " + file.getName()); + } + } + + /** + * Determines if unlimited-strength cryptography is allowed, i.e. if this JRE has then the unlimited strength policy + * files installed. + * + * @return true if unlimited strength cryptography is allowed, otherwise false + */ + public static boolean isUnlimitedStrengthAllowed() { + try { + return Cipher.getMaxAllowedKeyLength("AES") >= 256; + } catch (NoSuchAlgorithmException e) { + return false; + } + + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java b/mitm/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java new file mode 100644 index 000000000..7edfc47c9 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java @@ -0,0 +1,103 @@ +package net.lightbody.bmp.mitm.util; + +import net.lightbody.bmp.mitm.exception.KeyStoreAccessException; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * Utility for loading, saving, and manipulating {@link KeyStore}s. + */ +public class KeyStoreUtil { + /** + * Creates and initializes an empty KeyStore using the specified keyStoreType. + * + * @param keyStoreType type of key store to initialize, or null to use the system default + * @param provider JCA provider to use, or null to use the system default + * @return a new KeyStore + */ + public static KeyStore createEmptyKeyStore(String keyStoreType, String provider) { + if (keyStoreType == null) { + keyStoreType = KeyStore.getDefaultType(); + } + + KeyStore keyStore; + try { + if (provider == null) { + keyStore = KeyStore.getInstance(keyStoreType); + } else { + keyStore = KeyStore.getInstance(keyStoreType, provider); + } + keyStore.load(null, null); + } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException | IOException e) { + throw new KeyStoreAccessException("Error creating or initializing new KeyStore of type: " + keyStoreType, e); + } + return keyStore; + } + + /** + * Creates a new KeyStore containing the specified root certificate and private key. + * + * @param keyStoreType type of the generated KeyStore, such as PKCS12 or JKS + * @param certificate root certificate to add to the KeyStore + * @param privateKeyAlias alias for the private key in the KeyStore + * @param privateKey private key to add to the KeyStore + * @param privateKeyPassword password for the private key + * @param provider JCA provider to use, or null to use the system default + * @return new KeyStore containing the root certificate and private key + */ + public static KeyStore createRootCertificateKeyStore(String keyStoreType, X509Certificate certificate, String privateKeyAlias, PrivateKey privateKey, String privateKeyPassword, String provider) { + if (privateKeyPassword == null) { + throw new IllegalArgumentException("Must specify a KeyStore password"); + } + + KeyStore newKeyStore = KeyStoreUtil.createEmptyKeyStore(keyStoreType, provider); + + try { + newKeyStore.setKeyEntry(privateKeyAlias, privateKey, privateKeyPassword.toCharArray(), new Certificate[]{certificate}); + } catch (KeyStoreException e) { + throw new KeyStoreAccessException("Unable to store certificate and private key in KeyStore", e); + } + return newKeyStore; + } + + /** + * Retrieve the KeyManagers for the specified KeyStore. + * + * @param keyStore the KeyStore to retrieve KeyManagers from + * @param keyStorePassword the KeyStore password + * @param keyManagerAlgorithm key manager algorithm to use, or null to use the system default + * @param provider JCA provider to use, or null to use the system default + * @return KeyManagers for the specified KeyStore + */ + public static KeyManager[] getKeyManagers(KeyStore keyStore, String keyStorePassword, String keyManagerAlgorithm, String provider) { + if (keyManagerAlgorithm == null) { + keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + } + + try { + KeyManagerFactory kmf; + if (provider == null) { + kmf = KeyManagerFactory.getInstance(keyManagerAlgorithm); + } else { + kmf = KeyManagerFactory.getInstance(keyManagerAlgorithm, provider); + } + + kmf.init(keyStore, keyStorePassword.toCharArray()); + + return kmf.getKeyManagers(); + } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | NoSuchProviderException e) { + throw new KeyStoreAccessException("Unable to get KeyManagers for KeyStore", e); + } + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/util/MitmConstants.java b/mitm/src/main/java/net/lightbody/bmp/mitm/util/MitmConstants.java new file mode 100644 index 000000000..bb24ad27c --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/util/MitmConstants.java @@ -0,0 +1,34 @@ +package net.lightbody.bmp.mitm.util; + +/** + * Default values for basic MITM properties. + */ +public class MitmConstants { + /** + * The default message digest to use when signing certificates (CA or server). On 64-bit systems this is set to + * SHA512, on 32-bit systems this is SHA256. On 64-bit systems, SHA512 generally performs better than SHA256; see + * this question for details: http://crypto.stackexchange.com/questions/26336/sha512-faster-than-sha256. SHA384 is + * SHA512 with a smaller output size. + */ + public static final String DEFAULT_MESSAGE_DIGEST = is32BitJvm() ? "SHA256": "SHA384"; + + /** + * The default {@link java.security.KeyStore} type to use when creating KeyStores (e.g. for impersonated server + * certificates). PKCS12 is widely supported. + */ + public static final String DEFAULT_KEYSTORE_TYPE = "PKCS12"; + + /** + * Uses the non-portable system property sun.arch.data.model to help determine if we are running on a 32-bit JVM. + * Since the majority of modern systems are 64 bits, this method "assumes" 64 bits and only returns true if + * sun.arch.data.model explicitly indicates a 32-bit JVM. + * + * @return true if we can determine definitively that this is a 32-bit JVM, otherwise false + */ + private static boolean is32BitJvm() { + Integer bits = Integer.getInteger("sun.arch.data.model"); + + return bits != null && bits == 32; + + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java b/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java new file mode 100644 index 000000000..198f194e1 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java @@ -0,0 +1,180 @@ +package net.lightbody.bmp.mitm.util; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.io.CharStreams; +import io.netty.handler.ssl.OpenSsl; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SupportedCipherSuiteFilter; +import net.lightbody.bmp.mitm.trustmanager.InsecureTrustManagerFactory; +import net.lightbody.bmp.mitm.TrustSource; +import net.lightbody.bmp.mitm.exception.SslContextInitializationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Utility for creating SSLContexts. + */ +public class SslUtil { + private static final Logger log = LoggerFactory.getLogger(SslUtil.class); + + /** + * Classpath resource containing a list of default ciphers. + */ + private static final String DEFAULT_CIPHERS_LIST_RESOURCE = "/default-ciphers.txt"; + + /** + * The default cipher list to prefer when creating client or server connections. Stored as a lazily-loaded singleton + * due to the relatively expensive initialization time, especially when determining the enabled JDK ciphers. + * If OpenSsl support is enabled, this simply returns the list provided by {@link #getBuiltInCipherList()}. + * If OpenSsl is not available, retrieves the default ciphers enabled on java SSLContexts. If the enabled JDK cipher + * list cannot be read, returns the list provided by {@link #getBuiltInCipherList()}. + */ + private static final Supplier> defaultCipherList = Suppliers.memoize(new Supplier>() { + @Override + public List get() { + List ciphers; + if (OpenSsl.isAvailable()) { + // TODO: consider switching to the list of all available ciphers using OpenSsl.availableCipherSuites() + ciphers = getBuiltInCipherList(); + } else { + ciphers = getEnabledJdkCipherSuites(); + + if (ciphers.isEmpty()) { + // could not retrieve the list of enabled ciphers from the JDK SSLContext, so use the hard-coded list + ciphers = getBuiltInCipherList(); + } + } + + return ciphers; + } + }); + + /** + * Creates a netty SslContext for use when connecting to upstream servers. Retrieves the list of trusted root CAs + * from the trustSource. When trustSource is true, no upstream certificate verification will be performed. + * This will make it possible for attackers to MITM communications with the upstream server, so always + * supply an appropriate trustSource except in extraordinary circumstances (e.g. testing with dynamically-generated + * certificates). + * + * @param cipherSuites cipher suites to allow when connecting to the upstream server + * @param trustSource the trust store that will be used to validate upstream servers' certificates, or null to accept all upstream server certificates + * @return an SSLContext to connect to upstream servers with + */ + public static SslContext getUpstreamServerSslContext(Collection cipherSuites, TrustSource trustSource) { + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); + + if (trustSource == null) { + log.warn("Disabling upstream server certificate verification. This will allow attackers to intercept communications with upstream servers."); + + sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); + } else { + sslContextBuilder.trustManager(trustSource.getTrustedCAs()); + } + + sslContextBuilder.ciphers(cipherSuites, SupportedCipherSuiteFilter.INSTANCE); + + try { + return sslContextBuilder.build(); + } catch (SSLException e) { + throw new SslContextInitializationException("Error creating new SSL context for connection to upstream server", e); + } + } + + /** + * Returns the X509Certificate for the server this session is connected to. The certificate may be null. + * + * @param sslSession SSL session connected to upstream server + * @return the X.509 certificate from the upstream server, or null if no certificate is available + */ + public static X509Certificate getServerCertificate(SSLSession sslSession) { + Certificate[] peerCertificates; + try { + peerCertificates = sslSession.getPeerCertificates(); + } catch (SSLPeerUnverifiedException e) { + peerCertificates = null; + } + + if (peerCertificates != null && peerCertificates.length > 0) { + Certificate peerCertificate = peerCertificates[0]; + if (peerCertificate != null && peerCertificate instanceof X509Certificate) { + return (X509Certificate) peerCertificates[0]; + } + } + + // no X.509 certificate was found for this server + return null; + } + + /** + * Returns the list of default "enabled" ciphers for server TLS connections, as reported by the default Java security provider. + * This is most likely a subset of "available" ciphers. + * + * @return list of default server ciphers, or an empty list if the default cipher list cannot be loaded + */ + public static List getEnabledJdkCipherSuites() { + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, null); + + String[] defaultCiphers = sslContext.getServerSocketFactory().getDefaultCipherSuites(); + + return Arrays.asList(defaultCiphers); + } catch (Throwable t) { + log.info("Unable to load default JDK server cipher list from SSLContext"); + + // log the actual exception for debugging + log.debug("An error occurred while initializing an SSLContext or ServerSocketFactory", t); + + return Collections.emptyList(); + } + } + + /** + * Returns a reasonable default cipher list for new client and server SSL connections. Not all of the ciphers may be supported + * by the underlying SSL implementation (OpenSsl or JDK). The default list itself may also vary between OpenSsl and JDK + * implementations. See {@link #defaultCipherList} for implementation details. + * + * @return default ciphers for client and server connections + */ + public static List getDefaultCipherList() { + return defaultCipherList.get(); + } + + /** + * Returns ciphers from the hard-coded list of "reasonable" default ciphers in {@link #DEFAULT_CIPHERS_LIST_RESOURCE}. + * + * @return ciphers from the {@link #DEFAULT_CIPHERS_LIST_RESOURCE} + */ + public static List getBuiltInCipherList() { + try (InputStream cipherListStream = SslUtil.class.getResourceAsStream(DEFAULT_CIPHERS_LIST_RESOURCE)) { + if (cipherListStream == null) { + return Collections.emptyList(); + } + + Reader reader = new InputStreamReader(cipherListStream, StandardCharsets.UTF_8); + + return CharStreams.readLines(reader); + } catch (IOException e) { + return Collections.emptyList(); + } + } + +} diff --git a/mitm/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java b/mitm/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java new file mode 100644 index 000000000..156f91089 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java @@ -0,0 +1,198 @@ +package net.lightbody.bmp.mitm.util; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import net.lightbody.bmp.mitm.exception.KeyStoreAccessException; +import net.lightbody.bmp.mitm.exception.TrustSourceException; +import net.lightbody.bmp.mitm.exception.UncheckedIOException; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.SecurityProviderTool; +import net.lightbody.bmp.util.ClasspathResourceUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility class for interacting with the default trust stores on this JVM. + */ +public class TrustUtil { + private static final Logger log = LoggerFactory.getLogger(TrustUtil.class); + + /** + * Regex that matches a single certificate within a PEM file containing (potentially multiple) certificates. + */ + private static final Pattern CA_PEM_PATTERN = Pattern.compile("-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----", Pattern.DOTALL); + + /** + * The file containing the built-in list of trusted CAs. + */ + private static final String DEFAULT_TRUSTED_CA_RESOURCE = "/cacerts.pem"; + + /** + * Empty X509 certificate array, useful for indicating an empty root CA trust store. + */ + public static final X509Certificate[] EMPTY_CERTIFICATE_ARRAY = new X509Certificate[0]; + + /** + * Security provider used to transform PEM files into Certificates. + * TODO: Modify the architecture of TrustUtil and TrustSource so that they do not need a hard-coded SecurityProviderTool. + */ + private static final SecurityProviderTool securityProviderTool = new DefaultSecurityProviderTool(); + + /** + * Singleton for the list of CAs trusted by Java by default. + */ + private static final Supplier javaTrustedCAs = Suppliers.memoize(new Supplier() { + @Override + public X509Certificate[] get() { + X509TrustManager defaultTrustManager = getDefaultJavaTrustManager(); + + X509Certificate[] defaultJavaTrustedCerts = defaultTrustManager.getAcceptedIssuers(); + + if (defaultJavaTrustedCerts != null) { + return defaultJavaTrustedCerts; + } else { + return EMPTY_CERTIFICATE_ARRAY; + } + } + }); + + /** + * Singleton for the built-in list of trusted CAs. + */ + private static final Supplier builtinTrustedCAs = Suppliers.memoize(new Supplier() { + @Override + public X509Certificate[] get() { + try { + // the file may contain UTF-8 characters, but the PEM-encoded certificate data itself must be US-ASCII + String allCAs = ClasspathResourceUtil.classpathResourceToString(DEFAULT_TRUSTED_CA_RESOURCE, StandardCharsets.UTF_8); + + return readX509CertificatesFromPem(allCAs); + } catch (UncheckedIOException e) { + log.warn("Unable to load built-in trusted CAs; no built-in CAs will be trusted", e); + return new X509Certificate[0]; + } + } + }); + + /** + * Returns the built-in list of trusted CAs. This is a copy of cURL's list (https://curl.haxx.se/ca/cacert.pem), which is + * ultimately derived from Firefox/NSS' list of trusted CAs. + */ + public static X509Certificate[] getBuiltinTrustedCAs() { + return builtinTrustedCAs.get(); + } + + /** + * Returns the list of root CAs trusted by default in this JVM, according to the TrustManager returned by + * {@link #getDefaultJavaTrustManager()}. + */ + public static X509Certificate[] getJavaTrustedCAs() { + return javaTrustedCAs.get(); + } + + /** + * Parses a String containing zero or more PEM-encoded X509 certificates into an array of {@link X509Certificate}. + * Everything outside of BEGIN CERTIFICATE and END CERTIFICATE lines will be ignored. + * + * @param pemEncodedCAs a String containing PEM-encoded certficiates + * @return array containing certificates in the String + */ + public static X509Certificate[] readX509CertificatesFromPem(String pemEncodedCAs) { + List certificates = new ArrayList<>(500); + + Matcher pemMatcher = CA_PEM_PATTERN.matcher(pemEncodedCAs); + + while (pemMatcher.find()) { + String singleCAPem = pemMatcher.group(); + + X509Certificate certificate = readSingleX509Certificate(singleCAPem); + certificates.add(certificate); + } + + return certificates.toArray(new X509Certificate[0]); + } + + /** + * Parses a single PEM-encoded X509 certificate into an {@link X509Certificate}. + * + * @param x509CertificateAsPem PEM-encoded X509 certificate + * @return parsed Java X509Certificate + */ + public static X509Certificate readSingleX509Certificate(String x509CertificateAsPem) { + return securityProviderTool.decodePemEncodedCertificate(new StringReader(x509CertificateAsPem)); + } + + /** + * Returns a new instance of the default TrustManager for this JVM. Uses the default JVM trust store, which is + * generally the cacerts file in JAVA_HOME/jre/lib/security, but this can be overridden using JVM parameters. + */ + public static X509TrustManager getDefaultJavaTrustManager() { + TrustManagerFactory tmf; + try { + tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + // initializing the trust store with a null KeyStore will load the default JVM trust store + tmf.init((KeyStore) null); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + throw new TrustSourceException("Unable to retrieve default TrustManagerFactory", e); + } + + // Get hold of the default trust manager + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + return (X509TrustManager) tm; + } + } + + // didn't find an X509TrustManager + throw new TrustSourceException("No X509TrustManager found"); + } + + /** + * Extracts the {@link java.security.KeyStore.TrustedCertificateEntry}s from the specified KeyStore. All other entry + * types, including private keys, will be ignored. + * + * @param trustStore keystore containing trusted certificate entries + * @return the trusted certificate entries in the specified keystore + */ + public static List extractTrustedCertificateEntries(KeyStore trustStore) { + try { + Enumeration aliases = trustStore.aliases(); + List keyStoreAliases = Collections.list(aliases); + + List trustedCertificates = new ArrayList<>(keyStoreAliases.size()); + + for (String alias : keyStoreAliases) { + if (trustStore.entryInstanceOf(alias, KeyStore.TrustedCertificateEntry.class)) { + Certificate certificate = trustStore.getCertificate(alias); + if (!(certificate instanceof X509Certificate)) { + log.debug("Skipping non-X509Certificate in KeyStore. Certificate type: {}", certificate.getType()); + continue; + } + + trustedCertificates.add((X509Certificate) certificate); + } + } + + return trustedCertificates; + } catch (KeyStoreException e) { + throw new KeyStoreAccessException("Error occurred while retrieving trusted CAs from KeyStore", e); + } + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java b/mitm/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java new file mode 100644 index 000000000..66bd3b18d --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java @@ -0,0 +1,50 @@ +package net.lightbody.bmp.util; + +import com.google.common.io.CharStreams; +import net.lightbody.bmp.mitm.exception.UncheckedIOException; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +/** + * Utility class for dealing with classpath resources. + */ +public class ClasspathResourceUtil { + /** + * Retrieves a classpath resource using the {@link ClasspathResourceUtil} classloader and converts it to a String using the specified + * character set. If any error occurs while reading the resource, this method throws + * {@link net.lightbody.bmp.mitm.exception.UncheckedIOException}. If the classpath resource cannot be found, this + * method throws a FileNotFoundException wrapped in an UncheckedIOException. + * + * @param resource classpath resource to load + * @param charset charset to use to decode the classpath resource + * @return a String + * @throws UncheckedIOException if the classpath resource cannot be found or cannot be read for any reason + */ + public static String classpathResourceToString(String resource, Charset charset) throws UncheckedIOException { + if (resource == null) { + throw new IllegalArgumentException("Classpath resource to load cannot be null"); + } + + if (charset == null) { + throw new IllegalArgumentException("Character set cannot be null"); + } + + try (InputStream resourceAsStream = ClasspathResourceUtil.class.getResourceAsStream(resource)) { + if (resourceAsStream == null) { + throw new UncheckedIOException(new FileNotFoundException("Unable to locate classpath resource: " + resource)); + } + + // the classpath resource was found and opened. wrap it in a Reader and return its contents. + Reader resourceReader = new InputStreamReader(resourceAsStream, charset); + + return CharStreams.toString(resourceReader); + } catch (IOException e) { + throw new UncheckedIOException("Error occurred while reading classpath resource", e); + } + } +} diff --git a/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java b/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java new file mode 100644 index 000000000..2cc2a2158 --- /dev/null +++ b/mitm/src/main/java/net/lightbody/bmp/util/HttpUtil.java @@ -0,0 +1,123 @@ +package net.lightbody.bmp.util; + +import com.google.common.net.HostAndPort; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Locale; + +/** + * Contains utility methods for netty {@link HttpRequest} and related objects. + */ +public class HttpUtil { + /** + * Identify the host of an HTTP request. This method uses the URI of the request if possible, otherwise it attempts to find the host + * in the request headers. + * + * @param httpRequest HTTP request to parse the host from + * @return the host the request is connecting to, or null if no host can be found + */ + public static String getHostFromRequest(HttpRequest httpRequest) { + // try to use the URI from the request first, if the URI starts with http:// or https://. checking for http/https avoids confusing + // java's URI class when the request is for a malformed URL like '//some-resource'. + String host = null; + if (startsWithHttpOrHttps(httpRequest.getUri())) { + try { + URI uri = new URI(httpRequest.getUri()); + host = uri.getHost(); + } catch (URISyntaxException e) { + } + } + + // if there was no host in the URI, attempt to grab the host from the Host header + if (host == null || host.isEmpty()) { + host = parseHostHeader(httpRequest, false); + } + + return host; + } + + /** + * Gets the host and port from the specified request. Returns the host and port from the request URI if available, + * otherwise retrieves the host and port from the Host header. + * + * @param httpRequest HTTP request + * @return host and port of the request + */ + public static String getHostAndPortFromRequest(HttpRequest httpRequest) { + if (startsWithHttpOrHttps(httpRequest.getUri())) { + try { + return getHostAndPortFromUri(httpRequest.getUri()); + } catch (URISyntaxException e) { + // the URI could not be parsed, so return the host and port in the Host header + } + } + + return parseHostHeader(httpRequest, true); + } + + /** + * Returns true if the string starts with http:// or https://. + * + * @param uri string to evaluate + * @return true if the string starts with http:// or https:// + */ + public static boolean startsWithHttpOrHttps(String uri) { + if (uri == null) { + return false; + } + + // the scheme is case insensitive, according to RFC 7230, section 2.7.3: + /* + The scheme and host + are case-insensitive and normally provided in lowercase; all other + components are compared in a case-sensitive manner. + */ + String lowercaseUri = uri.toLowerCase(Locale.US); + + return lowercaseUri.startsWith("http://") || lowercaseUri.startsWith("https://"); + } + + /** + * Retrieves the host and port from the specified URI. + * + * @param uriString URI to retrieve the host and port from + * @return the host and port from the URI as a String + * @throws URISyntaxException if the specified URI is invalid or cannot be parsed + */ + public static String getHostAndPortFromUri(String uriString) throws URISyntaxException { + URI uri = new URI(uriString); + if (uri.getPort() == -1) { + return uri.getHost(); + } else { + return HostAndPort.fromParts(uri.getHost(), uri.getPort()).toString(); + } + } + + /** + * Retrieves the host and, optionally, the port from the specified request's Host header. + * + * @param httpRequest HTTP request + * @param includePort when true, include the port + * @return the host and, optionally, the port specified in the request's Host header + */ + private static String parseHostHeader(HttpRequest httpRequest, boolean includePort) { + // this header parsing logic is adapted from ClientToProxyConnection#identifyHostAndPort. + List hosts = httpRequest.headers().getAll(HttpHeaders.Names.HOST); + if (!hosts.isEmpty()) { + String hostAndPort = hosts.get(0); + + if (includePort) { + return hostAndPort; + } else { + HostAndPort parsedHostAndPort = HostAndPort.fromString(hostAndPort); + return parsedHostAndPort.getHost(); + } + } else { + return null; + } + } +} diff --git a/mitm/src/main/resources/cacerts.pem b/mitm/src/main/resources/cacerts.pem new file mode 100644 index 000000000..b86eb62f1 --- /dev/null +++ b/mitm/src/main/resources/cacerts.pem @@ -0,0 +1,3956 @@ +### This is a copy of https://curl.haxx.se/ca/cacert.pem +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Wed Jun 7 03:12:05 2017 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.27. +## SHA256: 93753268e1c596aee21893fb1c6975338389132f15c942ed65fc394a904371d7 +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4 +MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM +MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF +NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe +AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC +b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn +7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN +cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp +WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5 +haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY +MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9 +MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn +jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ +MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H +VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl +vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl +OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3 +1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq +nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy +x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ +5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn +vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj +CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil +e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR +OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI +CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 +48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi +trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 +qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB +AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC +ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA +A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz ++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj +f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN +kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk +CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF +URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb +CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h +oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV +IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm +66+KAQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD +T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4 +MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG +A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk +WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD +YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew +MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb +m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk +HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT +xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2 +3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9 +2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq +TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz +4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU +9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg +aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP +eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk +zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1 +ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI +KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq +nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE +I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp +MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o +tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================ +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2 +MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM +LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo +ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ +wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH +Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a +SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS +NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab +mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY +Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3 +qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu +MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO +v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ +82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz +o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs +a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx +OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW +mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o ++sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC +rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE +BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl +cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN +MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT +HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg +Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz +o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy +Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti +GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li +qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH +Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG +alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa +m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox +bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi +xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB +bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL +j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU +wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7 +XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH +59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/ +23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq +J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA +HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi +uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +WoSign +====== +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG +EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNVBAMTIUNlcnRpZmljYXRpb24g +QXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJ +BgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +vcqNrLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1UfcIiePyO +CbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcSccf+Hb0v1naMQFXQoOXXDX +2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2ZjC1vt7tj/id07sBMOby8w7gLJKA84X5 +KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4Mx1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR ++ScPewavVIMYe+HdVHpRaG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ez +EC8wQjchzDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDaruHqk +lWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221KmYo0SLwX3OSACCK2 +8jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvASh0JWzko/amrzgD5LkhLJuYwTKVY +yrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWvHYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0C +AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R +8bNLtwYgFP6HEtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1 +LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJMuYhOZO9sxXq +T2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2eJXLOC62qx1ViC777Y7NhRCOj +y+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VNg64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC +2nz4SNAzqfkHx5Xh9T71XXG68pWpdIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes +5cVAWubXbHssw1abR80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/ +EaEQPkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGcexGATVdVh +mVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+J7x6v+Db9NpSvd4MVHAx +kUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMlOtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGi +kpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWTee5Ehr7XHuQe+w== +-----END CERTIFICATE----- + +WoSign China +============ +-----BEGIN CERTIFICATE----- +MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQG +EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMMEkNBIOayg+mAmuagueiv +geS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYD +VQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k +8H/rD195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld19AXbbQs5 +uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExfv5RxadmWPgxDT74wwJ85 +dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnkUkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5 +Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+LNVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFy +b7Ao65vh4YOhn0pdr8yb+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc +76DbT52VqyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6KyX2m ++Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0GAbQOXDBGVWCvOGU6 +yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaKJ/kR8slC/k7e3x9cxKSGhxYzoacX +GKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUA +A4ICAQBqinA4WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6 +yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj/feTZU7n85iY +r83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6jBAyvd0zaziGfjk9DgNyp115 +j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0A +kLppRQjbbpCBhqcqBT/mhDn4t/lXX0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97 +qA4bLJyuQHCH2u2nFoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Y +jj4Du9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10lO1Hm13ZB +ONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Leie2uPAmvylezkolwQOQv +T8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR12KvxAmLBsX5VYc8T1yaw15zLKYs4SgsO +kI26oQ== +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl +OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV +MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF +JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G3 +================================== +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y +olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t +x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy +EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K +Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur +mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5 +1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp +07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo +FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE +41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu +yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq +KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1 +v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA +8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b +8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r +mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq +1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI +JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV +tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk= +-----END CERTIFICATE----- + +Staat der Nederlanden EV Root CA +================================ +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M +MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl +cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk +SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW +O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r +0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 +Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV +XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr +08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV +0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd +74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx +fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa +ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu +c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq +5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN +b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN +f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi +5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 +WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK +DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy +eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 +==================================================== +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1Qg +RWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAw +ODA3MDFaFw0yMzA0MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0w +SwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnE +n2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRp +ZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEApCUZ4WWe60ghUEoI5RHwWrom/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537 +jVJp45wnEFPzpALFp/kRGml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1m +ep5Fimh34khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z5UNP +9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0hO8EuPbJbKoCPrZV +4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QIDAQABo0IwQDAdBgNVHQ4EFgQUVpkH +HtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBAJ5FdnsXSDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPo +BP5yCccLqh0lVX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nfpeYVhDfwwvJl +lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8 +B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- + +Certinomis - Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg +LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx +EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD +ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos +P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo +d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap +z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00 +8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x +RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE +6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t +FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV +PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH +i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj +YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I +6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV +WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw +Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX +lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ +y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9 +Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng +DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi +I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM +cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr +hkIGuUE= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +Certification Authority of WoSign G2 +==================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQayXaioidfLwPBbOxemFFRDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQG +EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxLTArBgNVBAMTJENlcnRpZmljYXRpb24g +QXV0aG9yaXR5IG9mIFdvU2lnbiBHMjAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMFgx +CzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsGA1UEAxMkQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvsXEoCKASU+/2YcRxlPhuw+9YH+v9oIOH9ywjj2X4FA8jzrvZjtFB5sg+OPXJYY1kBai +XW8wGQiHC38Gsp1ij96vkqVg1CuAmlI/9ZqD6TRay9nVYlzmDuDfBpgOgHzKtB0TiGsOqCR3A9Du +W/PKaZE1OVbFbeP3PU9ekzgkyhjpJMuSA93MHD0JcOQg5PGurLtzaaNjOg9FD6FKmsLRY6zLEPg9 +5k4ot+vElbGs/V6r+kHLXZ1L3PR8du9nfwB6jdKgGlxNIuG12t12s9R23164i5jIFFTMaxeSt+BK +v0mUYQs4kI9dJGwlezt52eJ+na2fmKEG/HgUYFf47oB3sQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU+mCp62XF3RYUCE4MD42b4Pdkr2cwDQYJKoZI +hvcNAQELBQADggEBAFfDejaCnI2Y4qtAqkePx6db7XznPWZaOzG73/MWM5H8fHulwqZm46qwtyeY +P0nXYGdnPzZPSsvxFPpahygc7Y9BMsaV+X3avXtbwrAh449G3CE4Q3RM+zD4F3LBMvzIkRfEzFg3 +TgvMWvchNSiDbGAtROtSjFA9tWwS1/oJu2yySrHFieT801LYYRf+epSEj3m2M1m6D8QL4nCgS3gu ++sif/a+RZQp4OBXllxcU3fngLDT4ONCEIgDAFFEYKwLcMFrw6AF8NTojrwjkr6qOKEJJLvD1mTS+ +7Q9LGOHSJDy7XUe3IfKN0QqZjuNuPq1w4I+5ysxugTH2e5x6eeRncRg= +-----END CERTIFICATE----- + +CA WoSign ECC Root +================== +-----BEGIN CERTIFICATE----- +MIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQswCQYDVQQGEwJD +TjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMTEkNBIFdvU2lnbiBFQ0MgUm9v +dDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQK +ExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiU +t5v8KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES1ns2o0Iw +QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqv3VWqP2h4syhf3R +MluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0 +Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu +a/GRspBl9JrmkO5K +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +Certplus Root CA G1 +=================== +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV +BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe +Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD +ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN +r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx +Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj +BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv +LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2 +z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc +4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd +4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj +jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+ +ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY +lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh +66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG +YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/ +2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F +6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX +CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe +tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC +VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/ ++mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+ +qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo= +-----END CERTIFICATE----- + +Certplus Root CA G2 +=================== +-----BEGIN CERTIFICATE----- +MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT +AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x +NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0 +cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN +Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud +IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV +HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl +vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw== +-----END CERTIFICATE----- + +OpenTrust Root CA G1 +==================== +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx +MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM +CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa +Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87 +ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO +YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9 +xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO +9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq +3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi +n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9 +URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr +TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px +N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E +PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv +uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK +n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh +X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80 +nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm +GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/ +bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o +4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA +OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx +-----END CERTIFICATE----- + +OpenTrust Root CA G2 +==================== +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy +MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM +CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+ +Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz +4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV +eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt +UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz +3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj +3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz +9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0 +0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT +y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59 +M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz +Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI +mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG +S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp +EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ +6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr +gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo +SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0 +YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm +u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK +-----END CERTIFICATE----- + +OpenTrust Root CA G3 +==================== +-----BEGIN CERTIFICATE----- +MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X +DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w +ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B +ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf +BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM +BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta +3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +LuxTrust Global Root 2 +====================== +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG +A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh +bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW +MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm +Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2 +xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC +wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm +1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm +FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF +wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/ +a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U +ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ +MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB +/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5 +Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ +FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN +H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW +7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu +ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA +VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR +TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt +/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc +7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I +iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- diff --git a/mitm/src/main/resources/default-ciphers.txt b/mitm/src/main/resources/default-ciphers.txt new file mode 100644 index 000000000..2295941f6 --- /dev/null +++ b/mitm/src/main/resources/default-ciphers.txt @@ -0,0 +1,29 @@ +TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 +TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 +TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 +TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 +TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 +TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 +TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 +TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA +TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA +TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA +TLS_DHE_RSA_WITH_AES_256_CBC_SHA +TLS_DHE_RSA_WITH_AES_128_CBC_SHA +TLS_RSA_WITH_AES_256_GCM_SHA384 +TLS_RSA_WITH_AES_256_GCM_SHA384 +TLS_RSA_WITH_AES_128_GCM_SHA256 +TLS_RSA_WITH_AES_256_CBC_SHA +TLS_RSA_WITH_AES_128_CBC_SHA +SSL_RSA_WITH_3DES_EDE_CBC_SHA +TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 +TLS_DHE_DSS_WITH_AES_256_CBC_SHA +TLS_DHE_DSS_WITH_AES_128_CBC_SHA + diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/ExistingCertificateSourceTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/ExistingCertificateSourceTest.groovy new file mode 100644 index 000000000..9337ec724 --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/ExistingCertificateSourceTest.groovy @@ -0,0 +1,36 @@ +package net.lightbody.bmp.mitm + +import org.junit.Test + +import java.security.PrivateKey +import java.security.cert.X509Certificate + +import static org.junit.Assert.assertEquals +import static org.mockito.Mockito.mock + +class ExistingCertificateSourceTest { + + X509Certificate mockCertificate = mock(X509Certificate) + PrivateKey mockPrivateKey = mock(PrivateKey) + + @Test + void testLoadExistingCertificateAndKey() { + ExistingCertificateSource certificateSource = new ExistingCertificateSource(mockCertificate, mockPrivateKey) + CertificateAndKey certificateAndKey = certificateSource.load() + + assertEquals(mockCertificate, certificateAndKey.certificate) + assertEquals(mockPrivateKey, certificateAndKey.privateKey) + } + + @Test(expected = IllegalArgumentException) + void testMustSupplyCertificate() { + ExistingCertificateSource certificateSource = new ExistingCertificateSource(null, mockPrivateKey) + certificateSource.load() + } + + @Test(expected = IllegalArgumentException) + void testMustSupplyPrivateKey() { + ExistingCertificateSource certificateSource = new ExistingCertificateSource(mockCertificate, null) + certificateSource.load() + } +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/ImpersonatingMitmManagerTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/ImpersonatingMitmManagerTest.groovy new file mode 100644 index 000000000..e3a5eea04 --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/ImpersonatingMitmManagerTest.groovy @@ -0,0 +1,53 @@ +package net.lightbody.bmp.mitm + +import io.netty.handler.codec.http.DefaultFullHttpRequest +import io.netty.handler.codec.http.HttpMethod +import io.netty.handler.codec.http.HttpVersion +import net.lightbody.bmp.mitm.keys.ECKeyGenerator +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager +import org.junit.Test + +import javax.net.ssl.SSLEngine +import javax.net.ssl.SSLSession + +import static org.junit.Assert.assertNotNull +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +class ImpersonatingMitmManagerTest { + SSLSession mockSession = mock(SSLSession) + + @Test + void testCreateDefaultServerEngine() { + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder().build() + + SSLEngine serverSslEngine = mitmManager.serverSslEngine("hostname", 80) + assertNotNull(serverSslEngine) + } + + @Test + void testCreateDefaultClientEngine() { + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder().build() + + when(mockSession.getPeerHost()).thenReturn("hostname") + + def request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "https://test.connection") + SSLEngine clientSslEngine = mitmManager.clientSslEngineFor(request, mockSession) + assertNotNull(clientSslEngine) + } + + @Test + void testCreateCAAndServerCertificatesOfDifferentTypes() { + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(RootCertificateGenerator.builder().keyGenerator(new RSAKeyGenerator()).build()) + .serverKeyGenerator(new ECKeyGenerator()) + .build() + + when(mockSession.getPeerHost()).thenReturn("hostname") + + def request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "https://test.connection") + SSLEngine clientSslEngine = mitmManager.clientSslEngineFor(request, mockSession) + assertNotNull(clientSslEngine) + } +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/KeyStoreCertificateSourceTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/KeyStoreCertificateSourceTest.groovy new file mode 100644 index 000000000..3f0540fab --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/KeyStoreCertificateSourceTest.groovy @@ -0,0 +1,33 @@ +package net.lightbody.bmp.mitm + +import org.junit.Test + +import java.security.KeyStore + +import static org.mockito.Mockito.mock + +class KeyStoreCertificateSourceTest { + + KeyStore mockKeyStore = mock(KeyStore) + + // the happy-path test cases are already covered implicitly as part of KeyStoreFileCertificateSourceTest, so just test negative cases + + @Test(expected = IllegalArgumentException) + void testMustSupplyKeystore() { + KeyStoreCertificateSource keyStoreCertificateSource = new KeyStoreCertificateSource(null, "privatekey", "password") + keyStoreCertificateSource.load() + } + + @Test(expected = IllegalArgumentException) + void testMustSupplyPassword() { + KeyStoreCertificateSource keyStoreCertificateSource = new KeyStoreCertificateSource(mockKeyStore, "privatekey", null) + keyStoreCertificateSource.load() + } + + @Test(expected = IllegalArgumentException) + void testMustSupplyPrivateKeyAlias() { + KeyStoreCertificateSource keyStoreCertificateSource = new KeyStoreCertificateSource(mockKeyStore, null, "password") + keyStoreCertificateSource.load() + } + +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/KeyStoreFileCertificateSourceTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/KeyStoreFileCertificateSourceTest.groovy new file mode 100644 index 000000000..9b6bcc39b --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/KeyStoreFileCertificateSourceTest.groovy @@ -0,0 +1,64 @@ +package net.lightbody.bmp.mitm + +import net.lightbody.bmp.mitm.test.util.CertificateTestUtil +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +public class KeyStoreFileCertificateSourceTest { + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder() + + File pkcs12File + File jksFile + + @Before + void stageFiles() { + pkcs12File = tmpDir.newFile("keystore.p12") + jksFile = tmpDir.newFile("keystore.jks") + + Files.copy(KeyStoreFileCertificateSourceTest.getResourceAsStream("/net/lightbody/bmp/mitm/keystore.p12"), pkcs12File.toPath(), StandardCopyOption.REPLACE_EXISTING) + Files.copy(KeyStoreFileCertificateSourceTest.getResourceAsStream("/net/lightbody/bmp/mitm/keystore.jks"), jksFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + } + + @Test + void testPkcs12FileOnClasspath() { + KeyStoreFileCertificateSource keyStoreFileCertificateSource = new KeyStoreFileCertificateSource("PKCS12", "/net/lightbody/bmp/mitm/keystore.p12", "privateKey", "password") + + CertificateAndKey certificateAndKey = keyStoreFileCertificateSource.load(); + + CertificateTestUtil.verifyTestRSACertWithCNandO(certificateAndKey) + } + + @Test + void testPkcs12FileOnDisk() { + KeyStoreFileCertificateSource keyStoreFileCertificateSource = new KeyStoreFileCertificateSource("PKCS12", pkcs12File, "privateKey", "password") + + CertificateAndKey certificateAndKey = keyStoreFileCertificateSource.load(); + + CertificateTestUtil.verifyTestRSACertWithCNandO(certificateAndKey) + } + + @Test + void testJksFileOnClasspath() { + KeyStoreFileCertificateSource keyStoreFileCertificateSource = new KeyStoreFileCertificateSource("JKS", "/net/lightbody/bmp/mitm/keystore.jks", "privateKey", "password") + + CertificateAndKey certificateAndKey = keyStoreFileCertificateSource.load(); + + CertificateTestUtil.verifyTestRSACertWithCNandO(certificateAndKey) + } + + @Test + void testJksFileOnDisk() { + KeyStoreFileCertificateSource keyStoreFileCertificateSource = new KeyStoreFileCertificateSource("JKS", jksFile, "privateKey", "password") + + CertificateAndKey certificateAndKey = keyStoreFileCertificateSource.load(); + + CertificateTestUtil.verifyTestRSACertWithCNandO(certificateAndKey) + } +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/PemFileCertificateSourceTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/PemFileCertificateSourceTest.groovy new file mode 100644 index 000000000..bdd6c96ae --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/PemFileCertificateSourceTest.groovy @@ -0,0 +1,87 @@ +package net.lightbody.bmp.mitm + +import net.lightbody.bmp.mitm.exception.ImportException +import net.lightbody.bmp.mitm.test.util.CertificateTestUtil +import net.lightbody.bmp.mitm.util.EncryptionUtil +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +import static org.junit.Assert.assertNotNull +import static org.junit.Assume.assumeTrue + +class PemFileCertificateSourceTest { + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder() + + File certificateFile + File encryptedPrivateKeyFile + File unencryptedPrivateKeyFile + + @Before + void stageFiles() { + certificateFile = tmpDir.newFile("certificate.crt") + encryptedPrivateKeyFile = tmpDir.newFile("encrypted-private-key.key") + unencryptedPrivateKeyFile = tmpDir.newFile("unencrypted-private-key.key") + + Files.copy(KeyStoreFileCertificateSourceTest.getResourceAsStream("/net/lightbody/bmp/mitm/certificate.crt"), certificateFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + Files.copy(KeyStoreFileCertificateSourceTest.getResourceAsStream("/net/lightbody/bmp/mitm/encrypted-private-key.key"), encryptedPrivateKeyFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + Files.copy(KeyStoreFileCertificateSourceTest.getResourceAsStream("/net/lightbody/bmp/mitm/unencrypted-private-key.key"), unencryptedPrivateKeyFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + } + + @Test + void testCanLoadCertificateAndPasswordProtectedKey() { + assumeTrue("Skipping test because unlimited strength cryptography is not available", EncryptionUtil.isUnlimitedStrengthAllowed()) + + PemFileCertificateSource pemFileCertificateSource = new PemFileCertificateSource(certificateFile, encryptedPrivateKeyFile, "password") + + CertificateAndKey certificateAndKey = pemFileCertificateSource.load() + assertNotNull(certificateAndKey) + + CertificateTestUtil.verifyTestRSACertWithCNandO(certificateAndKey) + } + + @Test + void testCanLoadCertificateAndUnencryptedKey() { + PemFileCertificateSource pemFileCertificateSource = new PemFileCertificateSource(certificateFile, unencryptedPrivateKeyFile, null) + + CertificateAndKey certificateAndKey = pemFileCertificateSource.load() + assertNotNull(certificateAndKey) + + CertificateTestUtil.verifyTestRSACertWithCNandO(certificateAndKey) + } + + @Test(expected = ImportException) + void testCannotLoadEncryptedKeyWithoutPassword() { + PemFileCertificateSource pemFileCertificateSource = new PemFileCertificateSource(certificateFile, encryptedPrivateKeyFile, "wrongpassword") + + pemFileCertificateSource.load() + } + + @Test(expected = ImportException) + void testIncorrectCertificateFile() { + PemFileCertificateSource pemFileCertificateSource = new PemFileCertificateSource(new File("does-not-exist.crt"), encryptedPrivateKeyFile, "password") + + pemFileCertificateSource.load() + } + + @Test(expected = IllegalArgumentException) + void testNullCertificateFile() { + PemFileCertificateSource pemFileCertificateSource = new PemFileCertificateSource(null, encryptedPrivateKeyFile, "password") + + pemFileCertificateSource.load() + } + + @Test(expected = IllegalArgumentException) + void testNullPrivateKeyFile() { + PemFileCertificateSource pemFileCertificateSource = new PemFileCertificateSource(certificateFile, null, "password") + + pemFileCertificateSource.load() + } +} + diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/RootCertificateGeneratorTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/RootCertificateGeneratorTest.groovy new file mode 100644 index 000000000..f406e0344 --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/RootCertificateGeneratorTest.groovy @@ -0,0 +1,88 @@ +package net.lightbody.bmp.mitm + +import net.lightbody.bmp.mitm.test.util.CertificateTestUtil +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +import static org.hamcrest.Matchers.greaterThan +import static org.hamcrest.Matchers.isEmptyOrNullString +import static org.hamcrest.Matchers.not +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertNotNull +import static org.junit.Assert.assertThat + +class RootCertificateGeneratorTest { + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder() + + CertificateInfo certificateInfo = new CertificateInfo() + .commonName("littleproxy-test") + .notAfter(new Date()) + .notBefore(new Date()) + + @Test + void testGenerateRootCertificate() { + RootCertificateGenerator generator = RootCertificateGenerator.builder() + .certificateInfo(certificateInfo) + .keyGenerator(new RSAKeyGenerator()) + .messageDigest("SHA256") + .build() + + CertificateAndKey certificateAndKey = generator.load() + + CertificateTestUtil.verifyTestRSACertWithCN(certificateAndKey) + + CertificateAndKey secondLoad = generator.load() + + assertEquals("Expected RootCertificateGenerator to return the same instance between calls to .load()", certificateAndKey, secondLoad) + } + + @Test + void testCanUseDefaultValues() { + RootCertificateGenerator generator = RootCertificateGenerator.builder().build() + + CertificateAndKey certificateAndKey = generator.load() + + assertNotNull(certificateAndKey) + } + + @Test + void testCanSaveAsPKCS12File() { + RootCertificateGenerator generator = RootCertificateGenerator.builder().build() + + File file = tmpDir.newFile() + + generator.saveRootCertificateAndKey("PKCS12", file, "privateKey", "password") + + // trivial verification that something was written to the file + assertThat("Expected file to be >0 bytes after writing certificate and private key", file.length(), greaterThan(0L)) + } + + @Test + void testCanSaveAsJKSFile() { + RootCertificateGenerator generator = RootCertificateGenerator.builder().build() + + File file = tmpDir.newFile() + + generator.saveRootCertificateAndKey("JKS", file, "privateKey", "password") + + // trivial verification that something was written to the file + assertThat("Expected file to be >0 bytes after writing certificate and private key", file.length(), greaterThan(0L)) + } + + @Test + void testCanEncodeAsPem() { + RootCertificateGenerator generator = RootCertificateGenerator.builder().build() + + String pemEncodedPrivateKey = generator.encodePrivateKeyAsPem("password") + + // trivial verification that something was written to the string + assertThat("Expected string containing PEM-encoded private key to contain characters", pemEncodedPrivateKey, not(isEmptyOrNullString())) + + String pemEncodedCertificate = generator.encodeRootCertificateAsPem() + assertThat("Expected string containing PEM-encoded certificate to contain characters", pemEncodedCertificate , not(isEmptyOrNullString())) + } + +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/TrustSourceTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/TrustSourceTest.groovy new file mode 100644 index 000000000..5abacc16a --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/TrustSourceTest.groovy @@ -0,0 +1,107 @@ +package net.lightbody.bmp.mitm + +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +import java.nio.file.Files +import java.nio.file.StandardCopyOption +import java.security.KeyStore +import java.security.cert.X509Certificate + +import static org.hamcrest.Matchers.both +import static org.hamcrest.Matchers.emptyArray +import static org.hamcrest.Matchers.not +import static org.hamcrest.Matchers.notNullValue +import static org.junit.Assert.assertEquals +import static org.junit.Assert.assertNotNull +import static org.junit.Assert.assertThat + +class TrustSourceTest { + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder() + + File certificateFile + + @Before + void stageFiles() { + certificateFile = tmpDir.newFile("certificate.crt") + + Files.copy(KeyStoreFileCertificateSourceTest.getResourceAsStream("/net/lightbody/bmp/mitm/certificate.crt"), certificateFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + } + + @Test + void testLoadJavaTrustSource() { + TrustSource trustSource = TrustSource.javaTrustSource() + X509Certificate[] trustedCAs = trustSource.getTrustedCAs() + + assertThat("Expected default Java trust source to contain some trusted CAs", trustedCAs, + both(notNullValue()).and(not(emptyArray()))) + } + + @Test + void testLoadBuiltinTrustSource() { + TrustSource trustSource = TrustSource.builtinTrustSource() + X509Certificate[] trustedCAs = trustSource.getTrustedCAs() + + assertThat("Expected default Java trust source to contain some trusted CAs", trustedCAs, + both(notNullValue()).and(not(emptyArray()))) + } + + @Test + void testCanAddCertificateToJavaTrustSource() { + TrustSource trustSource = TrustSource.javaTrustSource() + int trustedCACount = trustSource.getTrustedCAs().length + + TrustSource newTrustSource = trustSource.add("-----BEGIN CERTIFICATE-----\n" + + "MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\n" + + "GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\n" + + "b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\n" + + "BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\n" + + "VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\n" + + "DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\n" + + "THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\n" + + "Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\n" + + "c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\n" + + "gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" + + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\n" + + "AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\n" + + "Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\n" + + "j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\n" + + "hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\n" + + "X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n" + + "-----END CERTIFICATE-----") + + int newTrustedCACount = newTrustSource.getTrustedCAs().length + + assertEquals("Expected trust source with additional CA to be larger than original trust source", trustedCACount + 1, newTrustedCACount) + } + + @Test + void testCanAddTrustedCertificateInFile() { + TrustSource trustSource = TrustSource.javaTrustSource() + int trustedCACount = trustSource.getTrustedCAs().length + + TrustSource newTrustSource = trustSource.add(certificateFile) + int newTrustedCACount = newTrustSource.getTrustedCAs().length + + assertEquals("Expected trust source with additional CA to be larger than original trust source", trustedCACount + 1, newTrustedCACount) + } + + @Test + void testCanAddTrustedCertificateInKeyStore() { + InputStream keystoreStream = TrustSource.class.getResourceAsStream("/net/lightbody/bmp/mitm/trusted-cert.jks") + assertNotNull("Unable to load keystore", keystoreStream) + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) + keyStore.load(keystoreStream, null) + + TrustSource trustSource = TrustSource.javaTrustSource() + int trustedCACount = trustSource.getTrustedCAs().length + + TrustSource newTrustSource = trustSource.add(keyStore) + int newTrustedCACount = newTrustSource.getTrustedCAs().length + + assertEquals("Expected trust source with additional CA to be larger than original trust source", trustedCACount + 1, newTrustedCACount) + } +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/tools/ECKeyGeneratorTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/tools/ECKeyGeneratorTest.groovy new file mode 100644 index 000000000..aa10e9849 --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/tools/ECKeyGeneratorTest.groovy @@ -0,0 +1,27 @@ +package net.lightbody.bmp.mitm.tools + +import net.lightbody.bmp.mitm.keys.ECKeyGenerator +import org.junit.Test + +import java.security.KeyPair + +import static org.junit.Assert.assertNotNull + +class ECKeyGeneratorTest { + @Test + void testGenerateWithDefaults() { + ECKeyGenerator keyGenerator = new ECKeyGenerator() + KeyPair keyPair = keyGenerator.generate() + + assertNotNull(keyPair) + } + + @Test + void testGenerateWithExplicitNamedCurve() { + ECKeyGenerator keyGenerator = new ECKeyGenerator("secp384r1") + KeyPair keyPair = keyGenerator.generate() + + assertNotNull(keyPair) + // not much else to verify, other than successful generation + } +} diff --git a/mitm/src/test/groovy/net/lightbody/bmp/mitm/tools/RSAKeyGeneratorTest.groovy b/mitm/src/test/groovy/net/lightbody/bmp/mitm/tools/RSAKeyGeneratorTest.groovy new file mode 100644 index 000000000..a2bf8ed7b --- /dev/null +++ b/mitm/src/test/groovy/net/lightbody/bmp/mitm/tools/RSAKeyGeneratorTest.groovy @@ -0,0 +1,27 @@ +package net.lightbody.bmp.mitm.tools + +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator +import org.junit.Test + +import java.security.KeyPair + +import static org.junit.Assert.assertNotNull + +class RSAKeyGeneratorTest { + @Test + void testGenerateWithDefaults() { + RSAKeyGenerator keyGenerator = new RSAKeyGenerator() + KeyPair keyPair = keyGenerator.generate() + + assertNotNull(keyPair) + } + + @Test + void testGenerateWithExplicitKeySize() { + RSAKeyGenerator keyGenerator = new RSAKeyGenerator(1024) + KeyPair keyPair = keyGenerator.generate() + + assertNotNull(keyPair) + // not much else to verify, other than successful generation + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/ImpersonationPerformanceTests.java b/mitm/src/test/java/net/lightbody/bmp/mitm/ImpersonationPerformanceTests.java new file mode 100644 index 000000000..14cbe3d80 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/ImpersonationPerformanceTests.java @@ -0,0 +1,155 @@ +package net.lightbody.bmp.mitm; + +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpVersion; +import net.lightbody.bmp.mitm.keys.ECKeyGenerator; +import net.lightbody.bmp.mitm.keys.KeyGenerator; +import net.lightbody.bmp.mitm.keys.RSAKeyGenerator; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import net.lightbody.bmp.mitm.tools.BouncyCastleSecurityProviderTool; +import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; +import net.lightbody.bmp.mitm.util.MitmConstants; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLSession; +import java.security.KeyPair; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +// ignored as a quick work-around to running these tests with unit tests +@Ignore +@RunWith(Parameterized.class) +public class ImpersonationPerformanceTests { + private static final Logger log = LoggerFactory.getLogger(ImpersonationPerformanceTests.class); + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][]{ + {new RSAKeyGenerator(), "SHA384", new RSAKeyGenerator(), "SHA384"}, + {new RSAKeyGenerator(), "SHA384", new RSAKeyGenerator(1024), "SHA384"}, + {new RSAKeyGenerator(1024), "SHA384", new RSAKeyGenerator(1024), "SHA384"}, + {new RSAKeyGenerator(), "SHA384", new ECKeyGenerator(), "SHA384"}, + {new ECKeyGenerator(), "SHA384", new ECKeyGenerator(), "SHA384"}, + {new ECKeyGenerator(), "SHA384", new RSAKeyGenerator(), "SHA384"} + }); + } + + @Parameter + public KeyGenerator rootCertKeyGen; + + @Parameter(1) + public String rootCertDigest; + + @Parameter(2) + public KeyGenerator serverCertKeyGen; + + @Parameter(3) + public String serverCertDigest; + + private static final int WARM_UP_ITERATIONS = 5; + + private static final int ITERATIONS = 50; + + @Test + public void testImpersonatingMitmManagerPerformance() { + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(RootCertificateGenerator.builder() + .keyGenerator(rootCertKeyGen) + .messageDigest(rootCertDigest) + .build()) + .serverKeyGenerator(serverCertKeyGen) + .serverMessageDigest(serverCertDigest) + .build(); + + final AtomicInteger iteration = new AtomicInteger(); + + SSLSession mockSession = Mockito.mock(SSLSession.class); + + log.info("Test parameters:\n\tRoot Cert Key Gen: {}\n\tRoot Cert Digest: {}\n\tServer Cert Key Gen: {}\n\tServer Cert Digest: {}", + rootCertKeyGen, rootCertDigest, serverCertKeyGen, serverCertDigest); + + // warm up, init root cert, etc. + log.info("Executing {} warm up iterations", WARM_UP_ITERATIONS); + for (iteration.set(0); iteration.get() < WARM_UP_ITERATIONS; iteration.incrementAndGet()) { + HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "https://warmup-" + iteration.get() + ".com"); + mitmManager.clientSslEngineFor(request, mockSession); + } + + log.info("Executing {} performance test iterations", ITERATIONS); + + long start = System.currentTimeMillis(); + + for (iteration.set(0); iteration.get() < ITERATIONS; iteration.incrementAndGet()) { + HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "https://" + iteration.get() + ".com"); + mitmManager.clientSslEngineFor(request, mockSession); + } + + long finish = System.currentTimeMillis(); + + log.info("Finished performance test:\n\tRoot Cert Key Gen: {}\n\tRoot Cert Digest: {}\n\tServer Cert Key Gen: {}\n\tServer Cert Digest: {}", + rootCertKeyGen, rootCertDigest, serverCertKeyGen, serverCertDigest); + log.info("Generated {} certificates in {}ms. Average time per certificate: {}ms", iteration.get(), finish - start, (finish - start) / iteration.get()); + } + + @Test + public void testServerCertificateCreationAndAssembly() { + CertificateAndKey rootCert = RootCertificateGenerator.builder() + .keyGenerator(rootCertKeyGen) + .messageDigest(rootCertDigest) + .build() + .load(); + + log.info("Test parameters:\n\tRoot Cert Key Gen: {}\n\tRoot Cert Digest: {}\n\tServer Cert Key Gen: {}\n\tServer Cert Digest: {}", + rootCertKeyGen, rootCertDigest, serverCertKeyGen, serverCertDigest); + + log.info("Executing {} warm up iterations", WARM_UP_ITERATIONS); + for (int i = 0; i < WARM_UP_ITERATIONS; i++) { + KeyPair serverCertKeyPair = serverCertKeyGen.generate(); + CertificateAndKey serverCert = new BouncyCastleSecurityProviderTool().createServerCertificate( + createCertificateInfo("warnmup-" + i + ".com"), + rootCert.getCertificate(), + rootCert.getPrivateKey(), + serverCertKeyPair, + serverCertDigest); + + new DefaultSecurityProviderTool().createServerKeyStore(MitmConstants.DEFAULT_KEYSTORE_TYPE, serverCert, rootCert.getCertificate(), "alias", "password"); + } + + log.info("Executing {} performance test iterations", ITERATIONS); + + long start = System.currentTimeMillis(); + + for (int i = 0; i < ITERATIONS; i++) { + KeyPair serverCertKeyPair = serverCertKeyGen.generate(); + CertificateAndKey serverCert = new BouncyCastleSecurityProviderTool().createServerCertificate( + createCertificateInfo(i + ".com"), + rootCert.getCertificate(), + rootCert.getPrivateKey(), + serverCertKeyPair, + serverCertDigest); + + new DefaultSecurityProviderTool().createServerKeyStore(MitmConstants.DEFAULT_KEYSTORE_TYPE, serverCert, rootCert.getCertificate(), "alias", "password"); + } + + long finish = System.currentTimeMillis(); + + log.info("Finished performance test:\n\tRoot Cert Key Gen: {}\n\tRoot Cert Digest: {}\n\tServer Cert Key Gen: {}\n\tServer Cert Digest: {}", + rootCertKeyGen, rootCertDigest, serverCertKeyGen, serverCertDigest); + log.info("Assembled {} Key Stores in {}ms. Average time per Key Store: {}ms", ITERATIONS, finish - start, (finish - start) / ITERATIONS); + } + + private static CertificateInfo createCertificateInfo(String hostname) { + return new CertificateInfo().commonName(hostname).notBefore(new Date()).notAfter(new Date()); + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/example/CustomCAKeyStoreExample.java b/mitm/src/test/java/net/lightbody/bmp/mitm/example/CustomCAKeyStoreExample.java new file mode 100644 index 000000000..2ceca87ca --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/example/CustomCAKeyStoreExample.java @@ -0,0 +1,39 @@ +package net.lightbody.bmp.mitm.example; + +import net.lightbody.bmp.mitm.KeyStoreFileCertificateSource; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +import java.io.File; + +/** + * This example creates an ImpersonatingMitmManager which loads the CA Root Certificate and Private Key + * from a custom KeyStore. + */ +public class CustomCAKeyStoreExample { + public static void main(String[] args) { + // load the root certificate and private key from an existing KeyStore + KeyStoreFileCertificateSource fileCertificateSource = new KeyStoreFileCertificateSource( + "PKCS12", // KeyStore type. for .jks files (Java KeyStore), use "JKS" + new File("/path/to/my/keystore.p12"), + "keyAlias", // alias of the private key in the KeyStore; if you did not specify an alias when creating it, use "1" + "keystorePassword"); + + + // tell the MitmManager to use the custom certificate and private key + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(fileCertificateSource) + .build(); + + // tell the HttpProxyServerBootstrap to use the new MitmManager + HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager) + .start(); + + // make your requests to the proxy server + //... + + proxyServer.abort(); + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/example/CustomCAPemFileExample.java b/mitm/src/test/java/net/lightbody/bmp/mitm/example/CustomCAPemFileExample.java new file mode 100644 index 000000000..3ad488fa6 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/example/CustomCAPemFileExample.java @@ -0,0 +1,38 @@ +package net.lightbody.bmp.mitm.example; + +import net.lightbody.bmp.mitm.PemFileCertificateSource; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +import java.io.File; + +/** + * This example creates an ImpersonatingMitmManager which loads the CA Root Certificate and Private Key + * from a PEM-encoded certificate and a PEM-encoded private key file. + */ +public class CustomCAPemFileExample { + public static void main(String[] args) { + // load the root certificate and private key from existing PEM-encoded certificate and private key files + PemFileCertificateSource fileCertificateSource = new PemFileCertificateSource( + new File("/path/to/my/certificate.cer"), // the PEM-encoded certificate file + new File("/path/to/my/private-key.pem"), // the PEM-encoded private key file + "privateKeyPassword"); // the password for the private key -- can be null if the private key is not encrypted + + + // tell the MitmManager to use the custom certificate and private key + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(fileCertificateSource) + .build(); + + // tell the HttpProxyServerBootstrap to use the new MitmManager + HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager) + .start(); + + // make your requests to the proxy server + //... + + proxyServer.abort(); + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/example/EllipticCurveCAandServerExample.java b/mitm/src/test/java/net/lightbody/bmp/mitm/example/EllipticCurveCAandServerExample.java new file mode 100644 index 000000000..f3fff3af5 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/example/EllipticCurveCAandServerExample.java @@ -0,0 +1,47 @@ +package net.lightbody.bmp.mitm.example; + +import net.lightbody.bmp.mitm.RootCertificateGenerator; +import net.lightbody.bmp.mitm.keys.ECKeyGenerator; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +import java.io.File; + +/** + * This example creates a dynamically-generated Elliptic Curve CA Root Certificate and Private Key and saves them to + * PEM files for import into a browser and later reuse. It also uses Elliptic Curve keys when impersonating server + * certificates. + */ +public class EllipticCurveCAandServerExample { + public static void main(String[] args) { + // create a dyamic CA root certificate generator using Elliptic Curve keys + RootCertificateGenerator ecRootCertificateGenerator = RootCertificateGenerator.builder() + .keyGenerator(new ECKeyGenerator()) // use EC keys, instead of the default RSA + .build(); + + // save the dynamically-generated CA root certificate for installation in a browser + ecRootCertificateGenerator.saveRootCertificateAsPemFile(new File("/tmp/my-dynamic-ca.cer")); + + // save the dynamically-generated CA private key for use in future LittleProxy executions + // (see CustomCAPemFileExample.java for an example loading a previously-generated CA cert + key from a PEM file) + ecRootCertificateGenerator.savePrivateKeyAsPemFile(new File("/tmp/my-ec-private-key.pem"), "secretPassword"); + + // tell the MitmManager to use the root certificate we just generated, and to use EC keys when + // creating impersonated server certs + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(ecRootCertificateGenerator) + .serverKeyGenerator(new ECKeyGenerator()) + .build(); + + // tell the HttpProxyServerBootstrap to use the new MitmManager + HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager) + .start(); + + // make your requests to the proxy server + //... + + proxyServer.abort(); + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/example/LittleProxyDefaultConfigExample.java b/mitm/src/test/java/net/lightbody/bmp/mitm/example/LittleProxyDefaultConfigExample.java new file mode 100644 index 000000000..31cc9f618 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/example/LittleProxyDefaultConfigExample.java @@ -0,0 +1,30 @@ +package net.lightbody.bmp.mitm.example; + +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +/** + * This example creates an ImpersonatingMitmManager with all-default settings: + * - Dynamically-generated CA Root Certificate and Private Key (2048-bit RSA. SHA512 signature) + * - Server certificate impersonation by domain name (2048-bit RSA, SHA512 signature) + * - Default Java trust store (upstream servers' certificates validated against Java's trusted CAs) + */ +public class LittleProxyDefaultConfigExample { + public static void main(String[] args) { + // initialize an MitmManager with default settings + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder().build(); + + // to save the generated CA certificate for installation in a browser, see SaveGeneratedCAExample.java + + // tell the HttpProxyServerBootstrap to use the new MitmManager + HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager) + .start(); + + // make your requests to the proxy server + //... + + proxyServer.abort(); + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/example/SaveGeneratedCAExample.java b/mitm/src/test/java/net/lightbody/bmp/mitm/example/SaveGeneratedCAExample.java new file mode 100644 index 000000000..1cecef147 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/example/SaveGeneratedCAExample.java @@ -0,0 +1,37 @@ +package net.lightbody.bmp.mitm.example; + +import net.lightbody.bmp.mitm.RootCertificateGenerator; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +import java.io.File; + +/** + * This example creates an ImpersonatingMitmManager with all-default settings and saves the dynamically generated + * CA Root Certificate as a PEM file for installation in a browser. + */ +public class SaveGeneratedCAExample { + public static void main(String[] args) { + // create a dynamic CA root certificate generator using default settings (2048-bit RSA keys) + RootCertificateGenerator rootCertificateGenerator = RootCertificateGenerator.builder().build(); + + // save the dynamically-generated CA root certificate for installation in a browser + rootCertificateGenerator.saveRootCertificateAsPemFile(new File("/tmp/my-dynamic-ca.cer")); + + // tell the MitmManager to use the root certificate we just generated + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder() + .rootCertificateSource(rootCertificateGenerator) + .build(); + + // tell the HttpProxyServerBootstrap to use the new MitmManager + HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() + .withManInTheMiddle(mitmManager) + .start(); + + // make your requests to the proxy server + //... + + proxyServer.abort(); + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java b/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java new file mode 100644 index 000000000..19e30ec57 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/integration/LittleProxyIntegrationTest.java @@ -0,0 +1,130 @@ +package net.lightbody.bmp.mitm.integration; + +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager; +import org.apache.http.HttpHost; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.Test; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersAdapter; +import org.littleshoot.proxy.HttpFiltersSource; +import org.littleshoot.proxy.HttpFiltersSourceAdapter; +import org.littleshoot.proxy.HttpProxyServer; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests out-of-the-box integration with LittleProxy. + */ +public class LittleProxyIntegrationTest { + @Test + public void testLittleProxyMitm() throws IOException, InterruptedException { + final AtomicBoolean interceptedGetRequest = new AtomicBoolean(); + final AtomicBoolean interceptedGetResponse = new AtomicBoolean(); + + HttpFiltersSource filtersSource = new HttpFiltersSourceAdapter() { + @Override + public HttpFilters filterRequest(HttpRequest originalRequest) { + return new HttpFiltersAdapter(originalRequest) { + @Override + public HttpResponse proxyToServerRequest(HttpObject httpObject) { + if (httpObject instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) httpObject; + if (httpRequest.getMethod().equals(HttpMethod.GET)) { + interceptedGetRequest.set(true); + } + } + + return super.proxyToServerRequest(httpObject); + } + + @Override + public HttpObject serverToProxyResponse(HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + HttpResponse httpResponse = (HttpResponse) httpObject; + if (httpResponse.getStatus().code() == 200) { + interceptedGetResponse.set(true); + } + } + return super.serverToProxyResponse(httpObject); + } + }; + } + }; + + ImpersonatingMitmManager mitmManager = ImpersonatingMitmManager.builder().build(); + + HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap() + .withPort(0) + .withManInTheMiddle(mitmManager) + .withFiltersSource(filtersSource) + .start(); + + try (CloseableHttpClient httpClient = getNewHttpClient(proxyServer.getListenAddress().getPort())) { + try (CloseableHttpResponse response = httpClient.execute(new HttpGet("https://www.google.com"))) { + assertEquals("Expected to receive an HTTP 200 from http://www.google.com", 200, response.getStatusLine().getStatusCode()); + + EntityUtils.consume(response.getEntity()); + } + } + + Thread.sleep(500); + + assertTrue("Expected HttpFilters to successfully intercept the HTTP GET request", interceptedGetRequest.get()); + assertTrue("Expected HttpFilters to successfully intercept the server's response to the HTTP GET", interceptedGetResponse.get()); + + proxyServer.abort(); + } + + /** + * Creates an HTTP client that trusts all upstream servers and uses a localhost proxy on the specified port. + */ + private static CloseableHttpClient getNewHttpClient(int proxyPort) { + try { + // Trust all certs -- under no circumstances should this ever be used outside of testing + SSLContext sslcontext = SSLContexts.custom() + .useTLS() + .loadTrustMaterial(null, new TrustStrategy() { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return true; + } + }) + .build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslcontext, + SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + + CloseableHttpClient httpclient = HttpClients.custom() + .setSSLSocketFactory(sslsf) + .setProxy(new HttpHost("127.0.0.1", proxyPort)) + // disable decompressing content, since some tests want uncompressed content for testing purposes + .disableContentCompression() + .disableAutomaticRetries() + .build(); + + return httpclient; + } catch (Exception e) { + throw new RuntimeException("Unable to create new HTTP client", e); + } + } +} diff --git a/mitm/src/test/java/net/lightbody/bmp/mitm/test/util/CertificateTestUtil.java b/mitm/src/test/java/net/lightbody/bmp/mitm/test/util/CertificateTestUtil.java new file mode 100644 index 000000000..8b92a1dc1 --- /dev/null +++ b/mitm/src/test/java/net/lightbody/bmp/mitm/test/util/CertificateTestUtil.java @@ -0,0 +1,44 @@ +package net.lightbody.bmp.mitm.test.util; + +import net.lightbody.bmp.mitm.CertificateAndKey; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Utility methods for X.509 certificate verification in unit tests. + */ +public class CertificateTestUtil { + /** + * Asserts that the specified {@link CertificateAndKey} contains an RSA private key and an X.509 certificate + * with CN="littleproxy-test" and O="LittleProxy test". + */ + public static void verifyTestRSACertWithCNandO(CertificateAndKey certificateAndKey) { + X509Certificate certificate = certificateAndKey.getCertificate(); + assertNotNull(certificate); + assertNotNull(certificate.getIssuerDN()); + assertEquals("CN=littleproxy-test, O=LittleProxy test", certificate.getIssuerDN().getName()); + + PrivateKey privateKey = certificateAndKey.getPrivateKey(); + assertNotNull(privateKey); + assertEquals("RSA", privateKey.getAlgorithm()); + } + + /** + * Asserts that the specified {@link CertificateAndKey} contains an RSA private key and an X.509 certificate + * with CN="littleproxy-test". + */ + public static void verifyTestRSACertWithCN(CertificateAndKey certificateAndKey) { + X509Certificate certificate = certificateAndKey.getCertificate(); + assertNotNull(certificate); + assertNotNull(certificate.getIssuerDN()); + assertEquals("CN=littleproxy-test", certificate.getIssuerDN().getName()); + + PrivateKey privateKey = certificateAndKey.getPrivateKey(); + assertNotNull(privateKey); + assertEquals("RSA", privateKey.getAlgorithm()); + } +} diff --git a/mitm/src/test/resources/log4j2-test.json b/mitm/src/test/resources/log4j2-test.json new file mode 100644 index 000000000..f3e5e72ec --- /dev/null +++ b/mitm/src/test/resources/log4j2-test.json @@ -0,0 +1,23 @@ +{ + "configuration" : { + "name": "test", + "appenders": { + "Console": { + "name": "console", + "target": "SYSTEM_OUT", + "PatternLayout": { + "pattern": "%-7r %date %level [%thread] %logger - %msg%n" + } + } + }, + + "loggers": { + "root": { + "level": "info", + "appender-ref": { + "ref": "console" + } + } + } + } +} \ No newline at end of file diff --git a/mitm/src/test/resources/net/lightbody/bmp/mitm/certificate.crt b/mitm/src/test/resources/net/lightbody/bmp/mitm/certificate.crt new file mode 100644 index 000000000..f632cb32d --- /dev/null +++ b/mitm/src/test/resources/net/lightbody/bmp/mitm/certificate.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPzCCAiegAwIBAgIJALPOOC+0RqAQMA0GCSqGSIb3DQEBCwUAMDYxGTAXBgNV +BAoMEExpdHRsZVByb3h5IHRlc3QxGTAXBgNVBAMMEGxpdHRsZXByb3h5LXRlc3Qw +HhcNMTUxMjE4MDIxMDI4WhcNMjUxMjE1MDIxMDI4WjA2MRkwFwYDVQQKDBBMaXR0 +bGVQcm94eSB0ZXN0MRkwFwYDVQQDDBBsaXR0bGVwcm94eS10ZXN0MIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2vcm/qETdg92EjYka3Zt+UEraLKzTovz +gvVx4F0EmCC3KDU2v2+WeX4wmVjP6qtMr6nJOSYkFedAvIRGi7hbf3JKQ1nqhH1q +U2zP9HXNt+/LCw8kOFJFii9cp6/h8OnlF8hoIWz4lRTMMcjoiBzV5vfyTb2zWE0l +1DXGypKTxjqg8Pd/tqsMl2uc4+xnEL/ZK9cK8wxg0suUqaGQRaX2R3SovbFKZ1c3 +VApS8zHksSm6qQStbisuEEHSYLpFCrMnXsLJ9KfzTUDwBqrKaMyDAEjoS2LpoijF +YalTW3bF721GiYl2GtcwCIzqpHcIbHAPn1PxQY4UHUt3z4YSjTsuXwIDAQABo1Aw +TjAdBgNVHQ4EFgQUElRcE71sJsuWUVOyYALgQQhPHUUwHwYDVR0jBBgwFoAUElRc +E71sJsuWUVOyYALgQQhPHUUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AQEArU29g5/V5rywfQizo5J/XEbWjfN0zvuFithS7Zc2OqrSKV8c3NY6NdER/0jj +CmhohnUkduMW+/bizWcuiyt5KDwg6Ar2kEXYY0YgOLbxTmoNvBG84zO+54BV0qy7 +16D8ItrPbcIn83eW8TXug1dWISxWhVRIpHdH2Ok4/XDRFgTxORsC08v2OwwQ566g +PqphR6eIJN8MwThcT9D9rv0HcX9esBJJlNc9OigB9T3O2Xg7+qPJtgqktkN8vlTb +Co0BWoR43xVPTNam533ioUX7woUWnlsQtclN//vazr+uofJ8jnHjpAkh9gZ/l6kn +AdZVrExA+SSUqtWgk9cD0WGSEQ== +-----END CERTIFICATE----- diff --git a/mitm/src/test/resources/net/lightbody/bmp/mitm/encrypted-private-key.key b/mitm/src/test/resources/net/lightbody/bmp/mitm/encrypted-private-key.key new file mode 100644 index 000000000..12c4afc1b --- /dev/null +++ b/mitm/src/test/resources/net/lightbody/bmp/mitm/encrypted-private-key.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,2D5055AA227DC3421AEBE5C8480F57B1 + +VZnrQPjan9fUvkJVKwJrJLWqWx7qgw9M8eeqZoqjrueZLNFu74mp2qbKeJ4hkbqf +dBvMuBaSaqrS2SxfHf/T8uZg+K0vMB0hH+1pVt+AtJRYLybQLmjZSnU/8CDEp358 +59FX5rYFvMRD3fUkESWeQSlSDuE/YG5YlqmxnSVOKEmAQQ0QTGEDIDKl77gRrg7r +mr6ey8NgW2OBaJvLbXLAldHd2lBlj/NH7sXwWq1LgbF/CMm5LLtIEMkOSZ5jgFvU +UqMOTTSGrhtPa4vvy1m8XFx9Z33usi/mEvXQpVeLCmE4BQkS7rSMV1E/ETVkzsfF +wq3c6cWNmh/JvPYUIa6dXjDZPwCz/WpVH7fVwTqBkBTbTUuIJSP//jJB7npZoQrE +rfpdJ9eX9QAjbFI6goYnc4SVWhpzR9LDSk9mpEikGysN8yDYabKbl0OzT9KovGn0 +CLKYxo2LQBwGJR171y5yyC/3s39b9pPC6JQAzji4S7uJQjayYx8Yv3xb1f+4t2Xa +/3M46QtAUJMGAqdmpTXhvNzPyGzJFK1gwNoUf6oM/Vww8KRvVNJTfPljEARxTyRO +jzan4Dd5jtSqw87E2b76ECrj92C7qp3iC/4zkkCHTulPMjwvIzY9WQ2wx1qyVy9B +4qwat9h90AnrAX9q6kvDTglD3n605vgU6P/u1PCzTsTLT1DL5OyMfT3MVSS/MyNs +SKLx5vJh93aq7EyWurWVCzUfYDIFngb+WFINEGb2XYFD3Ah/y5D6kD24vooATgFJ +KQBQdCBWWjZbYS4nZUTOSJqSMp5V/RzjjKr9QpCKpXLvxwfQ9ME6MaobgfUSy1er +S+btHNItD/xA7/rO/eAfmlkFLJgGi6cfvnyBxHsR7+/T5yDyZAPbDwp8kErxFFeY +xqQmY6hvaSiQXek23AIiTOCuzyYluMsTnxRfQGlL/A77R+t7gJv5in0FPQqHzD1T +h5V4fIBcpOSmc6Qk7U7vYNi3AyvpWDsr03E+bNlY5dzNguxsO9QzJMWGd17VZiwc +Q2qkKxl5JC0c3h4mYh4e3V5C0c9z32ffYS6sICsNaAw0r8C2svULBCtm29oDDc5X +oC5EzfZb5vG9qpQ8H/LE2I215YOwsvmda9gdpnxrbk4y8MlSMOJwMXVYGKNSEg2h +wWPo/4qsNV8hX7IlOYPOnVRCzpjCWTc2CzvhHISxv1CrXKV0D2qAxkX/ezRYsaiE +T7E0K5kyXquVjhjnUTMjpOc/LM2bQ9v0lmmJIcnJFSlCTS5+DvUrngYSnqfyPgkS +7dbSuW8E1UusxnY8664snWugxCLB4E0PacKTr+0E77qukFAE02j8hwHbn+TplWaH +jOVSyLicsqwi0TTxWoWk/fTk6StSGVuUTaPPoz6gkrN3H01V8vhcx/PPqWWbfjjz +yGqOm6pXXaNwPYjgQUWFPc3QHOaPbTAl1Y4teCaAqnSDlEBYqDUGK0gSMAQ/vgdC +uvUAGctb2Kikp4sQcvzKRHsStHrHDVdeum842r5zyHKMuEbhTGjmDR70FknpDaoS +vQIfgasKqXjRKgQfSSiGTX5CsvYvSRqFSD4qDbQbYnR0XMNphJxK/3rWP86oEXk1 +-----END RSA PRIVATE KEY----- diff --git a/mitm/src/test/resources/net/lightbody/bmp/mitm/keystore.jks b/mitm/src/test/resources/net/lightbody/bmp/mitm/keystore.jks new file mode 100644 index 000000000..e886fe14c Binary files /dev/null and b/mitm/src/test/resources/net/lightbody/bmp/mitm/keystore.jks differ diff --git a/mitm/src/test/resources/net/lightbody/bmp/mitm/keystore.p12 b/mitm/src/test/resources/net/lightbody/bmp/mitm/keystore.p12 new file mode 100644 index 000000000..6667aa3f6 Binary files /dev/null and b/mitm/src/test/resources/net/lightbody/bmp/mitm/keystore.p12 differ diff --git a/mitm/src/test/resources/net/lightbody/bmp/mitm/trusted-cert.jks b/mitm/src/test/resources/net/lightbody/bmp/mitm/trusted-cert.jks new file mode 100644 index 000000000..e035a11dc Binary files /dev/null and b/mitm/src/test/resources/net/lightbody/bmp/mitm/trusted-cert.jks differ diff --git a/mitm/src/test/resources/net/lightbody/bmp/mitm/unencrypted-private-key.key b/mitm/src/test/resources/net/lightbody/bmp/mitm/unencrypted-private-key.key new file mode 100644 index 000000000..ff3fb4b16 --- /dev/null +++ b/mitm/src/test/resources/net/lightbody/bmp/mitm/unencrypted-private-key.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2vcm/qETdg92EjYka3Zt+UEraLKzTovzgvVx4F0EmCC3KDU2 +v2+WeX4wmVjP6qtMr6nJOSYkFedAvIRGi7hbf3JKQ1nqhH1qU2zP9HXNt+/LCw8k +OFJFii9cp6/h8OnlF8hoIWz4lRTMMcjoiBzV5vfyTb2zWE0l1DXGypKTxjqg8Pd/ +tqsMl2uc4+xnEL/ZK9cK8wxg0suUqaGQRaX2R3SovbFKZ1c3VApS8zHksSm6qQSt +bisuEEHSYLpFCrMnXsLJ9KfzTUDwBqrKaMyDAEjoS2LpoijFYalTW3bF721GiYl2 +GtcwCIzqpHcIbHAPn1PxQY4UHUt3z4YSjTsuXwIDAQABAoIBAB/Opyt12o3b0Rr0 +InY5zd/XR6b9zm4qhkUPwmsFGBXBKtn8YOeOHh2n5wdfj1RXbdxWnZRfpf5IiW7Z +CCZjsWbiA0elWBvG3BsiQ1MPicKeYrBIkspbqR5Zouv48Kk+ULkTs4ynd7SwQLk6 +pgyfo7LZcak5VUQOcOBSr33drPmuZcTyozuEs+XmOaUIH9SHr5dLP3mysb7dEZQS +gaHMR1a3BQQrQk9H5oVVU0kI0iqqX5NltiWodl3shUHqlXrlCEop+RxHhkfBgz8H +w4MlyFSiYrrzTyL7yTkMa6JTcP0s4oNH2E+foIMyf46z/lxH7Nk0bg4pmsfXb0o7 +bKqJzAECgYEA8kRvRpDB9z6ddQkJ1I++fGh8vJMUuB6MNlS7Shhp6ClHQwdUK80f +dpWT8CxIOX6sKJgm9HGbxaPxn7RnYc4v+M0L/QsPU8RGfVggNvQ9xQEaocp3CD9D +TUeZUtTcTmKd86h9ICjlkbtF2coKNcIfM3teVtGnMKjf5VowupYyzj8CgYEA52CU +6Su4ewgTcEPRqWWyg/GHTPN8TJaXjutgvhnt7w8po8xMmIK1qSeOP4paF/UOuGsT +rX6IoXmSzSz5MkFkjGLBd/wUd4p9YQbgjtv/lv52XBZ8mqffjDGTGl/n0r803yMH +So7Fup9f8kXkgw+vszxzCqnHq3usx1WjCKNj1+ECgYAT1wTh24L283rDldznOmpY +F9p3OvhMZ7wFywSXec5ag97hH12GRMMZ3AAEgCveAYCpxmQSSqd+FQH5mTWKLe+B +yZD8xQYZTw6Svz/MIE5ars92hnUfCMdDMeTdgq8UAEF9LcQpeQ/r0lFTF5ekdWRG +vAiqxXqSopHLX4p0DU7V0wKBgGi16c44Tg3H0tw8pPbPomFZ/gxSKM+UW1R/q1F8 +9JP6vbJ2M7fVd5bs4tBYsXskGRxWwRoEKJtDJK+cCc63j2SFEN9XAoAy+ZjefuPI +JjxUPoZgWtW24VFV4ifOfWB/zdKpzJPuVwelNsuy276Aa9hmo/2QZl9x4fh4BgdT +wkyhAoGBAKrzqA0+kotqocA9cT7GuJb7mfjewXZx1jztQMC5mHs+L/tRGL85dZZk +5hK8Rtessw5TKmfA53bKbOEklEcsjSNWgIq3wYjJVtBDUvcmA2Hgm3psjUCrl1iv +h/31bf05fYaWFFEeuHE/Pz5ev8ecY8gsKtX63HVVWLYtDxKnYuXk +-----END RSA PRIVATE KEY----- diff --git a/new-interface-compatibility.md b/new-interface-compatibility.md new file mode 100644 index 000000000..718df7b96 --- /dev/null +++ b/new-interface-compatibility.md @@ -0,0 +1,92 @@ +# New BrowserMobProxy interface +The `BrowserMobProxyServer` class, powered by LitleProxy, implements the ``BrowserMobProxy` interface. The following table lists the current level of support for the new interface in the modern and legacy BMP implementations: + +`BrowserMobProxy` method | Legacy `ProxyServer` (Jetty 5) | `BrowserMobProxyServer` (LittleProxy) +:----------------------- | :---------------------: | :-----------------------------------: +`start` (and related) | X | X +`stop` | X | X +`isStarted` | X | X +`abort` | X | X +`getClientBindAddress` | X | X +`getPort` | X | X +`getServerBindAddress` | [Will not support](#server-bind-address) | X +`getHar` | X | X +`newHar` | X | X +`setHarCaptureTypes` | [Partial support](#har-capture-types) | X +`getHarCaptureTypes` | X | X +`enableHarCaptureTypes` | X | X +`disableHarCaptureTypes` | X | X +`newPage` | X | X +`endHar` | TBD | X +`setReadBandwidthLimit` | X | X +`setWriteBandwidthLimit` | X | X +`setLatency` | X | X +`setConnectTimeout` | X | [Must be enabled before start()](#timeouts) +`setIdleConnectionTimeout` | X | [Must be enabled before start()](#timeouts) +`setRequestTimeout` | X | Planned +`autoAuthorization` | X | X +`stopAutoAuthorization` | [Will not support](#auto-authorization) | X +`rewriteUrl` | X | X +`rewriteUrls` | X | X +`removeRewriteRule` | X | X +`clearRewriteRules` | X | X +`blacklistRequests` | X | X +`setBlacklist` | X | X +`getBlacklist` | X | X +`clearBlacklist` | X | X +`whitelistRequests` | X | X +`addWhitelistPattern` | X | X +`enableEmptyWhitelist` | X | X +`disableWhitelist` | X | X +`getWhitelistUrls` | X | X +`getWhitelistStatusCode` | X | X +`isWhitelistEnabled` | X | X +`addHeaders` | X | X +`addHeader` | X | X +`removeHeader` | X | X +`removeAllHeaders` | X | X +`getAllHeaders` | X | X +`setHostNameResolver` | [Supported (see notes)](#dns-resolvers) | X +`getHostNameResolver` | [Supported (see notes)](#dns-resolvers) | X +`waitForQuiescence` | X | X +`setChainedProxy` | X | X +`getChainedProxy` | X | X +`addFirstHttpFilterFactory` | [Will not support](#interceptors) | X +`addLastHttpFilterFactory` | [Will not support](#interceptors) | X +`addResponseFilter` | [Will not support](#interceptors) | X +`addRequestFilter` | [Will not support](#interceptors) | X + +# Limitations +## Interceptors +Interceptors are tightly coupled to the underlying BrowserMob Proxy implementation (Jetty 5 or LittleProxy). As a result, +the Jetty 5-based `ProxyServer` implementation will continue to support the legacy interceptor methods, `addRequestInterceptor` +and `addResponseInterceptor`, but **will not support the new interceptor methods in `BrowserMobProxy`**. The new LittleProxy-based +implementation will fully support the new interceptor methods (`addResponseFilter`, `addRequestFilter`, `addFirstHttpFilterFactory` +and `addLastHttpFilterFactory`), and will not support the legacy interceptor methods. + +To continue using interceptors with the Jetty 5-based implementation, downcast to `LegacyProxyServer` when adding the interceptor: +```java + BrowserMobProxy legacyImpl = new ProxyServer(); + ((LegacyProxyServer)legacyImpl).addRequestInterceptor(new RequestInterceptor() { + @Override + public void process(BrowserMobHttpRequest request, Har har) { + // interceptor code goes here + } + }); +``` + +## DNS resolvers +Both the legacy and new LittleProxy-based implementations support the new get/setHostNameResolver methods. The legacy implementation uses XBill/dnsjava by default, with failover to native JVM name resolution enabled by default. The LittleProxy implementation uses native name resolution by default, but fully supports the DnsJavaResolver when calling the setHostNameResolver method. + +## Server bind address +The legacy implementation does not support server bind addresses. LittleProxy fully supports server bind addresses. + +## HAR capture types +The legacy implementation supports all HAR capture types, but does not support controlling request and response capture types separately +(e.g. enabling content capture only for requests). Additionally, the Jetty 5 implementation does not allow disabling cookie capture. + +## Timeouts +The new LittleProxy implementation requires that all timeouts be set before calling a `start()` method. + +## Auto authorization +The legacy implementation does not support the `stopAutoAuthorization` method. \ No newline at end of file diff --git a/pom.xml b/pom.xml index a63e2d6c6..2bd63686e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,18 +1,23 @@ 4.0.0 - biz.neustar + net.lightbody.bmp browsermob-proxy - 2.0-beta-8-SNAPSHOT - BrowserMob Proxy + 2.1.6-SNAPSHOT + + browsermob-core + browsermob-legacy + browsermob-rest + browsermob-dist + mitm + + BrowserMob Proxy Parent Project A programmatic HTTP/S designed for performance and functional testing - http://opensource.webmetrics.com/browsermob-proxy - jar + http://bmp.lightbody.net + pom - - org.sonatype.oss - oss-parent - 7 - + + 3.0.4 + @@ -27,8 +32,6 @@ Patrick Lightbody patrick@lightbody.net http://lightbody.net - Neustar - http://neustar.biz PST Administrator @@ -37,238 +40,504 @@ - scm:git:git@github.com:webmetrics/browsermob-proxy.git - scm:git:git@github.com:webmetrics/browsermob-proxy.git - git@github.com:webmetrics/browsermob-proxy.git + scm:git:git@github.com:lightbody/browsermob-proxy.git + scm:git:git@github.com:lightbody/browsermob-proxy.git + git@github.com:lightbody/browsermob-proxy.git + HEAD + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + UTF-8 UTF-8 - - - - release + 1.7.25 + 2.53.1 - - - - org.codehaus.mojo - appassembler-maven-plugin - 1.1.1 - - flat - lib - - - org.browsermob.proxy.Main - browsermob-proxy - - - - - - make-assembly - install - - assemble - - - - - - maven-assembly-plugin - 2.4 - - - src/main/assembly.xml - - - - - make-assembly - install - - single - - - - - - - - + 2.8.9 + + 3.0.2 + + 2.9.0 + + 2.4.12 + 2.4.3-01 + + 4.0.51.Final + + 4.1.15.Final + + 1.58 + install + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + groovy-eclipse-compiler + 1.7 + 1.7 + + + + org.codehaus.groovy + groovy-eclipse-compiler + 2.9.2-01 + + + org.codehaus.groovy + groovy-eclipse-batch + ${groovy-eclipse-batch.version} + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + package + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + -Xmx1g -XX:MaxPermSize=256m + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + package + + jar + + + ${javadoc.opts} + + + + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-site-plugin + 3.6 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.0 + + + - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - + org.apache.maven.plugins maven-source-plugin - 2.1.2 - - - attach-sources - package - - jar-no-fork - - - org.apache.maven.plugins maven-javadoc-plugin - 2.7 - - - attach-javadocs - package - - jar - - - - + + + + org.slf4j + slf4j-api + ${slf4j.version} + - - org.slf4j - slf4j-jdk14 - 1.5.3 - + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + - - com.google.sitebricks - sitebricks - 0.8.3 - + + com.google.guava + guava + + 23.0-android + - - com.google.inject.extensions - guice-multibindings - 3.0 - + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + - - org.codehaus.jackson - jackson-core-asl - 1.7.1 - + + junit + junit + 4.12 + - - org.codehaus.jackson - jackson-mapper-asl - 1.7.1 - + + org.hamcrest + hamcrest-library + 1.3 + - - org.apache.httpcomponents - httpclient - 4.2.3 - - - org.apache.httpcomponents - httpmime - 4.2.3 - + + org.mockito + mockito-core + 2.8.47 + - - org.apache.commons - commons-io - 1.3.2 - + + org.seleniumhq.selenium + selenium-api + ${selenium.version} + + + org.seleniumhq.selenium + selenium-java + ${selenium.version} + + + org.seleniumhq.selenium + selenium-firefox-driver + ${selenium.version} + + + org.codehaus.groovy + groovy-all + ${groovy.version} + - - net.sf.jopt-simple - jopt-simple - 3.2 - + + net.lightbody.bmp + littleproxy + 1.1.0-beta-bmp-17 + - - org.apache.ant - ant - 1.8.2 - + + org.mock-server + mockserver-netty + 3.10.4 + + + ch.qos.logback + logback-classic + + + - - org.bouncycastle - bcprov-jdk15on - 1.47 - + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + - - org.eclipse.jetty - jetty-server - 7.3.0.v20110203 - - - org.eclipse.jetty - jetty-servlet - 7.3.0.v20110203 - + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + - - com.google.inject - guice - 3.0 - + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + - - com.google.inject.extensions - guice-servlet - 3.0 - + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + - - net.jcip - jcip-annotations - 1.0 - + + org.apache.httpcomponents + httpclient + 4.5.3 + - - org.seleniumhq.selenium - selenium-api - 2.29.1 - + + org.apache.httpcomponents + httpmime + 4.5.3 + + + + com.jcraft + jzlib + 1.1.3 + + + + + io.netty + netty-all + ${netty.version} + + + io.netty + netty-buffer + ${netty.version} + + + io.netty + netty-codec + ${netty.version} + + + io.netty + netty-codec-haproxy + ${netty.version} + + + io.netty + netty-codec-http + ${netty.version} + + + io.netty + netty-codec-socks + ${netty.version} + + + io.netty + netty-common + ${netty.version} + + + io.netty + netty-handler + ${netty.version} + + + io.netty + netty-transport + ${netty.version} + + + io.netty + netty-transport-rxtx + ${netty.version} + + + io.netty + netty-transport-sctp + ${netty.version} + + + io.netty + netty-transport-udp + ${netty.version} + + + io.netty + netty-example + ${netty.version} + + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + + + + org.javassist + javassist + 3.21.0-GA + + + + + + - junit - junit - 4.9 + org.codehaus.groovy + groovy-all test - - - - - org.codehaus.mojo - findbugs-maven-plugin - 2.3.1 - - Max - - - - org.codehaus.mojo - cobertura-maven-plugin - 2.4 - - - + + + release + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + true + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + package + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + package + + jar + + + ${javadoc.opts} + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + true + false + release + deploy + + + + + + + + doclint-java8-disable + + [1.8,) + + + -Xdoclint:none + + + + netty-4.1 + + ${netty-4.1.version} + + + diff --git a/src/main/assembly.xml b/src/main/assembly.xml deleted file mode 100644 index 56ee5e142..000000000 --- a/src/main/assembly.xml +++ /dev/null @@ -1,42 +0,0 @@ - - bin - - zip - - - - ${project.basedir}/target/appassembler/bin - /bin - keep - 0744 - 0755 - - - ${project.basedir}/target/appassembler/lib - /lib - - - ${project.basedir}/src/main/resources/sslSupport - /ssl-support - - - ${project.basedir}/target - - browsermob-proxy-${project.version}-sources.jar - browsermob-proxy-${project.version}-javadoc.jar - - / - - - ${project.basedir} - - LICENSE.txt - README.txt - README.md - - / - - - diff --git a/src/main/java/cz/mallat/uasparser/BrowserEntry.java b/src/main/java/cz/mallat/uasparser/BrowserEntry.java deleted file mode 100644 index 3ab2d37a4..000000000 --- a/src/main/java/cz/mallat/uasparser/BrowserEntry.java +++ /dev/null @@ -1,117 +0,0 @@ -package cz.mallat.uasparser; - -import java.util.Iterator; -import java.util.List; - -/** - * JavaBean that holds the data from the [browser] section in the data file - * - * @author oli - * - */ -class BrowserEntry { - - private Long type; - private String family; - private String name; - private String url; - private String company; - private String companyUrl; - private String ico; - private String infoUrl; - - public BrowserEntry(List data) { - Iterator it = data.iterator(); - this.type = Long.parseLong(it.next()); - this.family = it.next(); - this.url = it.next(); - this.company = it.next(); - this.companyUrl = it.next(); - this.ico = it.next(); - this.infoUrl = it.next(); - // this.name stays empty, will be filled with family + version - } - - public String getFamily() { - return family; - } - - public void setFamily(String family) { - this.family = family; - } - - public Long getType() { - return type; - } - - public void setType(Long type) { - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getCompany() { - return company; - } - - public void setCompany(String company) { - this.company = company; - } - - public String getCompanyUrl() { - return companyUrl; - } - - public void setCompanyUrl(String companyUrl) { - this.companyUrl = companyUrl; - } - - public String getIco() { - return ico; - } - - public void setIco(String ico) { - this.ico = ico; - } - - public String getInfoUrl() { - return infoUrl; - } - - public void setInfoUrl(String infoUrl) { - this.infoUrl = infoUrl; - } - - public void copyTo(UserAgentInfo uai, String browserVersionInfo) { - if (Utils.validString(getFamily())) { - uai.setUaFamily(getFamily()); - uai.setUaName(uai.getUaFamily() - + (browserVersionInfo != null && !browserVersionInfo.isEmpty() ? " " + browserVersionInfo : "")); - } - if (Utils.validString(getUrl())) - uai.setUaUrl(getUrl()); - if (Utils.validString(getCompany())) - uai.setUaCompany(getCompany()); - if (Utils.validString(getCompanyUrl())) - uai.setUaCompanyUrl(getCompanyUrl()); - if (Utils.validString(getIco())) - uai.setUaIcon(getIco()); - if (Utils.validString(getInfoUrl())) - uai.setUaInfoUrl(UASparser.INFO_URL + getInfoUrl()); - } - -} \ No newline at end of file diff --git a/src/main/java/cz/mallat/uasparser/CachingOnlineUpdateUASparser.java b/src/main/java/cz/mallat/uasparser/CachingOnlineUpdateUASparser.java deleted file mode 100644 index 0473e077b..000000000 --- a/src/main/java/cz/mallat/uasparser/CachingOnlineUpdateUASparser.java +++ /dev/null @@ -1,136 +0,0 @@ -package cz.mallat.uasparser; - -import java.io.*; -import java.net.URL; -import java.util.Properties; - -/** - * Adds a cache to the OnlineUpdateUAParser - * - * @author oli - */ -public class CachingOnlineUpdateUASparser extends OnlineUpdateUASparser { - - private static final String CACHE_FILENAME = "userAgentString.txt"; - private static final String PROPERTIES_FILENAME = "userAgentString.properties"; - - private Properties prop; - private String cacheDir; - - /** - * The cache files are put into the java tmp directory - * - * @throws IOException - */ - public CachingOnlineUpdateUASparser() throws IOException { - this(null); - } - - /** - * The cache files are put into the cacheDir - * - * @param cacheDir - * @throws IOException - */ - public CachingOnlineUpdateUASparser(String cacheDir) throws IOException { - this.cacheDir = cacheDir; - this.prop = new Properties(); - - if (cacheDir != null && !(new File(cacheDir).canWrite())) { - throw new RuntimeException("Can't write to cacheDir: " + cacheDir); - } - - File propFile = getPropertiesFile(); - if (propFile.exists()) { - FileInputStream fis = new FileInputStream(getPropertiesFile()); - try { - prop.load(fis); - lastUpdateCheck = Long.parseLong(prop.getProperty("lastUpdateCheck")); - currentVersion = prop.getProperty("currentVersion"); - } finally { - fis.close(); - } - - try { - loadDataFromFile(getCacheFile()); - } catch (IOException e) { - e.printStackTrace(); - // reset the status variables, so we'll load the data file again - lastUpdateCheck = 0; - currentVersion = ""; - } - } - } - - /** - * This implementation uses a local properties file to keep the lastUpdate time and the local data file version - */ - @Override - protected synchronized void checkDataMaps() throws IOException { - if (lastUpdateCheck == 0 || lastUpdateCheck < System.currentTimeMillis() - updateInterval) { - String versionOnServer = getVersionFromServer(); - if (currentVersion == null || versionOnServer.compareTo(currentVersion) > 0) { - loadDataFromInternetAndSave(); - loadDataFromFile(getCacheFile()); - currentVersion = versionOnServer; - prop.setProperty("currentVersion", currentVersion); - } - lastUpdateCheck = System.currentTimeMillis(); - prop.setProperty("lastUpdateCheck", Long.toString(lastUpdateCheck)); - saveProperties(prop); - } - } - - private File getCacheFile() { - return new File(cacheDir == null ? System.getProperty("java.io.tmpdir") : cacheDir, CACHE_FILENAME); - } - - private File getPropertiesFile() { - return new File(cacheDir == null ? System.getProperty("java.io.tmpdir") : cacheDir, PROPERTIES_FILENAME); - } - - /** - * loads the data file from the server and saves it to the local file system - * - * @throws IOException - */ - private void loadDataFromInternetAndSave() throws IOException { - InputStream is = null; - FileOutputStream fos = null; - try { - URL url = new URL(DATA_RETRIVE_URL); - is = url.openStream(); - fos = new FileOutputStream(getCacheFile()); - byte[] buff = new byte[1024 * 8]; - int len = 0; - while ((len = is.read(buff)) != -1) { - fos.write(buff, 0, len); - } - } finally { - if (is != null) { - is.close(); - } - if (fos != null) { - fos.close(); - } - } - - } - - /** - * Saves the properties file to the local filesystem - * - * @param prop - * @throws FileNotFoundException - * @throws IOException - */ - private void saveProperties(Properties prop) throws FileNotFoundException, IOException { - FileOutputStream fos = new FileOutputStream(getPropertiesFile()); - try { - prop.store(fos, null); - } finally { - fos.close(); - } - } - -} diff --git a/src/main/java/cz/mallat/uasparser/OnlineUpdateUASparser.java b/src/main/java/cz/mallat/uasparser/OnlineUpdateUASparser.java deleted file mode 100644 index 58b1860f2..000000000 --- a/src/main/java/cz/mallat/uasparser/OnlineUpdateUASparser.java +++ /dev/null @@ -1,76 +0,0 @@ -package cz.mallat.uasparser; - -import cz.mallat.uasparser.fileparser.PHPFileParser; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -/** - * The parser will download the definition file from the internet - * - * @author oli - */ -public class OnlineUpdateUASparser extends UASparser { - - protected static final String DATA_RETRIVE_URL = "http://user-agent-string.info/rpc/get_data.php?key=free&format=ini"; - protected static final String VERSION_CHECK_URL = "http://user-agent-string.info/rpc/get_data.php?key=free&format=ini&ver=y"; - protected long updateInterval = 1000 * 60 * 60 * 24; // 1 day - - protected long lastUpdateCheck; - protected String currentVersion; - - public void setUpdateInterval(long updateInterval) { - this.updateInterval = updateInterval; - } - - /** - * Since we've online access to the data file, we check every day for an update - */ - @Override - protected synchronized void checkDataMaps() throws IOException { - if (lastUpdateCheck == 0 || lastUpdateCheck < System.currentTimeMillis() - updateInterval) { - String versionOnServer = getVersionFromServer(); - if (currentVersion == null || versionOnServer.compareTo(currentVersion) > 0) { - loadDataFromInternet(); - currentVersion = versionOnServer; - } - lastUpdateCheck = System.currentTimeMillis(); - } - } - - /** - * Loads the data file from user-agent-string.info - * - * @throws IOException - */ - private void loadDataFromInternet() throws IOException { - URL url = new URL(DATA_RETRIVE_URL); - InputStream is = url.openStream(); - try { - PHPFileParser fp = new PHPFileParser(is); - createInternalDataStructre(fp.getSections()); - } finally { - is.close(); - } - } - - /** - * Gets the current version from user-agent-string.info - * - * @return - * @throws IOException - */ - protected String getVersionFromServer() throws IOException { - URL url = new URL(VERSION_CHECK_URL); - InputStream is = url.openStream(); - try { - byte[] buff = new byte[4048]; - int len = is.read(buff); - return new String(buff, 0, len); - } finally { - is.close(); - } - } - -} diff --git a/src/main/java/cz/mallat/uasparser/OsEntry.java b/src/main/java/cz/mallat/uasparser/OsEntry.java deleted file mode 100644 index 7dfbfb68a..000000000 --- a/src/main/java/cz/mallat/uasparser/OsEntry.java +++ /dev/null @@ -1,94 +0,0 @@ -package cz.mallat.uasparser; - -import java.util.Iterator; -import java.util.List; - -/** - * JavaBean that holds the data from the [os] section in the data file - * - * @author oli - * - */ -class OsEntry { - - private String family; - private String name; - private String url; - private String company; - private String companyUrl; - private String ico; - - public OsEntry(List data) { - Iterator it = data.iterator(); - this.family = it.next(); - this.name = it.next(); - this.url = it.next(); - this.company = it.next(); - this.companyUrl = it.next(); - this.ico = it.next(); - } - - public String getFamily() { - return family; - } - - public void setFamily(String family) { - this.family = family; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getCompany() { - return company; - } - - public void setCompany(String company) { - this.company = company; - } - - public String getCompanyUrl() { - return companyUrl; - } - - public void setCompanyUrl(String companyUrl) { - this.companyUrl = companyUrl; - } - - public String getIco() { - return ico; - } - - public void setIco(String ico) { - this.ico = ico; - } - - public void copyTo(UserAgentInfo uai) { - if (Utils.validString(getFamily())) - uai.setOsFamily(getFamily()); - if (Utils.validString(getName())) - uai.setOsName(getName()); - if (Utils.validString(getUrl())) - uai.setOsUrl(getUrl()); - if (Utils.validString(getCompany())) - uai.setOsCompany(getCompany()); - if (Utils.validString(getFamily())) - uai.setOsCompanyUrl(getFamily()); - if (Utils.validString(getIco())) - uai.setOsIcon(getIco()); - } - -} \ No newline at end of file diff --git a/src/main/java/cz/mallat/uasparser/RobotEntry.java b/src/main/java/cz/mallat/uasparser/RobotEntry.java deleted file mode 100644 index 63e8536f3..000000000 --- a/src/main/java/cz/mallat/uasparser/RobotEntry.java +++ /dev/null @@ -1,126 +0,0 @@ -package cz.mallat.uasparser; - -import java.util.Iterator; -import java.util.List; - -/** - * JavaBean that holds the data from the [robots] section in the data file - * - * @author oli - * - */ -class RobotEntry { - - private String userAgentString; - private String family; - private String name; - private String url; - private String company; - private String companyUrl; - private String ico; - private String osId; - private String infoUrl; - - public RobotEntry(List data) { - Iterator it = data.iterator(); - this.userAgentString = it.next(); - this.family = it.next(); - this.name = it.next(); - this.url = it.next(); - this.company = it.next(); - this.companyUrl = it.next(); - this.ico = it.next(); - this.osId = it.next(); - this.infoUrl = it.next(); - } - - public String getUserAgentString() { - return userAgentString; - } - - public void setUserAgentString(String userAgentString) { - this.userAgentString = userAgentString; - } - - public String getFamily() { - return family; - } - - public void setFamily(String family) { - this.family = family; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getCompany() { - return company; - } - - public void setCompany(String company) { - this.company = company; - } - - public String getCompanyUrl() { - return companyUrl; - } - - public void setCompanyUrl(String companyUrl) { - this.companyUrl = companyUrl; - } - - public String getIco() { - return ico; - } - - public void setIco(String ico) { - this.ico = ico; - } - - public String getOsId() { - return osId; - } - - public void setOsId(String osId) { - this.osId = osId; - } - - public String getInfoUrl() { - return infoUrl; - } - - public void setInfoUrl(String infoUrl) { - this.infoUrl = infoUrl; - } - - public void copyTo(UserAgentInfo uai) { - if (Utils.validString(getFamily())) - uai.setUaFamily(getFamily()); - if (Utils.validString(getName())) - uai.setUaName(getName()); - if (Utils.validString(getUrl())) - uai.setUaUrl(getUrl()); - if (Utils.validString(getCompany())) - uai.setUaCompany(getCompany()); - if (Utils.validString(getCompanyUrl())) - uai.setUaCompanyUrl(getCompanyUrl()); - if (Utils.validString(getIco())) - uai.setUaIcon(getIco()); - if (Utils.validString(getInfoUrl())) - uai.setUaInfoUrl(UASparser.INFO_URL + getInfoUrl()); - } - -} \ No newline at end of file diff --git a/src/main/java/cz/mallat/uasparser/UASparser.java b/src/main/java/cz/mallat/uasparser/UASparser.java deleted file mode 100644 index d7516f78c..000000000 --- a/src/main/java/cz/mallat/uasparser/UASparser.java +++ /dev/null @@ -1,330 +0,0 @@ -package cz.mallat.uasparser; - -import cz.mallat.uasparser.fileparser.Entry; -import cz.mallat.uasparser.fileparser.PHPFileParser; -import cz.mallat.uasparser.fileparser.Section; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.concurrent.locks.ReentrantLock; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * User agent parser. - * - * @author oli - * - */ -public class UASparser { - - private ReentrantLock lock = new ReentrantLock(); - - static final String INFO_URL = "http://user-agent-string.info"; - - private Map robotsMap; - private Map osMap; - private Map browserMap; - private Map browserTypeMap; - private Map browserRegMap; - private Map browserOsMap; - private Map osRegMap; - - /** - * Use the given filename to load the definition file from the local filesystem - * - * @param localDefinitionFilename - * @throws IOException - */ - public UASparser(String localDefinitionFilename) throws IOException { - loadDataFromFile(new File(localDefinitionFilename)); - } - - /** - * Use the given inputstream to load the definition file from the local filesystem - * - * @param inputStreamToDefinitionFile - * @throws IOException - */ - public UASparser(InputStream inputStreamToDefinitionFile) throws IOException { - loadDataFromFile(inputStreamToDefinitionFile); - } - - /** - * Constructor for inherented classes - */ - public UASparser() { - // empty - } - - /** - * When a class inherents from this class, it probably has to override this method - */ - protected void checkDataMaps() throws IOException { - // empty for this base class - } - - /** - * Parse the given user agent string and returns a UserAgentInfo object with the related data - * - * @param useragent - * @throws IOException - * may happen when the retrieval of the data file fails - * @return - */ - public UserAgentInfo parse(String useragent) throws IOException { - UserAgentInfo retObj = new UserAgentInfo(); - - if (useragent == null) { - return retObj; - } - useragent = useragent.trim(); - - // check that the data maps are up-to-date - checkDataMaps(); - - // first check if it's a robot - if (!processRobot(useragent, retObj)) { - // search for a browser on the browser regex patterns - boolean osFound = processBrowserRegex(useragent, retObj); - - if (!osFound) { - // search the OS regex patterns for the used OS - processOsRegex(useragent, retObj); - } - } - return retObj; - } - - /** - * Searches in the os regex table. if found a match copies the os data - * - * @param useragent - * @param retObj - */ - private void processOsRegex(String useragent, UserAgentInfo retObj) { - try { - lock.lock(); - - for (Map.Entry entry : osRegMap.entrySet()) { - Matcher matcher = entry.getKey().matcher(useragent); - if (matcher.find()) { - // simply copy the OS data into the result object - Long idOs = entry.getValue(); - OsEntry os = osMap.get(idOs); - if (os != null) { - os.copyTo(retObj); - } - break; - } - } - } finally { - lock.unlock(); - } - } - - /** - * Searchs in the browser regex table. if found a match copies the browser data and if possible os data - * - * @param useragent - * @param retObj - * @return - */ - private boolean processBrowserRegex(String useragent, UserAgentInfo retObj) { - try { - lock.lock(); - boolean osFound = false; - for (Map.Entry entry : browserRegMap.entrySet()) { - Pattern pattern = Pattern.compile(entry.getKey(), Pattern.CASE_INSENSITIVE | Pattern.DOTALL); - Matcher matcher = pattern.matcher(useragent); - if (matcher.find()) { - // if a browse was found... - Long idBrowser = entry.getValue(); - // ... but the browser type from browser type map into the typ - copyType(retObj, idBrowser); - // get all the browser data from the browser map - BrowserEntry be = browserMap.get(idBrowser); - if (be != null) { - // first try to get the browser version from the first subgroup of the regex - String browserVersionInfo = null; - if (matcher.groupCount() > 0) { - browserVersionInfo = matcher.group(1); - } - // copy the browser data into the result - be.copyTo(retObj, browserVersionInfo); - } - // check if this browser has exactly one OS mapped - Long idOs = browserOsMap.get(idBrowser); - if (idOs != null) { - osFound = true; - OsEntry os = osMap.get(idOs); - if (os != null) { - os.copyTo(retObj); - } - } - break; - } - } - return osFound; - } finally { - lock.unlock(); - } - } - - /** - * Sets the source type, if possible - * - * @param retObj - * @param idBrowser - */ - private void copyType(UserAgentInfo retObj, Long idBrowser) { - try { - lock.lock(); - - BrowserEntry be = browserMap.get(idBrowser); - if (be != null) { - Long type = be.getType(); - if (type != null) { - String typeString = browserTypeMap.get(type); - if (typeString != null) { - retObj.setTyp(typeString); - } - } - } - } finally { - lock.unlock(); - } - } - - /** - * Checks if the useragent comes from a robot. if yes copies all the data to the result object - * - * @param useragent - * @param retObj - * @return true if the useragent belongs to a robot, else false - */ - private boolean processRobot(String useragent, UserAgentInfo retObj) { - try { - lock.lock(); - - if (robotsMap.containsKey(useragent)) { - retObj.setTyp("Robot"); - RobotEntry robotEntry = robotsMap.get(useragent); - robotEntry.copyTo(retObj); - if (robotEntry.getOsId() != null) { - OsEntry os = osMap.get(robotEntry.getOsId()); - if (os != null) { - os.copyTo(retObj); - } - } - return true; - } - } finally { - lock.unlock(); - } - return false; - } - - /** - * loads the data file and creates all internal data structs - * - * @param definitionFile - * @throws IOException - */ - protected void loadDataFromFile(File definitionFile) throws IOException { - PHPFileParser fp = new PHPFileParser(definitionFile); - createInternalDataStructre(fp.getSections()); - } - - /** - * loads the data file and creates all internal data structs - * - * @param is - * @throws IOException - */ - protected void loadDataFromFile(InputStream is) throws IOException { - PHPFileParser fp = new PHPFileParser(is); - createInternalDataStructre(fp.getSections()); - } - - /** - * Creates the internal data structes from the seciontList - * - * @param sectionList - */ - protected void createInternalDataStructre(List

    sectionList) { - try { - lock.lock(); - - for (Section sec : sectionList) { - if ("robots".equals(sec.getName())) { - Map robotsMapTmp = new HashMap(); - for (Entry en : sec.getEntries()) { - RobotEntry re = new RobotEntry(en.getData()); - robotsMapTmp.put(re.getUserAgentString(), re); - } - robotsMap = robotsMapTmp; - } else if ("os".equals(sec.getName())) { - Map osMapTmp = new HashMap(); - for (Entry en : sec.getEntries()) { - OsEntry oe = new OsEntry(en.getData()); - osMapTmp.put(Long.parseLong(en.getKey()), oe); - } - osMap = osMapTmp; - } else if ("browser".equals(sec.getName())) { - Map browserMapTmp = new HashMap(); - for (Entry en : sec.getEntries()) { - BrowserEntry be = new BrowserEntry(en.getData()); - browserMapTmp.put(Long.parseLong(en.getKey()), be); - } - browserMap = browserMapTmp; - } else if ("browser_type".equals(sec.getName())) { - Map browserTypeMapTmp = new HashMap(); - for (Entry en : sec.getEntries()) { - browserTypeMapTmp.put(Long.parseLong(en.getKey()), en.getData().iterator().next()); - } - browserTypeMap = browserTypeMapTmp; - } else if ("browser_reg".equals(sec.getName())) { - Map browserRegMapTmp = new LinkedHashMap(); - for (Entry en : sec.getEntries()) { - Iterator it = en.getData().iterator(); - browserRegMapTmp.put(convertPerlToJavaRegex(it.next()), Long.parseLong(it.next())); - } - browserRegMap = browserRegMapTmp; - } else if ("browser_os".equals(sec.getName())) { - Map browserOsMapTmp = new HashMap(); - for (Entry en : sec.getEntries()) { - browserOsMapTmp.put(Long.parseLong(en.getKey()), Long.parseLong(en.getData().iterator().next())); - } - browserOsMap = browserOsMapTmp; - } else if ("os_reg".equals(sec.getName())) { - Map osRegMapTmp = new LinkedHashMap(); - for (Entry en : sec.getEntries()) { - Iterator it = en.getData().iterator(); - Pattern pattern = Pattern.compile(convertPerlToJavaRegex(it.next()), Pattern.CASE_INSENSITIVE | Pattern.DOTALL); - osRegMapTmp.put(pattern, Long.parseLong(it.next())); - } - osRegMap = osRegMapTmp; - } - } - } finally { - lock.unlock(); - } - } - - /** - * Converts a PERL style regex into the Java style. That means in removes the leading and the last / and removes the modifiers - * - * @param regex - * @return - */ - private String convertPerlToJavaRegex(String regex) { - regex = regex.substring(1); - int lastIndex = regex.lastIndexOf('/'); - regex = regex.substring(0, lastIndex); - return regex; - } - -} diff --git a/src/main/java/cz/mallat/uasparser/UserAgentInfo.java b/src/main/java/cz/mallat/uasparser/UserAgentInfo.java deleted file mode 100644 index fd348c4ab..000000000 --- a/src/main/java/cz/mallat/uasparser/UserAgentInfo.java +++ /dev/null @@ -1,155 +0,0 @@ -package cz.mallat.uasparser; - -/** - * JavaBean that returns the data to the calling user from UAParser.parse() - * - * @author oli - * - */ -public class UserAgentInfo { - - private String typ; - private String uaFamily; - private String uaName; - private String uaUrl; - private String uaCompany; - private String uaCompanyUrl; - private String uaIcon; - private String uaInfoUrl; - private String osFamily; - private String osName; - private String osUrl; - private String osCompany; - private String osCompanyUrl; - private String osIcon; - - public UserAgentInfo() { - this.typ = "unknown"; - this.uaFamily = "unknown"; - this.uaName = "unknown"; - this.uaUrl = "unknown"; - this.uaCompany = "unknown"; - this.uaCompanyUrl = "unknown"; - this.uaIcon = "unknown"; - this.uaInfoUrl = "unknown"; - this.osFamily = "unknown"; - this.osName = "unknown"; - this.osUrl = "unknown"; - this.osCompany = "unknown"; - this.osCompanyUrl = "unknown"; - this.osIcon = "unknown"; - } - - public String getTyp() { - return typ; - } - - public void setTyp(String typ) { - this.typ = typ; - } - - public String getUaFamily() { - return uaFamily; - } - - public void setUaFamily(String uaFamily) { - this.uaFamily = uaFamily; - } - - public String getUaName() { - return uaName; - } - - public void setUaName(String uaName) { - this.uaName = uaName; - } - - public String getUaUrl() { - return uaUrl; - } - - public void setUaUrl(String uaUrl) { - this.uaUrl = uaUrl; - } - - public String getUaCompany() { - return uaCompany; - } - - public void setUaCompany(String uaCompany) { - this.uaCompany = uaCompany; - } - - public String getUaCompanyUrl() { - return uaCompanyUrl; - } - - public void setUaCompanyUrl(String uaCompanyUrl) { - this.uaCompanyUrl = uaCompanyUrl; - } - - public String getUaIcon() { - return uaIcon; - } - - public void setUaIcon(String uaIcon) { - this.uaIcon = uaIcon; - } - - public String getOsFamily() { - return osFamily; - } - - public void setOsFamily(String osFamily) { - this.osFamily = osFamily; - } - - public String getOsName() { - return osName; - } - - public void setOsName(String osName) { - this.osName = osName; - } - - public String getOsUrl() { - return osUrl; - } - - public void setOsUrl(String osUrl) { - this.osUrl = osUrl; - } - - public String getOsCompany() { - return osCompany; - } - - public void setOsCompany(String osCompany) { - this.osCompany = osCompany; - } - - public String getOsCompanyUrl() { - return osCompanyUrl; - } - - public void setOsCompanyUrl(String osCompanyUrl) { - this.osCompanyUrl = osCompanyUrl; - } - - public String getOsIcon() { - return osIcon; - } - - public void setOsIcon(String osIcon) { - this.osIcon = osIcon; - } - - public String getUaInfoUrl() { - return uaInfoUrl; - } - - public void setUaInfoUrl(String uaInfoUrl) { - this.uaInfoUrl = uaInfoUrl; - } - -} \ No newline at end of file diff --git a/src/main/java/cz/mallat/uasparser/Utils.java b/src/main/java/cz/mallat/uasparser/Utils.java deleted file mode 100644 index 30f833905..000000000 --- a/src/main/java/cz/mallat/uasparser/Utils.java +++ /dev/null @@ -1,9 +0,0 @@ -package cz.mallat.uasparser; - -public class Utils { - - public static boolean validString(String s) { - return s != null && !s.trim().isEmpty(); - } - -} diff --git a/src/main/java/cz/mallat/uasparser/fileparser/Entry.java b/src/main/java/cz/mallat/uasparser/fileparser/Entry.java deleted file mode 100644 index e6aff18f6..000000000 --- a/src/main/java/cz/mallat/uasparser/fileparser/Entry.java +++ /dev/null @@ -1,36 +0,0 @@ -package cz.mallat.uasparser.fileparser; - -import java.util.ArrayList; -import java.util.List; - -/** - * JavaBean that holds an entry from a parsed file - * - * @author oli - */ -public class Entry { - - private String key; - private List data = new ArrayList(); - - public Entry(String key) { - this.key = key; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public List getData() { - return data; - } - - public void setData(List data) { - this.data = data; - } - -} diff --git a/src/main/java/cz/mallat/uasparser/fileparser/PHPFileParser.java b/src/main/java/cz/mallat/uasparser/fileparser/PHPFileParser.java deleted file mode 100644 index 640cdef48..000000000 --- a/src/main/java/cz/mallat/uasparser/fileparser/PHPFileParser.java +++ /dev/null @@ -1,92 +0,0 @@ -package cz.mallat.uasparser.fileparser; - -import java.io.*; -import java.util.ArrayList; -import java.util.List; - -/** - * Emulates the behavior of the php function "parse_ini_file". - * - * Does NOT support all features of the php function. - * - * @author oli - */ -public class PHPFileParser { - - private List
    sections; - - public PHPFileParser(InputStream is) throws IOException { - loadFile(new InputStreamReader(is)); - } - - public PHPFileParser(Reader reader) throws IOException { - loadFile(reader); - } - - public PHPFileParser(File file) throws IOException { - Reader reader = new FileReader(file); - try { - loadFile(reader); - } finally { - try { - reader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private void loadFile(Reader reader) throws IOException { - this.sections = new ArrayList
    (); - - BufferedReader bufferedReader = new BufferedReader(reader); - - int unnamedSectionCounter = 0; - - Section currentSection = null; - Entry currentEntry = null; - - String line = bufferedReader.readLine(); - while (line != null) { - if (line.trim().startsWith(";")) { - // comment, do nothing - } else if (line.trim().startsWith("[") && line.trim().endsWith("]")) { - String rawLine = line.trim(); - String sectionName = rawLine.substring(1, rawLine.length() - 1); - currentSection = new Section(sectionName); - sections.add(currentSection); - } else { - if (currentSection == null) { - currentSection = new Section("unname section" + (++unnamedSectionCounter)); - sections.add(currentSection); - } - - int indexOfEquals = line.indexOf('='); - String key = line.substring(0, indexOfEquals); - String data = line.substring(indexOfEquals + 1); - key = key.replace('[', ' '); - key = key.replace(']', ' '); - key = key.trim(); - data = data.trim(); - if (data.startsWith("\"") && data.endsWith("\"")) { - data = data.substring(1, data.length() - 1); - } - - if (currentEntry == null || !currentEntry.getKey().equals(key)) { - currentEntry = new Entry(key); - currentSection.getEntries().add(currentEntry); - } - - currentEntry.getData().add(data); - } - - line = bufferedReader.readLine(); - } - - } - - public List
    getSections() { - return sections; - } - -} diff --git a/src/main/java/cz/mallat/uasparser/fileparser/Section.java b/src/main/java/cz/mallat/uasparser/fileparser/Section.java deleted file mode 100644 index f3a83eb17..000000000 --- a/src/main/java/cz/mallat/uasparser/fileparser/Section.java +++ /dev/null @@ -1,37 +0,0 @@ -package cz.mallat.uasparser.fileparser; - -import java.util.ArrayList; -import java.util.List; - -/** - * JavaBean that holds a section from a parsed file. A section is a row in square brackets, e.g. [main] - * - * @author oli - */ -public class Section { - - private String name; - private List entries; - - public Section(String sectionName) { - this.name = sectionName; - this.entries = new ArrayList(); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getEntries() { - return entries; - } - - public void setEntries(List entries) { - this.entries = entries; - } - -} diff --git a/src/main/java/org/browsermob/core/har/HarCacheStatus.java b/src/main/java/org/browsermob/core/har/HarCacheStatus.java deleted file mode 100644 index c0c4e2888..000000000 --- a/src/main/java/org/browsermob/core/har/HarCacheStatus.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.browsermob.core.har; - -import org.browsermob.core.json.ISO8601DateFormatter; -import org.codehaus.jackson.annotate.JsonWriteNullProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import java.util.Date; - -@JsonWriteNullProperties(value=false) -public class HarCacheStatus { - private Date expires; - private Date lastAccess; - private String eTag; - private int hitCount; - - @JsonSerialize(using = ISO8601DateFormatter.class) - public Date getExpires() { - return expires; - } - - public void setExpires(Date expires) { - this.expires = expires; - } - - @JsonSerialize(using = ISO8601DateFormatter.class) - public Date getLastAccess() { - return lastAccess; - } - - public void setLastAccess(Date lastAccess) { - this.lastAccess = lastAccess; - } - - public String geteTag() { - return eTag; - } - - public void seteTag(String eTag) { - this.eTag = eTag; - } - - public int getHitCount() { - return hitCount; - } - - public void setHitCount(int hitCount) { - this.hitCount = hitCount; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarContent.java b/src/main/java/org/browsermob/core/har/HarContent.java deleted file mode 100644 index 4fa3bb9dd..000000000 --- a/src/main/java/org/browsermob/core/har/HarContent.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.browsermob.core.har; - -import org.codehaus.jackson.map.annotate.JsonSerialize; - -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) -public class HarContent { - private long size; - private Long compression; - private String mimeType = ""; - private String text; - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public Long getCompression() { - return compression; - } - - public void setCompression(Long compression) { - this.compression = compression; - } - - public String getMimeType() { - return mimeType; - } - - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarCookie.java b/src/main/java/org/browsermob/core/har/HarCookie.java deleted file mode 100644 index 50fa291a7..000000000 --- a/src/main/java/org/browsermob/core/har/HarCookie.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.browsermob.core.har; - -import org.browsermob.core.json.ISO8601DateFormatter; -import org.codehaus.jackson.annotate.JsonWriteNullProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import java.util.Date; - -@JsonWriteNullProperties(value=false) -public class HarCookie { - private String name; - private String value; - private String path; - private String domain; - private Date expires; - private Boolean httpOnly; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public String getDomain() { - return domain; - } - - public void setDomain(String domain) { - this.domain = domain; - } - - @JsonSerialize(using = ISO8601DateFormatter.class) - public Date getExpires() { - return expires; - } - - public void setExpires(Date expires) { - this.expires = expires; - } - - public Boolean getHttpOnly() { - return httpOnly; - } - - public void setHttpOnly(Boolean httpOnly) { - this.httpOnly = httpOnly; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarEntry.java b/src/main/java/org/browsermob/core/har/HarEntry.java deleted file mode 100644 index 4a6fa92de..000000000 --- a/src/main/java/org/browsermob/core/har/HarEntry.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.browsermob.core.har; - -import org.browsermob.core.json.ISO8601DateFormatter; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import java.util.Date; - -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) -@JsonAutoDetect -public class HarEntry { - private String pageref; - private Date startedDateTime; - private long time; - private HarRequest request; - private HarResponse response; - private HarCache cache = new HarCache(); - private HarTimings timings; - private String serverIPAddress; - - public HarEntry() { - } - - public HarEntry(String pageref) { - this.pageref = pageref; - this.startedDateTime = new Date(); - } - - public String getPageref() { - return pageref; - } - - public void setPageref(String pageref) { - this.pageref = pageref; - } - - @JsonSerialize(using = ISO8601DateFormatter.class) - public Date getStartedDateTime() { - return startedDateTime; - } - - public void setStartedDateTime(Date startedDateTime) { - this.startedDateTime = startedDateTime; - } - - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } - - public HarRequest getRequest() { - return request; - } - - public void setRequest(HarRequest request) { - this.request = request; - } - - public HarResponse getResponse() { - return response; - } - - public void setResponse(HarResponse response) { - this.response = response; - } - - public HarCache getCache() { - return cache; - } - - public void setCache(HarCache cache) { - this.cache = cache; - } - - public HarTimings getTimings() { - return timings; - } - - public void setTimings(HarTimings timings) { - this.timings = timings; - } - - public String getServerIPAddress() { - return serverIPAddress; - } - - public void setServerIPAddress(String serverIPAddress) { - this.serverIPAddress = serverIPAddress; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarNameValuePair.java b/src/main/java/org/browsermob/core/har/HarNameValuePair.java deleted file mode 100644 index 358c1423a..000000000 --- a/src/main/java/org/browsermob/core/har/HarNameValuePair.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.browsermob.core.har; - -public final class HarNameValuePair { - private String name; - private String value; - - public HarNameValuePair() { - } - - public HarNameValuePair(String name, String value) { - this.name = name; - this.value = value; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - // TODO: Perhaps these should be done the right way - public boolean equals(Object o) { - HarNameValuePair obj = (HarNameValuePair)o; - return obj.getName().equals(this.getName()) && obj.getValue().equals(this.getValue()); - - } - - public int hashCode() { - return toString().hashCode(); - } - - public String toString() { - return name + "=" + value; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarPage.java b/src/main/java/org/browsermob/core/har/HarPage.java deleted file mode 100644 index cab5e71bd..000000000 --- a/src/main/java/org/browsermob/core/har/HarPage.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.browsermob.core.har; - -import org.browsermob.core.json.ISO8601DateFormatter; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import java.util.Date; - -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) -public class HarPage { - private String id; - private Date startedDateTime; - private String title = ""; - private HarPageTimings pageTimings = new HarPageTimings(); - - public HarPage() { - } - - public HarPage(String id) { - this.id = id; - startedDateTime = new Date(); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @JsonSerialize(using = ISO8601DateFormatter.class) - public Date getStartedDateTime() { - return startedDateTime; - } - - public void setStartedDateTime(Date startedDateTime) { - this.startedDateTime = startedDateTime; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public HarPageTimings getPageTimings() { - return pageTimings; - } - - public void setPageTimings(HarPageTimings pageTimings) { - this.pageTimings = pageTimings; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarResponse.java b/src/main/java/org/browsermob/core/har/HarResponse.java deleted file mode 100644 index d5ba73a8a..000000000 --- a/src/main/java/org/browsermob/core/har/HarResponse.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.browsermob.core.har; - -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) -public class HarResponse { - private int status; - private String statusText; - private String httpVersion; - private List cookies = new CopyOnWriteArrayList(); - private List headers = new CopyOnWriteArrayList(); - private HarContent content = new HarContent(); - private String redirectURL = ""; - private long headersSize; - private long bodySize; - - public HarResponse() { - } - - public HarResponse(int status, String statusText, String httpVersion) { - this.status = status; - this.statusText = statusText; - this.httpVersion = httpVersion; - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - public String getStatusText() { - return statusText; - } - - public void setStatusText(String statusText) { - this.statusText = statusText; - } - - public String getHttpVersion() { - return httpVersion; - } - - public void setHttpVersion(String httpVersion) { - this.httpVersion = httpVersion; - } - - public List getCookies() { - return cookies; - } - - public void setCookies(List cookies) { - this.cookies = cookies; - } - - public List getHeaders() { - return headers; - } - - public void setHeaders(List headers) { - this.headers = headers; - } - - public HarContent getContent() { - return content; - } - - public void setContent(HarContent content) { - this.content = content; - } - - public String getRedirectURL() { - return redirectURL; - } - - public void setRedirectURL(String redirectURL) { - this.redirectURL = redirectURL; - } - - public long getHeadersSize() { - return headersSize; - } - - public void setHeadersSize(long headersSize) { - this.headersSize = headersSize; - } - - public long getBodySize() { - return bodySize; - } - - public void setBodySize(long bodySize) { - this.bodySize = bodySize; - } -} diff --git a/src/main/java/org/browsermob/core/har/HarTimings.java b/src/main/java/org/browsermob/core/har/HarTimings.java deleted file mode 100644 index 84504aecc..000000000 --- a/src/main/java/org/browsermob/core/har/HarTimings.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.browsermob.core.har; - -public class HarTimings { - private long blocked; - private long dns; - private long connect; - private long send; - private long wait; - private long receive; - - public HarTimings() { - } - - public HarTimings(long blocked, long dns, long connect, long send, long wait, long receive) { - this.blocked = blocked; - this.dns = dns; - this.connect = connect; - this.send = send; - this.wait = wait; - this.receive = receive; - } - - public Long getBlocked() { - return blocked; - } - - public void setBlocked(Long blocked) { - this.blocked = blocked; - } - - public Long getDns() { - return dns; - } - - public void setDns(Long dns) { - this.dns = dns; - } - - public Long getConnect() { - return connect; - } - - public void setConnect(Long connect) { - this.connect = connect; - } - - public long getSend() { - return send; - } - - public void setSend(long send) { - this.send = send; - } - - public long getWait() { - return wait; - } - - public void setWait(long wait) { - this.wait = wait; - } - - public long getReceive() { - return receive; - } - - public void setReceive(long receive) { - this.receive = receive; - } -} diff --git a/src/main/java/org/browsermob/core/json/ISO8601DateFormatter.java b/src/main/java/org/browsermob/core/json/ISO8601DateFormatter.java deleted file mode 100644 index f282360a3..000000000 --- a/src/main/java/org/browsermob/core/json/ISO8601DateFormatter.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.browsermob.core.json; - -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.map.JsonMappingException; -import org.codehaus.jackson.map.SerializerProvider; -import org.codehaus.jackson.map.ser.ScalarSerializerBase; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.text.DateFormat; -import java.util.Date; - -public class ISO8601DateFormatter extends ScalarSerializerBase { - public final static ISO8601DateFormatter instance = new ISO8601DateFormatter(); - - public ISO8601DateFormatter() { - super(java.util.Date.class); - } - - @Override - public void serialize(java.util.Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { - DateFormat df = (DateFormat) provider.getConfig().getDateFormat().clone(); - jgen.writeString(df.format(value)); - } - - - @Override - public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { - return createSchemaNode("string", true); - } - -} diff --git a/src/main/java/org/browsermob/core/util/ThreadUtils.java b/src/main/java/org/browsermob/core/util/ThreadUtils.java deleted file mode 100644 index d18bbb303..000000000 --- a/src/main/java/org/browsermob/core/util/ThreadUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.browsermob.core.util; - -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.util.concurrent.TimeUnit; - -public class ThreadUtils { - public static String dumpAllThreads() { - ThreadInfo[] dumps = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true); - StringBuilder out = new StringBuilder("Thread Dump\n"); - for (ThreadInfo dump : dumps) { - out.append("-------------------------\n").append(dump); - } - - return out.toString(); - } - - public static interface WaitCondition { - public boolean checkCondition(long elapsedTimeInMs); - } - - public static void sleep(TimeUnit timeUnit, long duration) { - try { - timeUnit.sleep(duration); - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - - public static boolean waitFor(WaitCondition condition) { - boolean result = false; - if (condition != null) { - long startTime = System.currentTimeMillis(); - - while (!(result = condition.checkCondition(System.currentTimeMillis() - startTime))) { - try { - Thread.sleep(100); - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - - return result; - } - - public static boolean waitFor(WaitCondition condition, TimeUnit timeUnit, long timeoutDuration) { - long timeout = timeUnit.toMillis(timeoutDuration); - - boolean result = false; - if (condition != null) { - long startTime = System.currentTimeMillis(); - long curTime = startTime; - - while (!(result = condition.checkCondition(curTime - startTime)) && (curTime - startTime < timeout)) { - try { - Thread.sleep(100); - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - curTime = System.currentTimeMillis(); - } - } - - return result; - } - - public static boolean waitFor(WaitCondition condition, TimeUnit timeUnitTimeout, long timeoutDuration, TimeUnit timeUnitSleep, long sleepDuration) { - long timeout = timeUnitTimeout.toMillis(timeoutDuration); - long sleepBetween = timeUnitSleep.toMillis(sleepDuration); - - boolean result = false; - if (condition != null) { - long startTime = System.currentTimeMillis(); - long curTime = startTime; - - while (!(result = condition.checkCondition(curTime - startTime)) && (curTime - startTime < timeout)) { - try { - Thread.sleep(sleepBetween); - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - curTime = System.currentTimeMillis(); - } - } - - return result; - } -} \ No newline at end of file diff --git a/src/main/java/org/browsermob/proxy/FirefoxErrorConstants.java b/src/main/java/org/browsermob/proxy/FirefoxErrorConstants.java deleted file mode 100644 index 2543dc87d..000000000 --- a/src/main/java/org/browsermob/proxy/FirefoxErrorConstants.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.browsermob.proxy; - -public class FirefoxErrorConstants { - public static final String SHARED_LONG_DESC = "
      \n" + - "
    • The site could be temporarily unavailable or too busy. Try again in a few\n" + - " moments.
    • \n" + - "
    • If you are unable to load any pages, check your computer's network\n" + - " connection.
    • \n" + - "
    • If your computer or network is protected by a firewall or proxy, make sure\n" + - " that Firefox is permitted to access the Web.
    • \n" + - "
    "; - - public static final String ERROR_PAGE = "\n" + - " \n" + - " Problem loading page\n" + - " \n" + - " \n" + - " \n" + - "\n" + - " \n" + - "\n" + - " \n" + - "
    \n" + - " \n" + - " \n" + - "
    \n" + - "

    %s

    \n" + - "
    \n" + - " \n" + - " \n" + - "
    \n" + - " \n" + - " \n" + - "
    \n" + - "

    \n" + - " %s\n" + - "

    \n" + - "
    \n" + - "\n" + - " \n" + - "
    \n" + - " %s\n" + - "
    \n" + - "
    \n" + - "\n" + - " \n" + - " \n" + - "\n" + - "
    \n" + - "\n" + - " \n" + - ""; - -} diff --git a/src/main/java/org/browsermob/proxy/FirefoxErrorContent.java b/src/main/java/org/browsermob/proxy/FirefoxErrorContent.java deleted file mode 100644 index 55c71c69c..000000000 --- a/src/main/java/org/browsermob/proxy/FirefoxErrorContent.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.browsermob.proxy; - -public enum FirefoxErrorContent { - CONN_FAILURE("Unable to connect", "Firefox can't establish a connection to the server at %s", FirefoxErrorConstants.SHARED_LONG_DESC), - DNS_NOT_FOUND("Server not found", "Firefox can't find the server at %s.", "
      \n"+ - "
    • Check the address for typing errors such as\n"+ - " ww.example.com instead of\n"+ - " www.example.com
    • \n"+ - "
    • If you are unable to load any pages, check your computer's network\n"+ - " connection.
    • \n"+ - "
    • If your computer or network is protected by a firewall or proxy, make sure\n"+ - " that Firefox is permitted to access the Web.
    • \n"+ - "
    "), - GENERIC("Oops.", "Something went wrong.", "

    Firefox can't load this page for some reason.

    "), - MALFORMED_URI("The address isn't valid", "The URL is not valid and cannot be loaded.", "
      \n" + - "
    • Web addresses are usually written like\n" + - " http://www.example.com/
    • \n" + - "
    • Make sure that you're using forward slashes (i.e.\n" + - " /).
    • \n" + - "
    "), - NET_INTERRUPT("The connection was interrupted", "The connection to %s was interrupted while the page was loading.", FirefoxErrorConstants.SHARED_LONG_DESC), - NET_RESET("The connection was reset", "The connection to the server was reset while the page was loading.", FirefoxErrorConstants.SHARED_LONG_DESC), - NET_TIMEOUT("The connection has timed out", "The server at %s is taking too long to respond.", FirefoxErrorConstants.SHARED_LONG_DESC), - ; - - private String title; - private String shortDesc; - private String longDesc; - - FirefoxErrorContent(String title, String shortDesc, String longDesc) { - this.title = title; - this.shortDesc = shortDesc; - this.longDesc = longDesc; - } - - public String getTitle() { - return title; - } - - public String getShortDesc() { - return shortDesc; - } - - public String getLongDesc() { - return longDesc; - } -} diff --git a/src/main/java/org/browsermob/proxy/Main.java b/src/main/java/org/browsermob/proxy/Main.java deleted file mode 100644 index f28bc27d6..000000000 --- a/src/main/java/org/browsermob/proxy/Main.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.browsermob.proxy; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.servlet.GuiceServletContextListener; -import com.google.sitebricks.SitebricksModule; -import org.browsermob.proxy.bricks.ProxyResource; -import org.browsermob.proxy.guice.ConfigModule; -import org.browsermob.proxy.guice.JettyModule; -import org.browsermob.proxy.util.Log; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; - -import javax.servlet.ServletContextEvent; -import java.io.InputStream; -import java.util.Properties; - -public class Main { - private static final Log LOG = new Log(); - - public static void main(String[] args) throws Exception { - String version = "UNKNOWN/DEVELOPMENT"; - InputStream is = Main.class.getResourceAsStream("/META-INF/maven/biz.neustar/browsermob-proxy/pom.properties"); - if (is != null) { - Properties props = new Properties(); - props.load(is); - version = props.getProperty("version"); - } - - final Injector injector = Guice.createInjector(new ConfigModule(args), new JettyModule(), new SitebricksModule() { - @Override - protected void configureSitebricks() { - scan(ProxyResource.class.getPackage()); - } - }); - - LOG.info("Starting BrowserMob Proxy version %s", version); - - Server server = injector.getInstance(Server.class); - GuiceServletContextListener gscl = new GuiceServletContextListener() { - @Override - protected Injector getInjector() { - return injector; - } - }; - server.start(); - - ServletContextHandler context = (ServletContextHandler) server.getHandler(); - gscl.contextInitialized(new ServletContextEvent(context.getServletContext())); - - server.join(); - } -} diff --git a/src/main/java/org/browsermob/proxy/ProxyManager.java b/src/main/java/org/browsermob/proxy/ProxyManager.java deleted file mode 100644 index 026ed901c..000000000 --- a/src/main/java/org/browsermob/proxy/ProxyManager.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.browsermob.proxy; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.google.inject.Provider; - -import java.util.Hashtable; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -@Singleton -public class ProxyManager { - private AtomicInteger portCounter = new AtomicInteger(9090); - private Provider proxyServerProvider; - private Map proxies = new ConcurrentHashMap(); - - @Inject - public ProxyManager(Provider proxyServerProvider) { - this.proxyServerProvider = proxyServerProvider; - } - - public ProxyServer create(Map options, int port) throws Exception { - ProxyServer proxy = proxyServerProvider.get(); - proxy.setPort(port); - proxy.start(); - proxy.setOptions(options); - proxies.put(port, proxy); - return proxy; - } - - public ProxyServer create(Map options) throws Exception { - int port = portCounter.incrementAndGet(); - ProxyServer proxy = proxyServerProvider.get(); - - proxy.setPort(port); - proxy.start(); - proxy.setOptions(options); - - proxies.put(port, proxy); - - return proxy; - } - - public ProxyServer get(int port) { - return proxies.get(port); - } - - public void delete(int port) throws Exception { - ProxyServer proxy = proxies.remove(port); - proxy.stop(); - } -} diff --git a/src/main/java/org/browsermob/proxy/ProxyServer.java b/src/main/java/org/browsermob/proxy/ProxyServer.java deleted file mode 100644 index 12eab9c6c..000000000 --- a/src/main/java/org/browsermob/proxy/ProxyServer.java +++ /dev/null @@ -1,302 +0,0 @@ -package org.browsermob.proxy; - -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponseInterceptor; -import org.browsermob.core.har.*; -import org.browsermob.core.util.ThreadUtils; -import org.browsermob.proxy.http.BrowserMobHttpClient; -import org.browsermob.proxy.http.RequestInterceptor; -import org.browsermob.proxy.http.ResponseInterceptor; -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpListener; -import org.browsermob.proxy.jetty.http.SocketListener; -import org.browsermob.proxy.jetty.jetty.Server; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.util.Log; -import org.java_bandwidthlimiter.BandwidthLimiter; -import org.java_bandwidthlimiter.StreamManager; -import org.openqa.selenium.Proxy; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Date; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - - -public class ProxyServer { - private static final HarNameVersion CREATOR = new HarNameVersion("BrowserMob Proxy", "2.0"); - private static final Log LOG = new Log(); - - private Server server; - private int port = -1; - private BrowserMobHttpClient client; - private StreamManager streamManager; - private HarPage currentPage; - private BrowserMobProxyHandler handler; - private int pageCount = 1; - private AtomicInteger requestCounter = new AtomicInteger(0); - - public ProxyServer() { - } - - public ProxyServer(int port) { - this.port = port; - } - - public void start() throws Exception { - if (port == -1) { - throw new IllegalStateException("Must set port before starting"); - } - - //create a stream manager that will be capped to 100 Megabits - //remember that by default it is disabled! - streamManager = new StreamManager( 100 * BandwidthLimiter.OneMbps ); - - server = new Server(); - HttpListener listener = new SocketListener(new InetAddrPort(getPort())); - server.addListener(listener); - HttpContext context = new HttpContext(); - context.setContextPath("/"); - server.addContext(context); - - handler = new BrowserMobProxyHandler(); - handler.setJettyServer(server); - handler.setShutdownLock(new Object()); - client = new BrowserMobHttpClient(streamManager, requestCounter); - client.prepareForBrowser(); - handler.setHttpClient(client); - - context.addHandler(handler); - - server.start(); - - setPort(listener.getPort()); - } - - public org.openqa.selenium.Proxy seleniumProxy() throws UnknownHostException { - Proxy proxy = new Proxy(); - proxy.setProxyType(Proxy.ProxyType.MANUAL); - String proxyStr = String.format("%s:%d", InetAddress.getLocalHost().getCanonicalHostName(), getPort()); - proxy.setHttpProxy(proxyStr); - proxy.setSslProxy(proxyStr); - - return proxy; - } - - public void cleanup() { - handler.cleanup(); - } - - public void stop() throws Exception { - cleanup(); - client.shutdown(); - server.stop(); - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public Har getHar() { - // Wait up to 5 seconds for all active requests to cease before returning the HAR. - // This helps with race conditions but won't cause deadlocks should a request hang - // or error out in an unexpected way (which of course would be a bug!) - boolean success = ThreadUtils.waitFor(new ThreadUtils.WaitCondition() { - @Override - public boolean checkCondition(long elapsedTimeInMs) { - return requestCounter.get() == 0; - } - }, TimeUnit.SECONDS, 5); - - if (!success) { - LOG.warn("Waited 5 seconds for requests to cease before returning HAR; giving up!"); - } - - return client.getHar(); - } - - public Har newHar(String initialPageRef) { - pageCount = 1; - - Har oldHar = getHar(); - - Har har = new Har(new HarLog(CREATOR)); - client.setHar(har); - newPage(initialPageRef); - - return oldHar; - } - - public void newPage(String pageRef) { - if (pageRef == null) { - pageRef = "Page " + pageCount; - } - - client.setHarPageRef(pageRef); - currentPage = new HarPage(pageRef); - client.getHar().getLog().addPage(currentPage); - - pageCount++; - } - - public void endPage() { - if (currentPage == null) { - return; - } - - currentPage.getPageTimings().setOnLoad(new Date().getTime() - currentPage.getStartedDateTime().getTime()); - client.setHarPageRef(null); - currentPage = null; - } - - public void setRetryCount(int count) { - client.setRetryCount(count); - } - - public void remapHost(String source, String target) { - client.remapHost(source, target); - } - - @Deprecated - public void addRequestInterceptor(HttpRequestInterceptor i) { - client.addRequestInterceptor(i); - } - - public void addRequestInterceptor(RequestInterceptor interceptor) { - client.addRequestInterceptor(interceptor); - } - - @Deprecated - public void addResponseInterceptor(HttpResponseInterceptor i) { - client.addResponseInterceptor(i); - } - - public void addResponseInterceptor(ResponseInterceptor interceptor) { - client.addResponseInterceptor(interceptor); - } - - public StreamManager getStreamManager() { - return streamManager; - } - - //use getStreamManager().setDownstreamKbps instead - @Deprecated - public void setDownstreamKbps(long downstreamKbps) { - streamManager.setDownstreamKbps(downstreamKbps); - streamManager.enable(); - } - - //use getStreamManager().setUpstreamKbps instead - @Deprecated - public void setUpstreamKbps(long upstreamKbps) { - streamManager.setUpstreamKbps(upstreamKbps); - streamManager.enable(); - } - - //use getStreamManager().setLatency instead - @Deprecated - public void setLatency(long latency) { - streamManager.setLatency(latency); - streamManager.enable(); - } - - public void setRequestTimeout(int requestTimeout) { - client.setRequestTimeout(requestTimeout); - } - - public void setSocketOperationTimeout(int readTimeout) { - client.setSocketOperationTimeout(readTimeout); - } - - public void setConnectionTimeout(int connectionTimeout) { - client.setConnectionTimeout(connectionTimeout); - } - - public void autoBasicAuthorization(String domain, String username, String password) { - client.autoBasicAuthorization(domain, username, password); - } - - public void rewriteUrl(String match, String replace) { - client.rewriteUrl(match, replace); - } - - public void blacklistRequests(String pattern, int responseCode) { - client.blacklistRequests(pattern, responseCode); - } - - public void whitelistRequests(String[] patterns, int responseCode) { - client.whitelistRequests(patterns, responseCode); - } - - public void addHeader(String name, String value) { - client.addHeader(name, value); - } - - public void setCaptureHeaders(boolean captureHeaders) { - client.setCaptureHeaders(captureHeaders); - } - - public void setCaptureContent(boolean captureContent) { - client.setCaptureContent(captureContent); - } - - public void setCaptureBinaryContent(boolean captureBinaryContent) { - client.setCaptureBinaryContent(captureBinaryContent); - } - - public void clearDNSCache() { - client.clearDNSCache(); - } - - public void setDNSCacheTimeout(int timeout) { - client.setDNSCacheTimeout(timeout); - } - - public void waitForNetworkTrafficToStop(final long quietPeriodInMs, long timeoutInMs) { - long start = System.currentTimeMillis(); - boolean result = ThreadUtils.waitFor(new ThreadUtils.WaitCondition() { - @Override - public boolean checkCondition(long elapsedTimeInMs) { - Date lastCompleted = null; - Har har = client.getHar(); - if (har == null || har.getLog() == null) { - return true; - } - - for (HarEntry entry : har.getLog().getEntries()) { - // if there is an active request, just stop looking - if (entry.getResponse().getStatus() < 0) { - return false; - } - - Date end = new Date(entry.getStartedDateTime().getTime() + entry.getTime()); - if (lastCompleted == null) { - lastCompleted = end; - } else if (end.after(lastCompleted)) { - lastCompleted = end; - } - } - - return lastCompleted != null && System.currentTimeMillis() - lastCompleted.getTime() >= quietPeriodInMs; - } - }, TimeUnit.MILLISECONDS, timeoutInMs); - long end = System.currentTimeMillis(); - long time = (end - start); - - if (!result) { - throw new RuntimeException("Timed out after " + timeoutInMs + " ms while waiting for network traffic to stop"); - } - } - - public void setOptions(Map options) { - if (options.containsKey("httpProxy")) { - client.setHttpProxy(options.get("httpProxy")); - } - } -} diff --git a/src/main/java/org/browsermob/proxy/Test.java b/src/main/java/org/browsermob/proxy/Test.java deleted file mode 100644 index ff512eafb..000000000 --- a/src/main/java/org/browsermob/proxy/Test.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.browsermob.proxy; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.sitebricks.SitebricksModule; -import com.google.sitebricks.client.Web; -import com.google.sitebricks.client.WebResponse; -import com.google.sitebricks.client.transport.Json; -import org.browsermob.core.har.Har; - -public class Test { - public static void main(String[] args) { - Injector injector = Guice.createInjector(new SitebricksModule()); - WebResponse response = injector.getInstance(Web.class) - .clientOf("http://localhost:8080/proxy/9091/har") - .transports(Har.class) - .over(Json.class).get(); - Har har = response.to(Har.class).using(Json.class); - System.out.println(har.getLog().getBrowser().getName()); - } -} diff --git a/src/main/java/org/browsermob/proxy/bricks/ProxyResource.java b/src/main/java/org/browsermob/proxy/bricks/ProxyResource.java deleted file mode 100644 index fe2ee185c..000000000 --- a/src/main/java/org/browsermob/proxy/bricks/ProxyResource.java +++ /dev/null @@ -1,387 +0,0 @@ -package org.browsermob.proxy.bricks; - -import com.google.inject.Inject; -import com.google.inject.name.Named; -import com.google.sitebricks.At; -import com.google.sitebricks.client.transport.Json; -import com.google.sitebricks.headless.Reply; -import com.google.sitebricks.headless.Request; -import com.google.sitebricks.headless.Service; -import com.google.sitebricks.http.Delete; -import com.google.sitebricks.http.Get; -import com.google.sitebricks.http.Post; -import com.google.sitebricks.http.Put; -import org.browsermob.core.har.Har; -import org.browsermob.proxy.ProxyManager; -import org.browsermob.proxy.ProxyServer; -import org.browsermob.proxy.http.BrowserMobHttpRequest; -import org.browsermob.proxy.http.BrowserMobHttpResponse; -import org.browsermob.proxy.http.RequestInterceptor; -import org.browsermob.proxy.http.ResponseInterceptor; -import org.browsermob.proxy.util.Log; -import org.java_bandwidthlimiter.StreamManager; - -import javax.script.*; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; - -@At("/proxy") -@Service -public class ProxyResource { - private static final Log LOG = new Log(); - - private ProxyManager proxyManager; - - @Inject - public ProxyResource(ProxyManager proxyManager) { - this.proxyManager = proxyManager; - } - - @Post - public Reply newProxy(Request request) throws Exception { - String systemProxyHost = System.getProperty("http.proxyHost"); - String systemProxyPort = System.getProperty("http.proxyPort"); - String httpProxy = request.param("httpProxy"); - Hashtable options = new Hashtable(); - - // If the upstream proxy is specified via query params that should override any default system level proxy. - if (httpProxy != null) { - options.put("httpProxy", httpProxy); - } else if ((systemProxyHost != null) && (systemProxyPort != null)) { - options.put("httpProxy", String.format("%s:%s", systemProxyHost, systemProxyPort)); - } - - String paramPort = request.param("port"); - int port = 0; - if (paramPort != null) { - port = Integer.parseInt(paramPort); - ProxyServer proxy = proxyManager.create(options, port); - } else { - ProxyServer proxy = proxyManager.create(options); - port = proxy.getPort(); - } - return Reply.with(new ProxyDescriptor(port)).as(Json.class); - } - - @Get - @At("/:port/har") - public Reply getHar(@Named("port") int port) { - ProxyServer proxy = proxyManager.get(port); - Har har = proxy.getHar(); - - return Reply.with(har).as(Json.class); - } - - @Put - @At("/:port/har") - public Reply newHar(@Named("port") int port, Request request) { - String initialPageRef = request.param("initialPageRef"); - ProxyServer proxy = proxyManager.get(port); - Har oldHar = proxy.newHar(initialPageRef); - - String captureHeaders = request.param("captureHeaders"); - String captureContent = request.param("captureContent"); - String captureBinaryContent = request.param("captureBinaryContent"); - proxy.setCaptureHeaders(Boolean.parseBoolean(captureHeaders)); - proxy.setCaptureContent(Boolean.parseBoolean(captureContent)); - proxy.setCaptureBinaryContent(Boolean.parseBoolean(captureBinaryContent)); - - if (oldHar != null) { - return Reply.with(oldHar).as(Json.class); - } else { - return Reply.saying().noContent(); - } - } - - @Put - @At("/:port/har/pageRef") - public Reply setPage(@Named("port") int port, Request request) { - String pageRef = request.param("pageRef"); - ProxyServer proxy = proxyManager.get(port); - proxy.newPage(pageRef); - - return Reply.saying().ok(); - } - - @Put - @At("/:port/blacklist") - public Reply blacklist(@Named("port") int port, Request request) { - String blacklist = request.param("regex"); - int responseCode = parseResponseCode(request.param("status")); - ProxyServer proxy = proxyManager.get(port); - proxy.blacklistRequests(blacklist, responseCode); - - return Reply.saying().ok(); - } - - @Put - @At("/:port/whitelist") - public Reply whitelist(@Named("port") int port, Request request) { - String regex = request.param("regex"); - int responseCode = parseResponseCode(request.param("status")); - ProxyServer proxy = proxyManager.get(port); - proxy.whitelistRequests(regex.split(","), responseCode); - - return Reply.saying().ok(); - } - - @Post - @At("/:port/auth/basic/:domain") - public Reply autoBasicAuth(@Named("port") int port, @Named("domain") String domain, Request request) { - Map credentials = request.read(HashMap.class).as(Json.class); - ProxyServer proxy = proxyManager.get(port); - proxy.autoBasicAuthorization(domain, credentials.get("username"), credentials.get("password")); - - return Reply.saying().ok(); - } - - @Post - @At("/:port/headers") - public Reply updateHeaders(@Named("port") int port, Request request) { - ProxyServer proxy = proxyManager.get(port); - Map headers = request.read(Map.class).as(Json.class); - for (Map.Entry entry : headers.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - proxy.addHeader(key, value); - } - return Reply.saying().ok(); - } - - @Post - @At("/:port/interceptor/response") - public Reply addResponseInterceptor(@Named("port") int port, Request request) throws IOException, ScriptException { - ProxyServer proxy = proxyManager.get(port); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - request.readTo(baos); - - ScriptEngineManager mgr = new ScriptEngineManager(); - final ScriptEngine engine = mgr.getEngineByName("JavaScript"); - Compilable compilable = (Compilable) engine; - final CompiledScript script = compilable.compile(baos.toString()); - - proxy.addResponseInterceptor(new ResponseInterceptor() { - @Override - public void process(BrowserMobHttpResponse response) { - Bindings bindings = engine.createBindings(); - bindings.put("response", response); - bindings.put("log", LOG); - try { - script.eval(bindings); - } catch (ScriptException e) { - LOG.severe("Could not execute JS-based response interceptor", e); - } - } - }); - - - - return Reply.saying().ok(); - } - - @Post - @At("/:port/interceptor/request") - public Reply addRequestInterceptor(@Named("port") int port, Request request) throws IOException, ScriptException { - ProxyServer proxy = proxyManager.get(port); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - request.readTo(baos); - - ScriptEngineManager mgr = new ScriptEngineManager(); - final ScriptEngine engine = mgr.getEngineByName("JavaScript"); - Compilable compilable = (Compilable) engine; - final CompiledScript script = compilable.compile(baos.toString()); - - proxy.addRequestInterceptor(new RequestInterceptor() { - @Override - public void process(BrowserMobHttpRequest request) { - Bindings bindings = engine.createBindings(); - bindings.put("request", request); - bindings.put("log", LOG); - try { - script.eval(bindings); - } catch (ScriptException e) { - LOG.severe("Could not execute JS-based response interceptor", e); - } - } - }); - - return Reply.saying().ok(); - } - - @Put - @At("/:port/limit") - public Reply limit(@Named("port") int port, Request request) { - ProxyServer proxy = proxyManager.get(port); - StreamManager streamManager = proxy.getStreamManager(); - String upstreamKbps = request.param("upstreamKbps"); - if (upstreamKbps != null) { - try { - streamManager.setUpstreamKbps(Integer.parseInt(upstreamKbps)); - streamManager.enable(); - } catch (NumberFormatException e) { } - } - String downstreamKbps = request.param("downstreamKbps"); - if (downstreamKbps != null) { - try { - streamManager.setDownstreamKbps(Integer.parseInt(downstreamKbps)); - streamManager.enable(); - } catch (NumberFormatException e) { } - } - String latency = request.param("latency"); - if (latency != null) { - try { - streamManager.setLatency(Integer.parseInt(latency)); - streamManager.enable(); - } catch (NumberFormatException e) { } - } - String payloadPercentage = request.param("payloadPercentage"); - if (payloadPercentage != null) { - try { - streamManager.setPayloadPercentage(Integer.parseInt(payloadPercentage)); - } catch (NumberFormatException e) { } - } - String maxBitsPerSecond = request.param("maxBitsPerSecond"); - if (maxBitsPerSecond != null) { - try { - streamManager.setMaxBitsPerSecondThreshold(Integer.parseInt(maxBitsPerSecond)); - } catch (NumberFormatException e) { } - } - String enable = request.param("enable"); - if (enable != null) { - if( Boolean.parseBoolean(enable) ) { - streamManager.enable(); - } else { - streamManager.disable(); - } - } - return Reply.saying().ok(); - } - - @Put - @At("/:port/timeout") - public Reply timeout(@Named("port") int port, Request request) { - ProxyServer proxy = proxyManager.get(port); - String requestTimeout = request.param("requestTimeout"); - if (requestTimeout != null) { - try { - proxy.setRequestTimeout(Integer.parseInt(requestTimeout)); - } catch (NumberFormatException e) { } - } - String readTimeout = request.param("readTimeout"); - if (readTimeout != null) { - try { - proxy.setSocketOperationTimeout(Integer.parseInt(readTimeout)); - } catch (NumberFormatException e) { } - } - String connectionTimeout = request.param("connectionTimeout"); - if (connectionTimeout != null) { - try { - proxy.setConnectionTimeout(Integer.parseInt(connectionTimeout)); - } catch (NumberFormatException e) { } - } - String dnsCacheTimeout = request.param("dnsCacheTimeout"); - if (dnsCacheTimeout != null) { - try { - proxy.setDNSCacheTimeout(Integer.parseInt(dnsCacheTimeout)); - } catch (NumberFormatException e) { } - } - return Reply.saying().ok(); - } - - @Delete - @At("/:port") - public Reply delete(@Named("port") int port) throws Exception { - proxyManager.delete(port); - return Reply.saying().ok(); - } - - @Post - @At("/:port/hosts") - public Reply remapHosts(@Named("port") int port, Request request) { - ProxyServer proxy = proxyManager.get(port); - @SuppressWarnings("unchecked") Map headers = request.read(Map.class).as(Json.class); - - for (Map.Entry entry : headers.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - proxy.remapHost(key, value); - proxy.setDNSCacheTimeout(0); - proxy.clearDNSCache(); - } - - return Reply.saying().ok(); - } - - - @Put - @At("/:port/wait") - public Reply wait(@Named("port") int port, Request request) { - String quietPeriodInMs = request.param("quietPeriodInMs"); - String timeoutInMs = request.param("timeoutInMs"); - ProxyServer proxy = proxyManager.get(port); - proxy.waitForNetworkTrafficToStop(Integer.parseInt(quietPeriodInMs), Integer.parseInt(timeoutInMs)); - return Reply.saying().ok(); - } - - @Delete - @At("/:port/dns/cache") - public Reply clearDnsCache(@Named("port") int port) throws Exception { - ProxyServer proxy = proxyManager.get(port); - proxy.clearDNSCache(); - return Reply.saying().ok(); - } - - @Put - @At("/:port/rewrite") - public Reply rewriteUrl(@Named("port") int port, Request request) { - String match = request.param("matchRegex"); - String replace = request.param("replace"); - ProxyServer proxy = proxyManager.get(port); - proxy.rewriteUrl(match, replace); - return Reply.saying().ok(); - } - - @Put - @At("/:port/retry") - public Reply retryCount(@Named("port") int port, Request request) { - String count = request.param("retrycount"); - ProxyServer proxy = proxyManager.get(port); - proxy.setRetryCount(Integer.parseInt(count)); - return Reply.saying().ok(); - } - - private int parseResponseCode(String response) - { - int responseCode = 200; - if (response != null) { - try { - responseCode = Integer.parseInt(response); - } catch (NumberFormatException e) { } - } - return responseCode; - } - - public static class ProxyDescriptor { - private int port; - - public ProxyDescriptor() { - } - - public ProxyDescriptor(int port) { - this.port = port; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - } -} diff --git a/src/main/java/org/browsermob/proxy/guice/ConfigModule.java b/src/main/java/org/browsermob/proxy/guice/ConfigModule.java deleted file mode 100644 index b3540f6f0..000000000 --- a/src/main/java/org/browsermob/proxy/guice/ConfigModule.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.browsermob.proxy.guice; - -import com.google.inject.Binder; -import com.google.inject.Key; -import com.google.inject.Module; -import cz.mallat.uasparser.OnlineUpdateUASparser; -import joptsimple.ArgumentAcceptingOptionSpec; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import org.browsermob.proxy.http.BrowserMobHttpClient; - -import java.io.IOException; - -import static java.util.Arrays.asList; - -public class ConfigModule implements Module { - private String[] args; - - public ConfigModule(String[] args) { - this.args = args; - } - - @Override - public void configure(Binder binder) { - OptionParser parser = new OptionParser(); - - ArgumentAcceptingOptionSpec portSpec = - parser.accepts("port", "The port to listen on") - .withOptionalArg().ofType(Integer.class).defaultsTo(8080); - - ArgumentAcceptingOptionSpec userAgentCacheSpec = - parser.accepts("uaCache", "The number of days to cache a database of User-Agent records from http://user-agent-string.info") - .withOptionalArg().ofType(Integer.class).defaultsTo(1); - - parser.acceptsAll(asList("help", "?"), "This help text"); - - OptionSet options = parser.parse(args); - - if (options.has("?")) { - try { - parser.printHelpOn(System.out); - System.exit(0); - } catch (IOException e) { - // should never happen, but... - e.printStackTrace(); - } - return; - } - - binder.bind(Key.get(Integer.class, new NamedImpl("port"))).toInstance(portSpec.value(options)); - - Integer userAgentCacheDays = userAgentCacheSpec.value(options); - if (BrowserMobHttpClient.PARSER instanceof OnlineUpdateUASparser) { - ((OnlineUpdateUASparser) BrowserMobHttpClient.PARSER).setUpdateInterval(1000 * 60 * 60 * 24 * userAgentCacheDays); - } - } -} diff --git a/src/main/java/org/browsermob/proxy/http/BadURIException.java b/src/main/java/org/browsermob/proxy/http/BadURIException.java deleted file mode 100644 index 620f84731..000000000 --- a/src/main/java/org/browsermob/proxy/http/BadURIException.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.browsermob.proxy.http; - -public class BadURIException extends RuntimeException { - public BadURIException(String message) { - super(message); - } -} diff --git a/src/main/java/org/browsermob/proxy/http/BlankCookieStore.java b/src/main/java/org/browsermob/proxy/http/BlankCookieStore.java deleted file mode 100644 index 6174d5fda..000000000 --- a/src/main/java/org/browsermob/proxy/http/BlankCookieStore.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.browsermob.proxy.http; - -import org.apache.http.client.CookieStore; -import org.apache.http.cookie.Cookie; -import org.browsermob.core.har.HarCookie; - -import java.util.Collections; -import java.util.Date; -import java.util.List; - -public class BlankCookieStore implements CookieStore { - @Override - public void addCookie(Cookie cookie) { - HarCookie hc = new HarCookie(); - hc.setDomain(cookie.getDomain()); - hc.setExpires(cookie.getExpiryDate()); - hc.setName(cookie.getName()); - hc.setValue(cookie.getValue()); - hc.setPath(cookie.getPath()); - RequestInfo.get().getEntry().getResponse().getCookies().add(hc); - } - - @Override - public List getCookies() { - return Collections.emptyList(); - } - - @Override - public boolean clearExpired(Date date) { - return false; - } - - @Override - public void clear() { - } -} diff --git a/src/main/java/org/browsermob/proxy/http/BrowserMobHostNameResolver.java b/src/main/java/org/browsermob/proxy/http/BrowserMobHostNameResolver.java deleted file mode 100644 index 4986b0d61..000000000 --- a/src/main/java/org/browsermob/proxy/http/BrowserMobHostNameResolver.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.browsermob.proxy.http; - -import org.apache.http.conn.scheme.HostNameResolver; -import org.browsermob.proxy.util.Log; -import org.xbill.DNS.*; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class BrowserMobHostNameResolver implements HostNameResolver { - private static final Log LOG = new Log(); - - public static ThreadLocal fakeSlow = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return false; - } - }; - - private Map remappings = new ConcurrentHashMap(); - private Map> reverseMapping = new ConcurrentHashMap>(); - - private Cache cache; - private Resolver resolver; - - public BrowserMobHostNameResolver(Cache cache) { - this.cache = cache; - try { - resolver = new ExtendedResolver(); - } catch (UnknownHostException e) { - throw new RuntimeException(e); - } - } - - @Override - public InetAddress resolve(String hostname) throws IOException { - String remapping = remappings.get(hostname); - if (remapping != null) { - hostname = remapping; - } - - try { - return Address.getByAddress(hostname); - } catch (UnknownHostException e) { - // that's fine, this just means it's not an IP address and we gotta look it up, which is common - } - - boolean isCached = this.isCached(hostname); - - Lookup lookup = new Lookup(Name.fromString(hostname), Type.A); - lookup.setCache(cache); - lookup.setResolver(resolver); - - Date start = new Date(); - Record[] records = lookup.run(); - if (fakeSlow.get()) { - fakeSlow.set(false); - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - Date end = new Date(); - - if (records == null || records.length == 0) { - throw new UnknownHostException(hostname); - } - - // assembly the addr object - ARecord a = (ARecord) records[0]; - InetAddress addr = InetAddress.getByAddress(hostname, a.getAddress().getAddress()); - - if (!isCached) { - // TODO: Associate the the host name with the connection. We do this because when using persistent - // connections there won't be a lookup on the 2nd, 3rd, etc requests, and as such we wouldn't be able to - // know what IP address we were requesting. - RequestInfo.get().dns(start, end, addr.getHostAddress()); - } else { - // if it is a cached hit, we just record zero since we don't want - // to skew the data with method call timings (specially under load) - RequestInfo.get().dns(end, end, addr.getHostAddress()); - } - - return addr; - } - - public void remap(String source, String target) { - remappings.put(source, target); - List list = reverseMapping.get(target); - if (list == null) { - list = new ArrayList(); - } - list.add(source); - reverseMapping.put(target, list); - } - - public String remapping(String host) { - return remappings.get(host); - } - - public List original(String host) { - return reverseMapping.get(host); - } - - public void clearCache() { - this.cache.clearCache(); - } - - public void setCacheTimeout(int timeout) { - cache.setMaxCache(timeout); - } - - public boolean isCached(String hostname) throws TextParseException { - return cache.lookupRecords(Name.fromString(hostname), Type.ANY, 3).isSuccessful(); - } -} diff --git a/src/main/java/org/browsermob/proxy/http/BrowserMobHttpClient.java b/src/main/java/org/browsermob/proxy/http/BrowserMobHttpClient.java deleted file mode 100644 index b66b99c16..000000000 --- a/src/main/java/org/browsermob/proxy/http/BrowserMobHttpClient.java +++ /dev/null @@ -1,1137 +0,0 @@ -package org.browsermob.proxy.http; - -import cz.mallat.uasparser.CachingOnlineUpdateUASparser; -import cz.mallat.uasparser.UASparser; -import cz.mallat.uasparser.UserAgentInfo; -import org.apache.http.*; -import org.apache.http.auth.*; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.methods.*; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.params.CookiePolicy; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.conn.ClientConnectionRequest; -import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.ManagedClientConnection; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.cookie.*; -import org.apache.http.cookie.params.CookieSpecPNames; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.impl.cookie.BasicClientCookie; -import org.apache.http.impl.cookie.BrowserCompatSpec; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.HttpRequestExecutor; -import org.browsermob.core.har.*; -import org.browsermob.proxy.util.*; -import org.eclipse.jetty.util.MultiMap; -import org.eclipse.jetty.util.UrlEncoded; -import org.java_bandwidthlimiter.StreamManager; -import org.xbill.DNS.Cache; -import org.xbill.DNS.DClass; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.GZIPInputStream; - -public class BrowserMobHttpClient { - private static final Log LOG = new Log(); - public static UASparser PARSER = null; - - static { - try { - PARSER = new CachingOnlineUpdateUASparser(); - } catch (IOException e) { - LOG.severe("Unable to create User-Agent parser, falling back but proxy is in damaged state and should be restarted", e); - try { - PARSER = new UASparser(); - } catch (Exception e1) { - // ignore - } - } - } - - public static void setUserAgentParser(UASparser parser) { - PARSER = parser; - } - - private static final int BUFFER = 4096; - - private Har har; - private String harPageRef; - - private boolean captureHeaders; - private boolean captureContent; - // if captureContent is set, default policy is to capture binary contents too - private boolean captureBinaryContent = true; - - private SimulatedSocketFactory socketFactory; - private TrustingSSLSocketFactory sslSocketFactory; - private ThreadSafeClientConnManager httpClientConnMgr; - private DefaultHttpClient httpClient; - private List blacklistEntries = null; - private WhitelistEntry whitelistEntry = null; - private List rewriteRules = new CopyOnWriteArrayList(); - private List requestInterceptors = new CopyOnWriteArrayList(); - private List responseInterceptors = new CopyOnWriteArrayList(); - private HashMap additionalHeaders = new LinkedHashMap(); - private int requestTimeout; - private AtomicBoolean allowNewRequests = new AtomicBoolean(true); - private BrowserMobHostNameResolver hostNameResolver; - private boolean decompress = true; - // not using CopyOnWriteArray because we're WRITE heavy and it is for READ heavy operations - // instead doing it the old fashioned way with a synchronized block - private final Set activeRequests = new HashSet(); - private WildcardMatchingCredentialsProvider credsProvider; - private boolean shutdown = false; - private AuthType authType; - - private boolean followRedirects = true; - private static final int MAX_REDIRECT = 10; - private AtomicInteger requestCounter; - - public BrowserMobHttpClient(StreamManager streamManager, AtomicInteger requestCounter) { - this.requestCounter = requestCounter; - SchemeRegistry schemeRegistry = new SchemeRegistry(); - hostNameResolver = new BrowserMobHostNameResolver(new Cache(DClass.ANY)); - - this.socketFactory = new SimulatedSocketFactory(hostNameResolver, streamManager); - this.sslSocketFactory = new TrustingSSLSocketFactory(hostNameResolver, streamManager); - - this.sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); - - schemeRegistry.register(new Scheme("http", 80, socketFactory)); - schemeRegistry.register(new Scheme("https", 443, sslSocketFactory)); - - httpClientConnMgr = new ThreadSafeClientConnManager(schemeRegistry) { - @Override - public ClientConnectionRequest requestConnection(HttpRoute route, Object state) { - final ClientConnectionRequest wrapped = super.requestConnection(route, state); - return new ClientConnectionRequest() { - @Override - public ManagedClientConnection getConnection(long timeout, TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { - Date start = new Date(); - try { - return wrapped.getConnection(timeout, tunit); - } finally { - RequestInfo.get().blocked(start, new Date()); - } - } - - @Override - public void abortRequest() { - wrapped.abortRequest(); - } - }; - } - }; - - // MOB-338: 30 total connections and 6 connections per host matches the behavior in Firefox 3 - httpClientConnMgr.setMaxTotal(30); - httpClientConnMgr.setDefaultMaxPerRoute(6); - - httpClient = new DefaultHttpClient(httpClientConnMgr) { - @Override - protected HttpRequestExecutor createRequestExecutor() { - return new HttpRequestExecutor() { - @Override - protected HttpResponse doSendRequest(HttpRequest request, HttpClientConnection conn, HttpContext context) throws IOException, HttpException { - Date start = new Date(); - HttpResponse response = super.doSendRequest(request, conn, context); - RequestInfo.get().send(start, new Date()); - return response; - } - - @Override - protected HttpResponse doReceiveResponse(HttpRequest request, HttpClientConnection conn, HttpContext context) throws HttpException, IOException { - Date start = new Date(); - HttpResponse response = super.doReceiveResponse(request, conn, context); - RequestInfo.get().wait(start, new Date()); - return response; - } - }; - } - }; - credsProvider = new WildcardMatchingCredentialsProvider(); - httpClient.setCredentialsProvider(credsProvider); - httpClient.addRequestInterceptor(new PreemptiveAuth(), 0); - httpClient.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, true); - httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY); - httpClient.getParams().setParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, Boolean.TRUE); - setRetryCount(0); - - // we always set this to false so it can be handled manually: - httpClient.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false); - - HttpClientInterrupter.watch(this); - setConnectionTimeout(60000); - setSocketOperationTimeout(60000); - setRequestTimeout(-1); - } - - public void setRetryCount(int count) { - httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(count, false)); - } - - public void remapHost(String source, String target) { - hostNameResolver.remap(source, target); - } - - @Deprecated - public void addRequestInterceptor(HttpRequestInterceptor i) { - httpClient.addRequestInterceptor(i); - } - - public void addRequestInterceptor(RequestInterceptor interceptor) { - requestInterceptors.add(interceptor); - } - - @Deprecated - public void addResponseInterceptor(HttpResponseInterceptor i) { - httpClient.addResponseInterceptor(i); - } - - public void addResponseInterceptor(ResponseInterceptor interceptor) { - responseInterceptors.add(interceptor); - } - - public void createCookie(String name, String value, String domain) { - createCookie(name, value, domain, null); - } - - public void createCookie(String name, String value, String domain, String path) { - BasicClientCookie cookie = new BasicClientCookie(name, value); - cookie.setDomain(domain); - if (path != null) { - cookie.setPath(path); - } - httpClient.getCookieStore().addCookie(cookie); - } - - public void clearCookies() { - httpClient.getCookieStore().clear(); - } - - public Cookie getCookie(String name) { - return getCookie(name, null, null); - } - - public Cookie getCookie(String name, String domain) { - return getCookie(name, domain, null); - } - - public Cookie getCookie(String name, String domain, String path) { - for (Cookie cookie : httpClient.getCookieStore().getCookies()) { - if(cookie.getName().equals(name)) { - if(domain != null && !domain.equals(cookie.getDomain())) { - continue; - } - if(path != null && !path.equals(cookie.getPath())) { - continue; - } - - return cookie; - } - } - - return null; - } - - public BrowserMobHttpRequest newPost(String url, org.browsermob.proxy.jetty.http.HttpRequest proxyRequest) { - try { - URI uri = makeUri(url); - return new BrowserMobHttpRequest(new HttpPost(uri), this, -1, captureContent, proxyRequest); - } catch (URISyntaxException e) { - throw reportBadURI(url, "POST"); - } - } - - public BrowserMobHttpRequest newGet(String url, org.browsermob.proxy.jetty.http.HttpRequest proxyRequest) { - try { - URI uri = makeUri(url); - return new BrowserMobHttpRequest(new HttpGet(uri), this, -1, captureContent, proxyRequest); - } catch (URISyntaxException e) { - throw reportBadURI(url, "GET"); - } - } - - public BrowserMobHttpRequest newPut(String url, org.browsermob.proxy.jetty.http.HttpRequest proxyRequest) { - try { - URI uri = makeUri(url); - return new BrowserMobHttpRequest(new HttpPut(uri), this, -1, captureContent, proxyRequest); - } catch (Exception e) { - throw reportBadURI(url, "PUT"); - } - } - - public BrowserMobHttpRequest newDelete(String url, org.browsermob.proxy.jetty.http.HttpRequest proxyRequest) { - try { - URI uri = makeUri(url); - return new BrowserMobHttpRequest(new HttpDelete(uri), this, -1, captureContent, proxyRequest); - } catch (URISyntaxException e) { - throw reportBadURI(url, "DELETE"); - } - } - - public BrowserMobHttpRequest newOptions(String url, org.browsermob.proxy.jetty.http.HttpRequest proxyRequest) { - try { - URI uri = makeUri(url); - return new BrowserMobHttpRequest(new HttpOptions(uri), this, -1, captureContent, proxyRequest); - } catch (URISyntaxException e) { - throw reportBadURI(url, "OPTIONS"); - } - } - - public BrowserMobHttpRequest newHead(String url, org.browsermob.proxy.jetty.http.HttpRequest proxyRequest) { - try { - URI uri = makeUri(url); - return new BrowserMobHttpRequest(new HttpHead(uri), this, -1, captureContent, proxyRequest); - } catch (URISyntaxException e) { - throw reportBadURI(url, "HEAD"); - } - } - - private URI makeUri(String url) throws URISyntaxException { - // MOB-120: check for | character and change to correctly escaped %7C - url = url.replace(" ", "%20"); - url = url.replace(">", "%3C"); - url = url.replace("<", "%3E"); - url = url.replace("#", "%23"); - url = url.replace("{", "%7B"); - url = url.replace("}", "%7D"); - url = url.replace("|", "%7C"); - url = url.replace("\\", "%5C"); - url = url.replace("^", "%5E"); - url = url.replace("~", "%7E"); - url = url.replace("[", "%5B"); - url = url.replace("]", "%5D"); - url = url.replace("`", "%60"); - url = url.replace("\"", "%22"); - - URI uri = new URI(url); - - // are we using the default ports for http/https? if so, let's rewrite the URI to make sure the :80 or :443 - // is NOT included in the string form the URI. The reason we do this is that in HttpClient 4.0 the Host header - // would include a value such as "yahoo.com:80" rather than "yahoo.com". Not sure why this happens but we don't - // want it to, and rewriting the URI solves it - if ((uri.getPort() == 80 && "http".equals(uri.getScheme())) - || (uri.getPort() == 443 && "https".equals(uri.getScheme()))) { - // we rewrite the URL with a StringBuilder (vs passing in the components of the URI) because if we were - // to pass in these components using the URI's 7-arg constructor query parameters get double escaped (bad!) - StringBuilder sb = new StringBuilder(uri.getScheme()).append("://"); - if (uri.getRawUserInfo() != null) { - sb.append(uri.getRawUserInfo()).append("@"); - } - sb.append(uri.getHost()); - if (uri.getRawPath() != null) { - sb.append(uri.getRawPath()); - } - if (uri.getRawQuery() != null) { - sb.append("?").append(uri.getRawQuery()); - } - if (uri.getRawFragment() != null) { - sb.append("#").append(uri.getRawFragment()); - } - - uri = new URI(sb.toString()); - } - return uri; - } - - private RuntimeException reportBadURI(String url, String method) { - if (this.har != null && harPageRef != null) { - HarEntry entry = new HarEntry(harPageRef); - entry.setTime(0); - entry.setRequest(new HarRequest(method, url, "HTTP/1.1")); - entry.setResponse(new HarResponse(-998, "Bad URI", "HTTP/1.1")); - entry.setTimings(new HarTimings()); - har.getLog().addEntry(entry); - } - - throw new BadURIException("Bad URI requested: " + url); - } - - public void checkTimeout() { - synchronized (activeRequests) { - for (ActiveRequest activeRequest : activeRequests) { - activeRequest.checkTimeout(); - } - } - } - - public BrowserMobHttpResponse execute(BrowserMobHttpRequest req) { - if (!allowNewRequests.get()) { - throw new RuntimeException("No more requests allowed"); - } - - try { - requestCounter.incrementAndGet(); - - for (RequestInterceptor interceptor : requestInterceptors) { - interceptor.process(req); - } - - BrowserMobHttpResponse response = execute(req, 1); - for (ResponseInterceptor interceptor : responseInterceptors) { - interceptor.process(response); - } - - return response; - } finally { - requestCounter.decrementAndGet(); - } - } - - // - //If we were making cake, this would be the filling :) - // - private BrowserMobHttpResponse execute(BrowserMobHttpRequest req, int depth) { - if (depth >= MAX_REDIRECT) { - throw new IllegalStateException("Max number of redirects (" + MAX_REDIRECT + ") reached"); - } - - RequestCallback callback = req.getRequestCallback(); - - HttpRequestBase method = req.getMethod(); - String verificationText = req.getVerificationText(); - String url = method.getURI().toString(); - - // save the browser and version if it's not yet been set - if (har != null && har.getLog().getBrowser() == null) { - Header[] uaHeaders = method.getHeaders("User-Agent"); - if (uaHeaders != null && uaHeaders.length > 0) { - String userAgent = uaHeaders[0].getValue(); - try { - // note: this doesn't work for 'Fandango/4.5.1 CFNetwork/548.1.4 Darwin/11.0.0' - UserAgentInfo uai = PARSER.parse(userAgent); - String name = uai.getUaName(); - int lastSpace = name.lastIndexOf(' '); - String browser = name.substring(0, lastSpace); - String version = name.substring(lastSpace + 1); - har.getLog().setBrowser(new HarNameVersion(browser, version)); - } catch (IOException e) { - // ignore it, it's fine - } catch (Exception e) { - LOG.warn("Failed to parse user agent string", e); - } - } - } - - // process any rewrite requests - boolean rewrote = false; - String newUrl = url; - for (RewriteRule rule : rewriteRules) { - Matcher matcher = rule.match.matcher(newUrl); - newUrl = matcher.replaceAll(rule.replace); - rewrote = true; - } - - if (rewrote) { - try { - method.setURI(new URI(newUrl)); - url = newUrl; - } catch (URISyntaxException e) { - LOG.warn("Could not rewrite url to %s", newUrl); - } - } - - // handle whitelist and blacklist entries - int mockResponseCode = -1; - if (whitelistEntry != null) { - boolean found = false; - for (Pattern pattern : whitelistEntry.patterns) { - if (pattern.matcher(url).matches()) { - found = true; - break; - } - } - - if (!found) { - mockResponseCode = whitelistEntry.responseCode; - } - } - - if (blacklistEntries != null) { - for (BlacklistEntry blacklistEntry : blacklistEntries) { - if (blacklistEntry.pattern.matcher(url).matches()) { - mockResponseCode = blacklistEntry.responseCode; - break; - } - } - } - - if (!additionalHeaders.isEmpty()) { - // Set the additional headers - for (Map.Entry entry : additionalHeaders.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - method.removeHeaders(key); - method.addHeader(key, value); - } - } - - - String charSet = "UTF-8"; - String responseBody = null; - - InputStream is = null; - int statusCode = -998; - long bytes = 0; - boolean gzipping = false; - boolean contentMatched = true; - OutputStream os = req.getOutputStream(); - if (os == null) { - os = new CappedByteArrayOutputStream(1024 * 1024); // MOB-216 don't buffer more than 1 MB - } - if (verificationText != null) { - contentMatched = false; - } - Date start = new Date(); - - // link the object up now, before we make the request, so that if we get cut off (ie: favicon.ico request and browser shuts down) - // we still have the attempt associated, even if we never got a response - HarEntry entry = new HarEntry(harPageRef); - - // clear out any connection-related information so that it's not stale from previous use of this thread. - RequestInfo.clear(url, entry); - - entry.setRequest(new HarRequest(method.getMethod(), url, method.getProtocolVersion().getProtocol())); - entry.setResponse(new HarResponse(-999, "NO RESPONSE", method.getProtocolVersion().getProtocol())); - if (this.har != null && harPageRef != null) { - har.getLog().addEntry(entry); - } - - String query = method.getURI().getRawQuery(); - if (query != null) { - MultiMap params = new MultiMap(); - UrlEncoded.decodeTo(query, params, "UTF-8"); - for (String k : params.keySet()) { - for (Object v : params.getValues(k)) { - entry.getRequest().getQueryString().add(new HarNameValuePair(k, (String) v)); - } - } - } - - String errorMessage = null; - HttpResponse response = null; - - BasicHttpContext ctx = new BasicHttpContext(); - - ActiveRequest activeRequest = new ActiveRequest(method, ctx, entry.getStartedDateTime()); - synchronized (activeRequests) { - activeRequests.add(activeRequest); - } - - // for dealing with automatic authentication - if (authType == AuthType.NTLM) { - // todo: not supported yet - //ctx.setAttribute("preemptive-auth", new NTLMScheme(new JCIFSEngine())); - } else if (authType == AuthType.BASIC) { - ctx.setAttribute("preemptive-auth", new BasicScheme()); - } - - StatusLine statusLine = null; - try { - // set the User-Agent if it's not already set - if (method.getHeaders("User-Agent").length == 0) { - method.addHeader("User-Agent", "BrowserMob VU/1.0"); - } - - // was the request mocked out? - if (mockResponseCode != -1) { - statusCode = mockResponseCode; - - // TODO: HACKY!! - callback.handleHeaders(new Header[]{ - new Header(){ - @Override - public String getName() { - return "Content-Type"; - } - - @Override - public String getValue() { - return "text/plain"; - } - - @Override - public HeaderElement[] getElements() throws ParseException { - return new HeaderElement[0]; - } - } - }); - } else { - response = httpClient.execute(method, ctx); - statusLine = response.getStatusLine(); - statusCode = statusLine.getStatusCode(); - - if (callback != null) { - callback.handleStatusLine(statusLine); - callback.handleHeaders(response.getAllHeaders()); - } - - if (response.getEntity() != null) { - is = response.getEntity().getContent(); - } - - // check for null (resp 204 can cause HttpClient to return null, which is what Google does with http://clients1.google.com/generate_204) - if (is != null) { - Header contentEncodingHeader = response.getFirstHeader("Content-Encoding"); - if (contentEncodingHeader != null && "gzip".equalsIgnoreCase(contentEncodingHeader.getValue())) { - gzipping = true; - } - - // deal with GZIP content! - if (decompress && gzipping) { - is = new GZIPInputStream(is); - } - - if (captureContent) { - // todo - something here? - os = new ClonedOutputStream(os); - - } - - bytes = copyWithStats(is, os); - } - } - } catch (Exception e) { - errorMessage = e.toString(); - - if (callback != null) { - callback.reportError(e); - } - - // only log it if we're not shutdown (otherwise, errors that happen during a shutdown can likely be ignored) - if (!shutdown) { - LOG.info(String.format("%s when requesting %s", errorMessage, url)); - } - } finally { - // the request is done, get it out of here - synchronized (activeRequests) { - activeRequests.remove(activeRequest); - } - - if (is != null) { - try { - is.close(); - } catch (IOException e) { - // this is OK to ignore - } - } - } - - // record the response as ended - RequestInfo.get().finish(); - - // set the start time and other timings - entry.setStartedDateTime(RequestInfo.get().getStart()); - entry.setTimings(RequestInfo.get().getTimings()); - entry.setServerIPAddress(RequestInfo.get().getResolvedAddress()); - entry.setTime(RequestInfo.get().getTotalTime()); - - // todo: where you store this in HAR? - // obj.setErrorMessage(errorMessage); - entry.getResponse().setBodySize(bytes); - entry.getResponse().getContent().setSize(bytes); - entry.getResponse().setStatus(statusCode); - if (statusLine != null) { - entry.getResponse().setStatusText(statusLine.getReasonPhrase()); - } - - boolean urlEncoded = false; - if (captureHeaders || captureContent) { - for (Header header : method.getAllHeaders()) { - if (header.getValue() != null && header.getValue().startsWith(URLEncodedUtils.CONTENT_TYPE)) { - urlEncoded = true; - } - - entry.getRequest().getHeaders().add(new HarNameValuePair(header.getName(), header.getValue())); - } - - if (response != null) { - for (Header header : response.getAllHeaders()) { - entry.getResponse().getHeaders().add(new HarNameValuePair(header.getName(), header.getValue())); - } - } - } - - if (captureContent) { - // can we understand the POST data at all? - if (method instanceof HttpEntityEnclosingRequestBase && req.getCopy() != null) { - HttpEntityEnclosingRequestBase enclosingReq = (HttpEntityEnclosingRequestBase) method; - HttpEntity entity = enclosingReq.getEntity(); - - HarPostData data = new HarPostData(); - data.setMimeType(req.getMethod().getFirstHeader("Content-Type").getValue()); - entry.getRequest().setPostData(data); - - if (urlEncoded || URLEncodedUtils.isEncoded(entity)) { - try { - final String content = new String(req.getCopy().toByteArray(), "UTF-8"); - if (content != null && content.length() > 0) { - List result = new ArrayList(); - URLEncodedUtils.parse(result, new Scanner(content), null); - - ArrayList params = new ArrayList(); - data.setParams(params); - - for (NameValuePair pair : result) { - params.add(new HarPostDataParam(pair.getName(), pair.getValue())); - } - } - } catch (Exception e) { - LOG.info("Unexpected problem when parsing input copy", e); - } - } else { - // not URL encoded, so let's grab the body of the POST and capture that - data.setText(new String(req.getCopy().toByteArray())); - } - } - } - - //capture request cookies - javax.servlet.http.Cookie[] cookies = req.getProxyRequest().getCookies(); - for (javax.servlet.http.Cookie cookie : cookies) { - HarCookie hc = new HarCookie(); - hc.setName(cookie.getName()); - hc.setValue(cookie.getValue()); - entry.getRequest().getCookies().add(hc); - } - - String contentType = null; - - if (response != null) { - try { - Header contentTypeHdr = response.getFirstHeader("Content-Type"); - if (contentTypeHdr != null) { - contentType = contentTypeHdr.getValue(); - entry.getResponse().getContent().setMimeType(contentType); - - if (captureContent && os != null && os instanceof ClonedOutputStream) { - ByteArrayOutputStream copy = ((ClonedOutputStream) os).getOutput(); - - if (gzipping) { - // ok, we need to decompress it before we can put it in the har file - try { - InputStream temp = new GZIPInputStream(new ByteArrayInputStream(copy.toByteArray())); - copy = new ByteArrayOutputStream(); - IOUtils.copy(temp, copy); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - if (contentType != null && (contentType.startsWith("text/") || - contentType.startsWith("application/x-javascript")) || - contentType.startsWith("application/javascript") || - contentType.startsWith("application/json") || - contentType.startsWith("application/xml") || - contentType.startsWith("application/xhtml+xml")) { - entry.getResponse().getContent().setText(new String(copy.toByteArray())); - } else if(captureBinaryContent){ - entry.getResponse().getContent().setText(Base64.byteArrayToBase64(copy.toByteArray())); - } - } - - - NameValuePair nvp = contentTypeHdr.getElements()[0].getParameterByName("charset"); - - if (nvp != null) { - charSet = nvp.getValue(); - } - } - - if (os instanceof ByteArrayOutputStream) { - responseBody = ((ByteArrayOutputStream) os).toString(charSet); - - if (verificationText != null) { - contentMatched = responseBody.contains(verificationText); - } - } - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - if (contentType != null) { - entry.getResponse().getContent().setMimeType(contentType); - } - - // checking to see if the client is being redirected - boolean isRedirect = false; - - String location = null; - if (response != null && statusCode >= 300 && statusCode < 400 && statusCode != 304) { - isRedirect = true; - - // pulling the header for the redirect - Header locationHeader = response.getLastHeader("location"); - if (locationHeader != null) { - location = locationHeader.getValue(); - } else if (this.followRedirects) { - throw new RuntimeException("Invalid redirect - missing location header"); - } - } - - // - // Response validation - they only work if we're not following redirects - // - - int expectedStatusCode = req.getExpectedStatusCode(); - - // if we didn't mock out the actual response code and the expected code isn't what we saw, we have a problem - if (mockResponseCode == -1 && expectedStatusCode > -1) { - if (this.followRedirects) { - throw new RuntimeException("Response validation cannot be used while following redirects"); - } - if (expectedStatusCode != statusCode) { - if (isRedirect) { - throw new RuntimeException("Expected status code of " + expectedStatusCode + " but saw " + statusCode - + " redirecting to: " + location); - } else { - throw new RuntimeException("Expected status code of " + expectedStatusCode + " but saw " + statusCode); - } - } - } - - // Location header check: - if (isRedirect && (req.getExpectedLocation() != null)) { - if (this.followRedirects) { - throw new RuntimeException("Response validation cannot be used while following redirects"); - } - - if (location.compareTo(req.getExpectedLocation()) != 0) { - throw new RuntimeException("Expected a redirect to " + req.getExpectedLocation() + " but saw " + location); - } - } - - // end of validation logic - - // basic tail recursion for redirect handling - if (isRedirect && this.followRedirects) { - // updating location: - try { - URI redirectUri = new URI(location); - URI newUri = method.getURI().resolve(redirectUri); - method.setURI(newUri); - - return execute(req, ++depth); - } catch (URISyntaxException e) { - LOG.warn("Could not parse URL", e); - } - } - - - return new BrowserMobHttpResponse(entry, method, response, contentMatched, verificationText, errorMessage, responseBody, contentType, charSet); - } - - public void shutdown() { - shutdown = true; - abortActiveRequests(); - rewriteRules.clear(); - credsProvider.clear(); - httpClientConnMgr.shutdown(); - HttpClientInterrupter.release(this); - } - - public void abortActiveRequests() { - allowNewRequests.set(true); - - synchronized (activeRequests) { - for (ActiveRequest activeRequest : activeRequests) { - activeRequest.abort(); - } - activeRequests.clear(); - } - } - - public void setHar(Har har) { - this.har = har; - } - - public void setHarPageRef(String harPageRef) { - this.harPageRef = harPageRef; - } - - public void setRequestTimeout(int requestTimeout) { - this.requestTimeout = requestTimeout; - } - - public void setSocketOperationTimeout(int readTimeout) { - httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeout); - } - - public void setConnectionTimeout(int connectionTimeout) { - httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout); - } - - public void setFollowRedirects(boolean followRedirects) { - this.followRedirects = followRedirects; - - } - - public boolean isFollowRedirects() { - return followRedirects; - } - - public void autoBasicAuthorization(String domain, String username, String password) { - authType = AuthType.BASIC; - httpClient.getCredentialsProvider().setCredentials( - new AuthScope(domain, -1), - new UsernamePasswordCredentials(username, password)); - } - - public void autoNTLMAuthorization(String domain, String username, String password) { - authType = AuthType.NTLM; - httpClient.getCredentialsProvider().setCredentials( - new AuthScope(domain, -1), - new NTCredentials(username, password, "workstation", domain)); - } - - public void rewriteUrl(String match, String replace) { - rewriteRules.add(new RewriteRule(match, replace)); - } - - // this method is provided for backwards compatibility before we renamed it to - // blacklistRequests (note the plural) - public void blacklistRequest(String pattern, int responseCode) { - blacklistRequests(pattern, responseCode); - } - - public void blacklistRequests(String pattern, int responseCode) { - if (blacklistEntries == null) { - blacklistEntries = new CopyOnWriteArrayList(); - } - - blacklistEntries.add(new BlacklistEntry(pattern, responseCode)); - } - - public void whitelistRequests(String[] patterns, int responseCode) { - whitelistEntry = new WhitelistEntry(patterns, responseCode); - } - - public void addHeader(String name, String value) { - additionalHeaders.put(name, value); - } - - public void prepareForBrowser() { - // Clear cookies, let the browser handle them - httpClient.setCookieStore(new BlankCookieStore()); - httpClient.getCookieSpecs().register("easy", new CookieSpecFactory() { - @Override - public CookieSpec newInstance(HttpParams params) { - return new BrowserCompatSpec() { - @Override - public void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException { - // easy! - } - }; - } - }); - httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, "easy"); - decompress = false; - setFollowRedirects(false); - } - - public String remappedHost(String host) { - return hostNameResolver.remapping(host); - } - - public List originalHosts(String host) { - return hostNameResolver.original(host); - } - - public Har getHar() { - return har; - } - - public void setCaptureHeaders(boolean captureHeaders) { - this.captureHeaders = captureHeaders; - } - - public void setCaptureContent(boolean captureContent) { - this.captureContent = captureContent; - } - - public void setCaptureBinaryContent(boolean captureBinaryContent) { - this.captureBinaryContent = captureBinaryContent; - } - - public void setHttpProxy(String httpProxy) { - String host = httpProxy.split(":")[0]; - Integer port = Integer.parseInt(httpProxy.split(":")[1]); - HttpHost proxy = new HttpHost(host, port); - httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,proxy); - } - - static class PreemptiveAuth implements HttpRequestInterceptor { - public void process( - final HttpRequest request, - final HttpContext context) throws HttpException, IOException { - - AuthState authState = (AuthState) context.getAttribute( - ClientContext.TARGET_AUTH_STATE); - - // If no auth scheme avaialble yet, try to initialize it preemptively - if (authState.getAuthScheme() == null) { - AuthScheme authScheme = (AuthScheme) context.getAttribute( - "preemptive-auth"); - CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute( - ClientContext.CREDS_PROVIDER); - HttpHost targetHost = (HttpHost) context.getAttribute( - ExecutionContext.HTTP_TARGET_HOST); - if (authScheme != null) { - Credentials creds = credsProvider.getCredentials( - new AuthScope( - targetHost.getHostName(), - targetHost.getPort())); - if (creds != null) { - authState.setAuthScheme(authScheme); - authState.setCredentials(creds); - } - } - } - } - } - - class ActiveRequest { - HttpRequestBase request; - BasicHttpContext ctx; - Date start; - - ActiveRequest(HttpRequestBase request, BasicHttpContext ctx, Date start) { - this.request = request; - this.ctx = ctx; - this.start = start; - } - - void checkTimeout() { - if (requestTimeout != -1) { - if (request != null && start != null && new Date(System.currentTimeMillis() - requestTimeout).after(start)) { - LOG.info("Aborting request to %s after it failed to complete in %d ms", request.getURI().toString(), requestTimeout); - - abort(); - } - } - } - - public void abort() { - request.abort(); - - // try to close the connection? is this necessary? unclear based on preliminary debugging of HttpClient, but - // it doesn't seem to hurt to try - HttpConnection conn = (HttpConnection) ctx.getAttribute("http.connection"); - if (conn != null) { - try { - conn.close(); - } catch (IOException e) { - // this is fine, we're shutting it down anyway - } - } - } - } - - private class WhitelistEntry { - private List patterns = new CopyOnWriteArrayList(); - private int responseCode; - - private WhitelistEntry(String[] patterns, int responseCode) { - for (String pattern : patterns) { - this.patterns.add(Pattern.compile(pattern)); - } - this.responseCode = responseCode; - } - } - - private class BlacklistEntry { - private Pattern pattern; - private int responseCode; - - private BlacklistEntry(String pattern, int responseCode) { - this.pattern = Pattern.compile(pattern); - this.responseCode = responseCode; - } - } - - private class RewriteRule { - private Pattern match; - private String replace; - - private RewriteRule(String match, String replace) { - this.match = Pattern.compile(match); - this.replace = replace; - } - } - - private enum AuthType { - NONE, BASIC, NTLM - } - - public void clearDNSCache() { - this.hostNameResolver.clearCache(); - } - - public void setDNSCacheTimeout(int timeout) { - this.hostNameResolver.setCacheTimeout(timeout); - } - - public static long copyWithStats(InputStream is, OutputStream os) throws IOException { - long bytesCopied = 0; - byte[] buffer = new byte[BUFFER]; - int length; - - try { - // read the first byte - int firstByte = is.read(); - - if (firstByte == -1) { - return 0; - } - - os.write(firstByte); - bytesCopied++; - - do { - length = is.read(buffer, 0, BUFFER); - if (length != -1) { - bytesCopied += length; - os.write(buffer, 0, length); - os.flush(); - } - } while (length != -1); - } finally { - try { - is.close(); - } catch (IOException e) { - // ok to ignore - } - - try { - os.close(); - } catch (IOException e) { - // ok to ignore - } - } - - return bytesCopied; - } -} diff --git a/src/main/java/org/browsermob/proxy/http/HttpClientInterrupter.java b/src/main/java/org/browsermob/proxy/http/HttpClientInterrupter.java deleted file mode 100644 index 8332fbb20..000000000 --- a/src/main/java/org/browsermob/proxy/http/HttpClientInterrupter.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.browsermob.proxy.http; - -import org.browsermob.proxy.util.Log; - -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -public class HttpClientInterrupter { - private static final Log LOG = new Log(); - private static Set clients = new CopyOnWriteArraySet(); - - static { - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - while (true) { - for (BrowserMobHttpClient client : clients) { - try { - client.checkTimeout(); - } catch (Exception e) { - LOG.severe("Unexpected problem while checking timeout on a client", e); - } - } - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // this is OK - } - } - } - }, "HttpClientInterrupter Thread"); - thread.setDaemon(true); - thread.start(); - } - - public static void watch(BrowserMobHttpClient client) { - clients.add(client); - } - - public static void release(BrowserMobHttpClient client) { - clients.remove(client); - } -} diff --git a/src/main/java/org/browsermob/proxy/http/RequestInfo.java b/src/main/java/org/browsermob/proxy/http/RequestInfo.java deleted file mode 100644 index deb866877..000000000 --- a/src/main/java/org/browsermob/proxy/http/RequestInfo.java +++ /dev/null @@ -1,233 +0,0 @@ -package org.browsermob.proxy.http; - -import org.browsermob.core.har.HarEntry; -import org.browsermob.core.har.HarTimings; -import org.browsermob.proxy.util.Log; - -import java.util.Date; - -public class RequestInfo { - private static final Log LOG = new Log(); - - private static ThreadLocal instance = new ThreadLocal() { - @Override - protected RequestInfo initialValue() { - return new RequestInfo(); - } - }; - - public static RequestInfo get() { - return instance.get(); - } - - public static void clear(String url, HarEntry entry) { - clear(); - RequestInfo info = get(); - info.url = url; - info.entry = entry; - } - - private static void clear() { - RequestInfo info = get(); - info.blocked = null; - info.dns = null; - info.connect = null; - info.ssl = null; - info.send = null; - info.wait = null; - info.receive = null; - info.resolvedAddress = null; - info.start = null; - info.end = null; - } - - private Long blocked; - private Long dns; - private Long connect; - private Long ssl; - private Long send; - private Long wait; - private Long receive; - private String resolvedAddress; - private Date start; - private Date end; - private String url; - private HarEntry entry; - - private Long ping(Date start, Date end) { - if (this.start == null) { - this.start = start; - } else if (this.start.after(start)) { - LOG.severe("Saw a later start time that was before the first start time for URL %s", url); - } - - return end.getTime() - start.getTime(); - } - - public Long getBlocked() { - // return blocked; - // purposely not sending back blocked timings for now until we know it's reliable - return null; - } - - public Long getDns() { - return dns; - } - - public Long getConnect() { - return connect; - } - - public Long getSsl() { - return ssl; - } - - public Long getSend() { - return send; - } - - public Long getWait() { - return wait; - } - - public Long getReceive() { - return receive; - } - - public String getResolvedAddress() { - return resolvedAddress; - } - - public void blocked(Date start, Date end) { - // blocked is special - we don't record this start time as we don't want it to count towards receive time and - // total time - blocked = end.getTime() - start.getTime(); - } - - public void dns(Date start, Date end, String resolvedAddress) { - dns = ping(start, end); - this.resolvedAddress = resolvedAddress; - } - - public void connect(Date start, Date end) { - connect = ping(start, end); - } - - public void ssl(Date start, Date end) { - ssl = ping(start, end); - } - - public void send(Date start, Date end) { - send = ping(start, end); - } - - public void wait(Date start, Date end) { - wait = ping(start, end); - } - - public void finish() { - end = new Date(); - - if (start == null) { - start = new Date(); - } - - long totalTime = end.getTime() - start.getTime(); - - receive = totalTime - norm(wait) - norm(send) - norm(ssl) - norm(connect) - norm(dns); - - // as per the Har 1.2 spec (to maintain backwards compatibility with 1.1) the connect time should actually - // include the ssl handshaking time, so doing that here after everything has been calculated - if (norm(ssl) > 0) { - connect += ssl; - } - - if (receive < 0) { - LOG.severe("Got a negative receiving time (%d) for URL %s", receive, url); - receive = 0L; - } - } - - private long norm(Long val) { - if (val == null) { - return 0; - } else { - return val; - } - } - - public Date getStart() { - return start; - } - - public Date getEnd() { - return end; - } - - public long getTotalTime() { - if (end == null || start == null) { - return -1; - } - - return end.getTime() - start.getTime(); - } - - @Override - public String toString() { - long totalTime = end.getTime() - start.getTime(); - - return "RequestInfo{" + - "blocked=" + blocked + - ", dns=" + dns + - ", connect=" + connect + - ", ssl=" + ssl + - ", send=" + send + - ", wait=" + wait + - ", receive=" + receive + - ", total=" + totalTime + - ", resolvedAddress='" + resolvedAddress + '\'' + - '}'; - } - - public HarTimings getTimings() { - long send = 0; - if (this.send != null) { - send = this.send; - } - - long wait = 0; - if (this.wait != null) { - wait = this.wait; - } - - long receive = 0; - if (this.receive != null) { - receive = this.receive; - } - - // We were setting the following to null, however - // some HAR viewers (e.g. the HTTP Archive Viewer js widget) - // have a problem when these are not set in the json. - // Keeping them set to zero for now, until - long blocked = 0; - if (this.blocked != null) { - blocked = this.blocked; - } - - long dns = 0; - if (this.dns != null) { - dns = this.dns; - } - - long connect = 0; - if (this.connect != null) { - connect = this.connect; - } - - return new HarTimings(blocked, dns, connect, send, wait, receive); - } - - public HarEntry getEntry() { - return entry; - } -} diff --git a/src/main/java/org/browsermob/proxy/http/RequestInterceptor.java b/src/main/java/org/browsermob/proxy/http/RequestInterceptor.java deleted file mode 100644 index a85ac35a8..000000000 --- a/src/main/java/org/browsermob/proxy/http/RequestInterceptor.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.browsermob.proxy.http; - -public interface RequestInterceptor { - void process(BrowserMobHttpRequest request); -} diff --git a/src/main/java/org/browsermob/proxy/http/ResponseInterceptor.java b/src/main/java/org/browsermob/proxy/http/ResponseInterceptor.java deleted file mode 100644 index 06077806c..000000000 --- a/src/main/java/org/browsermob/proxy/http/ResponseInterceptor.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.browsermob.proxy.http; - -public interface ResponseInterceptor { - void process(BrowserMobHttpResponse response); -} diff --git a/src/main/java/org/browsermob/proxy/http/SimulatedSSLSocket.java b/src/main/java/org/browsermob/proxy/http/SimulatedSSLSocket.java deleted file mode 100644 index dda33e221..000000000 --- a/src/main/java/org/browsermob/proxy/http/SimulatedSSLSocket.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.browsermob.proxy.http; - -import org.java_bandwidthlimiter.StreamManager; - -import javax.net.ssl.*; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.channels.SocketChannel; -import java.util.Date; - -public class SimulatedSSLSocket extends SSLSocket { - private SSLSocket socket; - private StreamManager streamManager; - private Date handshakeStart; - - public SimulatedSSLSocket(SSLSocket socket, StreamManager streamManager) { - this.socket = socket; - this.streamManager = streamManager; - } - - @Override - public String[] getSupportedCipherSuites() { - return socket.getSupportedCipherSuites(); - } - - @Override - public String[] getEnabledCipherSuites() { - return socket.getEnabledCipherSuites(); - } - - @Override - public void setEnabledCipherSuites(String[] strings) { - socket.setEnabledCipherSuites(strings); - } - - @Override - public String[] getSupportedProtocols() { - return socket.getSupportedProtocols(); - } - - @Override - public String[] getEnabledProtocols() { - return socket.getEnabledProtocols(); - } - - @Override - public void setEnabledProtocols(String[] strings) { - socket.setEnabledProtocols(strings); - } - - @Override - public SSLSession getSession() { - return socket.getSession(); - } - - @Override - public void addHandshakeCompletedListener(HandshakeCompletedListener handshakeCompletedListener) { - socket.addHandshakeCompletedListener(handshakeCompletedListener); - } - - @Override - public void removeHandshakeCompletedListener(HandshakeCompletedListener handshakeCompletedListener) { - socket.removeHandshakeCompletedListener(handshakeCompletedListener); - } - - @Override - public void startHandshake() throws IOException { - socket.startHandshake(); - } - - @Override - public void setUseClientMode(boolean b) { - socket.setUseClientMode(b); - } - - @Override - public boolean getUseClientMode() { - return socket.getUseClientMode(); - } - - @Override - public void setNeedClientAuth(boolean b) { - socket.setNeedClientAuth(b); - } - - @Override - public boolean getNeedClientAuth() { - return socket.getNeedClientAuth(); - } - - @Override - public void setWantClientAuth(boolean b) { - socket.setWantClientAuth(b); - } - - @Override - public boolean getWantClientAuth() { - return socket.getWantClientAuth(); - } - - @Override - public void setEnableSessionCreation(boolean b) { - socket.setEnableSessionCreation(b); - } - - @Override - public boolean getEnableSessionCreation() { - return socket.getEnableSessionCreation(); - } - - @Override - public SSLParameters getSSLParameters() { - return socket.getSSLParameters(); - } - - @Override - public void setSSLParameters(SSLParameters sslParameters) { - socket.setSSLParameters(sslParameters); - } - - @Override - public void connect(SocketAddress endpoint) throws IOException { - this.connect(endpoint, 60000); - } - - @Override - public void connect(SocketAddress endpoint, int timeout) throws IOException { - Date start = new Date(); - socket.connect(endpoint, timeout); - Date end = new Date(); - RequestInfo.get().connect(start, end); - handshakeStart = new Date(); - startHandshake(); - this.addHandshakeCompletedListener(new HandshakeCompletedListener() { - @Override - public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) { - if(handshakeStart != null) { - RequestInfo.get().ssl(handshakeStart, new Date()); - } - } - }); - } - - @Override - public void bind(SocketAddress bindpoint) throws IOException { - socket.bind(bindpoint); - } - - @Override - public InetAddress getInetAddress() { - return socket.getInetAddress(); - } - - @Override - public InetAddress getLocalAddress() { - return socket.getLocalAddress(); - } - - @Override - public int getPort() { - return socket.getPort(); - } - - @Override - public int getLocalPort() { - return socket.getLocalPort(); - } - - @Override - public SocketAddress getRemoteSocketAddress() { - return socket.getRemoteSocketAddress(); - } - - @Override - public SocketAddress getLocalSocketAddress() { - return socket.getLocalSocketAddress(); - } - - @Override - public SocketChannel getChannel() { - return socket.getChannel(); - } - - @Override - public InputStream getInputStream() throws IOException { - return streamManager.registerStream(socket.getInputStream()); - } - - @Override - public OutputStream getOutputStream() throws IOException { - return streamManager.registerStream(socket.getOutputStream()); - } - - @Override - public void setTcpNoDelay(boolean on) throws SocketException { - socket.setTcpNoDelay(on); - } - - @Override - public boolean getTcpNoDelay() throws SocketException { - return socket.getTcpNoDelay(); - } - - @Override - public void setSoLinger(boolean on, int linger) throws SocketException { - socket.setSoLinger(on, linger); - } - - @Override - public int getSoLinger() throws SocketException { - return socket.getSoLinger(); - } - - @Override - public void sendUrgentData(int data) throws IOException { - socket.sendUrgentData(data); - } - - @Override - public void setOOBInline(boolean on) throws SocketException { - socket.setOOBInline(on); - } - - @Override - public boolean getOOBInline() throws SocketException { - return socket.getOOBInline(); - } - - @Override - public void setSoTimeout(int timeout) throws SocketException { - socket.setSoTimeout(timeout); - } - - @Override - public int getSoTimeout() throws SocketException { - return socket.getSoTimeout(); - } - - @Override - public void setSendBufferSize(int size) throws SocketException { - socket.setSendBufferSize(size); - } - - @Override - public int getSendBufferSize() throws SocketException { - return socket.getSendBufferSize(); - } - - @Override - public void setReceiveBufferSize(int size) throws SocketException { - socket.setReceiveBufferSize(size); - } - - @Override - public int getReceiveBufferSize() throws SocketException { - return socket.getReceiveBufferSize(); - } - - @Override - public void setKeepAlive(boolean on) throws SocketException { - socket.setKeepAlive(on); - } - - @Override - public boolean getKeepAlive() throws SocketException { - return socket.getKeepAlive(); - } - - @Override - public void setTrafficClass(int tc) throws SocketException { - socket.setTrafficClass(tc); - } - - @Override - public int getTrafficClass() throws SocketException { - return socket.getTrafficClass(); - } - - @Override - public void setReuseAddress(boolean on) throws SocketException { - socket.setReuseAddress(on); - } - - @Override - public boolean getReuseAddress() throws SocketException { - return socket.getReuseAddress(); - } - - @Override - public void close() throws IOException { - socket.close(); - } - - @Override - public void shutdownInput() throws IOException { - socket.shutdownInput(); - } - - @Override - public void shutdownOutput() throws IOException { - socket.shutdownOutput(); - } - - @Override - public String toString() { - return socket.toString(); - } - - @Override - public boolean isConnected() { - return socket.isConnected(); - } - - @Override - public boolean isBound() { - return socket.isBound(); - } - - @Override - public boolean isClosed() { - return socket.isClosed(); - } - - @Override - public boolean isInputShutdown() { - return socket.isInputShutdown(); - } - - @Override - public boolean isOutputShutdown() { - return socket.isOutputShutdown(); - } - - @Override - public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { - socket.setPerformancePreferences(connectionTime, latency, bandwidth); - } -} diff --git a/src/main/java/org/browsermob/proxy/http/TrustingSSLSocketFactory.java b/src/main/java/org/browsermob/proxy/http/TrustingSSLSocketFactory.java deleted file mode 100644 index dd324e145..000000000 --- a/src/main/java/org/browsermob/proxy/http/TrustingSSLSocketFactory.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.browsermob.proxy.http; - -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.scheme.HostNameResolver; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.params.HttpParams; -import org.java_bandwidthlimiter.StreamManager; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -public class TrustingSSLSocketFactory extends SSLSocketFactory { - - public enum SSLAlgorithm { - SSLv3, - TLSv1 - } - - private static SSLContext sslContext; - private StreamManager streamManager; - - static { - try { - sslContext = SSLContext.getInstance( SSLAlgorithm.SSLv3.name() ); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("TLS algorithm not found! Critical SSL error!", e); - } - TrustManager easyTrustManager = new X509TrustManager() { - @Override - public void checkClientTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - @Override - public void checkServerTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - }; - try { - sslContext.init(null, new TrustManager[]{easyTrustManager}, null); - } catch (KeyManagementException e) { - throw new RuntimeException("Unexpected key management error", e); - } - } - - public TrustingSSLSocketFactory(HostNameResolver nameResolver, StreamManager streamManager) { - super(sslContext, nameResolver); - assert nameResolver != null; - assert streamManager != null; - this.streamManager = streamManager; - } - - //just an helper function to wrap a normal sslSocket into a simulated one so we can do throttling - private Socket createSimulatedSocket(SSLSocket socket) { - SimulatedSocketFactory.configure(socket); - socket.setEnabledProtocols(new String[] { SSLAlgorithm.SSLv3.name(), SSLAlgorithm.TLSv1.name() } ); - //socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" }); - return new SimulatedSSLSocket(socket, streamManager); - } - - @SuppressWarnings("deprecation") - @Override - public Socket createSocket() throws java.io.IOException { - SSLSocket sslSocket = (SSLSocket) super.createSocket(); - return createSimulatedSocket(sslSocket); - } - - @SuppressWarnings("deprecation") - @Override - public Socket connectSocket(Socket socket, String host, int port, InetAddress localAddress, int localPort, HttpParams params) - throws java.io.IOException, java.net.UnknownHostException, org.apache.http.conn.ConnectTimeoutException { - SSLSocket sslSocket = (SSLSocket) super.connectSocket(socket, host, port, localAddress, localPort, params); - if( sslSocket instanceof SimulatedSSLSocket ) { - return sslSocket; - } else { - return createSimulatedSocket(sslSocket); - } - } - - @Override - public Socket createSocket(org.apache.http.params.HttpParams params) throws java.io.IOException { - SSLSocket sslSocket = (SSLSocket) super.createSocket(params); - return createSimulatedSocket(sslSocket); - } - - @Override - public Socket connectSocket(Socket socket, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpParams params) - throws IOException, ConnectTimeoutException { - SSLSocket sslSocket = (SSLSocket) super.connectSocket(socket, remoteAddress, localAddress, params); - if( sslSocket instanceof SimulatedSSLSocket ) { - return sslSocket; - } else { - //not sure this is needed - return createSimulatedSocket(sslSocket); - } - } -} diff --git a/src/main/java/org/browsermob/proxy/selenium/CertificateCreator.java b/src/main/java/org/browsermob/proxy/selenium/CertificateCreator.java deleted file mode 100644 index 6486a941f..000000000 --- a/src/main/java/org/browsermob/proxy/selenium/CertificateCreator.java +++ /dev/null @@ -1,409 +0,0 @@ -package org.browsermob.proxy.selenium; - -import org.bouncycastle.asn1.DEREncodableVector; -import org.bouncycastle.asn1.DERObjectIdentifier; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.asn1.x509.KeyUsage; -import org.bouncycastle.asn1.x509.X509Extensions; -import org.bouncycastle.jce.X509Principal; -import org.bouncycastle.x509.X509V3CertificateGenerator; -import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; -import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; - -import javax.security.auth.x500.X500Principal; -import java.math.BigInteger; -import java.security.*; -import java.security.cert.*; -import java.util.*; - -/** - * Methods for creating certificates. - * - * *************************************************************************************** - * Copyright (c) 2007, Information Security Partners, LLC - * All rights reserved. - * - * In a special exception, Selenium/OpenQA is allowed to use this code under the Apache License 2.0. - * - * @author Brad Hill - * - */ -public class CertificateCreator { - - - private static final HashSet clientCertOidsNeverToCopy = new HashSet(); - private static final HashSet clientCertDefaultOidsNotToCopy = new HashSet(); - - /** - * The default key generation algorithm for this package is RSA. - */ - public static final String KEYGEN_ALGO = "RSA"; - - /** - * The default sign algorithm for this package is SHA1 with RSA. - */ - public static final String SIGN_ALGO = "SHA1withRSA"; - - - /** - * X.509 OID for Subject Key Identifier Extension - Replaced when duplicating a cert. - */ - public static final String OID_SUBJECT_KEY_IDENTIFIER = "2.5.29.14"; - - /** - * X.509 OID for Subject Authority Key Identifier - Replaced when duplicating a cert. - */ - public static final String OID_AUTHORITY_KEY_IDENTIFIER = "2.5.29.35"; - - /** - * X.509 OID for Issuer Alternative Name - Omitted when duplicating a cert by default. - */ - public static final String OID_ISSUER_ALTERNATIVE_NAME = "2.5.29.8"; - - /** - * X.509 OID for Issuer Alternative Name 2 - Omitted when duplicating a cert by default. - */ - public static final String OID_ISSUER_ALTERNATIVE_NAME_2 = "2.5.29.18"; - - /** - * X.509 OID for Certificate Revocation List Distribution Point - Omitted when duplicating a cert by default. - */ - public static final String OID_CRL_DISTRIBUTION_POINT = "2.5.28.31"; - - /** - * X.509 OID for Authority Information Access - Omitted when duplicating a cert by default. - */ - public static final String OID_AUTHORITY_INFO_ACCESS = "1.3.6.1.5.5.7.1.1"; - - /** - * X.509 OID for Additional CA Issuers for AIA - Omitted when duplicating a cert by default. - */ - public static final String OID_ID_AD_CAISSUERS = "1.3.6.1.5.5.7.48.2"; - - - static - { - clientCertOidsNeverToCopy.add(OID_SUBJECT_KEY_IDENTIFIER); - clientCertOidsNeverToCopy.add(OID_AUTHORITY_KEY_IDENTIFIER); - - clientCertDefaultOidsNotToCopy.add(OID_ISSUER_ALTERNATIVE_NAME); - clientCertDefaultOidsNotToCopy.add(OID_ISSUER_ALTERNATIVE_NAME_2); - clientCertDefaultOidsNotToCopy.add(OID_CRL_DISTRIBUTION_POINT); - clientCertDefaultOidsNotToCopy.add(OID_AUTHORITY_INFO_ACCESS); - } - - - /** - * Utility method for generating a "standard" server certificate. Recognized by most - * browsers as valid for SSL/TLS. These certificates are generated de novo, not from - * a template, so they will not retain the structure of the original certificate and may - * not be suitable for applications that require Extended Validation/High Assurance SSL - * or other distinct extensions or EKU. - * - * @param newPubKey - * @param caCert - * @param caPrivateKey - * @param hostname - * @return - * @throws CertificateParsingException - * @throws SignatureException - * @throws InvalidKeyException - * @throws CertificateExpiredException - * @throws CertificateNotYetValidException - * @throws CertificateException - * @throws NoSuchAlgorithmException - * @throws NoSuchProviderException - */ - @SuppressWarnings({ "deprecation", "unused" }) - public static X509Certificate generateStdSSLServerCertificate( - final PublicKey newPubKey, - final X509Certificate caCert, - final PrivateKey caPrivateKey, - final String subject) - throws CertificateParsingException, - SignatureException, - InvalidKeyException, - CertificateExpiredException, - CertificateNotYetValidException, - CertificateException, - NoSuchAlgorithmException, - NoSuchProviderException - { - X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); - - v3CertGen.setSubjectDN(new X500Principal(subject)); - v3CertGen.setSignatureAlgorithm(CertificateCreator.SIGN_ALGO); - v3CertGen.setPublicKey(newPubKey); - v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + 30L * 60 * 60 * 24 * 30 * 12)); - v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30 *12)); - v3CertGen.setIssuerDN(caCert.getSubjectX500Principal()); - - // Firefox actually tracks serial numbers within a CA and refuses to validate if it sees duplicates - // This is not a secure serial number generator, (duh!) but it's good enough for our purposes. - v3CertGen.setSerialNumber(new BigInteger(Long.toString(System.currentTimeMillis()))); - - v3CertGen.addExtension( - X509Extensions.BasicConstraints, - true, - new BasicConstraints(false) ); - - v3CertGen.addExtension( - X509Extensions.SubjectKeyIdentifier, - false, - new SubjectKeyIdentifierStructure(newPubKey)); - - - v3CertGen.addExtension( - X509Extensions.AuthorityKeyIdentifier, - false, - new AuthorityKeyIdentifierStructure(caCert.getPublicKey())); - -// Firefox 2 disallows these extensions in an SSL server cert. IE7 doesn't care. -// v3CertGen.addExtension( -// X509Extensions.KeyUsage, -// false, -// new KeyUsage(KeyUsage.dataEncipherment | KeyUsage.digitalSignature ) ); - - - DEREncodableVector typicalSSLServerExtendedKeyUsages = new DEREncodableVector(); - - typicalSSLServerExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.serverAuth)); - typicalSSLServerExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.clientAuth)); - typicalSSLServerExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.netscapeServerGatedCrypto)); - typicalSSLServerExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.msServerGatedCrypto)); - - v3CertGen.addExtension( - X509Extensions.ExtendedKeyUsage, - false, - new DERSequence(typicalSSLServerExtendedKeyUsages)); - -// Disabled by default. Left in comments in case this is desired. -// -// v3CertGen.addExtension( -// X509Extensions.AuthorityInfoAccess, -// false, -// new AuthorityInformationAccess(new DERObjectIdentifier(OID_ID_AD_CAISSUERS), -// new GeneralName(GeneralName.uniformResourceIdentifier, "http://" + subject + "/aia"))); - -// v3CertGen.addExtension( -// X509Extensions.CRLDistributionPoints, -// false, -// new CRLDistPoint(new DistributionPoint[] {})); - - - - X509Certificate cert = v3CertGen.generate(caPrivateKey, "BC"); - - return cert; - } - - /** - * This method creates an X509v3 certificate based on an an existing certificate. - * It attempts to create as faithful a copy of the existing certificate as possible - * by duplicating all certificate extensions. - * - * If you are testing an application that makes use of additional certificate - * extensions (e.g. logotype, S/MIME capabilities) this method will preserve those - * fields. - * - * You may optionally include a set of OIDs not to copy from the original certificate. - * The most common reason to do this would be to remove fields that would cause inconsistency, - * such as Authority Info Access or Issuer Alternative Name where these are not defined for - * the MITM authority certificate. - * - * OIDs 2.5.29.14 : Subject Key Identifier and 2.5.29.35 : Authority Key Identifier, - * are never copied, but generated directly based on the input keys and certificates. - * - * You may also optionally include maps of custom extensions which will be added to or replace - * extensions with the same OID on the original certificate for the the MITM certificate. - * - * FUTURE WORK: JDK 1.5 is very strict in parsing extensions. In particular, known extensions - * that include URIs must parse to valid URIs (including URL encoding all non-valid URI characters) - * or the extension will be rejected and not available to copy to the MITM certificate. Will need - * to directly extract these as ASN.1 fields and re-insert (hopefully BouncyCastle will handle them) - * - * - * @param originalCert The original certificate to duplicate. - * @param newPubKey The new public key for the MITM certificate. - * @param caCert The certificate of the signing authority fot the MITM certificate. - * @param caPrivateKey The private key of the signing authority. - * @param extensionOidsNotToCopy An optional list of certificate extension OIDs not to copy to the MITM certificate. - * @return The new MITM certificate. - * @throws CertificateParsingException - * @throws SignatureException - * @throws InvalidKeyException - * @throws CertificateExpiredException - * @throws CertificateNotYetValidException - * @throws CertificateException - * @throws NoSuchAlgorithmException - * @throws NoSuchProviderException - */ - public static X509Certificate mitmDuplicateCertificate(final X509Certificate originalCert, - final PublicKey newPubKey, - final X509Certificate caCert, - final PrivateKey caPrivateKey, - Set extensionOidsNotToCopy) - throws CertificateParsingException, - SignatureException, - InvalidKeyException, - CertificateException, - NoSuchAlgorithmException, - NoSuchProviderException - { - if(extensionOidsNotToCopy == null) - { - extensionOidsNotToCopy = new HashSet(); - } - - X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); - - v3CertGen.setSubjectDN(originalCert.getSubjectX500Principal()); - v3CertGen.setSignatureAlgorithm(CertificateCreator.SIGN_ALGO); // needs to be the same as the signing cert, not the copied cert - v3CertGen.setPublicKey(newPubKey); - v3CertGen.setNotAfter(originalCert.getNotAfter()); - v3CertGen.setNotBefore(originalCert.getNotBefore()); - v3CertGen.setIssuerDN(caCert.getSubjectX500Principal()); - v3CertGen.setSerialNumber(originalCert.getSerialNumber()); - - // copy other extensions: - Set critExts = originalCert.getCriticalExtensionOIDs(); - - // get extensions returns null, not an empty set! - if(critExts != null) { - for (String oid : critExts) { - if(!clientCertOidsNeverToCopy.contains(oid) - && !extensionOidsNotToCopy.contains(oid)) { - v3CertGen.copyAndAddExtension(new DERObjectIdentifier(oid), true, originalCert); - } - } - } - Set nonCritExs = originalCert.getNonCriticalExtensionOIDs(); - - if(nonCritExs != null) { - for(String oid: nonCritExs) { - - if(!clientCertOidsNeverToCopy.contains(oid) - && !extensionOidsNotToCopy.contains(oid)){ - v3CertGen.copyAndAddExtension(new DERObjectIdentifier(oid), false, originalCert); - } - } - } - - v3CertGen.addExtension( - X509Extensions.SubjectKeyIdentifier, - false, - new SubjectKeyIdentifierStructure(newPubKey)); - - - v3CertGen.addExtension( - X509Extensions.AuthorityKeyIdentifier, - false, - new AuthorityKeyIdentifierStructure(caCert.getPublicKey())); - - X509Certificate cert = v3CertGen.generate(caPrivateKey, "BC"); - - // For debugging purposes. - //cert.checkValidity(new Date()); - //cert.verify(caCert.getPublicKey()); - - return cert; - } - - /** - * Convenience method for the most common case of certificate duplication. - * - * This method will not add any custom extensions and won't copy the extensions 2.5.29.8 : Issuer Alternative Name, - * 2.5.29.18 : Issuer Alternative Name 2, 2.5.29.31 : CRL Distribution Point or 1.3.6.1.5.5.7.1.1 : Authority Info Access, if they are present. - * - * @param originalCert - * @param newPubKey - * @param caCert - * @param caPrivateKey - * @return - * @throws CertificateParsingException - * @throws SignatureException - * @throws InvalidKeyException - * @throws CertificateExpiredException - * @throws CertificateNotYetValidException - * @throws CertificateException - * @throws NoSuchAlgorithmException - * @throws NoSuchProviderException - */ - public static X509Certificate mitmDuplicateCertificate(final X509Certificate originalCert, - final PublicKey newPubKey, - final X509Certificate caCert, - final PrivateKey caPrivateKey) - throws CertificateParsingException, SignatureException, InvalidKeyException, CertificateExpiredException, CertificateNotYetValidException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException - { - return mitmDuplicateCertificate(originalCert, newPubKey, caCert, caPrivateKey, clientCertDefaultOidsNotToCopy); - } - - /** - * Creates a typical Certification Authority (CA) certificate. - * @param keyPair - * @throws SecurityException - * @throws InvalidKeyException - * @throws NoSuchProviderException - * @throws NoSuchAlgorithmException - * @throws CertificateException - */ - @SuppressWarnings("deprecation") - public static X509Certificate createTypicalMasterCert(final KeyPair keyPair) - throws SignatureException, InvalidKeyException, SecurityException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException - { - - X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); - - X509Principal issuer=new X509Principal("O=CyberVillians.com,OU=CyberVillians Certification Authority,C=US"); - - // Create - v3CertGen.setSerialNumber(BigInteger.valueOf(1)); - v3CertGen.setIssuerDN(issuer); - v3CertGen.setSubjectDN(issuer); - - //Set validity period - v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 12 /* months */ *(1000L * 60 * 60 * 24 * 30))); - v3CertGen.setNotAfter (new Date(System.currentTimeMillis() + 240 /* months */ *(1000L * 60 * 60 * 24 * 30))); - - //Set signature algorithm & public key - v3CertGen.setPublicKey(keyPair.getPublic()); - v3CertGen.setSignatureAlgorithm(CertificateCreator.SIGN_ALGO); - - // Add typical extensions for signing cert - v3CertGen.addExtension( - X509Extensions.SubjectKeyIdentifier, - false, - new SubjectKeyIdentifierStructure(keyPair.getPublic())); - - v3CertGen.addExtension( - X509Extensions.BasicConstraints, - true, - new BasicConstraints(0)); - - v3CertGen.addExtension( - X509Extensions.KeyUsage, - false, - new KeyUsage(KeyUsage.cRLSign | KeyUsage.keyCertSign) ); - - DEREncodableVector typicalCAExtendedKeyUsages = new DEREncodableVector(); - - typicalCAExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.serverAuth)); - typicalCAExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.OCSPSigning)); - typicalCAExtendedKeyUsages.add(new DERObjectIdentifier(ExtendedKeyUsageConstants.verisignUnknown)); - - v3CertGen.addExtension( - X509Extensions.ExtendedKeyUsage, - false, - new DERSequence(typicalCAExtendedKeyUsages)); - - X509Certificate cert = v3CertGen.generate(keyPair.getPrivate(), "BC"); - - cert.checkValidity(new Date()); - - cert.verify(keyPair.getPublic()); - - return cert; - } - -} diff --git a/src/main/java/org/browsermob/proxy/selenium/ClassPathResource.java b/src/main/java/org/browsermob/proxy/selenium/ClassPathResource.java deleted file mode 100644 index 6f2d7f634..000000000 --- a/src/main/java/org/browsermob/proxy/selenium/ClassPathResource.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.browsermob.proxy.selenium; - -import org.browsermob.proxy.jetty.util.Resource; -import org.eclipse.jetty.util.IO; - -import java.io.*; -import java.net.MalformedURLException; -import java.net.URL; - -/** - * Represents resource file off of the classpath. - * - * @author Patrick Lightbody (plightbo at gmail dot com) - */ -public class ClassPathResource extends Resource { - String path; - - ByteArrayOutputStream os; - - /** - * Specifies the classpath path containing the resource - */ - public ClassPathResource(String path) { - this.path = path; - InputStream is = LauncherUtils.getSeleniumResourceAsStream(path); - if (is != null) { - os = new ByteArrayOutputStream(); - try { - IO.copy(is, os); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /* ------------------------------------------------------------ */ - public Object getAssociate() { - return super.getAssociate(); - } - - public void release() { - } - - public boolean exists() { - return os != null; - } - - public boolean isDirectory() { - return false; - } - - /** - * Returns the lastModified time, which is always in the distant future to - * prevent caching. - */ - public long lastModified() { - return System.currentTimeMillis() + 1000l * 3600l * 24l * 365l; - } - - public long length() { - if (os != null) { - return os.size(); - } - - return 0; - } - - public URL getURL() { - return null; - } - - public File getFile() throws IOException { - return null; - } - - public String getName() { - return path; - } - - public InputStream getInputStream() throws IOException { - if (os != null) { - return new ByteArrayInputStream(os.toByteArray()); - } - return null; - } - - public OutputStream getOutputStream() throws IOException, SecurityException { - return null; - } - - public boolean delete() throws SecurityException { - return false; - } - - public boolean renameTo(Resource dest) throws SecurityException { - return false; - } - - public String[] list() { - return new String[0]; - } - - public Resource addPath(String pathParm) throws IOException, - MalformedURLException { - return new ClassPathResource(this.path + "/" + pathParm); - } - - @Override - public String toString() { - return getName(); - } -} diff --git a/src/main/java/org/browsermob/proxy/selenium/LauncherUtils.java b/src/main/java/org/browsermob/proxy/selenium/LauncherUtils.java deleted file mode 100644 index 1971de6e3..000000000 --- a/src/main/java/org/browsermob/proxy/selenium/LauncherUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.browsermob.proxy.selenium; - -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Copy; -import org.apache.tools.ant.taskdefs.Delete; -import org.apache.tools.ant.types.FileSet; - -import java.io.File; -import java.io.InputStream; - -public class LauncherUtils { - public static void copyDirectory(File source, File dest) { - Project p = new Project(); - Copy c = new Copy(); - c.setProject(p); - c.setTodir(dest); - FileSet fs = new FileSet(); - fs.setDir(source); - c.addFileset(fs); - c.execute(); - } - - public static InputStream getSeleniumResourceAsStream(String resourceFile) { - Class clazz = ClassPathResource.class; - InputStream input = clazz.getResourceAsStream(resourceFile); - if (input == null) { - try { - // This is hack for the OneJar version of Selenium-Server. - // Examine the contents of the jar made by - // https://svn.openqa.org/svn/selenium-rc/trunk/selenium-server-onejar/build.xml - clazz = Class.forName("OneJar"); - input = clazz.getResourceAsStream(resourceFile); - } catch (ClassNotFoundException e) { - } - } - return input; - } - - /** - * Delete a directory and all subdirectories - */ - public static void recursivelyDeleteDir(File customProfileDir) { - if (customProfileDir == null || !customProfileDir.exists()) { - return; - } - Delete delete = new Delete(); - delete.setProject(new Project()); - delete.setDir(customProfileDir); - delete.setFailOnError(true); - delete.execute(); - } -} diff --git a/src/main/java/org/browsermob/proxy/selenium/ModifiedIO.java b/src/main/java/org/browsermob/proxy/selenium/ModifiedIO.java deleted file mode 100644 index 1a3ecbcf2..000000000 --- a/src/main/java/org/browsermob/proxy/selenium/ModifiedIO.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.browsermob.proxy.selenium; - -import org.browsermob.proxy.jetty.util.IO; - -import java.io.*; - -public class ModifiedIO { - /** - * Copy Stream in to Stream out until EOF or exception. - */ - public static long copy(InputStream in, OutputStream out) - throws IOException { - return copy(in, out, -1); - } - - public static long copy(Reader in, Writer out) - throws IOException { - return copy(in, out, -1); - } - - /** - * Copy Stream in to Stream for byteCount bytes or until EOF or exception. - * - * @return Copied bytes count or -1 if no bytes were read *and* EOF was reached - */ - public static long copy(InputStream in, - OutputStream out, - long byteCount) - throws IOException { - byte buffer[] = new byte[IO.bufferSize]; - int len; - - long returnVal = 0; - - if (byteCount >= 0) { - while (byteCount > 0) { - if (byteCount < IO.bufferSize) - len = in.read(buffer, 0, (int) byteCount); - else - len = in.read(buffer, 0, IO.bufferSize); - - if (len == -1) { - break; - } - returnVal += len; - - byteCount -= len; - out.write(buffer, 0, len); - } - } else { - while (true) { - len = in.read(buffer, 0, IO.bufferSize); - if (len < 0) { - break; - } - returnVal += len; - out.write(buffer, 0, len); - } - } - - return returnVal; - } - - /** - * Copy Reader to Writer for byteCount bytes or until EOF or exception. - */ - public static long copy(Reader in, - Writer out, - long byteCount) - throws IOException { - char buffer[] = new char[IO.bufferSize]; - int len; - - long returnVal = 0; - - if (byteCount >= 0) { - while (byteCount > 0) { - if (byteCount < IO.bufferSize) - len = in.read(buffer, 0, (int) byteCount); - else - len = in.read(buffer, 0, IO.bufferSize); - - if (len == -1) { - break; - } - returnVal += len; - - byteCount -= len; - out.write(buffer, 0, len); - } - } else { - while (true) { - len = in.read(buffer, 0, IO.bufferSize); - if (len == -1) { - break; - } - returnVal += len; - out.write(buffer, 0, len); - } - } - - return returnVal; - } - -} diff --git a/src/main/java/org/browsermob/proxy/util/BandwidthSimulator.java b/src/main/java/org/browsermob/proxy/util/BandwidthSimulator.java deleted file mode 100644 index 17d09e8bb..000000000 --- a/src/main/java/org/browsermob/proxy/util/BandwidthSimulator.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.browsermob.proxy.util; - -public class BandwidthSimulator { - // Too large and the instantaneous bandwidth varies too much. - // Too small and we don't reach the target fast enough. - private static final float DAMPING_FACTOR = 0.5f; - - private long startTime; - private int sleepTime; - private float damping; - private final int targetBps; - private final int bufferIncrement; - - public BandwidthSimulator(int targetBPS) { - targetBps = targetBPS; - - // We must use a larger buffer increment for higher BPS rates due to the - // precision to which we can sleep (maybe ~10ms). Limiting for higher - // BPS rates will only apply to large messages, but that's not something - // we can help. - // - // I considered adjusting the buffer increment based on the target baud, or - // dynamically based on the measured performance. I discounted this because - // there's no obvious algorithm, and its likely to cause non-linear - // behaviour due to external influences such as the MTU size. Also, having - // the increment too small will increase the work that we have to do within - // The Grinder, which might significantly skew timings. - bufferIncrement = Math.max(100, targetBps / 500); - } - - public int maximumBytes(int position) { - if (targetBps == 0) { - return Integer.MAX_VALUE; - } - - final long now = System.currentTimeMillis(); - - if (position == 0) { - startTime = now; - - // Set the initial sleep time to 0 so we start pumping bytes straight - // away. - sleepTime = 0; - - // Set the second sleep time based on the first lot of bytes transfered. - // The damping is 2 to account for the initial call. - damping = 2; - } else { - final long expectedTime = (long) position * 8 * 1000 / targetBps; - final long actualTime = now - startTime; - sleepTime += (expectedTime - actualTime) * damping; - - if (sleepTime < 0) { - sleepTime = 0; - } - - damping = DAMPING_FACTOR; - } - - try { - Thread.sleep(sleepTime, 0); - } catch (InterruptedException e) { - Thread.interrupted(); - } - - // Allow bufferIncrement bytes to be read. - return bufferIncrement; - } -} diff --git a/src/main/java/org/browsermob/proxy/util/Base64.java b/src/main/java/org/browsermob/proxy/util/Base64.java deleted file mode 100644 index b351bae70..000000000 --- a/src/main/java/org/browsermob/proxy/util/Base64.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.browsermob.proxy.util; - -public class Base64 { - /** - * Translates the specified byte array into a Base64 string as per Preferences.put(byte[]). - */ - public static String byteArrayToBase64(byte[] a) { - return byteArrayToBase64(a, false); - } - - /** - * Translates the specified byte array into an "alternate representation" Base64 string. This non-standard variant - * uses an alphabet that does not contain the uppercase alphabetic characters, which makes it suitable for use in - * situations where case-folding occurs. - */ - public static String byteArrayToAltBase64(byte[] a) { - return byteArrayToBase64(a, true); - } - - private static String byteArrayToBase64(byte[] a, boolean alternate) { - int aLen = a.length; - int numFullGroups = aLen / 3; - int numBytesInPartialGroup = aLen - 3 * numFullGroups; - int resultLen = 4 * ((aLen + 2) / 3); - StringBuffer result = new StringBuffer(resultLen); - char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64); - - // Translate all full groups from byte array elements to Base64 - int inCursor = 0; - for (int i = 0; i < numFullGroups; i++) { - int byte0 = a[inCursor++] & 0xff; - int byte1 = a[inCursor++] & 0xff; - int byte2 = a[inCursor++] & 0xff; - result.append(intToAlpha[byte0 >> 2]); - result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]); - result.append(intToAlpha[(byte1 << 2) & 0x3f | (byte2 >> 6)]); - result.append(intToAlpha[byte2 & 0x3f]); - } - - // Translate partial group if present - if (numBytesInPartialGroup != 0) { - int byte0 = a[inCursor++] & 0xff; - result.append(intToAlpha[byte0 >> 2]); - if (numBytesInPartialGroup == 1) { - result.append(intToAlpha[(byte0 << 4) & 0x3f]); - result.append("=="); - } else { - // assert numBytesInPartialGroup == 2; - int byte1 = a[inCursor++] & 0xff; - result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]); - result.append(intToAlpha[(byte1 << 2) & 0x3f]); - result.append('='); - } - } - // assert inCursor == a.length; - // assert result.length() == resultLen; - return result.toString(); - } - - /** - * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" - * equivalents as specified in Table 1 of RFC 2045. - */ - private static final char intToBase64[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - /** - * This array is a lookup table that translates 6-bit positive integer index values into their "Alternate Base64 - * Alphabet" equivalents. This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. This alternate - * alphabet does not use the capital letters. It is designed for use in environments where "case folding" occurs. - */ - private static final char intToAltBase64[] = { - '!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':', - ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?' - }; - - /** - * Translates the specified Base64 string (as per Preferences.get(byte[])) into a byte array. - * - * @throw IllegalArgumentException if s is not a valid Base64 string. - */ - public static byte[] base64ToByteArray(String s) { - return base64ToByteArray(s, false); - } - - /** - * Translates the specified "alternate representation" Base64 string into a byte array. - * - * @throw IllegalArgumentException or ArrayOutOfBoundsException if s is not a valid alternate - * representation Base64 string. - */ - public static byte[] altBase64ToByteArray(String s) { - return base64ToByteArray(s, true); - } - - private static byte[] base64ToByteArray(String s, boolean alternate) { - byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt); - int sLen = s.length(); - int numGroups = sLen / 4; - if (4 * numGroups != sLen) - throw new IllegalArgumentException( - "String length must be a multiple of four."); - int missingBytesInLastGroup = 0; - int numFullGroups = numGroups; - if (sLen != 0) { - if (s.charAt(sLen - 1) == '=') { - missingBytesInLastGroup++; - numFullGroups--; - } - if (s.charAt(sLen - 2) == '=') - missingBytesInLastGroup++; - } - byte[] result = new byte[3 * numGroups - missingBytesInLastGroup]; - - // Translate all full groups from base64 to byte array elements - int inCursor = 0, outCursor = 0; - for (int i = 0; i < numFullGroups; i++) { - int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); - int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); - int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); - int ch3 = base64toInt(s.charAt(inCursor++), alphaToInt); - result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); - result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); - result[outCursor++] = (byte) ((ch2 << 6) | ch3); - } - - // Translate partial group, if present - if (missingBytesInLastGroup != 0) { - int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); - int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); - result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); - - if (missingBytesInLastGroup == 1) { - int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); - result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); - } - } - // assert inCursor == s.length()-missingBytesInLastGroup; - // assert outCursor == result.length; - return result; - } - - /** - * Translates the specified character, which is assumed to be in the "Base 64 Alphabet" into its equivalent 6-bit - * positive integer. - * - * @throw IllegalArgumentException or ArrayOutOfBoundsException if c is not in the Base64 Alphabet. - */ - private static int base64toInt(char c, byte[] alphaToInt) { - int result = alphaToInt[c]; - if (result < 0) - throw new IllegalArgumentException("Illegal character " + c); - return result; - } - - /** - * This array is a lookup table that translates unicode characters drawn from the "Base64 Alphabet" (as specified in - * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 - * alphabet but fall within the bounds of the array are translated to -1. - */ - private static final byte base64ToInt[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 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, -1, -1, -1, -1, -1, -1, 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 - }; - - /** - * This array is the analogue of base64ToInt, but for the nonstandard variant that avoids the use of uppercase - * alphabetic characters. - */ - private static final byte altBase64ToInt[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, - 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 17, -1, 18, 19, 21, 20, 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, 22, 23, 24, 25 - }; -} diff --git a/src/main/java/org/browsermob/proxy/util/GUID.java b/src/main/java/org/browsermob/proxy/util/GUID.java deleted file mode 100644 index e40376495..000000000 --- a/src/main/java/org/browsermob/proxy/util/GUID.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2002-2003 by OpenSymphony - * All rights reserved. - */ -package org.browsermob.proxy.util; - -import java.math.BigInteger; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - - -/** - * Convenience object for generating GUIDs. Uses the SHA1PRNG algorithm if available. If unavailable, it leaves this up - * to the JRE to provide a default. See http://java.sun.com/j2se/1.4.2/docs/api/java/security/SecureRandom.html for more - * details. - * - * @author Victor Salaman - * @version $Revision: 156 $ - */ -public final class GUID { - //~ Static fields/initializers ///////////////////////////////////////////// - - private static SecureRandom rnd; - - static { - try { - rnd = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - rnd = new SecureRandom(); //Use default if prefered provider is unavailable - } - - byte[] seed = rnd.generateSeed(64); - rnd.setSeed(seed); - } - - //~ Methods //////////////////////////////////////////////////////////////// - - public static String generateFormattedGUID() { - //xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - String guid = generateGUID(); - - return guid.substring(0, 8) + '-' + guid.substring(8, 12) + '-' + guid.substring(12, 16) + '-' + guid.substring(16, 20) + '-' + guid.substring(20); - } - - public static String generateGUID() { - return new BigInteger(165, rnd).toString(36).toUpperCase(); - } -} diff --git a/src/main/java/org/browsermob/proxy/util/IOUtils.java b/src/main/java/org/browsermob/proxy/util/IOUtils.java deleted file mode 100644 index 46fdc02aa..000000000 --- a/src/main/java/org/browsermob/proxy/util/IOUtils.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.browsermob.proxy.util; - -import java.io.*; -import java.util.Date; - -public class IOUtils { - private static final int BUFFER = 4096; - - public static void copy(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[BUFFER]; - int length; - while ((length = in.read(buffer)) != -1) { - out.write(buffer, 0, length); - } - - out.close(); - in.close(); - } - - public static Stats copyWithStats(InputStream is, OutputStream os, BandwidthSimulator simulator, boolean copyOutputForReadingLater) throws IOException { - Date timeToFirstByte = null; - - byte[] buffer = new byte[BUFFER]; - int length; - int bytes = 0; - - ByteArrayInputStream bais = null; - - try { - ByteArrayOutputStream baos = null; - if (copyOutputForReadingLater) { - baos = new ByteArrayOutputStream(); - } - - // read the first byte - int maxBytes = Math.min(simulator.maximumBytes(bytes), buffer.length); - int firstByte = is.read(); - os.write(firstByte); - if (copyOutputForReadingLater) { - baos.write(firstByte); - } - bytes++; - timeToFirstByte = new Date(); - - do { - length = is.read(buffer, 0, maxBytes); - if (length != -1) { - bytes += length; - os.write(buffer, 0, length); - if (copyOutputForReadingLater) { - baos.write(buffer, 0, length); - } - } - maxBytes = Math.min(simulator.maximumBytes(bytes), buffer.length); - } while (length != -1); - - if (copyOutputForReadingLater) { - bais = new ByteArrayInputStream(baos.toByteArray()); - } - } finally { - try { - is.close(); - } catch (IOException e) { - // ok to ignore - } - - try { - os.close(); - } catch (IOException e) { - // ok to ignore - } - } - - return new Stats(bytes, timeToFirstByte, bais); - } - - public static String readFully(InputStream in) throws IOException { - StringBuilder sb = new StringBuilder(); - byte[] buffer = new byte[BUFFER]; - int length; - while ((length = in.read(buffer)) != -1) { - sb.append(new String(buffer, 0, length, "UTF-8")); - } - - in.close(); - - return sb.toString(); - } - - public static String readFully(InputStreamReader in) throws IOException { - StringBuilder sb = new StringBuilder(); - char[] buffer = new char[BUFFER]; - int length; - while ((length = in.read(buffer)) != -1) { - sb.append(new String(buffer, 0, length)); - } - - in.close(); - - return sb.toString(); - } - - public static class Stats { - private long bytesCopied; - private Date timeToFirstByte; - private InputStream copy; - - public Stats(long bytesCopied, Date timeToFirstByte, InputStream copy) { - this.bytesCopied = bytesCopied; - this.timeToFirstByte = timeToFirstByte; - this.copy = copy; - } - - public long getBytesCopied() { - return bytesCopied; - } - - public Date getTimeToFirstByte() { - return timeToFirstByte; - } - - public InputStream getCopy() { - return copy; - } - } -} diff --git a/src/main/java/org/browsermob/proxy/util/Log.java b/src/main/java/org/browsermob/proxy/util/Log.java deleted file mode 100644 index f463d8639..000000000 --- a/src/main/java/org/browsermob/proxy/util/Log.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.browsermob.proxy.util; - -import java.util.logging.*; - -public class Log { - static { - Logger logger = Logger.getLogger(""); - Handler[] handlers = logger.getHandlers(); - for (Handler handler : handlers) { - logger.removeHandler(handler); - } - - ConsoleHandler handler = new ConsoleHandler(); - handler.setFormatter(new StandardFormatter()); - handler.setLevel(Level.FINE); - logger.addHandler(handler); - } - - static { - // tell commons-logging to use the JDK logging (otherwise it would default to log4j - System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Jdk14Logger"); - } - - protected Logger logger; - private String className; - - public Log() { - Exception e = new Exception(); - className = e.getStackTrace()[1].getClassName(); - logger = Logger.getLogger(className); - } - - public Log(Class clazz) { - className = clazz.getName(); - logger = Logger.getLogger(className); - } - - public void severe(String msg, Throwable e) { - log(Level.SEVERE, msg, e); - } - - public void severe(String msg, Object... args) { - log(Level.SEVERE, msg, args); - } - - public void severe(String msg, Throwable e, Object... args) { - log(Level.SEVERE, msg, e, args); - } - - public RuntimeException severeAndRethrow(String msg, Throwable e, Object... args) { - log(Level.SEVERE, msg, e, args); - - //noinspection ThrowableInstanceNeverThrown - return new RuntimeException(new java.util.Formatter().format(msg, args).toString()); - } - - public void warn(String msg, Throwable e) { - log(Level.WARNING, msg, e); - } - - public void warn(String msg, Object... args) { - log(Level.WARNING, msg, args); - } - - public void warn(String msg, Throwable e, Object... args) { - log(Level.WARNING, msg, e, args); - } - - public void info(String msg, Throwable e) { - log(Level.INFO, msg, e); - } - - public void info(String msg, Object... args) { - log(Level.INFO, msg, args); - } - - public void info(String msg, Throwable e, Object... args) { - log(Level.INFO, msg, e, args); - } - - public void fine(String msg, Throwable e) { - log(Level.FINE, msg, e); - } - - public void fine(String msg, Object... args) { - log(Level.FINE, msg, args); - } - - public void fine(String msg, Throwable e, Object... args) { - log(Level.FINE, msg, e, args); - } - - private void log(Level level, String msg, Throwable e) { - logger.log(level, msg, e); - } - - private void log(Level level, String msg, Object... args) { - logger.log(level, msg, args); - } - - private void log(Level level, String msg, Throwable e, Object... args) { - LogRecord lr = new LogRecord(level, msg); - lr.setThrown(e); - lr.setParameters(args); - lr.setSourceMethodName(""); - lr.setSourceClassName(className); - lr.setLoggerName(className); - logger.log(lr); - } -} \ No newline at end of file diff --git a/src/main/java/org/browsermob/proxy/util/ResourceExtractor.java b/src/main/java/org/browsermob/proxy/util/ResourceExtractor.java deleted file mode 100644 index 5f6297789..000000000 --- a/src/main/java/org/browsermob/proxy/util/ResourceExtractor.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.browsermob.proxy.util; - -import org.apache.commons.logging.LogFactory; -import org.apache.tools.ant.util.FileUtils; -import org.browsermob.proxy.selenium.LauncherUtils; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Enumeration; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -public class ResourceExtractor { - - static org.apache.commons.logging.Log log = LogFactory.getLog(ResourceExtractor.class); - private static final int BUF_SIZE = 8192; - - public static File extractResourcePath(String resourcePath, File dest) throws IOException { - return extractResourcePath(ResourceExtractor.class, resourcePath, dest); - } - - public static File extractResourcePath(Class cl, String resourcePath, File dest) - throws IOException { - boolean alwaysExtract = true; - URL url = cl.getResource(resourcePath); - if (url == null) { - throw new IllegalArgumentException("Resource not found: " + resourcePath); - } - if ("jar".equalsIgnoreCase(url.getProtocol())) { - File jarFile = getJarFileFromUrl(url); - extractResourcePathFromJar(cl, jarFile, resourcePath, dest); - } else { - try { - File resourceFile = new File(new URI(url.toExternalForm())); - if (!alwaysExtract) { - return resourceFile; - } - if (resourceFile.isDirectory()) { - LauncherUtils.copyDirectory(resourceFile, dest); - } else { - FileUtils.getFileUtils().copyFile(resourceFile, dest); - } - } catch (URISyntaxException e) { - throw new RuntimeException("Couldn't convert URL to File:" + url, e); - } - } - return dest; - } - - private static void extractResourcePathFromJar(Class cl, File jarFile, String resourcePath, File dest) throws IOException { - ZipFile z = new ZipFile(jarFile, ZipFile.OPEN_READ); - String zipStyleResourcePath = resourcePath.substring(1) + "/"; - ZipEntry ze = z.getEntry(zipStyleResourcePath); - log.debug( "Extracting "+resourcePath+" to " + dest.getAbsolutePath() ); - if (ze != null) { - // DGF If it's a directory, then we need to look at all the entries - for (Enumeration entries = z.entries(); entries.hasMoreElements();) { - ze = (ZipEntry) entries.nextElement(); - if (ze.getName().startsWith(zipStyleResourcePath)) { - String relativePath = ze.getName().substring(zipStyleResourcePath.length()); - File destFile = new File(dest, relativePath); - if (ze.isDirectory()) { - destFile.mkdirs(); - } else { - FileOutputStream fos = new FileOutputStream(destFile); - copyStream(z.getInputStream(ze), fos); - } - } - } - } else { - FileOutputStream fos = new FileOutputStream(dest); - copyStream(LauncherUtils.getSeleniumResourceAsStream(resourcePath), fos); - - } - } - - private static File getJarFileFromUrl(URL url) { - if (!"jar".equalsIgnoreCase(url.getProtocol())) - throw new IllegalArgumentException("This is not a Jar URL:" - + url.toString()); - String resourceFilePath = url.getFile(); - int index = resourceFilePath.indexOf("!"); - if (index == -1) { - throw new RuntimeException("Bug! " + url.toExternalForm() - + " does not have a '!'"); - } - String jarFileURI = resourceFilePath.substring(0, index).replace(" ", "%20"); - try { - File jarFile = new File(new URI(jarFileURI)); - return jarFile; - } catch (URISyntaxException e) { - throw new RuntimeException("Bug! URI failed to parse: " + jarFileURI, e); - } - - } - - - - private static void copyStream(InputStream in, OutputStream out) throws IOException { - try { - - byte[] buffer = new byte[BUF_SIZE]; - int count = 0; - do { - out.write(buffer, 0, count); - count = in.read(buffer, 0, buffer.length); - } while (count != -1); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) {} - } - if (in != null) { - try { - in.close(); - } catch (IOException e) {} - } - } - - } -} diff --git a/src/main/java/org/browsermob/proxy/util/StandardFormatter.java b/src/main/java/org/browsermob/proxy/util/StandardFormatter.java deleted file mode 100644 index ef28238a0..000000000 --- a/src/main/java/org/browsermob/proxy/util/StandardFormatter.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.browsermob.proxy.util; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; - -public class StandardFormatter extends Formatter { - private static final int CLASS_LENGTH = 20; - - public String format(LogRecord record) { - try { - StringBuilder sb = new StringBuilder(); - - String level = "UNKN"; - if (record.getLevel().equals(Level.WARNING)) { - level = "WARN"; - } else if (record.getLevel().equals(Level.SEVERE)) { - level = "SEVR"; - } else if (record.getLevel().equals(Level.INFO)) { - level = "INFO"; - } else if (record.getLevel().equals(Level.FINE)) { - level = "FINE"; - } else if (record.getLevel().equals(Level.FINEST)) { - level = "FNST"; - } else if (record.getLevel().equals(Level.FINER)) { - level = "FINR"; - } else if (record.getLevel().equals(Level.CONFIG)) { - level = "CONF"; - } else if (record.getLevel().equals(Level.OFF)) { - level = "OFF "; - } else if (record.getLevel().equals(Level.ALL)) { - level = "ALL "; - } - - sb.append(level).append(" "); - - SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm:ss"); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - sb.append(sdf.format(new Date(record.getMillis()))).append(" "); - - - String className = record.getLoggerName(); - int classNameLength = className.length(); - int before = sb.length(); - if (classNameLength > CLASS_LENGTH) { - int index = -1; - while (true) { - sb.append(className.charAt(index + 1)); - - int oldIndex = index; - index = className.indexOf(".", index + 1); - - if (index == -1) { - String str = className.substring(oldIndex + 2); - int rem = CLASS_LENGTH - (sb.length() - before); - - if (str.length() > rem) { - str = str.substring(0, rem - 1) + '~'; - } - - sb.append(str); - - break; - } else { - sb.append('.'); - } - } - } else { - sb.append(className); - } - int after = sb.length(); - - for (int i = (after - before); i <= CLASS_LENGTH - 1; i++) { - sb.append(' '); - } - - sb.append(" - "); - if (record.getParameters() != null && record.getParameters().length > 0) { - java.util.Formatter formatter = new java.util.Formatter(sb); - formatter.format(record.getMessage(), record.getParameters()); - formatter.format("\n"); - } else { - sb.append(record.getMessage()).append("\n"); - } - - if (record.getThrown() != null) { - StringWriter sw = new StringWriter(); - record.getThrown().printStackTrace(new PrintWriter(sw)); - sb.append(sw.toString()); - } - - return sb.toString(); - } catch (Exception e) { - System.err.println("*******************************************************"); - System.err.println("There was a problem formatting a log statement:"); - e.printStackTrace(); - System.err.println("We will return the raw message and print any stack now"); - System.err.println("*******************************************************"); - - if (record.getThrown() != null) { - System.err.println("Root stack trace:"); - record.getThrown().printStackTrace(); - System.err.println("*******************************************************"); - } - - return record.getMessage() + "\n"; - } - } -} diff --git a/src/main/java/org/xbill/DNS/A6Record.java b/src/main/java/org/xbill/DNS/A6Record.java deleted file mode 100644 index acb90fef3..000000000 --- a/src/main/java/org/xbill/DNS/A6Record.java +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * A6 Record - maps a domain name to an IPv6 address (experimental) - * - * @author Brian Wellington - */ - -public class A6Record extends Record { - -private static final long serialVersionUID = -8815026887337346789L; - -private int prefixBits; -private InetAddress suffix; -private Name prefix; - -A6Record() {} - -Record -getObject() { - return new A6Record(); -} - -/** - * Creates an A6 Record from the given data - * @param prefixBits The number of bits in the address prefix - * @param suffix The address suffix - * @param prefix The name of the prefix - */ -public -A6Record(Name name, int dclass, long ttl, int prefixBits, - InetAddress suffix, Name prefix) -{ - super(name, Type.A6, dclass, ttl); - this.prefixBits = checkU8("prefixBits", prefixBits); - if (suffix != null && Address.familyOf(suffix) != Address.IPv6) - throw new IllegalArgumentException("invalid IPv6 address"); - this.suffix = suffix; - if (prefix != null) - this.prefix = checkName("prefix", prefix); -} - -void -rrFromWire(DNSInput in) throws IOException { - prefixBits = in.readU8(); - int suffixbits = 128 - prefixBits; - int suffixbytes = (suffixbits + 7) / 8; - if (prefixBits < 128) { - byte [] bytes = new byte[16]; - in.readByteArray(bytes, 16 - suffixbytes, suffixbytes); - suffix = InetAddress.getByAddress(bytes); - } - if (prefixBits > 0) - prefix = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - prefixBits = st.getUInt8(); - if (prefixBits > 128) { - throw st.exception("prefix bits must be [0..128]"); - } else if (prefixBits < 128) { - String s = st.getString(); - try { - suffix = Address.getByAddress(s, Address.IPv6); - } - catch (UnknownHostException e) { - throw st.exception("invalid IPv6 address: " + s); - } - } - if (prefixBits > 0) - prefix = st.getName(origin); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(prefixBits); - if (suffix != null) { - sb.append(" "); - sb.append(suffix.getHostAddress()); - } - if (prefix != null) { - sb.append(" "); - sb.append(prefix); - } - return sb.toString(); -} - -/** Returns the number of bits in the prefix */ -public int -getPrefixBits() { - return prefixBits; -} - -/** Returns the address suffix */ -public InetAddress -getSuffix() { - return suffix; -} - -/** Returns the address prefix */ -public Name -getPrefix() { - return prefix; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU8(prefixBits); - if (suffix != null) { - int suffixbits = 128 - prefixBits; - int suffixbytes = (suffixbits + 7) / 8; - byte [] data = suffix.getAddress(); - out.writeByteArray(data, 16 - suffixbytes, suffixbytes); - } - if (prefix != null) - prefix.toWire(out, null, canonical); -} - -} diff --git a/src/main/java/org/xbill/DNS/AAAARecord.java b/src/main/java/org/xbill/DNS/AAAARecord.java deleted file mode 100644 index 371ae0aaa..000000000 --- a/src/main/java/org/xbill/DNS/AAAARecord.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.net.InetAddress; - -/** - * IPv6 Address Record - maps a domain name to an IPv6 address - * - * @author Brian Wellington - */ - -public class AAAARecord extends Record { - -private static final long serialVersionUID = -4588601512069748050L; - -private InetAddress address; - -AAAARecord() {} - -Record -getObject() { - return new AAAARecord(); -} - -/** - * Creates an AAAA Record from the given data - * @param address The address suffix - */ -public -AAAARecord(Name name, int dclass, long ttl, InetAddress address) { - super(name, Type.AAAA, dclass, ttl); - if (Address.familyOf(address) != Address.IPv6) - throw new IllegalArgumentException("invalid IPv6 address"); - this.address = address; -} - -void -rrFromWire(DNSInput in) throws IOException { - address = InetAddress.getByAddress(in.readByteArray(16)); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - address = st.getAddress(Address.IPv6); -} - -/** Converts rdata to a String */ -String -rrToString() { - return address.getHostAddress(); -} - -/** Returns the address */ -public InetAddress -getAddress() { - return address; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeByteArray(address.getAddress()); -} - -} diff --git a/src/main/java/org/xbill/DNS/AFSDBRecord.java b/src/main/java/org/xbill/DNS/AFSDBRecord.java deleted file mode 100644 index 4814faab5..000000000 --- a/src/main/java/org/xbill/DNS/AFSDBRecord.java +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * AFS Data Base Record - maps a domain name to the name of an AFS cell - * database server. - * - * - * @author Brian Wellington - */ - -public class AFSDBRecord extends U16NameBase { - -private static final long serialVersionUID = 3034379930729102437L; - -AFSDBRecord() {} - -Record -getObject() { - return new AFSDBRecord(); -} - -/** - * Creates an AFSDB Record from the given data. - * @param subtype Indicates the type of service provided by the host. - * @param host The host providing the service. - */ -public -AFSDBRecord(Name name, int dclass, long ttl, int subtype, Name host) { - super(name, Type.AFSDB, dclass, ttl, subtype, "subtype", host, "host"); -} - -/** Gets the subtype indicating the service provided by the host. */ -public int -getSubtype() { - return getU16Field(); -} - -/** Gets the host providing service for the domain. */ -public Name -getHost() { - return getNameField(); -} - -} diff --git a/src/main/java/org/xbill/DNS/APLRecord.java b/src/main/java/org/xbill/DNS/APLRecord.java deleted file mode 100644 index 77e1cdbf0..000000000 --- a/src/main/java/org/xbill/DNS/APLRecord.java +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.IOException; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * APL - Address Prefix List. See RFC 3123. - * - * @author Brian Wellington - */ - -/* - * Note: this currently uses the same constants as the Address class; - * this could change if more constants are defined for APL records. - */ - -public class APLRecord extends Record { - -public static class Element { - public final int family; - public final boolean negative; - public final int prefixLength; - public final Object address; - - private - Element(int family, boolean negative, Object address, int prefixLength) - { - this.family = family; - this.negative = negative; - this.address = address; - this.prefixLength = prefixLength; - if (!validatePrefixLength(family, prefixLength)) { - throw new IllegalArgumentException("invalid prefix " + - "length"); - } - } - - /** - * Creates an APL element corresponding to an IPv4 or IPv6 prefix. - * @param negative Indicates if this prefix is a negation. - * @param address The IPv4 or IPv6 address. - * @param prefixLength The length of this prefix, in bits. - * @throws IllegalArgumentException The prefix length is invalid. - */ - public - Element(boolean negative, InetAddress address, int prefixLength) { - this(Address.familyOf(address), negative, address, - prefixLength); - } - - public String - toString() { - StringBuffer sb = new StringBuffer(); - if (negative) - sb.append("!"); - sb.append(family); - sb.append(":"); - if (family == Address.IPv4 || family == Address.IPv6) - sb.append(((InetAddress) address).getHostAddress()); - else - sb.append(base16.toString((byte []) address)); - sb.append("/"); - sb.append(prefixLength); - return sb.toString(); - } - - public boolean - equals(Object arg) { - if (arg == null || !(arg instanceof Element)) - return false; - Element elt = (Element) arg; - return (family == elt.family && - negative == elt.negative && - prefixLength == elt.prefixLength && - address.equals(elt.address)); - } - - public int - hashCode() { - return address.hashCode() + prefixLength + (negative ? 1 : 0); - } -} - -private static final long serialVersionUID = -1348173791712935864L; - -private List elements; - -APLRecord() {} - -Record -getObject() { - return new APLRecord(); -} - -private static boolean -validatePrefixLength(int family, int prefixLength) { - if (prefixLength < 0 || prefixLength >= 256) - return false; - if ((family == Address.IPv4 && prefixLength > 32) || - (family == Address.IPv6 && prefixLength > 128)) - return false; - return true; -} - -/** - * Creates an APL Record from the given data. - * @param elements The list of APL elements. - */ -public -APLRecord(Name name, int dclass, long ttl, List elements) { - super(name, Type.APL, dclass, ttl); - this.elements = new ArrayList(elements.size()); - for (Iterator it = elements.iterator(); it.hasNext(); ) { - Object o = it.next(); - if (!(o instanceof Element)) { - throw new IllegalArgumentException("illegal element"); - } - Element element = (Element) o; - if (element.family != Address.IPv4 && - element.family != Address.IPv6) - { - throw new IllegalArgumentException("unknown family"); - } - this.elements.add(element); - - } -} - -private static byte [] -parseAddress(byte [] in, int length) throws WireParseException { - if (in.length > length) - throw new WireParseException("invalid address length"); - if (in.length == length) - return in; - byte [] out = new byte[length]; - System.arraycopy(in, 0, out, 0, in.length); - return out; -} - -void -rrFromWire(DNSInput in) throws IOException { - elements = new ArrayList(1); - while (in.remaining() != 0) { - int family = in.readU16(); - int prefix = in.readU8(); - int length = in.readU8(); - boolean negative = (length & 0x80) != 0; - length &= ~0x80; - - byte [] data = in.readByteArray(length); - Element element; - if (!validatePrefixLength(family, prefix)) { - throw new WireParseException("invalid prefix length"); - } - - if (family == Address.IPv4 || family == Address.IPv6) { - data = parseAddress(data, - Address.addressLength(family)); - InetAddress addr = InetAddress.getByAddress(data); - element = new Element(negative, addr, prefix); - } else { - element = new Element(family, negative, data, prefix); - } - elements.add(element); - - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - elements = new ArrayList(1); - while (true) { - Tokenizer.Token t = st.get(); - if (!t.isString()) - break; - - boolean negative = false; - int family = 0; - int prefix = 0; - - String s = t.value; - int start = 0; - if (s.startsWith("!")) { - negative = true; - start = 1; - } - int colon = s.indexOf(':', start); - if (colon < 0) - throw st.exception("invalid address prefix element"); - int slash = s.indexOf('/', colon); - if (slash < 0) - throw st.exception("invalid address prefix element"); - - String familyString = s.substring(start, colon); - String addressString = s.substring(colon + 1, slash); - String prefixString = s.substring(slash + 1); - - try { - family = Integer.parseInt(familyString); - } - catch (NumberFormatException e) { - throw st.exception("invalid family"); - } - if (family != Address.IPv4 && family != Address.IPv6) - throw st.exception("unknown family"); - - try { - prefix = Integer.parseInt(prefixString); - } - catch (NumberFormatException e) { - throw st.exception("invalid prefix length"); - } - - if (!validatePrefixLength(family, prefix)) { - throw st.exception("invalid prefix length"); - } - - byte [] bytes = Address.toByteArray(addressString, family); - if (bytes == null) - throw st.exception("invalid IP address " + - addressString); - - InetAddress address = InetAddress.getByAddress(bytes); - elements.add(new Element(negative, address, prefix)); - } - st.unget(); -} - -String -rrToString() { - StringBuffer sb = new StringBuffer(); - for (Iterator it = elements.iterator(); it.hasNext(); ) { - Element element = (Element) it.next(); - sb.append(element); - if (it.hasNext()) - sb.append(" "); - } - return sb.toString(); -} - -/** Returns the list of APL elements. */ -public List -getElements() { - return elements; -} - -private static int -addressLength(byte [] addr) { - for (int i = addr.length - 1; i >= 0; i--) { - if (addr[i] != 0) - return i + 1; - } - return 0; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - for (Iterator it = elements.iterator(); it.hasNext(); ) { - Element element = (Element) it.next(); - int length = 0; - byte [] data; - if (element.family == Address.IPv4 || - element.family == Address.IPv6) - { - InetAddress addr = (InetAddress) element.address; - data = addr.getAddress(); - length = addressLength(data); - } else { - data = (byte []) element.address; - length = data.length; - } - int wlength = length; - if (element.negative) { - wlength |= 0x80; - } - out.writeU16(element.family); - out.writeU8(element.prefixLength); - out.writeU8(wlength); - out.writeByteArray(data, 0, length); - } -} - -} diff --git a/src/main/java/org/xbill/DNS/ARecord.java b/src/main/java/org/xbill/DNS/ARecord.java deleted file mode 100644 index c4b2bf407..000000000 --- a/src/main/java/org/xbill/DNS/ARecord.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * Address Record - maps a domain name to an Internet address - * - * @author Brian Wellington - */ - -public class ARecord extends Record { - -private static final long serialVersionUID = -2172609200849142323L; - -private int addr; - -ARecord() {} - -Record -getObject() { - return new ARecord(); -} - -private static final int -fromArray(byte [] array) { - return (((array[0] & 0xFF) << 24) | - ((array[1] & 0xFF) << 16) | - ((array[2] & 0xFF) << 8) | - (array[3] & 0xFF)); -} - -private static final byte [] -toArray(int addr) { - byte [] bytes = new byte[4]; - bytes[0] = (byte) ((addr >>> 24) & 0xFF); - bytes[1] = (byte) ((addr >>> 16) & 0xFF); - bytes[2] = (byte) ((addr >>> 8) & 0xFF); - bytes[3] = (byte) (addr & 0xFF); - return bytes; -} - -/** - * Creates an A Record from the given data - * @param address The address that the name refers to - */ -public -ARecord(Name name, int dclass, long ttl, InetAddress address) { - super(name, Type.A, dclass, ttl); - if (Address.familyOf(address) != Address.IPv4) - throw new IllegalArgumentException("invalid IPv4 address"); - addr = fromArray(address.getAddress()); -} - -void -rrFromWire(DNSInput in) throws IOException { - addr = fromArray(in.readByteArray(4)); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - InetAddress address = st.getAddress(Address.IPv4); - addr = fromArray(address.getAddress()); -} - -/** Converts rdata to a String */ -String -rrToString() { - return (Address.toDottedQuad(toArray(addr))); -} - -/** Returns the Internet address */ -public InetAddress -getAddress() { - try { - return InetAddress.getByAddress(toArray(addr)); - } catch (UnknownHostException e) { - return null; - } -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU32(((long)addr) & 0xFFFFFFFFL); -} - -} diff --git a/src/main/java/org/xbill/DNS/Address.java b/src/main/java/org/xbill/DNS/Address.java deleted file mode 100644 index ecdf2c070..000000000 --- a/src/main/java/org/xbill/DNS/Address.java +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * Routines dealing with IP addresses. Includes functions similar to - * those in the java.net.InetAddress class. - * - * @author Brian Wellington - */ - -public final class Address { - - public static final int IPv4 = 1; - public static final int IPv6 = 2; - - private Address() { - } - - private static byte[] - parseV4(String s) { - int numDigits; - int currentOctet; - byte[] values = new byte[4]; - int currentValue; - int length = s.length(); - - currentOctet = 0; - currentValue = 0; - numDigits = 0; - for (int i = 0; i < length; i++) { - char c = s.charAt(i); - if (c >= '0' && c <= '9') { - /* Can't have more than 3 digits per octet. */ - if (numDigits == 3) - return null; - /* Octets shouldn't start with 0, unless they are 0. */ - if (numDigits > 0 && currentValue == 0) - return null; - numDigits++; - currentValue *= 10; - currentValue += (c - '0'); - /* 255 is the maximum value for an octet. */ - if (currentValue > 255) - return null; - } else if (c == '.') { - /* Can't have more than 3 dots. */ - if (currentOctet == 3) - return null; - /* Two consecutive dots are bad. */ - if (numDigits == 0) - return null; - values[currentOctet++] = (byte) currentValue; - currentValue = 0; - numDigits = 0; - } else - return null; - } - /* Must have 4 octets. */ - if (currentOctet != 3) - return null; - /* The fourth octet can't be empty. */ - if (numDigits == 0) - return null; - values[currentOctet] = (byte) currentValue; - return values; - } - - private static byte[] - parseV6(String s) { - int range = -1; - byte[] data = new byte[16]; - - String[] tokens = s.split(":", -1); - - int first = 0; - int last = tokens.length - 1; - - if (tokens[0].length() == 0) { - // If the first two tokens are empty, it means the string - // started with ::, which is fine. If only the first is - // empty, the string started with :, which is bad. - if (last - first > 0 && tokens[1].length() == 0) - first++; - else - return null; - } - - if (tokens[last].length() == 0) { - // If the last two tokens are empty, it means the string - // ended with ::, which is fine. If only the last is - // empty, the string ended with :, which is bad. - if (last - first > 0 && tokens[last - 1].length() == 0) - last--; - else - return null; - } - - if (last - first + 1 > 8) - return null; - - int i, j; - for (i = first, j = 0; i <= last; i++) { - if (tokens[i].length() == 0) { - if (range >= 0) - return null; - range = j; - continue; - } - - if (tokens[i].indexOf('.') >= 0) { - // An IPv4 address must be the last component - if (i < last) - return null; - // There can't have been more than 6 components. - if (i > 6) - return null; - byte[] v4addr = Address.toByteArray(tokens[i], IPv4); - if (v4addr == null) - return null; - for (int k = 0; k < 4; k++) - data[j++] = v4addr[k]; - break; - } - - try { - for (int k = 0; k < tokens[i].length(); k++) { - char c = tokens[i].charAt(k); - if (Character.digit(c, 16) < 0) - return null; - } - int x = Integer.parseInt(tokens[i], 16); - if (x > 0xFFFF || x < 0) - return null; - data[j++] = (byte) (x >>> 8); - data[j++] = (byte) (x & 0xFF); - } - catch (NumberFormatException e) { - return null; - } - } - - if (j < 16 && range < 0) - return null; - - if (range >= 0) { - int empty = 16 - j; - System.arraycopy(data, range, data, range + empty, j - range); - for (i = range; i < range + empty; i++) - data[i] = 0; - } - - return data; - } - - /** - * Convert a string containing an IP address to an array of 4 or 16 integers. - * - * @param s The address, in text format. - * @param family The address family. - * @return The address - */ - public static int[] - toArray(String s, int family) { - byte[] byteArray = toByteArray(s, family); - if (byteArray == null) - return null; - int[] intArray = new int[byteArray.length]; - for (int i = 0; i < byteArray.length; i++) - intArray[i] = byteArray[i] & 0xFF; - return intArray; - } - - /** - * Convert a string containing an IPv4 address to an array of 4 integers. - * - * @param s The address, in text format. - * @return The address - */ - public static int[] - toArray(String s) { - return toArray(s, IPv4); - } - - /** - * Convert a string containing an IP address to an array of 4 or 16 bytes. - * - * @param s The address, in text format. - * @param family The address family. - * @return The address - */ - public static byte[] - toByteArray(String s, int family) { - if (family == IPv4) - return parseV4(s); - else if (family == IPv6) - return parseV6(s); - else - throw new IllegalArgumentException("unknown address family"); - } - - /** - * Determines if a string contains a valid IP address. - * - * @param s The string - * @return Whether the string contains a valid IP address - */ - public static boolean - isDottedQuad(String s) { - byte[] address = Address.toByteArray(s, IPv4); - return (address != null); - } - - /** - * Converts a byte array containing an IPv4 address into a dotted quad string. - * - * @param addr The array - * @return The string representation - */ - public static String - toDottedQuad(byte[] addr) { - return ((addr[0] & 0xFF) + "." + (addr[1] & 0xFF) + "." + - (addr[2] & 0xFF) + "." + (addr[3] & 0xFF)); - } - - /** - * Converts an int array containing an IPv4 address into a dotted quad string. - * - * @param addr The array - * @return The string representation - */ - public static String - toDottedQuad(int[] addr) { - return (addr[0] + "." + addr[1] + "." + addr[2] + "." + addr[3]); - } - - private static Record[] - lookupHostName(String name) throws UnknownHostException { - try { - Record[] records = new Lookup(name).run(); - if (records == null) - throw new UnknownHostException("unknown host"); - return records; - } - catch (TextParseException e) { - throw new UnknownHostException("invalid name"); - } - } - - private static InetAddress - addrFromRecord(String name, Record r) throws UnknownHostException { - ARecord a = (ARecord) r; - return InetAddress.getByAddress(name, a.getAddress().getAddress()); - } - - /** - * Determines the IP address of a host - * - * @param name The hostname to look up - * @return The first matching IP address - * @throws UnknownHostException The hostname does not have any addresses - */ - public static InetAddress - getByName(String name) throws UnknownHostException { - try { - return getByAddress(name); - } catch (UnknownHostException e) { - Record[] records = lookupHostName(name); - return addrFromRecord(name, records[0]); - } - } - - /** - * Determines all IP address of a host - * - * @param name The hostname to look up - * @return All matching IP addresses - * @throws UnknownHostException The hostname does not have any addresses - */ - public static InetAddress[] - getAllByName(String name) throws UnknownHostException { - try { - InetAddress addr = getByAddress(name); - return new InetAddress[]{addr}; - } catch (UnknownHostException e) { - Record[] records = lookupHostName(name); - InetAddress[] addrs = new InetAddress[records.length]; - for (int i = 0; i < records.length; i++) - addrs[i] = addrFromRecord(name, records[i]); - return addrs; - } - } - - /** - * Converts an address from its string representation to an IP address. - * The address can be either IPv4 or IPv6. - * - * @param addr The address, in string form - * @return The IP addresses - * @throws UnknownHostException The address is not a valid IP address. - */ - public static InetAddress - getByAddress(String addr) throws UnknownHostException { - byte[] bytes; - bytes = toByteArray(addr, IPv4); - if (bytes != null) - return InetAddress.getByAddress(bytes); - bytes = toByteArray(addr, IPv6); - if (bytes != null) - return InetAddress.getByAddress(bytes); - throw new UnknownHostException("Invalid address: " + addr); - } - - /** - * Converts an address from its string representation to an IP address in - * a particular family. - * - * @param addr The address, in string form - * @param family The address family, either IPv4 or IPv6. - * @return The IP addresses - * @throws UnknownHostException The address is not a valid IP address in - * the specified address family. - */ - public static InetAddress - getByAddress(String addr, int family) throws UnknownHostException { - if (family != IPv4 && family != IPv6) - throw new IllegalArgumentException("unknown address family"); - byte[] bytes; - bytes = toByteArray(addr, family); - if (bytes != null) - return InetAddress.getByAddress(bytes); - throw new UnknownHostException("Invalid address: " + addr); - } - - /** - * Determines the hostname for an address - * - * @param addr The address to look up - * @return The associated host name - * @throws UnknownHostException There is no hostname for the address - */ - public static String - getHostName(InetAddress addr) throws UnknownHostException { - Name name = ReverseMap.fromAddress(addr); - Record[] records = new Lookup(name, Type.PTR).run(); - if (records == null) - throw new UnknownHostException("unknown address"); - PTRRecord ptr = (PTRRecord) records[0]; - return ptr.getTarget().toString(); - } - - /** - * Returns the family of an InetAddress. - * - * @param address The supplied address. - * @return The family, either IPv4 or IPv6. - */ - public static int - familyOf(InetAddress address) { - if (address instanceof Inet4Address) - return IPv4; - if (address instanceof Inet6Address) - return IPv6; - throw new IllegalArgumentException("unknown address family"); - } - - /** - * Returns the length of an address in a particular family. - * - * @param family The address family, either IPv4 or IPv6. - * @return The length of addresses in that family. - */ - public static int - addressLength(int family) { - if (family == IPv4) - return 4; - if (family == IPv6) - return 16; - throw new IllegalArgumentException("unknown address family"); - } - -} diff --git a/src/main/java/org/xbill/DNS/CERTRecord.java b/src/main/java/org/xbill/DNS/CERTRecord.java deleted file mode 100644 index a6fbecf57..000000000 --- a/src/main/java/org/xbill/DNS/CERTRecord.java +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; - -/** - * Certificate Record - Stores a certificate associated with a name. The - * certificate might also be associated with a KEYRecord. - * @see KEYRecord - * - * @author Brian Wellington - */ - -public class CERTRecord extends Record { - -public static class CertificateType { - /** Certificate type identifiers. See RFC 4398 for more detail. */ - - private CertificateType() {} - - /** PKIX (X.509v3) */ - public static final int PKIX = 1; - - /** Simple Public Key Infrastructure */ - public static final int SPKI = 2; - - /** Pretty Good Privacy */ - public static final int PGP = 3; - - /** URL of an X.509 data object */ - public static final int IPKIX = 4; - - /** URL of an SPKI certificate */ - public static final int ISPKI = 5; - - /** Fingerprint and URL of an OpenPGP packet */ - public static final int IPGP = 6; - - /** Attribute Certificate */ - public static final int ACPKIX = 7; - - /** URL of an Attribute Certificate */ - public static final int IACPKIX = 8; - - /** Certificate format defined by URI */ - public static final int URI = 253; - - /** Certificate format defined by OID */ - public static final int OID = 254; - - private static Mnemonic types = new Mnemonic("Certificate type", - Mnemonic.CASE_UPPER); - - static { - types.setMaximum(0xFFFF); - types.setNumericAllowed(true); - - types.add(PKIX, "PKIX"); - types.add(SPKI, "SPKI"); - types.add(PGP, "PGP"); - types.add(PKIX, "IPKIX"); - types.add(SPKI, "ISPKI"); - types.add(PGP, "IPGP"); - types.add(PGP, "ACPKIX"); - types.add(PGP, "IACPKIX"); - types.add(URI, "URI"); - types.add(OID, "OID"); - } - - /** - * Converts a certificate type into its textual representation - */ - public static String - string(int type) { - return types.getText(type); - } - - /** - * Converts a textual representation of an certificate type into its - * numeric code. Integers in the range 0..65535 are also accepted. - * @param s The textual representation of the algorithm - * @return The algorithm code, or -1 on error. - */ - public static int - value(String s) { - return types.getValue(s); - } -} - -/** PKIX (X.509v3) */ -public static final int PKIX = CertificateType.PKIX; - -/** Simple Public Key Infrastructure */ -public static final int SPKI = CertificateType.SPKI; - -/** Pretty Good Privacy */ -public static final int PGP = CertificateType.PGP; - -/** Certificate format defined by URI */ -public static final int URI = CertificateType.URI; - -/** Certificate format defined by IOD */ -public static final int OID = CertificateType.OID; - -private static final long serialVersionUID = 4763014646517016835L; - -private int certType, keyTag; -private int alg; -private byte [] cert; - -CERTRecord() {} - -Record -getObject() { - return new CERTRecord(); -} - -/** - * Creates a CERT Record from the given data - * @param certType The type of certificate (see constants) - * @param keyTag The ID of the associated KEYRecord, if present - * @param alg The algorithm of the associated KEYRecord, if present - * @param cert Binary data representing the certificate - */ -public -CERTRecord(Name name, int dclass, long ttl, int certType, int keyTag, - int alg, byte [] cert) -{ - super(name, Type.CERT, dclass, ttl); - this.certType = checkU16("certType", certType); - this.keyTag = checkU16("keyTag", keyTag); - this.alg = checkU8("alg", alg); - this.cert = cert; -} - -void -rrFromWire(DNSInput in) throws IOException { - certType = in.readU16(); - keyTag = in.readU16(); - alg = in.readU8(); - cert = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - String certTypeString = st.getString(); - certType = CertificateType.value(certTypeString); - if (certType < 0) - throw st.exception("Invalid certificate type: " + - certTypeString); - keyTag = st.getUInt16(); - String algString = st.getString(); - alg = DNSSEC.Algorithm.value(algString); - if (alg < 0) - throw st.exception("Invalid algorithm: " + algString); - cert = st.getBase64(); -} - -/** - * Converts rdata to a String - */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append (certType); - sb.append (" "); - sb.append (keyTag); - sb.append (" "); - sb.append (alg); - if (cert != null) { - if (Options.check("multiline")) { - sb.append(" (\n"); - sb.append(base64.formatString(cert, 64, "\t", true)); - } else { - sb.append(" "); - sb.append(base64.toString(cert)); - } - } - return sb.toString(); -} - -/** - * Returns the type of certificate - */ -public int -getCertType() { - return certType; -} - -/** - * Returns the ID of the associated KEYRecord, if present - */ -public int -getKeyTag() { - return keyTag; -} - -/** - * Returns the algorithm of the associated KEYRecord, if present - */ -public int -getAlgorithm() { - return alg; -} - -/** - * Returns the binary representation of the certificate - */ -public byte [] -getCert() { - return cert; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(certType); - out.writeU16(keyTag); - out.writeU8(alg); - out.writeByteArray(cert); -} - -} diff --git a/src/main/java/org/xbill/DNS/CNAMERecord.java b/src/main/java/org/xbill/DNS/CNAMERecord.java deleted file mode 100644 index 8db9453e7..000000000 --- a/src/main/java/org/xbill/DNS/CNAMERecord.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * CNAME Record - maps an alias to its real name - * - * @author Brian Wellington - */ - -public class CNAMERecord extends SingleCompressedNameBase { - -private static final long serialVersionUID = -4020373886892538580L; - -CNAMERecord() {} - -Record -getObject() { - return new CNAMERecord(); -} - -/** - * Creates a new CNAMERecord with the given data - * @param alias The name to which the CNAME alias points - */ -public -CNAMERecord(Name name, int dclass, long ttl, Name alias) { - super(name, Type.CNAME, dclass, ttl, alias, "alias"); -} - -/** - * Gets the target of the CNAME Record - */ -public Name -getTarget() { - return getSingleName(); -} - -/** Gets the alias specified by the CNAME Record */ -public Name -getAlias() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Cache.java b/src/main/java/org/xbill/DNS/Cache.java deleted file mode 100644 index afa34d765..000000000 --- a/src/main/java/org/xbill/DNS/Cache.java +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.*; - -/** - * A cache of DNS records. The cache obeys TTLs, so items are purged after - * their validity period is complete. Negative answers are cached, to - * avoid repeated failed DNS queries. The credibility of each RRset is - * maintained, so that more credible records replace less credible records, - * and lookups can specify the minimum credibility of data they are requesting. - * @see RRset - * @see Credibility - * - * @author Brian Wellington - */ - -public class Cache { - -private interface Element { - public boolean expired(); - public int compareCredibility(int cred); - public int getType(); -} - -private static int -limitExpire(long ttl, long maxttl) { - if (maxttl >= 0 && maxttl < ttl) - ttl = maxttl; - long expire = (System.currentTimeMillis() / 1000) + ttl; - if (expire < 0 || expire > Integer.MAX_VALUE) - return Integer.MAX_VALUE; - return (int)expire; -} - -private static class CacheRRset extends RRset implements Element { - private static final long serialVersionUID = 5971755205903597024L; - - int credibility; - int expire; - - public - CacheRRset(Record rec, int cred, long maxttl) { - super(); - this.credibility = cred; - this.expire = limitExpire(rec.getTTL(), maxttl); - addRR(rec); - } - - public - CacheRRset(RRset rrset, int cred, long maxttl) { - super(rrset); - this.credibility = cred; - this.expire = limitExpire(rrset.getTTL(), maxttl); - } - - public final boolean - expired() { - int now = (int)(System.currentTimeMillis() / 1000); - return (now >= expire); - } - - public final int - compareCredibility(int cred) { - return credibility - cred; - } - - public String - toString() { - StringBuffer sb = new StringBuffer(); - sb.append(super.toString()); - sb.append(" cl = "); - sb.append(credibility); - return sb.toString(); - } -} - -private static class NegativeElement implements Element { - int type; - Name name; - int credibility; - int expire; - - public - NegativeElement(Name name, int type, SOARecord soa, int cred, - long maxttl) - { - this.name = name; - this.type = type; - long cttl = 0; - if (soa != null) - cttl = soa.getMinimum(); - this.credibility = cred; - this.expire = limitExpire(cttl, maxttl); - } - - public int - getType() { - return type; - } - - public final boolean - expired() { - int now = (int)(System.currentTimeMillis() / 1000); - return (now >= expire); - } - - public final int - compareCredibility(int cred) { - return credibility - cred; - } - - public String - toString() { - StringBuffer sb = new StringBuffer(); - if (type == 0) - sb.append("NXDOMAIN " + name); - else - sb.append("NXRRSET " + name + " " + Type.string(type)); - sb.append(" cl = "); - sb.append(credibility); - return sb.toString(); - } -} - -private static class CacheMap extends LinkedHashMap { - private int maxsize = -1; - - CacheMap(int maxsize) { - super(16, (float) 0.75, true); - this.maxsize = maxsize; - } - - int - getMaxSize() { - return maxsize; - } - - void - setMaxSize(int maxsize) { - /* - * Note that this doesn't shrink the size of the map if - * the maximum size is lowered, but it should shrink as - * entries expire. - */ - this.maxsize = maxsize; - } - - protected boolean removeEldestEntry(Map.Entry eldest) { - return maxsize >= 0 && size() > maxsize; - } -} - -private CacheMap data; -private int maxncache = -1; -private int maxcache = -1; -private int dclass; - -private static final int defaultMaxEntries = 50000; - -/** - * Creates an empty Cache - * - * @param dclass The DNS class of this cache - * @see DClass - */ -public -Cache(int dclass) { - this.dclass = dclass; - data = new CacheMap(defaultMaxEntries); -} - -/** - * Creates an empty Cache for class IN. - * @see DClass - */ -public -Cache() { - this(DClass.IN); -} - -/** - * Creates a Cache which initially contains all records in the specified file. - */ -public -Cache(String file) throws IOException { - data = new CacheMap(defaultMaxEntries); - Master m = new Master(file); - Record record; - while ((record = m.nextRecord()) != null) - addRecord(record, Credibility.HINT, m); -} - -private synchronized Object -exactName(Name name) { - return data.get(name); -} - -private synchronized void -removeName(Name name) { - data.remove(name); -} - -private synchronized Element [] -allElements(Object types) { - if (types instanceof List) { - List typelist = (List) types; - int size = typelist.size(); - return (Element []) typelist.toArray(new Element[size]); - } else { - Element set = (Element) types; - return new Element[] {set}; - } -} - -private synchronized Element -oneElement(Name name, Object types, int type, int minCred) { - Element found = null; - - if (type == Type.ANY) - throw new IllegalArgumentException("oneElement(ANY)"); - if (types instanceof List) { - List list = (List) types; - for (int i = 0; i < list.size(); i++) { - Element set = (Element) list.get(i); - if (set.getType() == type) { - found = set; - break; - } - } - } else { - Element set = (Element) types; - if (set.getType() == type) - found = set; - } - if (found == null) - return null; - if (found.expired()) { - removeElement(name, type); - return null; - } - if (found.compareCredibility(minCred) < 0) - return null; - return found; -} - -private synchronized Element -findElement(Name name, int type, int minCred) { - Object types = exactName(name); - if (types == null) - return null; - return oneElement(name, types, type, minCred); -} - -private synchronized void -addElement(Name name, Element element) { - Object types = data.get(name); - if (types == null) { - data.put(name, element); - return; - } - int type = element.getType(); - if (types instanceof List) { - List list = (List) types; - for (int i = 0; i < list.size(); i++) { - Element elt = (Element) list.get(i); - if (elt.getType() == type) { - list.set(i, element); - return; - } - } - list.add(element); - } else { - Element elt = (Element) types; - if (elt.getType() == type) - data.put(name, element); - else { - LinkedList list = new LinkedList(); - list.add(elt); - list.add(element); - data.put(name, list); - } - } -} - -private synchronized void -removeElement(Name name, int type) { - Object types = data.get(name); - if (types == null) { - return; - } - if (types instanceof List) { - List list = (List) types; - for (int i = 0; i < list.size(); i++) { - Element elt = (Element) list.get(i); - if (elt.getType() == type) { - list.remove(i); - if (list.size() == 0) - data.remove(name); - return; - } - } - } else { - Element elt = (Element) types; - if (elt.getType() != type) - return; - data.remove(name); - } -} - -/** Empties the Cache. */ -public synchronized void -clearCache() { - data.clear(); -} - -/** - * Adds a record to the Cache. - * @param r The record to be added - * @param cred The credibility of the record - * @param o The source of the record (this could be a Message, for example) - * @see Record - */ -public synchronized void -addRecord(Record r, int cred, Object o) { - Name name = r.getName(); - int type = r.getRRsetType(); - if (!Type.isRR(type)) - return; - Element element = findElement(name, type, cred); - if (element == null) { - CacheRRset crrset = new CacheRRset(r, cred, maxcache); - addRRset(crrset, cred); - } else if (element.compareCredibility(cred) == 0) { - if (element instanceof CacheRRset) { - CacheRRset crrset = (CacheRRset) element; - crrset.addRR(r); - } - } -} - -/** - * Adds an RRset to the Cache. - * @param rrset The RRset to be added - * @param cred The credibility of these records - * @see RRset - */ -public synchronized void -addRRset(RRset rrset, int cred) { - long ttl = rrset.getTTL(); - Name name = rrset.getName(); - int type = rrset.getType(); - Element element = findElement(name, type, 0); - if (ttl == 0) { - if (element != null && element.compareCredibility(cred) <= 0) - removeElement(name, type); - } else { - if (element != null && element.compareCredibility(cred) <= 0) - element = null; - if (element == null) { - CacheRRset crrset; - if (rrset instanceof CacheRRset) - crrset = (CacheRRset) rrset; - else - crrset = new CacheRRset(rrset, cred, maxcache); - addElement(name, crrset); - } - } -} - -/** - * Adds a negative entry to the Cache. - * @param name The name of the negative entry - * @param type The type of the negative entry - * @param soa The SOA record to add to the negative cache entry, or null. - * The negative cache ttl is derived from the SOA. - * @param cred The credibility of the negative entry - */ -public synchronized void -addNegative(Name name, int type, SOARecord soa, int cred) { - long ttl = 0; - if (soa != null) - ttl = soa.getTTL(); - Element element = findElement(name, type, 0); - if (ttl == 0) { - if (element != null && element.compareCredibility(cred) <= 0) - removeElement(name, type); - } else { - if (element != null && element.compareCredibility(cred) <= 0) - element = null; - if (element == null) - addElement(name, new NegativeElement(name, type, - soa, cred, - maxncache)); - } -} - -/** - * Finds all matching sets or something that causes the lookup to stop. - */ -protected synchronized SetResponse -lookup(Name name, int type, int minCred) { - int labels; - int tlabels; - Element element; - Name tname; - Object types; - SetResponse sr; - - labels = name.labels(); - - for (tlabels = labels; tlabels >= 1; tlabels--) { - boolean isRoot = (tlabels == 1); - boolean isExact = (tlabels == labels); - - if (isRoot) - tname = Name.root; - else if (isExact) - tname = name; - else - tname = new Name(name, labels - tlabels); - - types = data.get(tname); - if (types == null) - continue; - - /* If this is an ANY lookup, return everything. */ - if (isExact && type == Type.ANY) { - sr = new SetResponse(SetResponse.SUCCESSFUL); - Element [] elements = allElements(types); - int added = 0; - for (int i = 0; i < elements.length; i++) { - element = elements[i]; - if (element.expired()) { - removeElement(tname, element.getType()); - continue; - } - if (!(element instanceof CacheRRset)) - continue; - if (element.compareCredibility(minCred) < 0) - continue; - sr.addRRset((CacheRRset)element); - added++; - } - /* There were positive entries */ - if (added > 0) - return sr; - } - - /* - * If this is the name, look for the actual type or a CNAME. - * Otherwise, look for a DNAME. - */ - if (isExact) { - element = oneElement(tname, types, type, minCred); - if (element != null && - element instanceof CacheRRset) - { - sr = new SetResponse(SetResponse.SUCCESSFUL); - sr.addRRset((CacheRRset) element); - return sr; - } else if (element != null) { - sr = new SetResponse(SetResponse.NXRRSET); - return sr; - } - - element = oneElement(tname, types, Type.CNAME, minCred); - if (element != null && - element instanceof CacheRRset) - { - return new SetResponse(SetResponse.CNAME, - (CacheRRset) element); - } - } else { - element = oneElement(tname, types, Type.DNAME, minCred); - if (element != null && - element instanceof CacheRRset) - { - return new SetResponse(SetResponse.DNAME, - (CacheRRset) element); - } - } - - /* Look for an NS */ - element = oneElement(tname, types, Type.NS, minCred); - if (element != null && element instanceof CacheRRset) - return new SetResponse(SetResponse.DELEGATION, - (CacheRRset) element); - - /* Check for the special NXDOMAIN element. */ - if (isExact) { - element = oneElement(tname, types, 0, minCred); - if (element != null) - return SetResponse.ofType(SetResponse.NXDOMAIN); - } - - } - return SetResponse.ofType(SetResponse.UNKNOWN); -} - -/** - * Looks up Records in the Cache. This follows CNAMEs and handles negatively - * cached data. - * @param name The name to look up - * @param type The type to look up - * @param minCred The minimum acceptable credibility - * @return A SetResponse object - * @see SetResponse - * @see Credibility - */ -public SetResponse -lookupRecords(Name name, int type, int minCred) { - return lookup(name, type, minCred); -} - -private RRset [] -findRecords(Name name, int type, int minCred) { - SetResponse cr = lookupRecords(name, type, minCred); - if (cr.isSuccessful()) - return cr.answers(); - else - return null; -} - -/** - * Looks up credible Records in the Cache (a wrapper around lookupRecords). - * Unlike lookupRecords, this given no indication of why failure occurred. - * @param name The name to look up - * @param type The type to look up - * @return An array of RRsets, or null - * @see Credibility - */ -public RRset [] -findRecords(Name name, int type) { - return findRecords(name, type, Credibility.NORMAL); -} - -/** - * Looks up Records in the Cache (a wrapper around lookupRecords). Unlike - * lookupRecords, this given no indication of why failure occurred. - * @param name The name to look up - * @param type The type to look up - * @return An array of RRsets, or null - * @see Credibility - */ -public RRset [] -findAnyRecords(Name name, int type) { - return findRecords(name, type, Credibility.GLUE); -} - -private final int -getCred(int section, boolean isAuth) { - if (section == Section.ANSWER) { - if (isAuth) - return Credibility.AUTH_ANSWER; - else - return Credibility.NONAUTH_ANSWER; - } else if (section == Section.AUTHORITY) { - if (isAuth) - return Credibility.AUTH_AUTHORITY; - else - return Credibility.NONAUTH_AUTHORITY; - } else if (section == Section.ADDITIONAL) { - return Credibility.ADDITIONAL; - } else - throw new IllegalArgumentException("getCred: invalid section"); -} - -private static void -markAdditional(RRset rrset, Set names) { - Record first = rrset.first(); - if (first.getAdditionalName() == null) - return; - - Iterator it = rrset.rrs(); - while (it.hasNext()) { - Record r = (Record) it.next(); - Name name = r.getAdditionalName(); - if (name != null) - names.add(name); - } -} - -/** - * Adds all data from a Message into the Cache. Each record is added with - * the appropriate credibility, and negative answers are cached as such. - * @param in The Message to be added - * @return A SetResponse that reflects what would be returned from a cache - * lookup, or null if nothing useful could be cached from the message. - * @see Message - */ -public SetResponse -addMessage(Message in) { - boolean isAuth = in.getHeader().getFlag(Flags.AA); - Record question = in.getQuestion(); - Name qname; - Name curname; - int qtype; - int qclass; - int cred; - int rcode = in.getHeader().getRcode(); - boolean completed = false; - RRset [] answers, auth, addl; - SetResponse response = null; - boolean verbose = Options.check("verbosecache"); - HashSet additionalNames; - - if ((rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) || - question == null) - return null; - - qname = question.getName(); - qtype = question.getType(); - qclass = question.getDClass(); - - curname = qname; - - additionalNames = new HashSet(); - - answers = in.getSectionRRsets(Section.ANSWER); - for (int i = 0; i < answers.length; i++) { - if (answers[i].getDClass() != qclass) - continue; - int type = answers[i].getType(); - Name name = answers[i].getName(); - cred = getCred(Section.ANSWER, isAuth); - if ((type == qtype || qtype == Type.ANY) && - name.equals(curname)) - { - addRRset(answers[i], cred); - completed = true; - if (curname == qname) { - if (response == null) - response = new SetResponse( - SetResponse.SUCCESSFUL); - response.addRRset(answers[i]); - } - markAdditional(answers[i], additionalNames); - } else if (type == Type.CNAME && name.equals(curname)) { - CNAMERecord cname; - addRRset(answers[i], cred); - if (curname == qname) - response = new SetResponse(SetResponse.CNAME, - answers[i]); - cname = (CNAMERecord) answers[i].first(); - curname = cname.getTarget(); - } else if (type == Type.DNAME && curname.subdomain(name)) { - DNAMERecord dname; - addRRset(answers[i], cred); - if (curname == qname) - response = new SetResponse(SetResponse.DNAME, - answers[i]); - dname = (DNAMERecord) answers[i].first(); - try { - curname = curname.fromDNAME(dname); - } - catch (NameTooLongException e) { - break; - } - } - } - - auth = in.getSectionRRsets(Section.AUTHORITY); - RRset soa = null, ns = null; - for (int i = 0; i < auth.length; i++) { - if (auth[i].getType() == Type.SOA && - curname.subdomain(auth[i].getName())) - soa = auth[i]; - else if (auth[i].getType() == Type.NS && - curname.subdomain(auth[i].getName())) - ns = auth[i]; - } - if (!completed) { - /* This is a negative response or a referral. */ - int cachetype = (rcode == Rcode.NXDOMAIN) ? 0 : qtype; - if (rcode == Rcode.NXDOMAIN || soa != null || ns == null) { - /* Negative response */ - cred = getCred(Section.AUTHORITY, isAuth); - SOARecord soarec = null; - if (soa != null) - soarec = (SOARecord) soa.first(); - addNegative(curname, cachetype, soarec, cred); - if (response == null) { - int responseType; - if (rcode == Rcode.NXDOMAIN) - responseType = SetResponse.NXDOMAIN; - else - responseType = SetResponse.NXRRSET; - response = SetResponse.ofType(responseType); - } - /* DNSSEC records are not cached. */ - } else { - /* Referral response */ - cred = getCred(Section.AUTHORITY, isAuth); - addRRset(ns, cred); - markAdditional(ns, additionalNames); - if (response == null) - response = new SetResponse( - SetResponse.DELEGATION, - ns); - } - } else if (rcode == Rcode.NOERROR && ns != null) { - /* Cache the NS set from a positive response. */ - cred = getCred(Section.AUTHORITY, isAuth); - addRRset(ns, cred); - markAdditional(ns, additionalNames); - } - - addl = in.getSectionRRsets(Section.ADDITIONAL); - for (int i = 0; i < addl.length; i++) { - int type = addl[i].getType(); - if (type != Type.A && type != Type.AAAA && type != Type.A6) - continue; - Name name = addl[i].getName(); - if (!additionalNames.contains(name)) - continue; - cred = getCred(Section.ADDITIONAL, isAuth); - addRRset(addl[i], cred); - } - if (verbose) - System.out.println("addMessage: " + response); - return (response); -} - -/** - * Flushes an RRset from the cache - * @param name The name of the records to be flushed - * @param type The type of the records to be flushed - * @see RRset - */ -public void -flushSet(Name name, int type) { - removeElement(name, type); -} - -/** - * Flushes all RRsets with a given name from the cache - * @param name The name of the records to be flushed - * @see RRset - */ -public void -flushName(Name name) { - removeName(name); -} - -/** - * Sets the maximum length of time that a negative response will be stored - * in this Cache. A negative value disables this feature (that is, sets - * no limit). - */ -public void -setMaxNCache(int seconds) { - maxncache = seconds; -} - -/** - * Gets the maximum length of time that a negative response will be stored - * in this Cache. A negative value indicates no limit. - */ -public int -getMaxNCache() { - return maxncache; -} - -/** - * Sets the maximum length of time that records will be stored in this - * Cache. A negative value disables this feature (that is, sets no limit). - */ -public void -setMaxCache(int seconds) { - maxcache = seconds; -} - -/** - * Gets the maximum length of time that records will be stored - * in this Cache. A negative value indicates no limit. - */ -public int -getMaxCache() { - return maxcache; -} - -/** - * Gets the current number of entries in the Cache, where an entry consists - * of all records with a specific Name. - */ -public int -getSize() { - return data.size(); -} - -/** - * Gets the maximum number of entries in the Cache, where an entry consists - * of all records with a specific Name. A negative value is treated as an - * infinite limit. - */ -public int -getMaxEntries() { - return data.getMaxSize(); -} - -/** - * Sets the maximum number of entries in the Cache, where an entry consists - * of all records with a specific Name. A negative value is treated as an - * infinite limit. - * - * Note that setting this to a value lower than the current number - * of entries will not cause the Cache to shrink immediately. - * - * The default maximum number of entries is 50000. - * - * @param entries The maximum number of entries in the Cache. - */ -public void -setMaxEntries(int entries) { - data.setMaxSize(entries); -} - -/** - * Returns the DNS class of this cache. - */ -public int -getDClass() { - return dclass; -} - -/** - * Returns the contents of the Cache as a string. - */ -public String -toString() { - StringBuffer sb = new StringBuffer(); - synchronized (this) { - Iterator it = data.values().iterator(); - while (it.hasNext()) { - Element [] elements = allElements(it.next()); - for (int i = 0; i < elements.length; i++) { - sb.append(elements[i]); - sb.append("\n"); - } - } - } - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Client.java b/src/main/java/org/xbill/DNS/Client.java deleted file mode 100644 index 6b33de19a..000000000 --- a/src/main/java/org/xbill/DNS/Client.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.hexdump; - -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; - -class Client { - -protected long endTime; -protected SelectionKey key; - -protected -Client(SelectableChannel channel, long endTime) throws IOException { - boolean done = false; - Selector selector = null; - this.endTime = endTime; - try { - selector = Selector.open(); - channel.configureBlocking(false); - key = channel.register(selector, SelectionKey.OP_READ); - done = true; - } - finally { - if (!done && selector != null) - selector.close(); - if (!done) - channel.close(); - } -} - -static protected void -blockUntil(SelectionKey key, long endTime) throws IOException { - long timeout = endTime - System.currentTimeMillis(); - int nkeys = 0; - if (timeout > 0) - nkeys = key.selector().select(timeout); - else if (timeout == 0) - nkeys = key.selector().selectNow(); - if (nkeys == 0) - throw new SocketTimeoutException(); -} - -static protected void -verboseLog(String prefix, byte [] data) { - if (Options.check("verbosemsg")) - System.err.println(hexdump.dump(prefix, data)); -} - -void -cleanup() throws IOException { - key.selector().close(); - key.channel().close(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Compression.java b/src/main/java/org/xbill/DNS/Compression.java deleted file mode 100644 index e3e81c05d..000000000 --- a/src/main/java/org/xbill/DNS/Compression.java +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * DNS Name Compression object. - * @see Message - * @see Name - * - * @author Brian Wellington - */ - -public class Compression { - -private static class Entry { - Name name; - int pos; - Entry next; -} - -private static final int TABLE_SIZE = 17; -private static final int MAX_POINTER = 0x3FFF; -private Entry [] table; -private boolean verbose = Options.check("verbosecompression"); - -/** - * Creates a new Compression object. - */ -public -Compression() { - table = new Entry[TABLE_SIZE]; -} - -/** - * Adds a compression entry mapping a name to a position in a message. - * @param pos The position at which the name is added. - * @param name The name being added to the message. - */ -public void -add(int pos, Name name) { - if (pos > MAX_POINTER) - return; - int row = (name.hashCode() & 0x7FFFFFFF) % TABLE_SIZE; - Entry entry = new Entry(); - entry.name = name; - entry.pos = pos; - entry.next = table[row]; - table[row] = entry; - if (verbose) - System.err.println("Adding " + name + " at " + pos); -} - -/** - * Retrieves the position of the given name, if it has been previously - * included in the message. - * @param name The name to find in the compression table. - * @return The position of the name, or -1 if not found. - */ -public int -get(Name name) { - int row = (name.hashCode() & 0x7FFFFFFF) % TABLE_SIZE; - int pos = -1; - for (Entry entry = table[row]; entry != null; entry = entry.next) { - if (entry.name.equals(name)) - pos = entry.pos; - } - if (verbose) - System.err.println("Looking for " + name + ", found " + pos); - return pos; -} - -} diff --git a/src/main/java/org/xbill/DNS/Credibility.java b/src/main/java/org/xbill/DNS/Credibility.java deleted file mode 100644 index fa106868a..000000000 --- a/src/main/java/org/xbill/DNS/Credibility.java +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants relating to the credibility of cached data, which is based on - * the data's source. The constants NORMAL and ANY should be used by most - * callers. - * @see Cache - * @see Section - * - * @author Brian Wellington - */ - -public final class Credibility { - -private -Credibility() {} - -/** A hint or cache file on disk. */ -public static final int HINT = 0; - -/** The additional section of a response. */ -public static final int ADDITIONAL = 1; - -/** The additional section of a response. */ -public static final int GLUE = 2; - -/** The authority section of a nonauthoritative response. */ -public static final int NONAUTH_AUTHORITY = 3; - -/** The answer section of a nonauthoritative response. */ -public static final int NONAUTH_ANSWER = 3; - -/** The authority section of an authoritative response. */ -public static final int AUTH_AUTHORITY = 4; - -/** The answer section of a authoritative response. */ -public static final int AUTH_ANSWER = 4; - -/** A zone. */ -public static final int ZONE = 5; - -/** Credible data. */ -public static final int NORMAL = 3; - -/** Data not required to be credible. */ -public static final int ANY = 1; - -} diff --git a/src/main/java/org/xbill/DNS/DClass.java b/src/main/java/org/xbill/DNS/DClass.java deleted file mode 100644 index 22180cf4a..000000000 --- a/src/main/java/org/xbill/DNS/DClass.java +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants and functions relating to DNS classes. This is called DClass - * to avoid confusion with Class. - * - * @author Brian Wellington - */ - -public final class DClass { - -/** Internet */ -public static final int IN = 1; - -/** Chaos network (MIT) */ -public static final int CH = 3; - -/** Chaos network (MIT, alternate name) */ -public static final int CHAOS = 3; - -/** Hesiod name server (MIT) */ -public static final int HS = 4; - -/** Hesiod name server (MIT, alternate name) */ -public static final int HESIOD = 4; - -/** Special value used in dynamic update messages */ -public static final int NONE = 254; - -/** Matches any class */ -public static final int ANY = 255; - -private static class DClassMnemonic extends Mnemonic { - public - DClassMnemonic() { - super("DClass", CASE_UPPER); - setPrefix("CLASS"); - } - - public void - check(int val) { - DClass.check(val); - } -} - -private static Mnemonic classes = new DClassMnemonic(); - -static { - classes.add(IN, "IN"); - classes.add(CH, "CH"); - classes.addAlias(CH, "CHAOS"); - classes.add(HS, "HS"); - classes.addAlias(HS, "HESIOD"); - classes.add(NONE, "NONE"); - classes.add(ANY, "ANY"); -} - -private -DClass() {} - -/** - * Checks that a numeric DClass is valid. - * @throws InvalidDClassException The class is out of range. - */ -public static void -check(int i) { - if (i < 0 || i > 0xFFFF) - throw new InvalidDClassException(i); -} - -/** - * Converts a numeric DClass into a String - * @return The canonical string representation of the class - * @throws InvalidDClassException The class is out of range. - */ -public static String -string(int i) { - return classes.getText(i); -} - -/** - * Converts a String representation of a DClass into its numeric value - * @return The class code, or -1 on error. - */ -public static int -value(String s) { - return classes.getValue(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/DHCIDRecord.java b/src/main/java/org/xbill/DNS/DHCIDRecord.java deleted file mode 100644 index 551f71ceb..000000000 --- a/src/main/java/org/xbill/DNS/DHCIDRecord.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2008 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; - -/** - * DHCID - Dynamic Host Configuration Protocol (DHCP) ID (RFC 4701) - * - * @author Brian Wellington - */ - -public class DHCIDRecord extends Record { - -private static final long serialVersionUID = -8214820200808997707L; - -private byte [] data; - -DHCIDRecord() {} - -Record -getObject() { - return new DHCIDRecord(); -} - -/** - * Creates an DHCID Record from the given data - * @param data The binary data, which is opaque to DNS. - */ -public -DHCIDRecord(Name name, int dclass, long ttl, byte [] data) { - super(name, Type.DHCID, dclass, ttl); - this.data = data; -} - -void -rrFromWire(DNSInput in) throws IOException { - data = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - data = st.getBase64(); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeByteArray(data); -} - -String -rrToString() { - return base64.toString(data); -} - -/** - * Returns the binary data. - */ -public byte [] -getData() { - return data; -} - -} diff --git a/src/main/java/org/xbill/DNS/DLVRecord.java b/src/main/java/org/xbill/DNS/DLVRecord.java deleted file mode 100644 index c9dfbb31f..000000000 --- a/src/main/java/org/xbill/DNS/DLVRecord.java +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.IOException; - -/** - * DLV - contains a Delegation Lookaside Validation record, which acts - * as the equivalent of a DS record in a lookaside zone. - * @see DNSSEC - * @see DSRecord - * - * @author David Blacka - * @author Brian Wellington - */ - -public class DLVRecord extends Record { - -public static final byte SHA1_DIGEST_ID = 1; -public static final byte SHA256_DIGEST_ID = 2; - -private static final long serialVersionUID = 1960742375677534148L; - -private int footprint; -private int alg; -private int digestid; -private byte [] digest; - -DLVRecord() {} - -Record -getObject() { - return new DLVRecord(); -} - -/** - * Creates a DLV Record from the given data - * @param footprint The original KEY record's footprint (keyid). - * @param alg The original key algorithm. - * @param digestid The digest id code. - * @param digest A hash of the original key. - */ -public -DLVRecord(Name name, int dclass, long ttl, int footprint, int alg, - int digestid, byte [] digest) -{ - super(name, Type.DLV, dclass, ttl); - this.footprint = checkU16("footprint", footprint); - this.alg = checkU8("alg", alg); - this.digestid = checkU8("digestid", digestid); - this.digest = digest; -} - -void -rrFromWire(DNSInput in) throws IOException { - footprint = in.readU16(); - alg = in.readU8(); - digestid = in.readU8(); - digest = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - footprint = st.getUInt16(); - alg = st.getUInt8(); - digestid = st.getUInt8(); - digest = st.getHex(); -} - -/** - * Converts rdata to a String - */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(footprint); - sb.append(" "); - sb.append(alg); - sb.append(" "); - sb.append(digestid); - if (digest != null) { - sb.append(" "); - sb.append(base16.toString(digest)); - } - - return sb.toString(); -} - -/** - * Returns the key's algorithm. - */ -public int -getAlgorithm() { - return alg; -} - -/** - * Returns the key's Digest ID. - */ -public int -getDigestID() -{ - return digestid; -} - -/** - * Returns the binary hash of the key. - */ -public byte [] -getDigest() { - return digest; -} - -/** - * Returns the key's footprint. - */ -public int -getFootprint() { - return footprint; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(footprint); - out.writeU8(alg); - out.writeU8(digestid); - if (digest != null) - out.writeByteArray(digest); -} - -} diff --git a/src/main/java/org/xbill/DNS/DNAMERecord.java b/src/main/java/org/xbill/DNS/DNAMERecord.java deleted file mode 100644 index cbb322f81..000000000 --- a/src/main/java/org/xbill/DNS/DNAMERecord.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * DNAME Record - maps a nonterminal alias (subtree) to a different domain - * - * @author Brian Wellington - */ - -public class DNAMERecord extends SingleNameBase { - -private static final long serialVersionUID = 2670767677200844154L; - -DNAMERecord() {} - -Record -getObject() { - return new DNAMERecord(); -} - -/** - * Creates a new DNAMERecord with the given data - * @param alias The name to which the DNAME alias points - */ -public -DNAMERecord(Name name, int dclass, long ttl, Name alias) { - super(name, Type.DNAME, dclass, ttl, alias, "alias"); -} - -/** - * Gets the target of the DNAME Record - */ -public Name -getTarget() { - return getSingleName(); -} - -/** Gets the alias specified by the DNAME Record */ -public Name -getAlias() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/DNSInput.java b/src/main/java/org/xbill/DNS/DNSInput.java deleted file mode 100644 index 5b80335be..000000000 --- a/src/main/java/org/xbill/DNS/DNSInput.java +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An class for parsing DNS messages. - * - * @author Brian Wellington - */ - -public class DNSInput { - -private byte [] array; -private int pos; -private int end; -private int saved_pos; -private int saved_end; - -/** - * Creates a new DNSInput - * @param input The byte array to read from - */ -public -DNSInput(byte [] input) { - array = input; - pos = 0; - end = array.length; - saved_pos = -1; - saved_end = -1; -} - -/** - * Returns the current position. - */ -public int -current() { - return pos; -} - -/** - * Returns the number of bytes that can be read from this stream before - * reaching the end. - */ -public int -remaining() { - return end - pos; -} - -private void -require(int n) throws WireParseException{ - if (n > remaining()) { - throw new WireParseException("end of input"); - } -} - -/** - * Marks the following bytes in the stream as active. - * @param len The number of bytes in the active region. - * @throws IllegalArgumentException The number of bytes in the active region - * is longer than the remainder of the input. - */ -public void -setActive(int len) { - if (len > array.length - pos) { - throw new IllegalArgumentException("cannot set active " + - "region past end of input"); - } - end = pos + len; -} - -/** - * Clears the active region of the string. Further operations are not - * restricted to part of the input. - */ -public void -clearActive() { - end = array.length; -} - -/** - * Resets the current position of the input stream to the specified index, - * and clears the active region. - * @param index The position to continue parsing at. - * @throws IllegalArgumentException The index is not within the input. - */ -public void -jump(int index) { - if (index >= array.length) { - throw new IllegalArgumentException("cannot jump past " + - "end of input"); - } - pos = index; - end = array.length; -} - -/** - * Saves the current state of the input stream. Both the current position and - * the end of the active region are saved. - * @throws IllegalArgumentException The index is not within the input. - */ -public void -save() { - saved_pos = pos; - saved_end = end; -} - -/** - * Restores the input stream to its state before the call to {@link #save}. - */ -public void -restore() { - if (saved_pos < 0) { - throw new IllegalStateException("no previous state"); - } - pos = saved_pos; - end = saved_end; - saved_pos = -1; - saved_end = -1; -} - -/** - * Reads an unsigned 8 bit value from the stream, as an int. - * @return An unsigned 8 bit value. - * @throws WireParseException The end of the stream was reached. - */ -public int -readU8() throws WireParseException { - require(1); - return (array[pos++] & 0xFF); -} - -/** - * Reads an unsigned 16 bit value from the stream, as an int. - * @return An unsigned 16 bit value. - * @throws WireParseException The end of the stream was reached. - */ -public int -readU16() throws WireParseException { - require(2); - int b1 = array[pos++] & 0xFF; - int b2 = array[pos++] & 0xFF; - return ((b1 << 8) + b2); -} - -/** - * Reads an unsigned 32 bit value from the stream, as a long. - * @return An unsigned 32 bit value. - * @throws WireParseException The end of the stream was reached. - */ -public long -readU32() throws WireParseException { - require(4); - int b1 = array[pos++] & 0xFF; - int b2 = array[pos++] & 0xFF; - int b3 = array[pos++] & 0xFF; - int b4 = array[pos++] & 0xFF; - return (((long)b1 << 24) + (b2 << 16) + (b3 << 8) + b4); -} - -/** - * Reads a byte array of a specified length from the stream into an existing - * array. - * @param b The array to read into. - * @param off The offset of the array to start copying data into. - * @param len The number of bytes to copy. - * @throws WireParseException The end of the stream was reached. - */ -public void -readByteArray(byte [] b, int off, int len) throws WireParseException { - require(len); - System.arraycopy(array, pos, b, off, len); - pos += len; -} - -/** - * Reads a byte array of a specified length from the stream. - * @return The byte array. - * @throws WireParseException The end of the stream was reached. - */ -public byte [] -readByteArray(int len) throws WireParseException { - require(len); - byte [] out = new byte[len]; - System.arraycopy(array, pos, out, 0, len); - pos += len; - return out; -} - -/** - * Reads a byte array consisting of the remainder of the stream (or the - * active region, if one is set. - * @return The byte array. - */ -public byte [] -readByteArray() { - int len = remaining(); - byte [] out = new byte[len]; - System.arraycopy(array, pos, out, 0, len); - pos += len; - return out; -} - -/** - * Reads a counted string from the stream. A counted string is a one byte - * value indicating string length, followed by bytes of data. - * @return A byte array containing the string. - * @throws WireParseException The end of the stream was reached. - */ -public byte [] -readCountedString() throws WireParseException { - require(1); - int len = array[pos++] & 0xFF; - return readByteArray(len); -} - -} diff --git a/src/main/java/org/xbill/DNS/DNSKEYRecord.java b/src/main/java/org/xbill/DNS/DNSKEYRecord.java deleted file mode 100644 index 82f29e86d..000000000 --- a/src/main/java/org/xbill/DNS/DNSKEYRecord.java +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Key - contains a cryptographic public key for use by DNS. - * The data can be converted to objects implementing - * java.security.interfaces.PublicKey - * @see DNSSEC - * - * @author Brian Wellington - */ - -public class DNSKEYRecord extends KEYBase { - -public static class Protocol { - private Protocol() {} - - /** Key will be used for DNSSEC */ - public static final int DNSSEC = 3; -} - -public static class Flags { - private Flags() {} - - /** Key is a zone key */ - public static final int ZONE_KEY = 0x100; - - /** Key is a secure entry point key */ - public static final int SEP_KEY = 0x1; - - /** Key has been revoked */ - public static final int REVOKE = 0x80; -} - -private static final long serialVersionUID = -8679800040426675002L; - -DNSKEYRecord() {} - -Record -getObject() { - return new DNSKEYRecord(); -} - -/** - * Creates a DNSKEY Record from the given data - * @param flags Flags describing the key's properties - * @param proto The protocol that the key was created for - * @param alg The key's algorithm - * @param key Binary data representing the key - */ -public -DNSKEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg, - byte [] key) -{ - super(name, Type.DNSKEY, dclass, ttl, flags, proto, alg, key); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - flags = st.getUInt16(); - proto = st.getUInt8(); - String algString = st.getString(); - alg = DNSSEC.Algorithm.value(algString); - if (alg < 0) - throw st.exception("Invalid algorithm: " + algString); - key = st.getBase64(); -} - -} diff --git a/src/main/java/org/xbill/DNS/DNSOutput.java b/src/main/java/org/xbill/DNS/DNSOutput.java deleted file mode 100644 index ae1bd16cf..000000000 --- a/src/main/java/org/xbill/DNS/DNSOutput.java +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * A class for rendering DNS messages. - * - * @author Brian Wellington - */ - - -public class DNSOutput { - -private byte [] array; -private int pos; -private int saved_pos; - -/** - * Create a new DNSOutput with a specified size. - * @param size The initial size - */ -public -DNSOutput(int size) { - array = new byte[size]; - pos = 0; - saved_pos = -1; -} - -/** - * Create a new DNSOutput - */ -public -DNSOutput() { - this(32); -} - -/** - * Returns the current position. - */ -public int -current() { - return pos; -} - -private void -check(long val, int bits) { - long max = 1; - max <<= bits; - if (val < 0 || val > max) { - throw new IllegalArgumentException(val + " out of range for " + - bits + " bit value"); - } -} - -private void -need(int n) { - if (array.length - pos >= n) { - return; - } - int newsize = array.length * 2; - if (newsize < pos + n) { - newsize = pos + n; - } - byte [] newarray = new byte[newsize]; - System.arraycopy(array, 0, newarray, 0, pos); - array = newarray; -} - -/** - * Resets the current position of the output stream to the specified index. - * @param index The new current position. - * @throws IllegalArgumentException The index is not within the output. - */ -public void -jump(int index) { - if (index > pos) { - throw new IllegalArgumentException("cannot jump past " + - "end of data"); - } - pos = index; -} - -/** - * Saves the current state of the output stream. - * @throws IllegalArgumentException The index is not within the output. - */ -public void -save() { - saved_pos = pos; -} - -/** - * Restores the input stream to its state before the call to {@link #save}. - */ -public void -restore() { - if (saved_pos < 0) { - throw new IllegalStateException("no previous state"); - } - pos = saved_pos; - saved_pos = -1; -} - -/** - * Writes an unsigned 8 bit value to the stream. - * @param val The value to be written - */ -public void -writeU8(int val) { - check(val, 8); - need(1); - array[pos++] = (byte)(val & 0xFF); -} - -/** - * Writes an unsigned 16 bit value to the stream. - * @param val The value to be written - */ -public void -writeU16(int val) { - check(val, 16); - need(2); - array[pos++] = (byte)((val >>> 8) & 0xFF); - array[pos++] = (byte)(val & 0xFF); -} - -/** - * Writes an unsigned 32 bit value to the stream. - * @param val The value to be written - */ -public void -writeU32(long val) { - check(val, 32); - need(4); - array[pos++] = (byte)((val >>> 24) & 0xFF); - array[pos++] = (byte)((val >>> 16) & 0xFF); - array[pos++] = (byte)((val >>> 8) & 0xFF); - array[pos++] = (byte)(val & 0xFF); -} - -/** - * Writes a byte array to the stream. - * @param b The array to write. - * @param off The offset of the array to start copying data from. - * @param len The number of bytes to write. - */ -public void -writeByteArray(byte [] b, int off, int len) { - need(len); - System.arraycopy(b, off, array, pos, len); - pos += len; -} - -/** - * Writes a byte array to the stream. - * @param b The array to write. - */ -public void -writeByteArray(byte [] b) { - writeByteArray(b, 0, b.length); -} - -/** - * Writes a counted string from the stream. A counted string is a one byte - * value indicating string length, followed by bytes of data. - * @param s The string to write. - */ -public void -writeCountedString(byte [] s) { - if (s.length > 0xFF) { - throw new IllegalArgumentException("Invalid counted string"); - } - need(1 + s.length); - array[pos++] = (byte)(s.length & 0xFF); - writeByteArray(s, 0, s.length); -} - -/** - * Returns a byte array containing the current contents of the stream. - */ -public byte [] -toByteArray() { - byte [] out = new byte[pos]; - System.arraycopy(array, 0, out, 0, pos); - return out; -} - -} diff --git a/src/main/java/org/xbill/DNS/DNSSEC.java b/src/main/java/org/xbill/DNS/DNSSEC.java deleted file mode 100644 index 16c2ac0c1..000000000 --- a/src/main/java/org/xbill/DNS/DNSSEC.java +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.Arrays; -import java.util.Iterator; - -/** - * Constants and functions relating to DNSSEC (algorithm constants). - * DNSSEC provides authentication for DNS information. RRsets are - * signed by an appropriate key, and a SIG record is added to the set. - * A KEY record is obtained from DNS and used to validate the signature, - * The KEY record must also be validated or implicitly trusted - to - * validate a key requires a series of validations leading to a trusted - * key. The key must also be authorized to sign the data. - * @see SIGRecord - * @see KEYRecord - * @see RRset - * - * @author Brian Wellington - */ - -public class DNSSEC { - -public static class Algorithm { - private Algorithm() {} - - /** RSA/MD5 public key (deprecated) */ - public static final int RSAMD5 = 1; - - /** Diffie Hellman key */ - public static final int DH = 2; - - /** DSA public key */ - public static final int DSA = 3; - - /** Elliptic Curve key */ - public static final int ECC = 4; - - /** RSA/SHA1 public key */ - public static final int RSASHA1 = 5; - - /** DSA/SHA1, NSEC3-aware public key */ - public static final int DSA_NSEC3_SHA1 = 6; - - /** RSA/SHA1, NSEC3-aware public key */ - public static final int RSA_NSEC3_SHA1 = 7; - - /** Indirect keys; the actual key is elsewhere. */ - public static final int INDIRECT = 252; - - /** Private algorithm, specified by domain name */ - public static final int PRIVATEDNS = 253; - - /** Private algorithm, specified by OID */ - public static final int PRIVATEOID = 254; - - private static Mnemonic algs = new Mnemonic("DNSSEC algorithm", - Mnemonic.CASE_UPPER); - - static { - algs.setMaximum(0xFF); - algs.setNumericAllowed(true); - - algs.add(RSAMD5, "RSAMD5"); - algs.add(DH, "DH"); - algs.add(DSA, "DSA"); - algs.add(ECC, "ECC"); - algs.add(RSASHA1, "RSASHA1"); - algs.add(DSA_NSEC3_SHA1, "DSA-NSEC3-SHA1"); - algs.add(RSA_NSEC3_SHA1, "RSA-NSEC3-SHA1"); - algs.add(INDIRECT, "INDIRECT"); - algs.add(PRIVATEDNS, "PRIVATEDNS"); - algs.add(PRIVATEOID, "PRIVATEOID"); - } - - /** - * Converts an algorithm into its textual representation - */ - public static String - string(int alg) { - return algs.getText(alg); - } - - /** - * Converts a textual representation of an algorithm into its numeric - * code. Integers in the range 0..255 are also accepted. - * @param s The textual representation of the algorithm - * @return The algorithm code, or -1 on error. - */ - public static int - value(String s) { - return algs.getValue(s); - } -} - -public static final int RSAMD5 = Algorithm.RSAMD5; -public static final int RSA = Algorithm.RSAMD5; -public static final int DH = Algorithm.DH; -public static final int DSA = Algorithm.DSA; -public static final int RSASHA1 = Algorithm.RSASHA1; -public static final int DSA_NSEC3_SHA1 = Algorithm.DSA_NSEC3_SHA1; -public static final int RSA_NSEC3_SHA1 = Algorithm.RSA_NSEC3_SHA1; - -public static final int Failed = -1; -public static final int Insecure = 0; -public static final int Secure = 1; - -private -DNSSEC() { } - -private static void -digestSIG(DNSOutput out, SIGBase sig) { - out.writeU16(sig.getTypeCovered()); - out.writeU8(sig.getAlgorithm()); - out.writeU8(sig.getLabels()); - out.writeU32(sig.getOrigTTL()); - out.writeU32(sig.getExpire().getTime() / 1000); - out.writeU32(sig.getTimeSigned().getTime() / 1000); - out.writeU16(sig.getFootprint()); - sig.getSigner().toWireCanonical(out); -} - -/** - * Creates a byte array containing the concatenation of the fields of the - * SIG record and the RRsets to be signed/verified. This does not perform - * a cryptographic digest. - * @param sig The SIG record used to sign/verify the rrset. - * @param rrset The data to be signed/verified. - * @return The data to be cryptographically signed or verified. - */ -public static byte [] -digestRRset(RRSIGRecord sig, RRset rrset) { - DNSOutput out = new DNSOutput(); - digestSIG(out, sig); - - int size = rrset.size(); - Record [] records = new Record[size]; - - Iterator it = rrset.rrs(); - Name name = rrset.getName(); - Name wild = null; - int sigLabels = sig.getLabels() + 1; // Add the root label back. - if (name.labels() > sigLabels) - wild = name.wild(name.labels() - sigLabels); - while (it.hasNext()) - records[--size] = (Record) it.next(); - Arrays.sort(records); - - DNSOutput header = new DNSOutput(); - if (wild != null) - wild.toWireCanonical(header); - else - name.toWireCanonical(header); - header.writeU16(rrset.getType()); - header.writeU16(rrset.getDClass()); - header.writeU32(sig.getOrigTTL()); - for (int i = 0; i < records.length; i++) { - out.writeByteArray(header.toByteArray()); - int lengthPosition = out.current(); - out.writeU16(0); - out.writeByteArray(records[i].rdataToWireCanonical()); - int rrlength = out.current() - lengthPosition - 2; - out.save(); - out.jump(lengthPosition); - out.writeU16(rrlength); - out.restore(); - } - return out.toByteArray(); -} - -/** - * Creates a byte array containing the concatenation of the fields of the - * SIG record and the message to be signed/verified. This does not perform - * a cryptographic digest. - * @param sig The SIG record used to sign/verify the rrset. - * @param msg The message to be signed/verified. - * @param previous If this is a response, the signature from the query. - * @return The data to be cryptographically signed or verified. - */ -public static byte [] -digestMessage(SIGRecord sig, Message msg, byte [] previous) { - DNSOutput out = new DNSOutput(); - digestSIG(out, sig); - - if (previous != null) - out.writeByteArray(previous); - - msg.toWire(out); - return out.toByteArray(); -} - -} diff --git a/src/main/java/org/xbill/DNS/DSRecord.java b/src/main/java/org/xbill/DNS/DSRecord.java deleted file mode 100644 index 44ac9af0c..000000000 --- a/src/main/java/org/xbill/DNS/DSRecord.java +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.IOException; - -/** - * DS - contains a Delegation Signer record, which acts as a - * placeholder for KEY records in the parent zone. - * @see DNSSEC - * - * @author David Blacka - * @author Brian Wellington - */ - -public class DSRecord extends Record { - -public static final byte SHA1_DIGEST_ID = 1; -public static final byte SHA256_DIGEST_ID = 2; - -private static final long serialVersionUID = -9001819329700081493L; - -private int footprint; -private int alg; -private int digestid; -private byte [] digest; - -DSRecord() {} - -Record -getObject() { - return new DSRecord(); -} - -/** - * Creates a DS Record from the given data - * @param footprint The original KEY record's footprint (keyid). - * @param alg The original key algorithm. - * @param digestid The digest id code. - * @param digest A hash of the original key. - */ -public -DSRecord(Name name, int dclass, long ttl, int footprint, int alg, - int digestid, byte [] digest) -{ - super(name, Type.DS, dclass, ttl); - this.footprint = checkU16("footprint", footprint); - this.alg = checkU8("alg", alg); - this.digestid = checkU8("digestid", digestid); - this.digest = digest; -} - -void -rrFromWire(DNSInput in) throws IOException { - footprint = in.readU16(); - alg = in.readU8(); - digestid = in.readU8(); - digest = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - footprint = st.getUInt16(); - alg = st.getUInt8(); - digestid = st.getUInt8(); - digest = st.getHex(); -} - -/** - * Converts rdata to a String - */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(footprint); - sb.append(" "); - sb.append(alg); - sb.append(" "); - sb.append(digestid); - if (digest != null) { - sb.append(" "); - sb.append(base16.toString(digest)); - } - - return sb.toString(); -} - -/** - * Returns the key's algorithm. - */ -public int -getAlgorithm() { - return alg; -} - -/** - * Returns the key's Digest ID. - */ -public int -getDigestID() -{ - return digestid; -} - -/** - * Returns the binary hash of the key. - */ -public byte [] -getDigest() { - return digest; -} - -/** - * Returns the key's footprint. - */ -public int -getFootprint() { - return footprint; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(footprint); - out.writeU8(alg); - out.writeU8(digestid); - if (digest != null) - out.writeByteArray(digest); -} - -} diff --git a/src/main/java/org/xbill/DNS/EmptyRecord.java b/src/main/java/org/xbill/DNS/EmptyRecord.java deleted file mode 100644 index 8ddaef93b..000000000 --- a/src/main/java/org/xbill/DNS/EmptyRecord.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * A class implementing Records with no data; that is, records used in - * the question section of messages and meta-records in dynamic update. - * - * @author Brian Wellington - */ - -class EmptyRecord extends Record { - -private static final long serialVersionUID = 3601852050646429582L; - -EmptyRecord() {} - -Record -getObject() { - return new EmptyRecord(); -} - -void -rrFromWire(DNSInput in) throws IOException { -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { -} - -String -rrToString() { - return ""; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { -} - -} diff --git a/src/main/java/org/xbill/DNS/ExtendedFlags.java b/src/main/java/org/xbill/DNS/ExtendedFlags.java deleted file mode 100644 index 8f3bbab89..000000000 --- a/src/main/java/org/xbill/DNS/ExtendedFlags.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants and functions relating to EDNS flags. - * - * @author Brian Wellington - */ - -public final class ExtendedFlags { - -private static Mnemonic extflags = new Mnemonic("EDNS Flag", - Mnemonic.CASE_LOWER); - -/** dnssec ok */ -public static final int DO = 0x8000; - -static { - extflags.setMaximum(0xFFFF); - extflags.setPrefix("FLAG"); - extflags.setNumericAllowed(true); - - extflags.add(DO, "do"); -} - -private -ExtendedFlags() {} - -/** Converts a numeric extended flag into a String */ -public static String -string(int i) { - return extflags.getText(i); -} - -/** - * Converts a textual representation of an extended flag into its numeric - * value - */ -public static int -value(String s) { - return extflags.getValue(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/ExtendedResolver.java b/src/main/java/org/xbill/DNS/ExtendedResolver.java deleted file mode 100644 index 563ebee1d..000000000 --- a/src/main/java/org/xbill/DNS/ExtendedResolver.java +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -/** - * An implementation of Resolver that can send queries to multiple servers, - * sending the queries multiple times if necessary. - * @see Resolver - * - * @author Brian Wellington - */ - -public class ExtendedResolver implements Resolver { - -private static class Resolution implements ResolverListener { - Resolver [] resolvers; - int [] sent; - Object [] inprogress; - int retries; - int outstanding; - boolean done; - Message query; - Message response; - Throwable thrown; - ResolverListener listener; - - public - Resolution(ExtendedResolver eres, Message query) { - List l = eres.resolvers; - resolvers = (Resolver []) l.toArray (new Resolver[l.size()]); - if (eres.loadBalance) { - int nresolvers = resolvers.length; - /* - * Note: this is not synchronized, since the - * worst thing that can happen is a random - * ordering, which is ok. - */ - int start = eres.lbStart++ % nresolvers; - if (eres.lbStart > nresolvers) - eres.lbStart %= nresolvers; - if (start > 0) { - Resolver [] shuffle = new Resolver[nresolvers]; - for (int i = 0; i < nresolvers; i++) { - int pos = (i + start) % nresolvers; - shuffle[i] = resolvers[pos]; - } - resolvers = shuffle; - } - } - sent = new int[resolvers.length]; - inprogress = new Object[resolvers.length]; - retries = eres.retries; - this.query = query; - } - - /* Asynchronously sends a message. */ - public void - send(int n) { - sent[n]++; - outstanding++; - try { - inprogress[n] = resolvers[n].sendAsync(query, this); - } - catch (Throwable t) { - synchronized (this) { - thrown = t; - done = true; - if (listener == null) { - notifyAll(); - return; - } - } - } - } - - /* Start a synchronous resolution */ - public Message - start() throws IOException { - try { - /* - * First, try sending synchronously. If this works, - * we're done. Otherwise, we'll get an exception - * and continue. It would be easier to call send(0), - * but this avoids a thread creation. If and when - * SimpleResolver.sendAsync() can be made to not - * create a thread, this could be changed. - */ - sent[0]++; - outstanding++; - inprogress[0] = new Object(); - return resolvers[0].send(query); - } - catch (Exception e) { - /* - * This will either cause more queries to be sent - * asynchronously or will set the 'done' flag. - */ - handleException(inprogress[0], e); - } - /* - * Wait for a successful response or for each - * subresolver to fail. - */ - synchronized (this) { - while (!done) { - try { - wait(); - } - catch (InterruptedException e) { - } - } - } - /* Return the response or throw an exception */ - if (response != null) - return response; - else if (thrown instanceof IOException) - throw (IOException) thrown; - else if (thrown instanceof RuntimeException) - throw (RuntimeException) thrown; - else if (thrown instanceof Error) - throw (Error) thrown; - else - throw new IllegalStateException - ("ExtendedResolver failure"); - } - - /* Start an asynchronous resolution */ - public void - startAsync(ResolverListener listener) { - this.listener = listener; - send(0); - } - - /* - * Receive a response. If the resolution hasn't been completed, - * either wake up the blocking thread or call the callback. - */ - public void - receiveMessage(Object id, Message m) { - if (Options.check("verbose")) - System.err.println("ExtendedResolver: " + - "received message"); - synchronized (this) { - if (done) - return; - response = m; - done = true; - if (listener == null) { - notifyAll(); - return; - } - } - listener.receiveMessage(this, response); - } - - /* - * Receive an exception. If the resolution has been completed, - * do nothing. Otherwise make progress. - */ - public void - handleException(Object id, Exception e) { - if (Options.check("verbose")) - System.err.println("ExtendedResolver: got " + e); - synchronized (this) { - outstanding--; - if (done) - return; - int n; - for (n = 0; n < inprogress.length; n++) - if (inprogress[n] == id) - break; - /* If we don't know what this is, do nothing. */ - if (n == inprogress.length) - return; - boolean startnext = false; - /* - * If this is the first response from server n, - * we should start sending queries to server n + 1. - */ - if (sent[n] == 1 && n < resolvers.length - 1) - startnext = true; - if (e instanceof InterruptedIOException) { - /* Got a timeout; resend */ - if (sent[n] < retries) - send(n); - if (thrown == null) - thrown = e; - } else if (e instanceof SocketException) { - /* - * Problem with the socket; don't resend - * on it - */ - if (thrown == null || - thrown instanceof InterruptedIOException) - thrown = e; - } else { - /* - * Problem with the response; don't resend - * on the same socket. - */ - thrown = e; - } - if (done) - return; - if (startnext) - send(n + 1); - if (done) - return; - if (outstanding == 0) { - /* - * If we're done and this is synchronous, - * wake up the blocking thread. - */ - done = true; - if (listener == null) { - notifyAll(); - return; - } - } - if (!done) - return; - } - /* If we're done and this is asynchronous, call the callback. */ - if (!(thrown instanceof Exception)) - thrown = new RuntimeException(thrown.getMessage()); - listener.handleException(this, (Exception) thrown); - } -} - -private static final int quantum = 5; - -private List resolvers; -private boolean loadBalance = false; -private int lbStart = 0; -private int retries = 3; - -private void -init() { - resolvers = new ArrayList(); -} - -/** - * Creates a new Extended Resolver. The default ResolverConfig is used to - * determine the servers for which SimpleResolver contexts should be - * initialized. - * @see SimpleResolver - * @see ResolverConfig - * @exception UnknownHostException Failure occured initializing SimpleResolvers - */ -public -ExtendedResolver() throws UnknownHostException { - init(); - String [] servers = ResolverConfig.getCurrentConfig().servers(); - if (servers != null) { - for (int i = 0; i < servers.length; i++) { - Resolver r = new SimpleResolver(servers[i]); - r.setTimeout(quantum); - resolvers.add(r); - } - } - else - resolvers.add(new SimpleResolver()); -} - -/** - * Creates a new Extended Resolver - * @param servers An array of server names for which SimpleResolver - * contexts should be initialized. - * @see SimpleResolver - * @exception UnknownHostException Failure occured initializing SimpleResolvers - */ -public -ExtendedResolver(String [] servers) throws UnknownHostException { - init(); - for (int i = 0; i < servers.length; i++) { - Resolver r = new SimpleResolver(servers[i]); - r.setTimeout(quantum); - resolvers.add(r); - } -} - -/** - * Creates a new Extended Resolver - * @param res An array of pre-initialized Resolvers is provided. - * @see SimpleResolver - * @exception UnknownHostException Failure occured initializing SimpleResolvers - */ -public -ExtendedResolver(Resolver [] res) throws UnknownHostException { - init(); - for (int i = 0; i < res.length; i++) - resolvers.add(res[i]); -} - -public void -setPort(int port) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setPort(port); -} - -public void -setTCP(boolean flag) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setTCP(flag); -} - -public void -setIgnoreTruncation(boolean flag) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setIgnoreTruncation(flag); -} - -public void -setEDNS(int level) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setEDNS(level); -} - -public void -setEDNS(int level, int payloadSize, int flags, List options) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setEDNS(level, payloadSize, - flags, options); -} - -public void -setTSIGKey(TSIG key) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setTSIGKey(key); -} - -public void -setTimeout(int secs, int msecs) { - for (int i = 0; i < resolvers.size(); i++) - ((Resolver)resolvers.get(i)).setTimeout(secs, msecs); -} - -public void -setTimeout(int secs) { - setTimeout(secs, 0); -} - -/** - * Sends a message and waits for a response. Multiple servers are queried, - * and queries are sent multiple times until either a successful response - * is received, or it is clear that there is no successful response. - * @param query The query to send. - * @return The response. - * @throws IOException An error occurred while sending or receiving. - */ -public Message -send(Message query) throws IOException { - Resolution res = new Resolution(this, query); - return res.start(); -} - -/** - * Asynchronously sends a message to multiple servers, potentially multiple - * times, registering a listener to receive a callback on success or exception. - * Multiple asynchronous lookups can be performed in parallel. Since the - * callback may be invoked before the function returns, external - * synchronization is necessary. - * @param query The query to send - * @param listener The object containing the callbacks. - * @return An identifier, which is also a parameter in the callback - */ -public Object -sendAsync(final Message query, final ResolverListener listener) { - Resolution res = new Resolution(this, query); - res.startAsync(listener); - return res; -} - -/** Returns the nth resolver used by this ExtendedResolver */ -public Resolver -getResolver(int n) { - if (n < resolvers.size()) - return (Resolver)resolvers.get(n); - return null; -} - -/** Returns all resolvers used by this ExtendedResolver */ -public Resolver [] -getResolvers() { - return (Resolver []) resolvers.toArray(new Resolver[resolvers.size()]); -} - -/** Adds a new resolver to be used by this ExtendedResolver */ -public void -addResolver(Resolver r) { - resolvers.add(r); -} - -/** Deletes a resolver used by this ExtendedResolver */ -public void -deleteResolver(Resolver r) { - resolvers.remove(r); -} - -/** Sets whether the servers should be load balanced. - * @param flag If true, servers will be tried in round-robin order. If false, - * servers will always be queried in the same order. - */ -public void -setLoadBalance(boolean flag) { - loadBalance = flag; -} - -/** Sets the number of retries sent to each server per query */ -public void -setRetries(int retries) { - this.retries = retries; -} - -} diff --git a/src/main/java/org/xbill/DNS/Flags.java b/src/main/java/org/xbill/DNS/Flags.java deleted file mode 100644 index 964ce23f4..000000000 --- a/src/main/java/org/xbill/DNS/Flags.java +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants and functions relating to flags in the DNS header. - * - * @author Brian Wellington - */ - -public final class Flags { - -private static Mnemonic flags = new Mnemonic("DNS Header Flag", - Mnemonic.CASE_LOWER); - -/** query/response */ -public static final byte QR = 0; - -/** authoritative answer */ -public static final byte AA = 5; - -/** truncated */ -public static final byte TC = 6; - -/** recursion desired */ -public static final byte RD = 7; - -/** recursion available */ -public static final byte RA = 8; - -/** authenticated data */ -public static final byte AD = 10; - -/** (security) checking disabled */ -public static final byte CD = 11; - -/** dnssec ok (extended) */ -public static final int DO = ExtendedFlags.DO; - -static { - flags.setMaximum(0xF); - flags.setPrefix("FLAG"); - flags.setNumericAllowed(true); - - flags.add(QR, "qr"); - flags.add(AA, "aa"); - flags.add(TC, "tc"); - flags.add(RD, "rd"); - flags.add(RA, "ra"); - flags.add(AD, "ad"); - flags.add(CD, "cd"); -} - -private -Flags() {} - -/** Converts a numeric Flag into a String */ -public static String -string(int i) { - return flags.getText(i); -} - -/** Converts a String representation of an Flag into its numeric value */ -public static int -value(String s) { - return flags.getValue(s); -} - -/** - * Indicates if a bit in the flags field is a flag or not. If it's part of - * the rcode or opcode, it's not. - */ -public static boolean -isFlag(int index) { - flags.check(index); - if ((index >= 1 && index <= 4) || (index >= 12)) - return false; - return true; -} - -} diff --git a/src/main/java/org/xbill/DNS/FormattedTime.java b/src/main/java/org/xbill/DNS/FormattedTime.java deleted file mode 100644 index 53f207300..000000000 --- a/src/main/java/org/xbill/DNS/FormattedTime.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Routines for converting time values to and from YYYYMMDDHHMMSS format. - * - * @author Brian Wellington - */ - -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -final class FormattedTime { - -private static NumberFormat w2, w4; - -static { - w2 = new DecimalFormat(); - w2.setMinimumIntegerDigits(2); - - w4 = new DecimalFormat(); - w4.setMinimumIntegerDigits(4); - w4.setGroupingUsed(false); -} - -private -FormattedTime() {} - -/** - * Converts a Date into a formatted string. - * @param date The Date to convert. - * @return The formatted string. - */ -public static String -format(Date date) { - Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - StringBuffer sb = new StringBuffer(); - - c.setTime(date); - sb.append(w4.format(c.get(Calendar.YEAR))); - sb.append(w2.format(c.get(Calendar.MONTH)+1)); - sb.append(w2.format(c.get(Calendar.DAY_OF_MONTH))); - sb.append(w2.format(c.get(Calendar.HOUR_OF_DAY))); - sb.append(w2.format(c.get(Calendar.MINUTE))); - sb.append(w2.format(c.get(Calendar.SECOND))); - return sb.toString(); -} - -/** - * Parses a formatted time string into a Date. - * @param s The string, in the form YYYYMMDDHHMMSS. - * @return The Date object. - * @throws TextParseExcetption The string was invalid. - */ -public static Date -parse(String s) throws TextParseException { - Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - - if (s.length() != 14) { - throw new TextParseException("Invalid time encoding: " + s); - } - try { - int year = Integer.parseInt(s.substring(0, 4)); - int month = Integer.parseInt(s.substring(4, 6)) - 1; - int date = Integer.parseInt(s.substring(6, 8)); - int hour = Integer.parseInt(s.substring(8, 10)); - int minute = Integer.parseInt(s.substring(10, 12)); - int second = Integer.parseInt(s.substring(12, 14)); - c.set(year, month, date, hour, minute, second); - } - catch (NumberFormatException e) { - throw new TextParseException("Invalid time encoding: " + s); - } - return c.getTime(); -} - -} diff --git a/src/main/java/org/xbill/DNS/GPOSRecord.java b/src/main/java/org/xbill/DNS/GPOSRecord.java deleted file mode 100644 index 225202478..000000000 --- a/src/main/java/org/xbill/DNS/GPOSRecord.java +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Geographical Location - describes the physical location of a host. - * - * @author Brian Wellington - */ - -public class GPOSRecord extends Record { - -private static final long serialVersionUID = -6349714958085750705L; - -private byte [] latitude, longitude, altitude; - -GPOSRecord() {} - -Record -getObject() { - return new GPOSRecord(); -} - -private void -validate(double longitude, double latitude) throws IllegalArgumentException -{ - if (longitude < -90.0 || longitude > 90.0) { - throw new IllegalArgumentException("illegal longitude " + - longitude); - } - if (latitude < -180.0 || latitude > 180.0) { - throw new IllegalArgumentException("illegal latitude " + - latitude); - } -} - -/** - * Creates an GPOS Record from the given data - * @param longitude The longitude component of the location. - * @param latitude The latitude component of the location. - * @param altitude The altitude component of the location (in meters above sea - * level). -*/ -public -GPOSRecord(Name name, int dclass, long ttl, double longitude, double latitude, - double altitude) -{ - super(name, Type.GPOS, dclass, ttl); - validate(longitude, latitude); - this.longitude = Double.toString(longitude).getBytes(); - this.latitude = Double.toString(latitude).getBytes(); - this.altitude = Double.toString(altitude).getBytes(); -} - -/** - * Creates an GPOS Record from the given data - * @param longitude The longitude component of the location. - * @param latitude The latitude component of the location. - * @param altitude The altitude component of the location (in meters above sea - * level). -*/ -public -GPOSRecord(Name name, int dclass, long ttl, String longitude, String latitude, - String altitude) -{ - super(name, Type.GPOS, dclass, ttl); - try { - this.longitude = byteArrayFromString(longitude); - this.latitude = byteArrayFromString(latitude); - validate(getLongitude(), getLatitude()); - this.altitude = byteArrayFromString(altitude); - } - catch (TextParseException e) { - throw new IllegalArgumentException(e.getMessage()); - } -} - -void -rrFromWire(DNSInput in) throws IOException { - longitude = in.readCountedString(); - latitude = in.readCountedString(); - altitude = in.readCountedString(); - try { - validate(getLongitude(), getLatitude()); - } - catch(IllegalArgumentException e) { - throw new WireParseException(e.getMessage()); - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - try { - longitude = byteArrayFromString(st.getString()); - latitude = byteArrayFromString(st.getString()); - altitude = byteArrayFromString(st.getString()); - } - catch (TextParseException e) { - throw st.exception(e.getMessage()); - } - try { - validate(getLongitude(), getLatitude()); - } - catch(IllegalArgumentException e) { - throw new WireParseException(e.getMessage()); - } -} - -/** Convert to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(byteArrayToString(longitude, true)); - sb.append(" "); - sb.append(byteArrayToString(latitude, true)); - sb.append(" "); - sb.append(byteArrayToString(altitude, true)); - return sb.toString(); -} - -/** Returns the longitude as a string */ -public String -getLongitudeString() { - return byteArrayToString(longitude, false); -} - -/** - * Returns the longitude as a double - * @throws NumberFormatException The string does not contain a valid numeric - * value. - */ -public double -getLongitude() { - return Double.parseDouble(getLongitudeString()); -} - -/** Returns the latitude as a string */ -public String -getLatitudeString() { - return byteArrayToString(latitude, false); -} - -/** - * Returns the latitude as a double - * @throws NumberFormatException The string does not contain a valid numeric - * value. - */ -public double -getLatitude() { - return Double.parseDouble(getLatitudeString()); -} - -/** Returns the altitude as a string */ -public String -getAltitudeString() { - return byteArrayToString(altitude, false); -} - -/** - * Returns the altitude as a double - * @throws NumberFormatException The string does not contain a valid numeric - * value. - */ -public double -getAltitude() { - return Double.parseDouble(getAltitudeString()); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeCountedString(longitude); - out.writeCountedString(latitude); - out.writeCountedString(altitude); -} - -} diff --git a/src/main/java/org/xbill/DNS/Generator.java b/src/main/java/org/xbill/DNS/Generator.java deleted file mode 100644 index a52e457e7..000000000 --- a/src/main/java/org/xbill/DNS/Generator.java +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * A representation of a $GENERATE statement in a master file. - * - * @author Brian Wellington - */ - -public class Generator { - -/** The start of the range. */ -public long start; - -/** The end of the range. */ -public long end; - -/** The step value of the range. */ -public long step; - -/** The pattern to use for generating record names. */ -public final String namePattern; - -/** The type of the generated records. */ -public final int type; - -/** The class of the generated records. */ -public final int dclass; - -/** The ttl of the generated records. */ -public final long ttl; - -/** The pattern to use for generating record data. */ -public final String rdataPattern; - -/** The origin to append to relative names. */ -public final Name origin; - -private long current; - -/** - * Indicates whether generation is supported for this type. - * @throws InvalidTypeException The type is out of range. - */ -public static boolean -supportedType(int type) { - Type.check(type); - return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME || - type == Type.A || type == Type.AAAA || type == Type.NS); -} - -/** - * Creates a specification for generating records, as a $GENERATE - * statement in a master file. - * @param start The start of the range. - * @param end The end of the range. - * @param step The step value of the range. - * @param namePattern The pattern to use for generating record names. - * @param type The type of the generated records. The supported types are - * PTR, CNAME, DNAME, A, AAAA, and NS. - * @param dclass The class of the generated records. - * @param ttl The ttl of the generated records. - * @param rdataPattern The pattern to use for generating record data. - * @param origin The origin to append to relative names. - * @throws IllegalArgumentException The range is invalid. - * @throws IllegalArgumentException The type does not support generation. - * @throws IllegalArgumentException The dclass is not a valid class. - */ -public -Generator(long start, long end, long step, String namePattern, - int type, int dclass, long ttl, String rdataPattern, Name origin) -{ - if (start < 0 || end < 0 || start > end || step <= 0) - throw new IllegalArgumentException - ("invalid range specification"); - if (!supportedType(type)) - throw new IllegalArgumentException("unsupported type"); - DClass.check(dclass); - - this.start = start; - this.end = end; - this.step = step; - this.namePattern = namePattern; - this.type = type; - this.dclass = dclass; - this.ttl = ttl; - this.rdataPattern = rdataPattern; - this.origin = origin; - this.current = start; -} - -private String -substitute(String spec, long n) throws IOException { - boolean escaped = false; - byte [] str = spec.getBytes(); - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < str.length; i++) { - char c = (char)(str[i] & 0xFF); - if (escaped) { - sb.append(c); - escaped = false; - } else if (c == '\\') { - if (i + 1 == str.length) - throw new TextParseException - ("invalid escape character"); - escaped = true; - } else if (c == '$') { - boolean negative = false; - long offset = 0; - long width = 0; - long base = 10; - boolean wantUpperCase = false; - if (i + 1 < str.length && str[i + 1] == '$') { - // '$$' == literal '$' for backwards - // compatibility with old versions of BIND. - c = (char)(str[++i] & 0xFF); - sb.append(c); - continue; - } else if (i + 1 < str.length && str[i + 1] == '{') { - // It's a substitution with modifiers. - i++; - if (i + 1 < str.length && str[i + 1] == '-') { - negative = true; - i++; - } - while (i + 1 < str.length) { - c = (char)(str[++i] & 0xFF); - if (c == ',' || c == '}') - break; - if (c < '0' || c > '9') - throw new TextParseException( - "invalid offset"); - c -= '0'; - offset *= 10; - offset += c; - } - if (negative) - offset = -offset; - - if (c == ',') { - while (i + 1 < str.length) { - c = (char)(str[++i] & 0xFF); - if (c == ',' || c == '}') - break; - if (c < '0' || c > '9') - throw new - TextParseException( - "invalid width"); - c -= '0'; - width *= 10; - width += c; - } - } - - if (c == ',') { - if (i + 1 == str.length) - throw new TextParseException( - "invalid base"); - c = (char)(str[++i] & 0xFF); - if (c == 'o') - base = 8; - else if (c == 'x') - base = 16; - else if (c == 'X') { - base = 16; - wantUpperCase = true; - } - else if (c != 'd') - throw new TextParseException( - "invalid base"); - } - - if (i + 1 == str.length || str[i + 1] != '}') - throw new TextParseException - ("invalid modifiers"); - i++; - } - long v = n + offset; - if (v < 0) - throw new TextParseException - ("invalid offset expansion"); - String number; - if (base == 8) - number = Long.toOctalString(v); - else if (base == 16) - number = Long.toHexString(v); - else - number = Long.toString(v); - if (wantUpperCase) - number = number.toUpperCase(); - if (width != 0 && width > number.length()) { - int zeros = (int)width - number.length(); - while (zeros-- > 0) - sb.append('0'); - } - sb.append(number); - } else { - sb.append(c); - } - } - return sb.toString(); -} - -/** - * Constructs and returns the next record in the expansion. - * @throws IOException The name or rdata was invalid after substitutions were - * performed. - */ -public Record -nextRecord() throws IOException { - if (current > end) - return null; - String namestr = substitute(namePattern, current); - Name name = Name.fromString(namestr, origin); - String rdata = substitute(rdataPattern, current); - current += step; - return Record.fromString(name, type, dclass, ttl, rdata, origin); -} - -/** - * Constructs and returns all records in the expansion. - * @throws IOException The name or rdata of a record was invalid after - * substitutions were performed. - */ -public Record [] -expand() throws IOException { - List list = new ArrayList(); - for (long i = start; i < end; i += step) { - String namestr = substitute(namePattern, current); - Name name = Name.fromString(namestr, origin); - String rdata = substitute(rdataPattern, current); - list.add(Record.fromString(name, type, dclass, ttl, - rdata, origin)); - } - return (Record []) list.toArray(new Record[list.size()]); -} - -/** - * Converts the generate specification to a string containing the corresponding - * $GENERATE statement. - */ -public String -toString() { - StringBuffer sb = new StringBuffer(); - sb.append("$GENERATE "); - sb.append(start + "-" + end); - if (step > 1) - sb.append("/" + step); - sb.append(" "); - sb.append(namePattern + " "); - sb.append(ttl + " "); - if (dclass != DClass.IN || !Options.check("noPrintIN")) - sb.append(DClass.string(dclass) + " "); - sb.append(Type.string(type) + " "); - sb.append(rdataPattern + " "); - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/HINFORecord.java b/src/main/java/org/xbill/DNS/HINFORecord.java deleted file mode 100644 index a9eff772b..000000000 --- a/src/main/java/org/xbill/DNS/HINFORecord.java +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Host Information - describes the CPU and OS of a host - * - * @author Brian Wellington - */ - -public class HINFORecord extends Record { - -private static final long serialVersionUID = -4732870630947452112L; - -private byte [] cpu, os; - -HINFORecord() {} - -Record -getObject() { - return new HINFORecord(); -} - -/** - * Creates an HINFO Record from the given data - * @param cpu A string describing the host's CPU - * @param os A string describing the host's OS - * @throws IllegalArgumentException One of the strings has invalid escapes - */ -public -HINFORecord(Name name, int dclass, long ttl, String cpu, String os) { - super(name, Type.HINFO, dclass, ttl); - try { - this.cpu = byteArrayFromString(cpu); - this.os = byteArrayFromString(os); - } - catch (TextParseException e) { - throw new IllegalArgumentException(e.getMessage()); - } -} - -void -rrFromWire(DNSInput in) throws IOException { - cpu = in.readCountedString(); - os = in.readCountedString(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - try { - cpu = byteArrayFromString(st.getString()); - os = byteArrayFromString(st.getString()); - } - catch (TextParseException e) { - throw st.exception(e.getMessage()); - } -} - -/** - * Returns the host's CPU - */ -public String -getCPU() { - return byteArrayToString(cpu, false); -} - -/** - * Returns the host's OS - */ -public String -getOS() { - return byteArrayToString(os, false); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeCountedString(cpu); - out.writeCountedString(os); -} - -/** - * Converts to a string - */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(byteArrayToString(cpu, true)); - sb.append(" "); - sb.append(byteArrayToString(os, true)); - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Header.java b/src/main/java/org/xbill/DNS/Header.java deleted file mode 100644 index 6d775acb7..000000000 --- a/src/main/java/org/xbill/DNS/Header.java +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.Random; - -/** - * A DNS message header - * @see Message - * - * @author Brian Wellington - */ - -public class Header implements Cloneable { - -private int id; -private int flags; -private int [] counts; - -private static Random random = new Random(); - -/** The length of a DNS Header in wire format. */ -public static final int LENGTH = 12; - -private void -init() { - counts = new int[4]; - flags = 0; - id = -1; -} - -/** - * Create a new empty header. - * @param id The message id - */ -public -Header(int id) { - init(); - setID(id); -} - -/** - * Create a new empty header with a random message id - */ -public -Header() { - init(); -} - -/** - * Parses a Header from a stream containing DNS wire format. - */ -Header(DNSInput in) throws IOException { - this(in.readU16()); - flags = in.readU16(); - for (int i = 0; i < counts.length; i++) - counts[i] = in.readU16(); -} - -/** - * Creates a new Header from its DNS wire format representation - * @param b A byte array containing the DNS Header. - */ -public -Header(byte [] b) throws IOException { - this(new DNSInput(b)); -} - -void -toWire(DNSOutput out) { - out.writeU16(getID()); - out.writeU16(flags); - for (int i = 0; i < counts.length; i++) - out.writeU16(counts[i]); -} - -public byte [] -toWire() { - DNSOutput out = new DNSOutput(); - toWire(out); - return out.toByteArray(); -} - -static private boolean -validFlag(int bit) { - return (bit >= 0 && bit <= 0xF && Flags.isFlag(bit)); -} - -static private void -checkFlag(int bit) { - if (!validFlag(bit)) - throw new IllegalArgumentException("invalid flag bit " + bit); -} - -/** - * Sets a flag to the supplied value - * @see Flags - */ -public void -setFlag(int bit) { - checkFlag(bit); - // bits are indexed from left to right - flags |= (1 << (15 - bit)); -} - -/** - * Sets a flag to the supplied value - * @see Flags - */ -public void -unsetFlag(int bit) { - checkFlag(bit); - // bits are indexed from left to right - flags &= ~(1 << (15 - bit)); -} - -/** - * Retrieves a flag - * @see Flags - */ -public boolean -getFlag(int bit) { - checkFlag(bit); - // bits are indexed from left to right - return (flags & (1 << (15 - bit))) != 0; -} - -boolean [] -getFlags() { - boolean [] array = new boolean[16]; - for (int i = 0; i < array.length; i++) - if (validFlag(i)) - array[i] = getFlag(i); - return array; -} - -/** - * Retrieves the message ID - */ -public int -getID() { - if (id >= 0) - return id; - synchronized (this) { - if (id < 0) - id = random.nextInt(0xffff); - return id; - } -} - -/** - * Sets the message ID - */ -public void -setID(int id) { - if (id < 0 || id > 0xffff) - throw new IllegalArgumentException("DNS message ID " + id + - " is out of range"); - this.id = id; -} - -/** - * Sets the message's rcode - * @see Rcode - */ -public void -setRcode(int value) { - if (value < 0 || value > 0xF) - throw new IllegalArgumentException("DNS Rcode " + value + - " is out of range"); - flags &= ~0xF; - flags |= value; -} - -/** - * Retrieves the mesasge's rcode - * @see Rcode - */ -public int -getRcode() { - return flags & 0xF; -} - -/** - * Sets the message's opcode - * @see Opcode - */ -public void -setOpcode(int value) { - if (value < 0 || value > 0xF) - throw new IllegalArgumentException("DNS Opcode " + value + - "is out of range"); - flags &= 0x87FF; - flags |= (value << 11); -} - -/** - * Retrieves the mesasge's opcode - * @see Opcode - */ -public int -getOpcode() { - return (flags >> 11) & 0xF; -} - -void -setCount(int field, int value) { - if (value < 0 || value > 0xFFFF) - throw new IllegalArgumentException("DNS section count " + - value + " is out of range"); - counts[field] = value; -} - -void -incCount(int field) { - if (counts[field] == 0xFFFF) - throw new IllegalStateException("DNS section count cannot " + - "be incremented"); - counts[field]++; -} - -void -decCount(int field) { - if (counts[field] == 0) - throw new IllegalStateException("DNS section count cannot " + - "be decremented"); - counts[field]--; -} - -/** - * Retrieves the record count for the given section - * @see Section - */ -public int -getCount(int field) { - return counts[field]; -} - -/** Converts the header's flags into a String */ -public String -printFlags() { - StringBuffer sb = new StringBuffer(); - - for (int i = 0; i < 16; i++) - if (validFlag(i) && getFlag(i)) { - sb.append(Flags.string(i)); - sb.append(" "); - } - return sb.toString(); -} - -String -toStringWithRcode(int newrcode) { - StringBuffer sb = new StringBuffer(); - - sb.append(";; ->>HEADER<<- "); - sb.append("opcode: " + Opcode.string(getOpcode())); - sb.append(", status: " + Rcode.string(newrcode)); - sb.append(", id: " + getID()); - sb.append("\n"); - - sb.append(";; flags: " + printFlags()); - sb.append("; "); - for (int i = 0; i < 4; i++) - sb.append(Section.string(i) + ": " + getCount(i) + " "); - return sb.toString(); -} - -/** Converts the header into a String */ -public String -toString() { - return toStringWithRcode(getRcode()); -} - -/* Creates a new Header identical to the current one */ -public Object -clone() { - Header h = new Header(); - h.id = id; - h.flags = flags; - System.arraycopy(counts, 0, h.counts, 0, counts.length); - return h; -} - -} diff --git a/src/main/java/org/xbill/DNS/IPSECKEYRecord.java b/src/main/java/org/xbill/DNS/IPSECKEYRecord.java deleted file mode 100644 index de8f9b8e2..000000000 --- a/src/main/java/org/xbill/DNS/IPSECKEYRecord.java +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetAddress; - -/** - * IPsec Keying Material (RFC 4025) - * - * @author Brian Wellington - */ - -public class IPSECKEYRecord extends Record { - -private static final long serialVersionUID = 3050449702765909687L; - -public static class Algorithm { - private Algorithm() {} - - public static final int DSA = 1; - public static final int RSA = 2; -} - -public static class Gateway { - private Gateway() {} - - public static final int None = 0; - public static final int IPv4 = 1; - public static final int IPv6 = 2; - public static final int Name = 3; -} - -private int precedence; -private int gatewayType; -private int algorithmType; -private Object gateway; -private byte [] key; - -IPSECKEYRecord() {} - -Record -getObject() { - return new IPSECKEYRecord(); -} - -/** - * Creates an IPSECKEY Record from the given data. - * @param precedence The record's precedence. - * @param gatewayType The record's gateway type. - * @param algorithmType The record's algorithm type. - * @param gateway The record's gateway. - * @param key The record's public key. - */ -public -IPSECKEYRecord(Name name, int dclass, long ttl, int precedence, - int gatewayType, int algorithmType, Object gateway, - byte [] key) -{ - super(name, Type.IPSECKEY, dclass, ttl); - this.precedence = checkU8("precedence", precedence); - this.gatewayType = checkU8("gatewayType", gatewayType); - this.algorithmType = checkU8("algorithmType", algorithmType); - switch (gatewayType) { - case Gateway.None: - this.gateway = null; - break; - case Gateway.IPv4: - if (!(gateway instanceof InetAddress)) - throw new IllegalArgumentException("\"gateway\" " + - "must be an IPv4 " + - "address"); - this.gateway = gateway; - break; - case Gateway.IPv6: - if (!(gateway instanceof Inet6Address)) - throw new IllegalArgumentException("\"gateway\" " + - "must be an IPv6 " + - "address"); - this.gateway = gateway; - break; - case Gateway.Name: - if (!(gateway instanceof Name)) - throw new IllegalArgumentException("\"gateway\" " + - "must be a DNS " + - "name"); - this.gateway = checkName("gateway", (Name) gateway); - break; - default: - throw new IllegalArgumentException("\"gatewayType\" " + - "must be between 0 and 3"); - } - - this.key = key; -} - -void -rrFromWire(DNSInput in) throws IOException { - precedence = in.readU8(); - gatewayType = in.readU8(); - algorithmType = in.readU8(); - switch (gatewayType) { - case Gateway.None: - gateway = null; - break; - case Gateway.IPv4: - gateway = InetAddress.getByAddress(in.readByteArray(4)); - break; - case Gateway.IPv6: - gateway = InetAddress.getByAddress(in.readByteArray(16)); - break; - case Gateway.Name: - gateway = new Name(in); - break; - default: - throw new WireParseException("invalid gateway type"); - } - if (in.remaining() > 0) - key = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - precedence = st.getUInt8(); - gatewayType = st.getUInt8(); - algorithmType = st.getUInt8(); - switch (gatewayType) { - case Gateway.None: - String s = st.getString(); - if (!s.equals(".")) - throw new TextParseException("invalid gateway format"); - gateway = null; - break; - case Gateway.IPv4: - gateway = st.getAddress(Address.IPv4); - break; - case Gateway.IPv6: - gateway = st.getAddress(Address.IPv6); - break; - case Gateway.Name: - gateway = st.getName(origin); - break; - default: - throw new WireParseException("invalid gateway type"); - } - key = st.getBase64(false); -} - -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(precedence); - sb.append(" "); - sb.append(gatewayType); - sb.append(" "); - sb.append(algorithmType); - sb.append(" "); - switch (gatewayType) { - case Gateway.None: - sb.append("."); - break; - case Gateway.IPv4: - case Gateway.IPv6: - InetAddress gatewayAddr = (InetAddress) gateway; - sb.append(gatewayAddr.getHostAddress()); - break; - case Gateway.Name: - sb.append(gateway); - break; - } - if (key != null) { - sb.append(" "); - sb.append(base64.toString(key)); - } - return sb.toString(); -} - -/** Returns the record's precedence. */ -public int -getPrecedence() { - return precedence; -} - -/** Returns the record's gateway type. */ -public int -getGatewayType() { - return gatewayType; -} - -/** Returns the record's algorithm type. */ -public int -getAlgorithmType() { - return algorithmType; -} - -/** Returns the record's gateway. */ -public Object -getGateway() { - return gateway; -} - -/** Returns the record's public key */ -public byte [] -getKey() { - return key; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU8(precedence); - out.writeU8(gatewayType); - out.writeU8(algorithmType); - switch (gatewayType) { - case Gateway.None: - break; - case Gateway.IPv4: - case Gateway.IPv6: - InetAddress gatewayAddr = (InetAddress) gateway; - out.writeByteArray(gatewayAddr.getAddress()); - break; - case Gateway.Name: - Name gatewayName = (Name) gateway; - gatewayName.toWire(out, null, canonical); - break; - } - if (key != null) - out.writeByteArray(key); -} - -} diff --git a/src/main/java/org/xbill/DNS/ISDNRecord.java b/src/main/java/org/xbill/DNS/ISDNRecord.java deleted file mode 100644 index 46aca2bde..000000000 --- a/src/main/java/org/xbill/DNS/ISDNRecord.java +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * ISDN - identifies the ISDN number and subaddress associated with a name. - * - * @author Brian Wellington - */ - -public class ISDNRecord extends Record { - -private static final long serialVersionUID = -8730801385178968798L; - -private byte [] address; -private byte [] subAddress; - -ISDNRecord() {} - -Record -getObject() { - return new ISDNRecord(); -} - -/** - * Creates an ISDN Record from the given data - * @param address The ISDN number associated with the domain. - * @param subAddress The subaddress, if any. - * @throws IllegalArgumentException One of the strings is invalid. - */ -public -ISDNRecord(Name name, int dclass, long ttl, String address, String subAddress) { - super(name, Type.ISDN, dclass, ttl); - try { - this.address = byteArrayFromString(address); - if (subAddress != null) - this.subAddress = byteArrayFromString(subAddress); - } - catch (TextParseException e) { - throw new IllegalArgumentException(e.getMessage()); - } -} - -void -rrFromWire(DNSInput in) throws IOException { - address = in.readCountedString(); - if (in.remaining() > 0) - subAddress = in.readCountedString(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - try { - address = byteArrayFromString(st.getString()); - Tokenizer.Token t = st.get(); - if (t.isString()) { - subAddress = byteArrayFromString(t.value); - } else { - st.unget(); - } - } - catch (TextParseException e) { - throw st.exception(e.getMessage()); - } -} - -/** - * Returns the ISDN number associated with the domain. - */ -public String -getAddress() { - return byteArrayToString(address, false); -} - -/** - * Returns the ISDN subaddress, or null if there is none. - */ -public String -getSubAddress() { - if (subAddress == null) - return null; - return byteArrayToString(subAddress, false); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeCountedString(address); - if (subAddress != null) - out.writeCountedString(subAddress); -} - -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(byteArrayToString(address, true)); - if (subAddress != null) { - sb.append(" "); - sb.append(byteArrayToString(subAddress, true)); - } - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/InvalidDClassException.java b/src/main/java/org/xbill/DNS/InvalidDClassException.java deleted file mode 100644 index 6c95cd4b3..000000000 --- a/src/main/java/org/xbill/DNS/InvalidDClassException.java +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An exception thrown when an invalid dclass code is specified. - * - * @author Brian Wellington - */ - -public class InvalidDClassException extends IllegalArgumentException { - -public -InvalidDClassException(int dclass) { - super("Invalid DNS class: " + dclass); -} - -} diff --git a/src/main/java/org/xbill/DNS/InvalidTTLException.java b/src/main/java/org/xbill/DNS/InvalidTTLException.java deleted file mode 100644 index 95776fe9e..000000000 --- a/src/main/java/org/xbill/DNS/InvalidTTLException.java +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An exception thrown when an invalid TTL is specified. - * - * @author Brian Wellington - */ - -public class InvalidTTLException extends IllegalArgumentException { - -public -InvalidTTLException(long ttl) { - super("Invalid DNS TTL: " + ttl); -} - -} diff --git a/src/main/java/org/xbill/DNS/InvalidTypeException.java b/src/main/java/org/xbill/DNS/InvalidTypeException.java deleted file mode 100644 index 7c6127665..000000000 --- a/src/main/java/org/xbill/DNS/InvalidTypeException.java +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An exception thrown when an invalid type code is specified. - * - * @author Brian Wellington - */ - -public class InvalidTypeException extends IllegalArgumentException { - -public -InvalidTypeException(int type) { - super("Invalid DNS type: " + type); -} - -} diff --git a/src/main/java/org/xbill/DNS/KEYBase.java b/src/main/java/org/xbill/DNS/KEYBase.java deleted file mode 100644 index d6e9ea48e..000000000 --- a/src/main/java/org/xbill/DNS/KEYBase.java +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; - -/** - * The base class for KEY/DNSKEY records, which have identical formats - * - * @author Brian Wellington - */ - -abstract class KEYBase extends Record { - -private static final long serialVersionUID = 3469321722693285454L; - -protected int flags, proto, alg; -protected byte [] key; -protected int footprint = -1; - -protected -KEYBase() {} - -public -KEYBase(Name name, int type, int dclass, long ttl, int flags, int proto, - int alg, byte [] key) -{ - super(name, type, dclass, ttl); - this.flags = checkU16("flags", flags); - this.proto = checkU8("proto", proto); - this.alg = checkU8("alg", alg); - this.key = key; -} - -void -rrFromWire(DNSInput in) throws IOException { - flags = in.readU16(); - proto = in.readU8(); - alg = in.readU8(); - if (in.remaining() > 0) - key = in.readByteArray(); -} - -/** Converts the DNSKEY/KEY Record to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(flags); - sb.append(" "); - sb.append(proto); - sb.append(" "); - sb.append(alg); - if (key != null) { - if (Options.check("multiline")) { - sb.append(" (\n"); - sb.append(base64.formatString(key, 64, "\t", true)); - sb.append(" ; key_tag = "); - sb.append(getFootprint()); - } else { - sb.append(" "); - sb.append(base64.toString(key)); - } - } - return sb.toString(); -} - -/** - * Returns the flags describing the key's properties - */ -public int -getFlags() { - return flags; -} - -/** - * Returns the protocol that the key was created for - */ -public int -getProtocol() { - return proto; -} - -/** - * Returns the key's algorithm - */ -public int -getAlgorithm() { - return alg; -} - -/** - * Returns the binary data representing the key - */ -public byte [] -getKey() { - return key; -} - -/** - * Returns the key's footprint (after computing it) - */ -public int -getFootprint() { - if (footprint >= 0) - return footprint; - - int foot = 0; - - DNSOutput out = new DNSOutput(); - rrToWire(out, null, false); - byte [] rdata = out.toByteArray(); - - if (alg == DNSSEC.Algorithm.RSAMD5) { - int d1 = rdata[rdata.length - 3] & 0xFF; - int d2 = rdata[rdata.length - 2] & 0xFF; - foot = (d1 << 8) + d2; - } - else { - int i; - for (i = 0; i < rdata.length - 1; i += 2) { - int d1 = rdata[i] & 0xFF; - int d2 = rdata[i + 1] & 0xFF; - foot += ((d1 << 8) + d2); - } - if (i < rdata.length) { - int d1 = rdata[i] & 0xFF; - foot += (d1 << 8); - } - foot += ((foot >> 16) & 0xFFFF); - } - footprint = (foot & 0xFFFF); - return footprint; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(flags); - out.writeU8(proto); - out.writeU8(alg); - if (key != null) - out.writeByteArray(key); -} - -} diff --git a/src/main/java/org/xbill/DNS/KEYRecord.java b/src/main/java/org/xbill/DNS/KEYRecord.java deleted file mode 100644 index 60a7b2e6f..000000000 --- a/src/main/java/org/xbill/DNS/KEYRecord.java +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.StringTokenizer; - -/** - * Key - contains a cryptographic public key. The data can be converted - * to objects implementing java.security.interfaces.PublicKey - * @see DNSSEC - * - * @author Brian Wellington - */ - -public class KEYRecord extends KEYBase { - -private static final long serialVersionUID = 6385613447571488906L; - -public static class Protocol { - /** - * KEY protocol identifiers. - */ - - private Protocol() {} - - /** No defined protocol. */ - public static final int NONE = 0; - - /** Transaction Level Security */ - public static final int TLS = 1; - - /** Email */ - public static final int EMAIL = 2; - - /** DNSSEC */ - public static final int DNSSEC = 3; - - /** IPSEC Control */ - public static final int IPSEC = 4; - - /** Any protocol */ - public static final int ANY = 255; - - private static Mnemonic protocols = new Mnemonic("KEY protocol", - Mnemonic.CASE_UPPER); - - static { - protocols.setMaximum(0xFF); - protocols.setNumericAllowed(true); - - protocols.add(NONE, "NONE"); - protocols.add(TLS, "TLS"); - protocols.add(EMAIL, "EMAIL"); - protocols.add(DNSSEC, "DNSSEC"); - protocols.add(IPSEC, "IPSEC"); - protocols.add(ANY, "ANY"); - } - - /** - * Converts an KEY protocol value into its textual representation - */ - public static String - string(int type) { - return protocols.getText(type); - } - - /** - * Converts a textual representation of a KEY protocol into its - * numeric code. Integers in the range 0..255 are also accepted. - * @param s The textual representation of the protocol - * @return The protocol code, or -1 on error. - */ - public static int - value(String s) { - return protocols.getValue(s); - } -} - -public static class Flags { - /** - * KEY flags identifiers. - */ - - private Flags() {} - - /** KEY cannot be used for confidentiality */ - public static final int NOCONF = 0x4000; - - /** KEY cannot be used for authentication */ - public static final int NOAUTH = 0x8000; - - /** No key present */ - public static final int NOKEY = 0xC000; - - /** Bitmask of the use fields */ - public static final int USE_MASK = 0xC000; - - /** Flag 2 (unused) */ - public static final int FLAG2 = 0x2000; - - /** Flags extension */ - public static final int EXTEND = 0x1000; - - /** Flag 4 (unused) */ - public static final int FLAG4 = 0x0800; - - /** Flag 5 (unused) */ - public static final int FLAG5 = 0x0400; - - /** Key is owned by a user. */ - public static final int USER = 0x0000; - - /** Key is owned by a zone. */ - public static final int ZONE = 0x0100; - - /** Key is owned by a host. */ - public static final int HOST = 0x0200; - - /** Key owner type 3 (reserved). */ - public static final int NTYP3 = 0x0300; - - /** Key owner bitmask. */ - public static final int OWNER_MASK = 0x0300; - - /** Flag 8 (unused) */ - public static final int FLAG8 = 0x0080; - - /** Flag 9 (unused) */ - public static final int FLAG9 = 0x0040; - - /** Flag 10 (unused) */ - public static final int FLAG10 = 0x0020; - - /** Flag 11 (unused) */ - public static final int FLAG11 = 0x0010; - - /** Signatory value 0 */ - public static final int SIG0 = 0; - - /** Signatory value 1 */ - public static final int SIG1 = 1; - - /** Signatory value 2 */ - public static final int SIG2 = 2; - - /** Signatory value 3 */ - public static final int SIG3 = 3; - - /** Signatory value 4 */ - public static final int SIG4 = 4; - - /** Signatory value 5 */ - public static final int SIG5 = 5; - - /** Signatory value 6 */ - public static final int SIG6 = 6; - - /** Signatory value 7 */ - public static final int SIG7 = 7; - - /** Signatory value 8 */ - public static final int SIG8 = 8; - - /** Signatory value 9 */ - public static final int SIG9 = 9; - - /** Signatory value 10 */ - public static final int SIG10 = 10; - - /** Signatory value 11 */ - public static final int SIG11 = 11; - - /** Signatory value 12 */ - public static final int SIG12 = 12; - - /** Signatory value 13 */ - public static final int SIG13 = 13; - - /** Signatory value 14 */ - public static final int SIG14 = 14; - - /** Signatory value 15 */ - public static final int SIG15 = 15; - - private static Mnemonic flags = new Mnemonic("KEY flags", - Mnemonic.CASE_UPPER); - - static { - flags.setMaximum(0xFFFF); - flags.setNumericAllowed(false); - - flags.add(NOCONF, "NOCONF"); - flags.add(NOAUTH, "NOAUTH"); - flags.add(NOKEY, "NOKEY"); - flags.add(FLAG2, "FLAG2"); - flags.add(EXTEND, "EXTEND"); - flags.add(FLAG4, "FLAG4"); - flags.add(FLAG5, "FLAG5"); - flags.add(USER, "USER"); - flags.add(ZONE, "ZONE"); - flags.add(HOST, "HOST"); - flags.add(NTYP3, "NTYP3"); - flags.add(FLAG8, "FLAG8"); - flags.add(FLAG9, "FLAG9"); - flags.add(FLAG10, "FLAG10"); - flags.add(FLAG11, "FLAG11"); - flags.add(SIG0, "SIG0"); - flags.add(SIG1, "SIG1"); - flags.add(SIG2, "SIG2"); - flags.add(SIG3, "SIG3"); - flags.add(SIG4, "SIG4"); - flags.add(SIG5, "SIG5"); - flags.add(SIG6, "SIG6"); - flags.add(SIG7, "SIG7"); - flags.add(SIG8, "SIG8"); - flags.add(SIG9, "SIG9"); - flags.add(SIG10, "SIG10"); - flags.add(SIG11, "SIG11"); - flags.add(SIG12, "SIG12"); - flags.add(SIG13, "SIG13"); - flags.add(SIG14, "SIG14"); - flags.add(SIG15, "SIG15"); - } - - /** - * Converts a textual representation of KEY flags into its - * numeric code. Integers in the range 0..65535 are also accepted. - * @param s The textual representation of the protocol - * @return The protocol code, or -1 on error. - */ - public static int - value(String s) { - int value; - try { - value = Integer.parseInt(s); - if (value >= 0 && value <= 0xFFFF) { - return value; - } - return -1; - } catch (NumberFormatException e) { - } - StringTokenizer st = new StringTokenizer(s, "|"); - value = 0; - while (st.hasMoreTokens()) { - int val = flags.getValue(st.nextToken()); - if (val < 0) { - return -1; - } - value |= val; - } - return value; - } -} - -/* flags */ -/** This key cannot be used for confidentiality (encryption) */ -public static final int FLAG_NOCONF = Flags.NOCONF; - -/** This key cannot be used for authentication */ -public static final int FLAG_NOAUTH = Flags.NOAUTH; - -/** This key cannot be used for authentication or confidentiality */ -public static final int FLAG_NOKEY = Flags.NOKEY; - -/** A zone key */ -public static final int OWNER_ZONE = Flags.ZONE; - -/** A host/end entity key */ -public static final int OWNER_HOST = Flags.HOST; - -/** A user key */ -public static final int OWNER_USER = Flags.USER; - -/* protocols */ -/** Key was created for use with transaction level security */ -public static final int PROTOCOL_TLS = Protocol.TLS; - -/** Key was created for use with email */ -public static final int PROTOCOL_EMAIL = Protocol.EMAIL; - -/** Key was created for use with DNSSEC */ -public static final int PROTOCOL_DNSSEC = Protocol.DNSSEC; - -/** Key was created for use with IPSEC */ -public static final int PROTOCOL_IPSEC = Protocol.IPSEC; - -/** Key was created for use with any protocol */ -public static final int PROTOCOL_ANY = Protocol.ANY; - -KEYRecord() {} - -Record -getObject() { - return new KEYRecord(); -} - -/** - * Creates a KEY Record from the given data - * @param flags Flags describing the key's properties - * @param proto The protocol that the key was created for - * @param alg The key's algorithm - * @param key Binary data representing the key - */ -public -KEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg, - byte [] key) -{ - super(name, Type.KEY, dclass, ttl, flags, proto, alg, key); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - String flagString = st.getIdentifier(); - flags = Flags.value(flagString); - if (flags < 0) - throw st.exception("Invalid flags: " + flagString); - String protoString = st.getIdentifier(); - proto = Protocol.value(protoString); - if (proto < 0) - throw st.exception("Invalid protocol: " + protoString); - String algString = st.getIdentifier(); - alg = DNSSEC.Algorithm.value(algString); - if (alg < 0) - throw st.exception("Invalid algorithm: " + algString); - /* If this is a null KEY, there's no key data */ - if ((flags & Flags.USE_MASK) == Flags.NOKEY) - key = null; - else - key = st.getBase64(); -} - -} diff --git a/src/main/java/org/xbill/DNS/KXRecord.java b/src/main/java/org/xbill/DNS/KXRecord.java deleted file mode 100644 index 481d21b8c..000000000 --- a/src/main/java/org/xbill/DNS/KXRecord.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Key Exchange - delegation of authority - * - * @author Brian Wellington - */ - -public class KXRecord extends U16NameBase { - -private static final long serialVersionUID = 7448568832769757809L; - -KXRecord() {} - -Record -getObject() { - return new KXRecord(); -} - -/** - * Creates a KX Record from the given data - * @param preference The preference of this KX. Records with lower priority - * are preferred. - * @param target The host that authority is delegated to - */ -public -KXRecord(Name name, int dclass, long ttl, int preference, Name target) { - super(name, Type.KX, dclass, ttl, preference, "preference", - target, "target"); -} - -/** Returns the target of the KX record */ -public Name -getTarget() { - return getNameField(); -} - -/** Returns the preference of this KX record */ -public int -getPreference() { - return getU16Field(); -} - -public Name -getAdditionalName() { - return getNameField(); -} - -} diff --git a/src/main/java/org/xbill/DNS/LOCRecord.java b/src/main/java/org/xbill/DNS/LOCRecord.java deleted file mode 100644 index 2ee0d5b60..000000000 --- a/src/main/java/org/xbill/DNS/LOCRecord.java +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.text.DecimalFormat; -import java.text.NumberFormat; - -/** - * Location - describes the physical location of hosts, networks, subnets. - * - * @author Brian Wellington - */ - -public class LOCRecord extends Record { - -private static final long serialVersionUID = 9058224788126750409L; - -private static NumberFormat w2, w3; - -private long size, hPrecision, vPrecision; -private long latitude, longitude, altitude; - -static { - w2 = new DecimalFormat(); - w2.setMinimumIntegerDigits(2); - - w3 = new DecimalFormat(); - w3.setMinimumIntegerDigits(3); -} - -LOCRecord() {} - -Record -getObject() { - return new LOCRecord(); -} - -/** - * Creates an LOC Record from the given data - * @param latitude The latitude of the center of the sphere - * @param longitude The longitude of the center of the sphere - * @param altitude The altitude of the center of the sphere, in m - * @param size The diameter of a sphere enclosing the described entity, in m. - * @param hPrecision The horizontal precision of the data, in m. - * @param vPrecision The vertical precision of the data, in m. -*/ -public -LOCRecord(Name name, int dclass, long ttl, double latitude, double longitude, - double altitude, double size, double hPrecision, double vPrecision) -{ - super(name, Type.LOC, dclass, ttl); - this.latitude = (long)(latitude * 3600 * 1000 + (1L << 31)); - this.longitude = (long)(longitude * 3600 * 1000 + (1L << 31)); - this.altitude = (long)((altitude + 100000) * 100); - this.size = (long)(size * 100); - this.hPrecision = (long)(hPrecision * 100); - this.vPrecision = (long)(vPrecision * 100); -} - -void -rrFromWire(DNSInput in) throws IOException { - int version; - - version = in.readU8(); - if (version != 0) - throw new WireParseException("Invalid LOC version"); - - size = parseLOCformat(in.readU8()); - hPrecision = parseLOCformat(in.readU8()); - vPrecision = parseLOCformat(in.readU8()); - latitude = in.readU32(); - longitude = in.readU32(); - altitude = in.readU32(); -} - -private double -parseFixedPoint(String s) -{ - if (s.matches("^\\d+$")) - return Integer.parseInt(s); - else if (s.matches("^\\d+\\.\\d*$")) { - String [] parts = s.split("\\."); - double value = Integer.parseInt(parts[0]); - double fraction = Integer.parseInt(parts[1]); - int digits = parts[1].length(); - return value + (fraction / Math.pow(10, digits)); - } else - throw new NumberFormatException(); -} - -private long -parsePosition(Tokenizer st, String type) throws IOException { - boolean isLatitude = type.equals("latitude"); - int deg = 0, min = 0; - double sec = 0; - long value; - String s; - - deg = st.getUInt16(); - if (deg > 180 || (deg > 90 && isLatitude)) - throw st.exception("Invalid LOC " + type + " degrees"); - - s = st.getString(); - try { - min = Integer.parseInt(s); - if (min < 0 || min > 59) - throw st.exception("Invalid LOC " + type + " minutes"); - s = st.getString(); - sec = parseFixedPoint(s); - if (sec < 0 || sec >= 60) - throw st.exception("Invalid LOC " + type + " seconds"); - s = st.getString(); - } catch (NumberFormatException e) { - } - - if (s.length() != 1) - throw st.exception("Invalid LOC " + type); - - value = (long) (1000 * (sec + 60L * (min + 60L * deg))); - - char c = Character.toUpperCase(s.charAt(0)); - if ((isLatitude && c == 'S') || (!isLatitude && c == 'W')) - value = -value; - else if ((isLatitude && c != 'N') || (!isLatitude && c != 'E')) - throw st.exception("Invalid LOC " + type); - - value += (1L << 31); - - return value; -} - -private long -parseDouble(Tokenizer st, String type, boolean required, long min, long max, - long defaultValue) -throws IOException -{ - Tokenizer.Token token = st.get(); - if (token.isEOL()) { - if (required) - throw st.exception("Invalid LOC " + type); - st.unget(); - return defaultValue; - } - String s = token.value; - if (s.length() > 1 && s.charAt(s.length() - 1) == 'm') - s = s.substring(0, s.length() - 1); - try { - long value = (long)(100 * parseFixedPoint(s)); - if (value < min || value > max) - throw st.exception("Invalid LOC " + type); - return value; - } - catch (NumberFormatException e) { - throw st.exception("Invalid LOC " + type); - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - latitude = parsePosition(st, "latitude"); - longitude = parsePosition(st, "longitude"); - altitude = parseDouble(st, "altitude", true, - -10000000, 4284967295L, 0) + 10000000; - size = parseDouble(st, "size", false, 0, 9000000000L, 100); - hPrecision = parseDouble(st, "horizontal precision", false, - 0, 9000000000L, 1000000); - vPrecision = parseDouble(st, "vertical precision", false, - 0, 9000000000L, 1000); -} - -private void -renderFixedPoint(StringBuffer sb, NumberFormat formatter, long value, - long divisor) -{ - sb.append(value / divisor); - value %= divisor; - if (value != 0) { - sb.append("."); - sb.append(formatter.format(value)); - } -} - -private String -positionToString(long value, char pos, char neg) { - StringBuffer sb = new StringBuffer(); - char direction; - - long temp = value - (1L << 31); - if (temp < 0) { - temp = -temp; - direction = neg; - } else - direction = pos; - - sb.append(temp / (3600 * 1000)); /* degrees */ - temp = temp % (3600 * 1000); - sb.append(" "); - - sb.append(temp / (60 * 1000)); /* minutes */ - temp = temp % (60 * 1000); - sb.append(" "); - - renderFixedPoint(sb, w3, temp, 1000); /* seconds */ - sb.append(" "); - - sb.append(direction); - - return sb.toString(); -} - - -/** Convert to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - - /* Latitude */ - sb.append(positionToString(latitude, 'N', 'S')); - sb.append(" "); - - /* Latitude */ - sb.append(positionToString(longitude, 'E', 'W')); - sb.append(" "); - - /* Altitude */ - renderFixedPoint(sb, w2, altitude - 10000000, 100); - sb.append("m "); - - /* Size */ - renderFixedPoint(sb, w2, size, 100); - sb.append("m "); - - /* Horizontal precision */ - renderFixedPoint(sb, w2, hPrecision, 100); - sb.append("m "); - - /* Vertical precision */ - renderFixedPoint(sb, w2, vPrecision, 100); - sb.append("m"); - - return sb.toString(); -} - -/** Returns the latitude */ -public double -getLatitude() { - return ((double)(latitude - (1L << 31))) / (3600 * 1000); -} - -/** Returns the longitude */ -public double -getLongitude() { - return ((double)(longitude - (1L << 31))) / (3600 * 1000); -} - -/** Returns the altitude */ -public double -getAltitude() { - return ((double)(altitude - 10000000)) / 100; -} - -/** Returns the diameter of the enclosing sphere */ -public double -getSize() { - return ((double)size) / 100; -} - -/** Returns the horizontal precision */ -public double -getHPrecision() { - return ((double)hPrecision) / 100; -} - -/** Returns the horizontal precision */ -public double -getVPrecision() { - return ((double)vPrecision) / 100; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU8(0); /* version */ - out.writeU8(toLOCformat(size)); - out.writeU8(toLOCformat(hPrecision)); - out.writeU8(toLOCformat(vPrecision)); - out.writeU32(latitude); - out.writeU32(longitude); - out.writeU32(altitude); -} - -private static long -parseLOCformat(int b) throws WireParseException { - long out = b >> 4; - int exp = b & 0xF; - if (out > 9 || exp > 9) - throw new WireParseException("Invalid LOC Encoding"); - while (exp-- > 0) - out *= 10; - return (out); -} - -private int -toLOCformat(long l) { - byte exp = 0; - while (l > 9) { - exp++; - l /= 10; - } - return (int)((l << 4) + exp); -} - -} diff --git a/src/main/java/org/xbill/DNS/Lookup.java b/src/main/java/org/xbill/DNS/Lookup.java deleted file mode 100644 index 682d67c00..000000000 --- a/src/main/java/org/xbill/DNS/Lookup.java +++ /dev/null @@ -1,657 +0,0 @@ -// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.UnknownHostException; -import java.util.*; - -/** - * The Lookup object issues queries to caching DNS servers. The input consists - * of a name, an optional type, and an optional class. Caching is enabled - * by default and used when possible to reduce the number of DNS requests. - * A Resolver, which defaults to an ExtendedResolver initialized with the - * resolvers located by the ResolverConfig class, performs the queries. A - * search path of domain suffixes is used to resolve relative names, and is - * also determined by the ResolverConfig class. - *

    - * A Lookup object may be reused, but should not be used by multiple threads. - * - * @author Brian Wellington - * @see Cache - * @see Resolver - * @see ResolverConfig - */ - -public final class Lookup { - - private static Resolver defaultResolver; - private static Name[] defaultSearchPath; - private static Map defaultCaches; - - private Resolver resolver; - private Name[] searchPath; - private Cache cache; - private boolean temporary_cache; - private int credibility; - private Name name; - private int type; - private int dclass; - private boolean verbose; - private int iterations; - private boolean foundAlias; - private boolean done; - private boolean doneCurrent; - private List aliases; - private Record[] answers; - private int result; - private String error; - private boolean nxdomain; - private boolean badresponse; - private String badresponse_error; - private boolean networkerror; - private boolean timedout; - private boolean nametoolong; - private boolean referral; - - private static final Name[] noAliases = new Name[0]; - - /** - * The lookup was successful. - */ - public static final int SUCCESSFUL = 0; - - /** - * The lookup failed due to a data or server error. Repeating the lookup - * would not be helpful. - */ - public static final int UNRECOVERABLE = 1; - - /** - * The lookup failed due to a network error. Repeating the lookup may be - * helpful. - */ - public static final int TRY_AGAIN = 2; - - /** - * The host does not exist. - */ - public static final int HOST_NOT_FOUND = 3; - - /** - * The host exists, but has no records associated with the queried type. - */ - public static final int TYPE_NOT_FOUND = 4; - - public static synchronized void - refreshDefault() { - - try { - defaultResolver = new ExtendedResolver(); - } - catch (UnknownHostException e) { - throw new RuntimeException("Failed to initialize resolver"); - } - defaultSearchPath = ResolverConfig.getCurrentConfig().searchPath(); - defaultCaches = new HashMap(); - } - - static { - refreshDefault(); - } - - /** - * Gets the Resolver that will be used as the default by future Lookups. - * - * @return The default resolver. - */ - public static synchronized Resolver - getDefaultResolver() { - return defaultResolver; - } - - /** - * Sets the default Resolver to be used as the default by future Lookups. - * - * @param resolver The default resolver. - */ - public static synchronized void - setDefaultResolver(Resolver resolver) { - defaultResolver = resolver; - } - - /** - * Gets the Cache that will be used as the default for the specified - * class by future Lookups. - * - * @param dclass The class whose cache is being retrieved. - * @return The default cache for the specified class. - */ - public static synchronized Cache - getDefaultCache(int dclass) { - DClass.check(dclass); - Cache c = (Cache) defaultCaches.get(Mnemonic.toInteger(dclass)); - if (c == null) { - c = new Cache(dclass); - defaultCaches.put(Mnemonic.toInteger(dclass), c); - } - return c; - } - - /** - * Sets the Cache to be used as the default for the specified class by future - * Lookups. - * - * @param cache The default cache for the specified class. - * @param dclass The class whose cache is being set. - */ - public static synchronized void - setDefaultCache(Cache cache, int dclass) { - DClass.check(dclass); - defaultCaches.put(Mnemonic.toInteger(dclass), cache); - } - - /** - * Gets the search path that will be used as the default by future Lookups. - * - * @return The default search path. - */ - public static synchronized Name[] - getDefaultSearchPath() { - return defaultSearchPath; - } - - /** - * Sets the search path to be used as the default by future Lookups. - * - * @param domains The default search path. - */ - public static synchronized void - setDefaultSearchPath(Name[] domains) { - defaultSearchPath = domains; - } - - /** - * Sets the search path that will be used as the default by future Lookups. - * - * @param domains The default search path. - * @throws TextParseException A name in the array is not a valid DNS name. - */ - public static synchronized void - setDefaultSearchPath(String[] domains) throws TextParseException { - if (domains == null) { - defaultSearchPath = null; - return; - } - Name[] newdomains = new Name[domains.length]; - for (int i = 0; i < domains.length; i++) - newdomains[i] = Name.fromString(domains[i], Name.root); - defaultSearchPath = newdomains; - } - - private final void - reset() { - iterations = 0; - foundAlias = false; - done = false; - doneCurrent = false; - aliases = null; - answers = null; - result = -1; - error = null; - nxdomain = false; - badresponse = false; - badresponse_error = null; - networkerror = false; - timedout = false; - nametoolong = false; - referral = false; - if (temporary_cache) - cache.clearCache(); - } - - /** - * Create a Lookup object that will find records of the given name, type, - * and class. The lookup will use the default cache, resolver, and search - * path, and look for records that are reasonably credible. - * - * @param name The name of the desired records - * @param type The type of the desired records - * @param dclass The class of the desired records - * @throws IllegalArgumentException The type is a meta type other than ANY. - * @see Cache - * @see Resolver - * @see Credibility - * @see Name - * @see Type - * @see DClass - */ - public Lookup(Name name, int type, int dclass) { - Type.check(type); - DClass.check(dclass); - if (!Type.isRR(type) && type != Type.ANY) - throw new IllegalArgumentException("Cannot query for " + - "meta-types other than ANY"); - this.name = name; - this.type = type; - this.dclass = dclass; - synchronized (Lookup.class) { - this.resolver = getDefaultResolver(); - this.searchPath = getDefaultSearchPath(); - this.cache = getDefaultCache(dclass); - } - this.credibility = Credibility.NORMAL; - this.verbose = Options.check("verbose"); - this.result = -1; - } - - /** - * Create a Lookup object that will find records of the given name and type - * in the IN class. - * - * @param name The name of the desired records - * @param type The type of the desired records - * @throws IllegalArgumentException The type is a meta type other than ANY. - * @see #Lookup(Name,int,int) - */ - public Lookup(Name name, int type) { - this(name, type, DClass.IN); - } - - /** - * Create a Lookup object that will find records of type A at the given name - * in the IN class. - * - * @param name The name of the desired records - * @see #Lookup(Name,int,int) - */ - public Lookup(Name name) { - this(name, Type.A, DClass.IN); - } - - /** - * Create a Lookup object that will find records of the given name, type, - * and class. - * - * @param name The name of the desired records - * @param type The type of the desired records - * @param dclass The class of the desired records - * @throws TextParseException The name is not a valid DNS name - * @throws IllegalArgumentException The type is a meta type other than ANY. - * @see #Lookup(Name,int,int) - */ - public Lookup(String name, int type, int dclass) throws TextParseException { - this(Name.fromString(name), type, dclass); - } - - /** - * Create a Lookup object that will find records of the given name and type - * in the IN class. - * - * @param name The name of the desired records - * @param type The type of the desired records - * @throws TextParseException The name is not a valid DNS name - * @throws IllegalArgumentException The type is a meta type other than ANY. - * @see #Lookup(Name,int,int) - */ - public Lookup(String name, int type) throws TextParseException { - this(Name.fromString(name), type, DClass.IN); - } - - /** - * Create a Lookup object that will find records of type A at the given name - * in the IN class. - * - * @param name The name of the desired records - * @throws TextParseException The name is not a valid DNS name - * @see #Lookup(Name,int,int) - */ - public Lookup(String name) throws TextParseException { - this(Name.fromString(name), Type.A, DClass.IN); - } - - /** - * Sets the resolver to use when performing this lookup. This overrides the - * default value. - * - * @param resolver The resolver to use. - */ - public void - setResolver(Resolver resolver) { - this.resolver = resolver; - } - - /** - * Sets the search path to use when performing this lookup. This overrides the - * default value. - * - * @param domains An array of names containing the search path. - */ - public void - setSearchPath(Name[] domains) { - this.searchPath = domains; - } - - /** - * Sets the search path to use when performing this lookup. This overrides the - * default value. - * - * @param domains An array of names containing the search path. - * @throws TextParseException A name in the array is not a valid DNS name. - */ - public void - setSearchPath(String[] domains) throws TextParseException { - if (domains == null) { - this.searchPath = null; - return; - } - Name[] newdomains = new Name[domains.length]; - for (int i = 0; i < domains.length; i++) - newdomains[i] = Name.fromString(domains[i], Name.root); - this.searchPath = newdomains; - } - - /** - * Sets the cache to use when performing this lookup. This overrides the - * default value. If the results of this lookup should not be permanently - * cached, null can be provided here. - * - * @param cache The cache to use. - */ - public void - setCache(Cache cache) { - if (cache == null) { - this.cache = new Cache(dclass); - this.temporary_cache = true; - } else { - this.cache = cache; - this.temporary_cache = false; - } - } - - /** - * Sets the minimum credibility level that will be accepted when performing - * the lookup. This defaults to Credibility.NORMAL. - * - * @param credibility The minimum credibility level. - */ - public void - setCredibility(int credibility) { - this.credibility = credibility; - } - - private void - follow(Name name, Name oldname) { - foundAlias = true; - badresponse = false; - networkerror = false; - timedout = false; - nxdomain = false; - referral = false; - iterations++; - if (iterations >= 6 || name.equals(oldname)) { - result = UNRECOVERABLE; - error = "CNAME loop"; - done = true; - return; - } - if (aliases == null) - aliases = new ArrayList(); - aliases.add(oldname); - lookup(name); - } - - private void - processResponse(Name name, SetResponse response) { - if (response.isSuccessful()) { - RRset[] rrsets = response.answers(); - List l = new ArrayList(); - Iterator it; - int i; - - for (i = 0; i < rrsets.length; i++) { - it = rrsets[i].rrs(); - while (it.hasNext()) - l.add(it.next()); - } - - result = SUCCESSFUL; - answers = (Record[]) l.toArray(new Record[l.size()]); - done = true; - } else if (response.isNXDOMAIN()) { - nxdomain = true; - doneCurrent = true; - if (iterations > 0) { - result = HOST_NOT_FOUND; - done = true; - } - } else if (response.isNXRRSET()) { - result = TYPE_NOT_FOUND; - answers = null; - done = true; - } else if (response.isCNAME()) { - CNAMERecord cname = response.getCNAME(); - follow(cname.getTarget(), name); - } else if (response.isDNAME()) { - DNAMERecord dname = response.getDNAME(); - try { - follow(name.fromDNAME(dname), name); - } catch (NameTooLongException e) { - result = UNRECOVERABLE; - error = "Invalid DNAME target"; - done = true; - } - } else if (response.isDelegation()) { - // We shouldn't get a referral. Ignore it. - referral = true; - } - } - - private void - lookup(Name current) { - SetResponse sr = cache.lookupRecords(current, type, credibility); - if (verbose) { - System.err.println("lookup " + current + " " + - Type.string(type)); - System.err.println(sr); - } - processResponse(current, sr); - if (done || doneCurrent) - return; - - Record question = Record.newRecord(current, type, dclass); - Message query = Message.newQuery(question); - Message response = null; - try { - response = resolver.send(query); - } - catch (IOException e) { - // A network error occurred. Press on. - if (e instanceof InterruptedIOException) - timedout = true; - else - networkerror = true; - return; - } - int rcode = response.getHeader().getRcode(); - if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) { - // The server we contacted is broken or otherwise unhelpful. - // Press on. - badresponse = true; - badresponse_error = Rcode.string(rcode); - return; - } - - if (!query.getQuestion().equals(response.getQuestion())) { - // The answer doesn't match the question. That's not good. - badresponse = true; - badresponse_error = "response does not match query"; - return; - } - - sr = cache.addMessage(response); - if (sr == null) - sr = cache.lookupRecords(current, type, credibility); - if (verbose) { - System.err.println("queried " + current + " " + - Type.string(type)); - System.err.println(sr); - } - processResponse(current, sr); - } - - private void - resolve(Name current, Name suffix) { - doneCurrent = false; - Name tname = null; - if (suffix == null) - tname = current; - else { - try { - tname = Name.concatenate(current, suffix); - } - catch (NameTooLongException e) { - nametoolong = true; - return; - } - } - lookup(tname); - } - - /** - * Performs the lookup, using the specified Cache, Resolver, and search path. - * - * @return The answers, or null if none are found. - */ - public Record[] - run() { - if (done) - reset(); - if (name.isAbsolute()) - resolve(name, null); - else if (searchPath == null) - resolve(name, Name.root); - else { - if (name.labels() > 1) - resolve(name, Name.root); - if (done) - return answers; - - for (int i = 0; i < searchPath.length; i++) { - resolve(name, searchPath[i]); - if (done) - return answers; - else if (foundAlias) - break; - } - } - if (!done) { - if (badresponse) { - result = TRY_AGAIN; - error = badresponse_error; - done = true; - } else if (timedout) { - result = TRY_AGAIN; - error = "timed out"; - done = true; - } else if (networkerror) { - result = TRY_AGAIN; - error = "network error"; - done = true; - } else if (nxdomain) { - result = HOST_NOT_FOUND; - done = true; - } else if (referral) { - result = UNRECOVERABLE; - error = "referral"; - done = true; - } else if (nametoolong) { - result = UNRECOVERABLE; - error = "name too long"; - done = true; - } - } - return answers; - } - - private void - checkDone() { - if (done && result != -1) - return; - StringBuffer sb = new StringBuffer("Lookup of " + name + " "); - if (dclass != DClass.IN) - sb.append(DClass.string(dclass) + " "); - sb.append(Type.string(type) + " isn't done"); - throw new IllegalStateException(sb.toString()); - } - - /** - * Returns the answers from the lookup. - * - * @return The answers, or null if none are found. - * @throws IllegalStateException The lookup has not completed. - */ - public Record[] - getAnswers() { - checkDone(); - return answers; - } - - /** - * Returns all known aliases for this name. Whenever a CNAME/DNAME is - * followed, an alias is added to this array. The last element in this - * array will be the owner name for records in the answer, if there are any. - * - * @return The aliases. - * @throws IllegalStateException The lookup has not completed. - */ - public Name[] - getAliases() { - checkDone(); - if (aliases == null) - return noAliases; - return (Name[]) aliases.toArray(new Name[aliases.size()]); - } - - /** - * Returns the result code of the lookup. - * - * @return The result code, which can be SUCCESSFUL, UNRECOVERABLE, TRY_AGAIN, - * HOST_NOT_FOUND, or TYPE_NOT_FOUND. - * @throws IllegalStateException The lookup has not completed. - */ - public int - getResult() { - checkDone(); - return result; - } - - /** - * Returns an error string describing the result code of this lookup. - * - * @return A string, which may either directly correspond the result code - * or be more specific. - * @throws IllegalStateException The lookup has not completed. - */ - public String - getErrorString() { - checkDone(); - if (error != null) - return error; - switch (result) { - case SUCCESSFUL: - return "successful"; - case UNRECOVERABLE: - return "unrecoverable error"; - case TRY_AGAIN: - return "try again"; - case HOST_NOT_FOUND: - return "host not found"; - case TYPE_NOT_FOUND: - return "type not found"; - } - throw new IllegalStateException("unknown result"); - } - -} diff --git a/src/main/java/org/xbill/DNS/MBRecord.java b/src/main/java/org/xbill/DNS/MBRecord.java deleted file mode 100644 index 6b65edf48..000000000 --- a/src/main/java/org/xbill/DNS/MBRecord.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Mailbox Record - specifies a host containing a mailbox. - * - * @author Brian Wellington - */ - -public class MBRecord extends SingleNameBase { - -private static final long serialVersionUID = 532349543479150419L; - -MBRecord() {} - -Record -getObject() { - return new MBRecord(); -} - -/** - * Creates a new MB Record with the given data - * @param mailbox The host containing the mailbox for the domain. - */ -public -MBRecord(Name name, int dclass, long ttl, Name mailbox) { - super(name, Type.MB, dclass, ttl, mailbox, "mailbox"); -} - -/** Gets the mailbox for the domain */ -public Name -getMailbox() { - return getSingleName(); -} - -public Name -getAdditionalName() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/MDRecord.java b/src/main/java/org/xbill/DNS/MDRecord.java deleted file mode 100644 index dbf51af97..000000000 --- a/src/main/java/org/xbill/DNS/MDRecord.java +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Mail Destination Record - specifies a mail agent which delivers mail - * for a domain (obsolete) - * - * @author Brian Wellington - */ - -public class MDRecord extends SingleNameBase { - -private static final long serialVersionUID = 5268878603762942202L; - -MDRecord() {} - -Record -getObject() { - return new MDRecord(); -} - -/** - * Creates a new MD Record with the given data - * @param mailAgent The mail agent that delivers mail for the domain. - */ -public -MDRecord(Name name, int dclass, long ttl, Name mailAgent) { - super(name, Type.MD, dclass, ttl, mailAgent, "mail agent"); -} - -/** Gets the mail agent for the domain */ -public Name -getMailAgent() { - return getSingleName(); -} - -public Name -getAdditionalName() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/MFRecord.java b/src/main/java/org/xbill/DNS/MFRecord.java deleted file mode 100644 index ff293d7ef..000000000 --- a/src/main/java/org/xbill/DNS/MFRecord.java +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Mail Forwarder Record - specifies a mail agent which forwards mail - * for a domain (obsolete) - * - * @author Brian Wellington - */ - -public class MFRecord extends SingleNameBase { - -private static final long serialVersionUID = -6670449036843028169L; - -MFRecord() {} - -Record -getObject() { - return new MFRecord(); -} - -/** - * Creates a new MF Record with the given data - * @param mailAgent The mail agent that forwards mail for the domain. - */ -public -MFRecord(Name name, int dclass, long ttl, Name mailAgent) { - super(name, Type.MF, dclass, ttl, mailAgent, "mail agent"); -} - -/** Gets the mail agent for the domain */ -public Name -getMailAgent() { - return getSingleName(); -} - -public Name -getAdditionalName() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/MGRecord.java b/src/main/java/org/xbill/DNS/MGRecord.java deleted file mode 100644 index 5752f4986..000000000 --- a/src/main/java/org/xbill/DNS/MGRecord.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Mail Group Record - specifies a mailbox which is a member of a mail group. - * - * @author Brian Wellington - */ - -public class MGRecord extends SingleNameBase { - -private static final long serialVersionUID = -3980055550863644582L; - -MGRecord() {} - -Record -getObject() { - return new MGRecord(); -} - -/** - * Creates a new MG Record with the given data - * @param mailbox The mailbox that is a member of the group specified by the - * domain. - */ -public -MGRecord(Name name, int dclass, long ttl, Name mailbox) { - super(name, Type.MG, dclass, ttl, mailbox, "mailbox"); -} - -/** Gets the mailbox in the mail group specified by the domain */ -public Name -getMailbox() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/MINFORecord.java b/src/main/java/org/xbill/DNS/MINFORecord.java deleted file mode 100644 index bc67e5206..000000000 --- a/src/main/java/org/xbill/DNS/MINFORecord.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Mailbox information Record - lists the address responsible for a mailing - * list/mailbox and the address to receive error messages relating to the - * mailing list/mailbox. - * - * @author Brian Wellington - */ - -public class MINFORecord extends Record { - -private static final long serialVersionUID = -3962147172340353796L; - -private Name responsibleAddress; -private Name errorAddress; - -MINFORecord() {} - -Record -getObject() { - return new MINFORecord(); -} - -/** - * Creates an MINFO Record from the given data - * @param responsibleAddress The address responsible for the - * mailing list/mailbox. - * @param errorAddress The address to receive error messages relating to the - * mailing list/mailbox. - */ -public -MINFORecord(Name name, int dclass, long ttl, - Name responsibleAddress, Name errorAddress) -{ - super(name, Type.MINFO, dclass, ttl); - - this.responsibleAddress = checkName("responsibleAddress", - responsibleAddress); - this.errorAddress = checkName("errorAddress", errorAddress); -} - -void -rrFromWire(DNSInput in) throws IOException { - responsibleAddress = new Name(in); - errorAddress = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - responsibleAddress = st.getName(origin); - errorAddress = st.getName(origin); -} - -/** Converts the MINFO Record to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(responsibleAddress); - sb.append(" "); - sb.append(errorAddress); - return sb.toString(); -} - -/** Gets the address responsible for the mailing list/mailbox. */ -public Name -getResponsibleAddress() { - return responsibleAddress; -} - -/** - * Gets the address to receive error messages relating to the mailing - * list/mailbox. - */ -public Name -getErrorAddress() { - return errorAddress; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - responsibleAddress.toWire(out, null, canonical); - errorAddress.toWire(out, null, canonical); -} - -} diff --git a/src/main/java/org/xbill/DNS/MRRecord.java b/src/main/java/org/xbill/DNS/MRRecord.java deleted file mode 100644 index a7ff4fc9b..000000000 --- a/src/main/java/org/xbill/DNS/MRRecord.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Mailbox Rename Record - specifies a rename of a mailbox. - * - * @author Brian Wellington - */ - -public class MRRecord extends SingleNameBase { - -private static final long serialVersionUID = -5617939094209927533L; - -MRRecord() {} - -Record -getObject() { - return new MRRecord(); -} - -/** - * Creates a new MR Record with the given data - * @param newName The new name of the mailbox specified by the domain. - * domain. - */ -public -MRRecord(Name name, int dclass, long ttl, Name newName) { - super(name, Type.MR, dclass, ttl, newName, "new name"); -} - -/** Gets the new name of the mailbox specified by the domain */ -public Name -getNewName() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/MXRecord.java b/src/main/java/org/xbill/DNS/MXRecord.java deleted file mode 100644 index 111977d1a..000000000 --- a/src/main/java/org/xbill/DNS/MXRecord.java +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Mail Exchange - specifies where mail to a domain is sent - * - * @author Brian Wellington - */ - -public class MXRecord extends U16NameBase { - -private static final long serialVersionUID = 2914841027584208546L; - -MXRecord() {} - -Record -getObject() { - return new MXRecord(); -} - -/** - * Creates an MX Record from the given data - * @param priority The priority of this MX. Records with lower priority - * are preferred. - * @param target The host that mail is sent to - */ -public -MXRecord(Name name, int dclass, long ttl, int priority, Name target) { - super(name, Type.MX, dclass, ttl, priority, "priority", - target, "target"); -} - -/** Returns the target of the MX record */ -public Name -getTarget() { - return getNameField(); -} - -/** Returns the priority of this MX record */ -public int -getPriority() { - return getU16Field(); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(u16Field); - nameField.toWire(out, c, canonical); -} - -public Name -getAdditionalName() { - return getNameField(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Master.java b/src/main/java/org/xbill/DNS/Master.java deleted file mode 100644 index f882d9119..000000000 --- a/src/main/java/org/xbill/DNS/Master.java +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * A DNS master file parser. This incrementally parses the file, returning - * one record at a time. When directives are seen, they are added to the - * state and used when parsing future records. - * - * @author Brian Wellington - */ - -public class Master { - -private Name origin; -private File file; -private Record last = null; -private long defaultTTL; -private Master included = null; -private Tokenizer st; -private int currentType; -private int currentDClass; -private long currentTTL; -private boolean needSOATTL; - -private Generator generator; -private List generators; -private boolean noExpandGenerate; - -Master(File file, Name origin, long initialTTL) throws IOException { - if (origin != null && !origin.isAbsolute()) { - throw new RelativeNameException(origin); - } - this.file = file; - st = new Tokenizer(file); - this.origin = origin; - defaultTTL = initialTTL; -} - -/** - * Initializes the master file reader and opens the specified master file. - * @param filename The master file. - * @param origin The initial origin to append to relative names. - * @param ttl The initial default TTL. - * @throws IOException The master file could not be opened. - */ -public -Master(String filename, Name origin, long ttl) throws IOException { - this(new File(filename), origin, ttl); -} - -/** - * Initializes the master file reader and opens the specified master file. - * @param filename The master file. - * @param origin The initial origin to append to relative names. - * @throws IOException The master file could not be opened. - */ -public -Master(String filename, Name origin) throws IOException { - this(new File(filename), origin, -1); -} - -/** - * Initializes the master file reader and opens the specified master file. - * @param filename The master file. - * @throws IOException The master file could not be opened. - */ -public -Master(String filename) throws IOException { - this(new File(filename), null, -1); -} - -/** - * Initializes the master file reader. - * @param in The input stream containing a master file. - * @param origin The initial origin to append to relative names. - * @param ttl The initial default TTL. - */ -public -Master(InputStream in, Name origin, long ttl) { - if (origin != null && !origin.isAbsolute()) { - throw new RelativeNameException(origin); - } - st = new Tokenizer(in); - this.origin = origin; - defaultTTL = ttl; -} - -/** - * Initializes the master file reader. - * @param in The input stream containing a master file. - * @param origin The initial origin to append to relative names. - */ -public -Master(InputStream in, Name origin) { - this(in, origin, -1); -} - -/** - * Initializes the master file reader. - * @param in The input stream containing a master file. - */ -public -Master(InputStream in) { - this(in, null, -1); -} - -private Name -parseName(String s, Name origin) throws TextParseException { - try { - return Name.fromString(s, origin); - } - catch (TextParseException e) { - throw st.exception(e.getMessage()); - } -} - -private void -parseTTLClassAndType() throws IOException { - String s; - boolean seen_class = false; - - - // This is a bit messy, since any of the following are legal: - // class ttl type - // ttl class type - // class type - // ttl type - // type - seen_class = false; - s = st.getString(); - if ((currentDClass = DClass.value(s)) >= 0) { - s = st.getString(); - seen_class = true; - } - - currentTTL = -1; - try { - currentTTL = TTL.parseTTL(s); - s = st.getString(); - } - catch (NumberFormatException e) { - if (defaultTTL >= 0) - currentTTL = defaultTTL; - else if (last != null) - currentTTL = last.getTTL(); - } - - if (!seen_class) { - if ((currentDClass = DClass.value(s)) >= 0) { - s = st.getString(); - } else { - currentDClass = DClass.IN; - } - } - - if ((currentType = Type.value(s)) < 0) - throw st.exception("Invalid type '" + s + "'"); - - // BIND allows a missing TTL for the initial SOA record, and uses - // the SOA minimum value. If the SOA is not the first record, - // this is an error. - if (currentTTL < 0) { - if (currentType != Type.SOA) - throw st.exception("missing TTL"); - needSOATTL = true; - currentTTL = 0; - } -} - -private long -parseUInt32(String s) { - if (!Character.isDigit(s.charAt(0))) - return -1; - try { - long l = Long.parseLong(s); - if (l < 0 || l > 0xFFFFFFFFL) - return -1; - return l; - } - catch (NumberFormatException e) { - return -1; - } -} - -private void -startGenerate() throws IOException { - String s; - int n; - - // The first field is of the form start-end[/step] - // Regexes would be useful here. - s = st.getIdentifier(); - n = s.indexOf("-"); - if (n < 0) - throw st.exception("Invalid $GENERATE range specifier: " + s); - String startstr = s.substring(0, n); - String endstr = s.substring(n + 1); - String stepstr = null; - n = endstr.indexOf("/"); - if (n >= 0) { - stepstr = endstr.substring(n + 1); - endstr = endstr.substring(0, n); - } - long start = parseUInt32(startstr); - long end = parseUInt32(endstr); - long step; - if (stepstr != null) - step = parseUInt32(stepstr); - else - step = 1; - if (start < 0 || end < 0 || start > end || step <= 0) - throw st.exception("Invalid $GENERATE range specifier: " + s); - - // The next field is the name specification. - String nameSpec = st.getIdentifier(); - - // Then the ttl/class/type, in the same form as a normal record. - // Only some types are supported. - parseTTLClassAndType(); - if (!Generator.supportedType(currentType)) - throw st.exception("$GENERATE does not support " + - Type.string(currentType) + " records"); - - // Next comes the rdata specification. - String rdataSpec = st.getIdentifier(); - - // That should be the end. However, we don't want to move past the - // line yet, so put back the EOL after reading it. - st.getEOL(); - st.unget(); - - generator = new Generator(start, end, step, nameSpec, - currentType, currentDClass, currentTTL, - rdataSpec, origin); - if (generators == null) - generators = new ArrayList(1); - generators.add(generator); -} - -private void -endGenerate() throws IOException { - // Read the EOL that we put back before. - st.getEOL(); - - generator = null; -} - -private Record -nextGenerated() throws IOException { - try { - return generator.nextRecord(); - } - catch (Tokenizer.TokenizerException e) { - throw st.exception("Parsing $GENERATE: " + e.getBaseMessage()); - } - catch (TextParseException e) { - throw st.exception("Parsing $GENERATE: " + e.getMessage()); - } -} - -/** - * Returns the next record in the master file. This will process any - * directives before the next record. - * @return The next record. - * @throws IOException The master file could not be read, or was syntactically - * invalid. - */ -public Record -_nextRecord() throws IOException { - Tokenizer.Token token; - String s; - - if (included != null) { - Record rec = included.nextRecord(); - if (rec != null) - return rec; - included = null; - } - if (generator != null) { - Record rec = nextGenerated(); - if (rec != null) - return rec; - endGenerate(); - } - while (true) { - Name name; - - token = st.get(true, false); - if (token.type == Tokenizer.WHITESPACE) { - Tokenizer.Token next = st.get(); - if (next.type == Tokenizer.EOL) - continue; - else if (next.type == Tokenizer.EOF) - return null; - else - st.unget(); - if (last == null) - throw st.exception("no owner"); - name = last.getName(); - } - else if (token.type == Tokenizer.EOL) - continue; - else if (token.type == Tokenizer.EOF) - return null; - else if (((String) token.value).charAt(0) == '$') { - s = token.value; - - if (s.equalsIgnoreCase("$ORIGIN")) { - origin = st.getName(Name.root); - st.getEOL(); - continue; - } else if (s.equalsIgnoreCase("$TTL")) { - defaultTTL = st.getTTL(); - st.getEOL(); - continue; - } else if (s.equalsIgnoreCase("$INCLUDE")) { - String filename = st.getString(); - File newfile; - if (file != null) { - String parent = file.getParent(); - newfile = new File(parent, filename); - } else { - newfile = new File(filename); - } - Name incorigin = origin; - token = st.get(); - if (token.isString()) { - incorigin = parseName(token.value, - Name.root); - st.getEOL(); - } - included = new Master(newfile, incorigin, - defaultTTL); - /* - * If we continued, we wouldn't be looking in - * the new file. Recursing works better. - */ - return nextRecord(); - } else if (s.equalsIgnoreCase("$GENERATE")) { - if (generator != null) - throw new IllegalStateException - ("cannot nest $GENERATE"); - startGenerate(); - if (noExpandGenerate) { - endGenerate(); - continue; - } - return nextGenerated(); - } else { - throw st.exception("Invalid directive: " + s); - } - } else { - s = token.value; - name = parseName(s, origin); - if (last != null && name.equals(last.getName())) { - name = last.getName(); - } - } - - parseTTLClassAndType(); - last = Record.fromString(name, currentType, currentDClass, - currentTTL, st, origin); - if (needSOATTL) { - long ttl = ((SOARecord)last).getMinimum(); - last.setTTL(ttl); - defaultTTL = ttl; - needSOATTL = false; - } - return last; - } -} - -/** - * Returns the next record in the master file. This will process any - * directives before the next record. - * @return The next record. - * @throws IOException The master file could not be read, or was syntactically - * invalid. - */ -public Record -nextRecord() throws IOException { - Record rec = null; - try { - rec = _nextRecord(); - } - finally { - if (rec == null) { - st.close(); - } - } - return rec; -} - -/** - * Specifies whether $GENERATE statements should be expanded. Whether - * expanded or not, the specifications for generated records are available - * by calling {@link #generators}. This must be called before a $GENERATE - * statement is seen during iteration to have an effect. - */ -public void -expandGenerate(boolean wantExpand) { - noExpandGenerate = !wantExpand; -} - -/** - * Returns an iterator over the generators specified in the master file; that - * is, the parsed contents of $GENERATE statements. - * @see Generator - */ -public Iterator -generators() { - if (generators != null) - return Collections.unmodifiableList(generators).iterator(); - else - return Collections.EMPTY_LIST.iterator(); -} - -protected void -finalize() { - st.close(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Message.java b/src/main/java/org/xbill/DNS/Message.java deleted file mode 100644 index 78a2e2802..000000000 --- a/src/main/java/org/xbill/DNS/Message.java +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.*; - -/** - * A DNS Message. A message is the basic unit of communication between - * the client and server of a DNS operation. A message consists of a Header - * and 4 message sections. - * @see Resolver - * @see Header - * @see Section - * - * @author Brian Wellington - */ - -public class Message implements Cloneable { - -/** The maximum length of a message in wire format. */ -public static final int MAXLENGTH = 65535; - -private Header header; -private List [] sections; -private int size; -private TSIG tsigkey; -private TSIGRecord querytsig; -private int tsigerror; - -int tsigstart; -int tsigState; - -/* The message was not signed */ -static final int TSIG_UNSIGNED = 0; - -/* The message was signed and verification succeeded */ -static final int TSIG_VERIFIED = 1; - -/* The message was an unsigned message in multiple-message response */ -static final int TSIG_INTERMEDIATE = 2; - -/* The message was signed and no verification was attempted. */ -static final int TSIG_SIGNED = 3; - -/* - * The message was signed and verification failed, or was not signed - * when it should have been. - */ -static final int TSIG_FAILED = 4; - -private static Record [] emptyRecordArray = new Record[0]; -private static RRset [] emptyRRsetArray = new RRset[0]; - -private -Message(Header header) { - sections = new List[4]; - this.header = header; -} - -/** Creates a new Message with the specified Message ID */ -public -Message(int id) { - this(new Header(id)); -} - -/** Creates a new Message with a random Message ID */ -public -Message() { - this(new Header()); -} - -/** - * Creates a new Message with a random Message ID suitable for sending as a - * query. - * @param r A record containing the question - */ -public static Message -newQuery(Record r) { - Message m = new Message(); - m.header.setOpcode(Opcode.QUERY); - m.header.setFlag(Flags.RD); - m.addRecord(r, Section.QUESTION); - return m; -} - -/** - * Creates a new Message to contain a dynamic update. A random Message ID - * and the zone are filled in. - * @param zone The zone to be updated - */ -public static Message -newUpdate(Name zone) { - return new Update(zone); -} - -Message(DNSInput in) throws IOException { - this(new Header(in)); - boolean isUpdate = (header.getOpcode() == Opcode.UPDATE); - boolean truncated = header.getFlag(Flags.TC); - try { - for (int i = 0; i < 4; i++) { - int count = header.getCount(i); - if (count > 0) - sections[i] = new ArrayList(count); - for (int j = 0; j < count; j++) { - int pos = in.current(); - Record rec = Record.fromWire(in, i, isUpdate); - sections[i].add(rec); - if (rec.getType() == Type.TSIG) - tsigstart = pos; - } - } - } catch (WireParseException e) { - if (!truncated) - throw e; - } - size = in.current(); -} - -/** - * Creates a new Message from its DNS wire format representation - * @param b A byte array containing the DNS Message. - */ -public -Message(byte [] b) throws IOException { - this(new DNSInput(b)); -} - -/** - * Replaces the Header with a new one. - * @see Header - */ -public void -setHeader(Header h) { - header = h; -} - -/** - * Retrieves the Header. - * @see Header - */ -public Header -getHeader() { - return header; -} - -/** - * Adds a record to a section of the Message, and adjusts the header. - * @see Record - * @see Section - */ -public void -addRecord(Record r, int section) { - if (sections[section] == null) - sections[section] = new LinkedList(); - header.incCount(section); - sections[section].add(r); -} - -/** - * Removes a record from a section of the Message, and adjusts the header. - * @see Record - * @see Section - */ -public boolean -removeRecord(Record r, int section) { - if (sections[section] != null && sections[section].remove(r)) { - header.decCount(section); - return true; - } - else - return false; -} - -/** - * Removes all records from a section of the Message, and adjusts the header. - * @see Record - * @see Section - */ -public void -removeAllRecords(int section) { - sections[section] = null; - header.setCount(section, 0); -} - -/** - * Determines if the given record is already present in the given section. - * @see Record - * @see Section - */ -public boolean -findRecord(Record r, int section) { - return (sections[section] != null && sections[section].contains(r)); -} - -/** - * Determines if the given record is already present in any section. - * @see Record - * @see Section - */ -public boolean -findRecord(Record r) { - for (int i = Section.ANSWER; i <= Section.ADDITIONAL; i++) - if (sections[i] != null && sections[i].contains(r)) - return true; - return false; -} - -/** - * Determines if an RRset with the given name and type is already - * present in the given section. - * @see RRset - * @see Section - */ -public boolean -findRRset(Name name, int type, int section) { - if (sections[section] == null) - return false; - for (int i = 0; i < sections[section].size(); i++) { - Record r = (Record) sections[section].get(i); - if (r.getType() == type && name.equals(r.getName())) - return true; - } - return false; -} - -/** - * Determines if an RRset with the given name and type is already - * present in any section. - * @see RRset - * @see Section - */ -public boolean -findRRset(Name name, int type) { - return (findRRset(name, type, Section.ANSWER) || - findRRset(name, type, Section.AUTHORITY) || - findRRset(name, type, Section.ADDITIONAL)); -} - -/** - * Returns the first record in the QUESTION section. - * @see Record - * @see Section - */ -public Record -getQuestion() { - List l = sections[Section.QUESTION]; - if (l == null || l.size() == 0) - return null; - return (Record) l.get(0); -} - -/** - * Returns the TSIG record from the ADDITIONAL section, if one is present. - * @see TSIGRecord - * @see TSIG - * @see Section - */ -public TSIGRecord -getTSIG() { - int count = header.getCount(Section.ADDITIONAL); - if (count == 0) - return null; - List l = sections[Section.ADDITIONAL]; - Record rec = (Record) l.get(count - 1); - if (rec.type != Type.TSIG) - return null; - return (TSIGRecord) rec; -} - -/** - * Was this message signed by a TSIG? - * @see TSIG - */ -public boolean -isSigned() { - return (tsigState == TSIG_SIGNED || - tsigState == TSIG_VERIFIED || - tsigState == TSIG_FAILED); -} - -/** - * If this message was signed by a TSIG, was the TSIG verified? - * @see TSIG - */ -public boolean -isVerified() { - return (tsigState == TSIG_VERIFIED); -} - -/** - * Returns the OPT record from the ADDITIONAL section, if one is present. - * @see OPTRecord - * @see Section - */ -public OPTRecord -getOPT() { - Record [] additional = getSectionArray(Section.ADDITIONAL); - for (int i = 0; i < additional.length; i++) - if (additional[i] instanceof OPTRecord) - return (OPTRecord) additional[i]; - return null; -} - -/** - * Returns the message's rcode (error code). This incorporates the EDNS - * extended rcode. - */ -public int -getRcode() { - int rcode = header.getRcode(); - OPTRecord opt = getOPT(); - if (opt != null) - rcode += (opt.getExtendedRcode() << 4); - return rcode; -} - -/** - * Returns an array containing all records in the given section, or an - * empty array if the section is empty. - * @see Record - * @see Section - */ -public Record [] -getSectionArray(int section) { - if (sections[section] == null) - return emptyRecordArray; - List l = sections[section]; - return (Record []) l.toArray(new Record[l.size()]); -} - -private static boolean -sameSet(Record r1, Record r2) { - return (r1.getRRsetType() == r2.getRRsetType() && - r1.getDClass() == r2.getDClass() && - r1.getName().equals(r2.getName())); -} - -/** - * Returns an array containing all records in the given section grouped into - * RRsets. - * @see RRset - * @see Section - */ -public RRset [] -getSectionRRsets(int section) { - if (sections[section] == null) - return emptyRRsetArray; - List sets = new LinkedList(); - Record [] recs = getSectionArray(section); - Set hash = new HashSet(); - for (int i = 0; i < recs.length; i++) { - Name name = recs[i].getName(); - boolean newset = true; - if (hash.contains(name)) { - for (int j = sets.size() - 1; j >= 0; j--) { - RRset set = (RRset) sets.get(j); - if (set.getType() == recs[i].getRRsetType() && - set.getDClass() == recs[i].getDClass() && - set.getName().equals(name)) - { - set.addRR(recs[i]); - newset = false; - break; - } - } - } - if (newset) { - RRset set = new RRset(recs[i]); - sets.add(set); - hash.add(name); - } - } - return (RRset []) sets.toArray(new RRset[sets.size()]); -} - -void -toWire(DNSOutput out) { - header.toWire(out); - Compression c = new Compression(); - for (int i = 0; i < 4; i++) { - if (sections[i] == null) - continue; - for (int j = 0; j < sections[i].size(); j++) { - Record rec = (Record)sections[i].get(j); - rec.toWire(out, i, c); - } - } -} - -/* Returns the number of records not successfully rendered. */ -private int -sectionToWire(DNSOutput out, int section, Compression c, - int maxLength) -{ - int n = sections[section].size(); - int pos = out.current(); - int rendered = 0; - Record lastrec = null; - - for (int i = 0; i < n; i++) { - Record rec = (Record)sections[section].get(i); - if (lastrec != null && !sameSet(rec, lastrec)) { - pos = out.current(); - rendered = i; - } - lastrec = rec; - rec.toWire(out, section, c); - if (out.current() > maxLength) { - out.jump(pos); - return n - rendered; - } - } - return 0; -} - -/* Returns true if the message could be rendered. */ -private boolean -toWire(DNSOutput out, int maxLength) { - if (maxLength < Header.LENGTH) - return false; - - Header newheader = null; - - int tempMaxLength = maxLength; - if (tsigkey != null) - tempMaxLength -= tsigkey.recordLength(); - - int startpos = out.current(); - header.toWire(out); - Compression c = new Compression(); - for (int i = 0; i < 4; i++) { - int skipped; - if (sections[i] == null) - continue; - skipped = sectionToWire(out, i, c, tempMaxLength); - if (skipped != 0) { - if (i != Section.ADDITIONAL) { - if (newheader == null) - newheader = (Header) header.clone(); - newheader.setFlag(Flags.TC); - int count = newheader.getCount(i); - newheader.setCount(i, count - skipped); - for (int j = i + 1; j < 4; j++) - newheader.setCount(j, 0); - - out.save(); - out.jump(startpos); - newheader.toWire(out); - out.restore(); - } - break; - } - } - - if (tsigkey != null) { - TSIGRecord tsigrec = tsigkey.generate(this, out.toByteArray(), - tsigerror, querytsig); - - if (newheader == null) - newheader = (Header) header.clone(); - tsigrec.toWire(out, Section.ADDITIONAL, c); - newheader.incCount(Section.ADDITIONAL); - - out.save(); - out.jump(startpos); - newheader.toWire(out); - out.restore(); - } - - return true; -} - -/** - * Returns an array containing the wire format representation of the Message. - */ -public byte [] -toWire() { - DNSOutput out = new DNSOutput(); - toWire(out); - size = out.current(); - return out.toByteArray(); -} - -/** - * Returns an array containing the wire format representation of the Message - * with the specified maximum length. This will generate a truncated - * message (with the TC bit) if the message doesn't fit, and will also - * sign the message with the TSIG key set by a call to setTSIG(). This - * method may return null if the message could not be rendered at all; this - * could happen if maxLength is smaller than a DNS header, for example. - * @param maxLength The maximum length of the message. - * @return The wire format of the message, or null if the message could not be - * rendered into the specified length. - * @see Flags - * @see TSIG - */ -public byte [] -toWire(int maxLength) { - DNSOutput out = new DNSOutput(); - toWire(out, maxLength); - size = out.current(); - return out.toByteArray(); -} - -/** - * Sets the TSIG key and other necessary information to sign a message. - * @param key The TSIG key. - * @param error The value of the TSIG error field. - * @param querytsig If this is a response, the TSIG from the request. - */ -public void -setTSIG(TSIG key, int error, TSIGRecord querytsig) { - this.tsigkey = key; - this.tsigerror = error; - this.querytsig = querytsig; -} - -/** - * Returns the size of the message. Only valid if the message has been - * converted to or from wire format. - */ -public int -numBytes() { - return size; -} - -/** - * Converts the given section of the Message to a String. - * @see Section - */ -public String -sectionToString(int i) { - if (i > 3) - return null; - - StringBuffer sb = new StringBuffer(); - - Record [] records = getSectionArray(i); - for (int j = 0; j < records.length; j++) { - Record rec = records[j]; - if (i == Section.QUESTION) { - sb.append(";;\t" + rec.name); - sb.append(", type = " + Type.string(rec.type)); - sb.append(", class = " + DClass.string(rec.dclass)); - } - else - sb.append(rec); - sb.append("\n"); - } - return sb.toString(); -} - -/** - * Converts the Message to a String. - */ -public String -toString() { - StringBuffer sb = new StringBuffer(); - OPTRecord opt = getOPT(); - if (opt != null) - sb.append(header.toStringWithRcode(getRcode()) + "\n"); - else - sb.append(header + "\n"); - if (isSigned()) { - sb.append(";; TSIG "); - if (isVerified()) - sb.append("ok"); - else - sb.append("invalid"); - sb.append('\n'); - } - for (int i = 0; i < 4; i++) { - if (header.getOpcode() != Opcode.UPDATE) - sb.append(";; " + Section.longString(i) + ":\n"); - else - sb.append(";; " + Section.updString(i) + ":\n"); - sb.append(sectionToString(i) + "\n"); - } - sb.append(";; Message size: " + numBytes() + " bytes"); - return sb.toString(); -} - -/** - * Creates a copy of this Message. This is done by the Resolver before adding - * TSIG and OPT records, for example. - * @see Resolver - * @see TSIGRecord - * @see OPTRecord - */ -public Object -clone() { - Message m = new Message(); - for (int i = 0; i < sections.length; i++) { - if (sections[i] != null) - m.sections[i] = new LinkedList(sections[i]); - } - m.header = (Header) header.clone(); - m.size = size; - return m; -} - -} diff --git a/src/main/java/org/xbill/DNS/Mnemonic.java b/src/main/java/org/xbill/DNS/Mnemonic.java deleted file mode 100644 index dd60f62bb..000000000 --- a/src/main/java/org/xbill/DNS/Mnemonic.java +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.HashMap; - -/** - * A utility class for converting between numeric codes and mnemonics - * for those codes. Mnemonics are case insensitive. - * - * @author Brian Wellington - */ - -class Mnemonic { - -private static Integer cachedInts[] = new Integer[64]; - -static { - for (int i = 0; i < cachedInts.length; i++) { - cachedInts[i] = new Integer(i); - } -} - -/* Strings are case-sensitive. */ -static final int CASE_SENSITIVE = 1; - -/* Strings will be stored/searched for in uppercase. */ -static final int CASE_UPPER = 2; - -/* Strings will be stored/searched for in lowercase. */ -static final int CASE_LOWER = 3; - -private HashMap strings; -private HashMap values; -private String description; -private int wordcase; -private String prefix; -private int max; -private boolean numericok; - -/** - * Creates a new Mnemonic table. - * @param description A short description of the mnemonic to use when - * @param wordcase Whether to convert strings into uppercase, lowercase, - * or leave them unchanged. - * throwing exceptions. - */ -public -Mnemonic(String description, int wordcase) { - this.description = description; - this.wordcase = wordcase; - strings = new HashMap(); - values = new HashMap(); - max = Integer.MAX_VALUE; -} - -/** Sets the maximum numeric value */ -public void -setMaximum(int max) { - this.max = max; -} - -/** - * Sets the prefix to use when converting to and from values that don't - * have mnemonics. - */ -public void -setPrefix(String prefix) { - this.prefix = sanitize(prefix); -} - -/** - * Sets whether numeric values stored in strings are acceptable. - */ -public void -setNumericAllowed(boolean numeric) { - this.numericok = numeric; -} - -/** - * Converts an int into a possibly cached Integer. - */ -public static Integer -toInteger(int val) { - if (val >= 0 && val < cachedInts.length) - return (cachedInts[val]); - return new Integer(val); -} - -/** - * Checks that a numeric value is within the range [0..max] - */ -public void -check(int val) { - if (val < 0 || val > max) { - throw new IllegalArgumentException(description + " " + val + - "is out of range"); - } -} - -/* Converts a String to the correct case. */ -private String -sanitize(String str) { - if (wordcase == CASE_UPPER) - return str.toUpperCase(); - else if (wordcase == CASE_LOWER) - return str.toLowerCase(); - return str; -} - -private int -parseNumeric(String s) { - try { - int val = Integer.parseInt(s); - if (val >= 0 && val <= max) - return val; - } - catch (NumberFormatException e) { - } - return -1; -} - -/** - * Defines the text representation of a numeric value. - * @param val The numeric value - * @param string The text string - */ -public void -add(int val, String str) { - check(val); - Integer value = toInteger(val); - str = sanitize(str); - strings.put(str, value); - values.put(value, str); -} - -/** - * Defines an additional text representation of a numeric value. This will - * be used by getValue(), but not getText(). - * @param val The numeric value - * @param string The text string - */ -public void -addAlias(int val, String str) { - check(val); - Integer value = toInteger(val); - str = sanitize(str); - strings.put(str, value); -} - -/** - * Copies all mnemonics from one table into another. - * @param val The numeric value - * @param string The text string - * @throws IllegalArgumentException The wordcases of the Mnemonics do not - * match. - */ -public void -addAll(Mnemonic source) { - if (wordcase != source.wordcase) - throw new IllegalArgumentException(source.description + - ": wordcases do not match"); - strings.putAll(source.strings); - values.putAll(source.values); -} - -/** - * Gets the text mnemonic corresponding to a numeric value. - * @param val The numeric value - * @return The corresponding text mnemonic. - */ -public String -getText(int val) { - check(val); - String str = (String) values.get(toInteger(val)); - if (str != null) - return str; - str = Integer.toString(val); - if (prefix != null) - return prefix + str; - return str; -} - -/** - * Gets the numeric value corresponding to a text mnemonic. - * @param str The text mnemonic - * @return The corresponding numeric value, or -1 if there is none - */ -public int -getValue(String str) { - str = sanitize(str); - Integer value = (Integer) strings.get(str); - if (value != null) { - return value.intValue(); - } - if (prefix != null) { - if (str.startsWith(prefix)) { - int val = parseNumeric(str.substring(prefix.length())); - if (val >= 0) { - return val; - } - } - } - if (numericok) { - return parseNumeric(str); - } - return -1; -} - -} diff --git a/src/main/java/org/xbill/DNS/NAPTRRecord.java b/src/main/java/org/xbill/DNS/NAPTRRecord.java deleted file mode 100644 index fcd198c59..000000000 --- a/src/main/java/org/xbill/DNS/NAPTRRecord.java +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2000-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Name Authority Pointer Record - specifies rewrite rule, that when applied - * to an existing string will produce a new domain. - * - * @author Chuck Santos - */ - -public class NAPTRRecord extends Record { - -private static final long serialVersionUID = 5191232392044947002L; - -private int order, preference; -private byte [] flags, service, regexp; -private Name replacement; - -NAPTRRecord() {} - -Record -getObject() { - return new NAPTRRecord(); -} - -/** - * Creates an NAPTR Record from the given data - * @param order The order of this NAPTR. Records with lower order are - * preferred. - * @param preference The preference, used to select between records at the - * same order. - * @param flags The control aspects of the NAPTRRecord. - * @param service The service or protocol available down the rewrite path. - * @param regexp The regular/substitution expression. - * @param replacement The domain-name to query for the next DNS resource - * record, depending on the value of the flags field. - * @throws IllegalArgumentException One of the strings has invalid escapes - */ -public -NAPTRRecord(Name name, int dclass, long ttl, int order, int preference, - String flags, String service, String regexp, Name replacement) -{ - super(name, Type.NAPTR, dclass, ttl); - this.order = checkU16("order", order); - this.preference = checkU16("preference", preference); - try { - this.flags = byteArrayFromString(flags); - this.service = byteArrayFromString(service); - this.regexp = byteArrayFromString(regexp); - } - catch (TextParseException e) { - throw new IllegalArgumentException(e.getMessage()); - } - this.replacement = checkName("replacement", replacement); -} - -void -rrFromWire(DNSInput in) throws IOException { - order = in.readU16(); - preference = in.readU16(); - flags = in.readCountedString(); - service = in.readCountedString(); - regexp = in.readCountedString(); - replacement = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - order = st.getUInt16(); - preference = st.getUInt16(); - try { - flags = byteArrayFromString(st.getString()); - service = byteArrayFromString(st.getString()); - regexp = byteArrayFromString(st.getString()); - } - catch (TextParseException e) { - throw st.exception(e.getMessage()); - } - replacement = st.getName(origin); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(order); - sb.append(" "); - sb.append(preference); - sb.append(" "); - sb.append(byteArrayToString(flags, true)); - sb.append(" "); - sb.append(byteArrayToString(service, true)); - sb.append(" "); - sb.append(byteArrayToString(regexp, true)); - sb.append(" "); - sb.append(replacement); - return sb.toString(); -} - -/** Returns the order */ -public int -getOrder() { - return order; -} - -/** Returns the preference */ -public int -getPreference() { - return preference; -} - -/** Returns flags */ -public String -getFlags() { - return byteArrayToString(flags, false); -} - -/** Returns service */ -public String -getService() { - return byteArrayToString(service, false); -} - -/** Returns regexp */ -public String -getRegexp() { - return byteArrayToString(regexp, false); -} - -/** Returns the replacement domain-name */ -public Name -getReplacement() { - return replacement; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(order); - out.writeU16(preference); - out.writeCountedString(flags); - out.writeCountedString(service); - out.writeCountedString(regexp); - replacement.toWire(out, null, canonical); -} - -public Name -getAdditionalName() { - return replacement; -} - -} diff --git a/src/main/java/org/xbill/DNS/NSAPRecord.java b/src/main/java/org/xbill/DNS/NSAPRecord.java deleted file mode 100644 index 54b61d43e..000000000 --- a/src/main/java/org/xbill/DNS/NSAPRecord.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * NSAP Address Record. - * - * @author Brian Wellington - */ - -public class NSAPRecord extends Record { - -private static final long serialVersionUID = -1037209403185658593L; - -private byte [] address; - -NSAPRecord() {} - -Record -getObject() { - return new NSAPRecord(); -} - -private static final byte [] -checkAndConvertAddress(String address) { - if (!address.substring(0, 2).equalsIgnoreCase("0x")) { - return null; - } - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - boolean partial = false; - int current = 0; - for (int i = 2; i < address.length(); i++) { - char c = address.charAt(i); - if (c == '.') { - continue; - } - int value = Character.digit(c, 16); - if (value == -1) { - return null; - } - if (partial) { - current += value; - bytes.write(current); - partial = false; - } else { - current = value << 4; - partial = true; - } - - } - if (partial) { - return null; - } - return bytes.toByteArray(); -} - -/** - * Creates an NSAP Record from the given data - * @param address The NSAP address. - * @throws IllegalArgumentException The address is not a valid NSAP address. - */ -public -NSAPRecord(Name name, int dclass, long ttl, String address) { - super(name, Type.NSAP, dclass, ttl); - this.address = checkAndConvertAddress(address); - if (this.address == null) { - throw new IllegalArgumentException("invalid NSAP address " + - address); - } -} - -void -rrFromWire(DNSInput in) throws IOException { - address = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - String addr = st.getString(); - this.address = checkAndConvertAddress(addr); - if (this.address == null) - throw st.exception("invalid NSAP address " + addr); -} - -/** - * Returns the NSAP address. - */ -public String -getAddress() { - return byteArrayToString(address, false); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeByteArray(address); -} - -String -rrToString() { - return "0x" + base16.toString(address); -} - -} diff --git a/src/main/java/org/xbill/DNS/NSAP_PTRRecord.java b/src/main/java/org/xbill/DNS/NSAP_PTRRecord.java deleted file mode 100644 index ecc609f49..000000000 --- a/src/main/java/org/xbill/DNS/NSAP_PTRRecord.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * NSAP Pointer Record - maps a domain name representing an NSAP Address to - * a hostname. - * - * @author Brian Wellington - */ - -public class NSAP_PTRRecord extends SingleNameBase { - -private static final long serialVersionUID = 2386284746382064904L; - -NSAP_PTRRecord() {} - -Record -getObject() { - return new NSAP_PTRRecord(); -} - -/** - * Creates a new NSAP_PTR Record with the given data - * @param target The name of the host with this address - */ -public -NSAP_PTRRecord(Name name, int dclass, long ttl, Name target) { - super(name, Type.NSAP_PTR, dclass, ttl, target, "target"); -} - -/** Gets the target of the NSAP_PTR Record */ -public Name -getTarget() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/NSEC3PARAMRecord.java b/src/main/java/org/xbill/DNS/NSEC3PARAMRecord.java deleted file mode 100644 index af7ccef07..000000000 --- a/src/main/java/org/xbill/DNS/NSEC3PARAMRecord.java +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; - -/** - * Next SECure name 3 Parameters - this record contains the parameters (hash - * algorithm, salt, iterations) used for a valid, complete NSEC3 chain present - * in a zone. Zones signed using NSEC3 must include this record at the zone apex - * to inform authoritative servers that NSEC3 is being used with the given - * parameters. - * - * @author Brian Wellington - * @author David Blacka - */ - -public class NSEC3PARAMRecord extends Record { - -private static final long serialVersionUID = -8689038598776316533L; - -private int hashAlg; -private int flags; -private int iterations; -private byte salt[]; - -NSEC3PARAMRecord() {} - -Record getObject() { - return new NSEC3PARAMRecord(); -} - -/** - * Creates an NSEC3PARAM record from the given data. - * - * @param name The ownername of the NSEC3PARAM record (generally the zone name). - * @param dclass The class. - * @param ttl The TTL. - * @param hashAlg The hash algorithm. - * @param flags The value of the flags field. - * @param iterations The number of hash iterations. - * @param salt The salt to use (may be null). - */ -public NSEC3PARAMRecord(Name name, int dclass, long ttl, int hashAlg, - int flags, int iterations, byte [] salt) -{ - super(name, Type.NSEC3PARAM, dclass, ttl); - this.hashAlg = checkU8("hashAlg", hashAlg); - this.flags = checkU8("flags", flags); - this.iterations = checkU16("iterations", iterations); - - if (salt != null) { - if (salt.length > 255) - throw new IllegalArgumentException("Invalid salt " + - "length"); - if (salt.length > 0) { - this.salt = new byte[salt.length]; - System.arraycopy(salt, 0, this.salt, 0, salt.length); - } - } -} - -void -rrFromWire(DNSInput in) throws IOException { - hashAlg = in.readU8(); - flags = in.readU8(); - iterations = in.readU16(); - - int salt_length = in.readU8(); - if (salt_length > 0) - salt = in.readByteArray(salt_length); - else - salt = null; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU8(hashAlg); - out.writeU8(flags); - out.writeU16(iterations); - - if (salt != null) { - out.writeU8(salt.length); - out.writeByteArray(salt); - } else - out.writeU8(0); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException -{ - hashAlg = st.getUInt8(); - flags = st.getUInt8(); - iterations = st.getUInt16(); - - String s = st.getString(); - if (s.equals("-")) - salt = null; - else { - st.unget(); - salt = st.getHexString(); - if (salt.length > 255) - throw st.exception("salt value too long"); - } -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(hashAlg); - sb.append(' '); - sb.append(flags); - sb.append(' '); - sb.append(iterations); - sb.append(' '); - if (salt == null) - sb.append('-'); - else - sb.append(base16.toString(salt)); - - return sb.toString(); -} - -/** Returns the hash algorithm */ -public int -getHashAlgorithm() { - return hashAlg; -} - -/** Returns the flags */ -public int -getFlags() { - return flags; -} - -/** Returns the number of iterations */ -public int -getIterations() { - return iterations; -} - -/** Returns the salt */ -public byte [] -getSalt() -{ - return salt; -} - -/** - * Hashes a name with the parameters of this NSEC3PARAM record. - * @param name The name to hash - * @return The hashed version of the name - * @throws NoSuchAlgorithmException The hash algorithm is unknown. - */ -public byte [] -hashName(Name name) throws NoSuchAlgorithmException -{ - return NSEC3Record.hashName(name, hashAlg, iterations, salt); -} - -} diff --git a/src/main/java/org/xbill/DNS/NSEC3Record.java b/src/main/java/org/xbill/DNS/NSEC3Record.java deleted file mode 100644 index 6e1a8f3e1..000000000 --- a/src/main/java/org/xbill/DNS/NSEC3Record.java +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; -import org.xbill.DNS.utils.base32; - -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * Next SECure name 3 - this record contains the next hashed name in an - * ordered list of hashed names in the zone, and a set of types for which - * records exist for this name. The presence of this record in a response - * signifies a negative response from a DNSSEC-signed zone. - * - * This replaces the NSEC and NXT records, when used. - * - * @author Brian Wellington - * @author David Blacka - */ - -public class NSEC3Record extends Record { - -public static class Flags { - /** - * NSEC3 flags identifiers. - */ - - private Flags() {} - - /** Unsigned delegation are not included in the NSEC3 chain. - * - */ - public static final int OPT_OUT = 0x01; -} - -public static final byte SHA1_DIGEST_ID = 1; - -private static final long serialVersionUID = -7123504635968932855L; - -private int hashAlg; -private int flags; -private int iterations; -private byte [] salt; -private byte [] next; -private TypeBitmap types; - -private static final base32 b32 = new base32(base32.Alphabet.BASE32HEX, - false, false); - -NSEC3Record() {} - -Record getObject() { - return new NSEC3Record(); -} - -/** - * Creates an NSEC3 record from the given data. - * - * @param name The ownername of the NSEC3 record (base32'd hash plus zonename). - * @param dclass The class. - * @param ttl The TTL. - * @param hashAlg The hash algorithm. - * @param flags The value of the flags field. - * @param iterations The number of hash iterations. - * @param salt The salt to use (may be null). - * @param next The next hash (may not be null). - * @param types The types present at the original ownername. - */ -public NSEC3Record(Name name, int dclass, long ttl, int hashAlg, - int flags, int iterations, byte [] salt, byte [] next, - int [] types) -{ - super(name, Type.NSEC3, dclass, ttl); - this.hashAlg = checkU8("hashAlg", hashAlg); - this.flags = checkU8("flags", flags); - this.iterations = checkU16("iterations", iterations); - - if (salt != null) { - if (salt.length > 255) - throw new IllegalArgumentException("Invalid salt"); - if (salt.length > 0) { - this.salt = new byte[salt.length]; - System.arraycopy(salt, 0, this.salt, 0, salt.length); - } - } - - if (next.length > 255) { - throw new IllegalArgumentException("Invalid next hash"); - } - this.next = new byte[next.length]; - System.arraycopy(next, 0, this.next, 0, next.length); - this.types = new TypeBitmap(types); -} - -void -rrFromWire(DNSInput in) throws IOException { - hashAlg = in.readU8(); - flags = in.readU8(); - iterations = in.readU16(); - - int salt_length = in.readU8(); - if (salt_length > 0) - salt = in.readByteArray(salt_length); - else - salt = null; - - int next_length = in.readU8(); - next = in.readByteArray(next_length); - types = new TypeBitmap(in); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU8(hashAlg); - out.writeU8(flags); - out.writeU16(iterations); - - if (salt != null) { - out.writeU8(salt.length); - out.writeByteArray(salt); - } else - out.writeU8(0); - - out.writeU8(next.length); - out.writeByteArray(next); - types.toWire(out); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - hashAlg = st.getUInt8(); - flags = st.getUInt8(); - iterations = st.getUInt16(); - - String s = st.getString(); - if (s.equals("-")) - salt = null; - else { - st.unget(); - salt = st.getHexString(); - if (salt.length > 255) - throw st.exception("salt value too long"); - } - - next = st.getBase32String(b32); - types = new TypeBitmap(st); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(hashAlg); - sb.append(' '); - sb.append(flags); - sb.append(' '); - sb.append(iterations); - sb.append(' '); - if (salt == null) - sb.append('-'); - else - sb.append(base16.toString(salt)); - sb.append(' '); - sb.append(b32.toString(next)); - - if (!types.empty()) { - sb.append(' '); - sb.append(types.toString()); - } - - return sb.toString(); -} - -/** Returns the hash algorithm */ -public int -getHashAlgorithm() { - return hashAlg; -} - -/** Returns the flags */ -public int -getFlags() { - return flags; -} - -/** Returns the number of iterations */ -public int -getIterations() { - return iterations; -} - -/** Returns the salt */ -public byte [] -getSalt() -{ - return salt; -} - -/** Returns the next hash */ -public byte [] -getNext() { - return next; -} - - /** Returns the set of types defined for this name */ -public int [] -getTypes() { - return types.toArray(); -} - -/** Returns whether a specific type is in the set of types. */ -public boolean -hasType(int type) -{ - return types.contains(type); -} - -static byte [] -hashName(Name name, int hashAlg, int iterations, byte [] salt) -throws NoSuchAlgorithmException -{ - MessageDigest digest; - switch (hashAlg) { - case SHA1_DIGEST_ID: - digest = MessageDigest.getInstance("sha-1"); - break; - default: - throw new NoSuchAlgorithmException("Unknown NSEC3 algorithm" + - "identifier: " + - hashAlg); - } - byte [] hash = null; - for (int i = 0; i <= iterations; i++) { - digest.reset(); - if (i == 0) - digest.update(name.toWireCanonical()); - else - digest.update(hash); - if (salt != null) - digest.update(salt); - hash = digest.digest(); - } - return hash; -} - -/** - * Hashes a name with the parameters of this NSEC3 record. - * @param name The name to hash - * @return The hashed version of the name - * @throws NoSuchAlgorithmException The hash algorithm is unknown. - */ -public byte [] -hashName(Name name) throws NoSuchAlgorithmException -{ - return hashName(name, hashAlg, iterations, salt); -} - -} diff --git a/src/main/java/org/xbill/DNS/NSECRecord.java b/src/main/java/org/xbill/DNS/NSECRecord.java deleted file mode 100644 index 219179f45..000000000 --- a/src/main/java/org/xbill/DNS/NSECRecord.java +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Next SECure name - this record contains the following name in an - * ordered list of names in the zone, and a set of types for which - * records exist for this name. The presence of this record in a response - * signifies a negative response from a DNSSEC-signed zone. - * - * This replaces the NXT record. - * - * @author Brian Wellington - * @author David Blacka - */ - -public class NSECRecord extends Record { - -private static final long serialVersionUID = -5165065768816265385L; - -private Name next; -private TypeBitmap types; - -NSECRecord() {} - -Record -getObject() { - return new NSECRecord(); -} - -/** - * Creates an NSEC Record from the given data. - * @param next The following name in an ordered list of the zone - * @param types An array containing the types present. - */ -public -NSECRecord(Name name, int dclass, long ttl, Name next, int [] types) { - super(name, Type.NSEC, dclass, ttl); - this.next = checkName("next", next); - for (int i = 0; i < types.length; i++) { - Type.check(types[i]); - } - this.types = new TypeBitmap(types); -} - -void -rrFromWire(DNSInput in) throws IOException { - next = new Name(in); - types = new TypeBitmap(in); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - // Note: The next name is not lowercased. - next.toWire(out, null, false); - types.toWire(out); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - next = st.getName(origin); - types = new TypeBitmap(st); -} - -/** Converts rdata to a String */ -String -rrToString() -{ - StringBuffer sb = new StringBuffer(); - sb.append(next); - if (!types.empty()) { - sb.append(' '); - sb.append(types.toString()); - } - return sb.toString(); -} - -/** Returns the next name */ -public Name -getNext() { - return next; -} - -/** Returns the set of types defined for this name */ -public int [] -getTypes() { - return types.toArray(); -} - -/** Returns whether a specific type is in the set of types. */ -public boolean -hasType(int type) { - return types.contains(type); -} - -} diff --git a/src/main/java/org/xbill/DNS/NSRecord.java b/src/main/java/org/xbill/DNS/NSRecord.java deleted file mode 100644 index 2908da4d0..000000000 --- a/src/main/java/org/xbill/DNS/NSRecord.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Name Server Record - contains the name server serving the named zone - * - * @author Brian Wellington - */ - -public class NSRecord extends SingleCompressedNameBase { - -private static final long serialVersionUID = 487170758138268838L; - -NSRecord() {} - -Record -getObject() { - return new NSRecord(); -} - -/** - * Creates a new NS Record with the given data - * @param target The name server for the given domain - */ -public -NSRecord(Name name, int dclass, long ttl, Name target) { - super(name, Type.NS, dclass, ttl, target, "target"); -} - -/** Gets the target of the NS Record */ -public Name -getTarget() { - return getSingleName(); -} - -public Name -getAdditionalName() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/NULLRecord.java b/src/main/java/org/xbill/DNS/NULLRecord.java deleted file mode 100644 index dad248dd8..000000000 --- a/src/main/java/org/xbill/DNS/NULLRecord.java +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * The NULL Record. This has no defined purpose, but can be used to - * hold arbitrary data. - * - * @author Brian Wellington - */ - -public class NULLRecord extends Record { - -private static final long serialVersionUID = -5796493183235216538L; - -private byte [] data; - -NULLRecord() {} - -Record -getObject() { - return new NULLRecord(); -} - -/** - * Creates a NULL record from the given data. - * @param data The contents of the record. - */ -public -NULLRecord(Name name, int dclass, long ttl, byte [] data) { - super(name, Type.NULL, dclass, ttl); - - if (data.length > 0xFFFF) { - throw new IllegalArgumentException("data must be <65536 bytes"); - } - this.data = data; -} - -void -rrFromWire(DNSInput in) throws IOException { - data = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - throw st.exception("no defined text format for NULL records"); -} - -String -rrToString() { - return unknownToString(data); -} - -/** Returns the contents of this record. */ -public byte [] -getData() { - return data; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeByteArray(data); -} - -} diff --git a/src/main/java/org/xbill/DNS/NXTRecord.java b/src/main/java/org/xbill/DNS/NXTRecord.java deleted file mode 100644 index d3b780d4a..000000000 --- a/src/main/java/org/xbill/DNS/NXTRecord.java +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.BitSet; - -/** - * Next name - this record contains the following name in an ordered list - * of names in the zone, and a set of types for which records exist for - * this name. The presence of this record in a response signifies a - * failed query for data in a DNSSEC-signed zone. - * - * @author Brian Wellington - */ - -public class NXTRecord extends Record { - -private static final long serialVersionUID = -8851454400765507520L; - -private Name next; -private BitSet bitmap; - -NXTRecord() {} - -Record -getObject() { - return new NXTRecord(); -} - -/** - * Creates an NXT Record from the given data - * @param next The following name in an ordered list of the zone - * @param bitmap The set of type for which records exist at this name -*/ -public -NXTRecord(Name name, int dclass, long ttl, Name next, BitSet bitmap) { - super(name, Type.NXT, dclass, ttl); - this.next = checkName("next", next); - this.bitmap = bitmap; -} - -void -rrFromWire(DNSInput in) throws IOException { - next = new Name(in); - bitmap = new BitSet(); - int bitmapLength = in.remaining(); - for (int i = 0; i < bitmapLength; i++) { - int t = in.readU8(); - for (int j = 0; j < 8; j++) - if ((t & (1 << (7 - j))) != 0) - bitmap.set(i * 8 + j); - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - next = st.getName(origin); - bitmap = new BitSet(); - while (true) { - Tokenizer.Token t = st.get(); - if (!t.isString()) - break; - int typecode = Type.value(t.value, true); - if (typecode <= 0 || typecode > 128) - throw st.exception("Invalid type: " + t.value); - bitmap.set(typecode); - } - st.unget(); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(next); - int length = bitmap.length(); - for (short i = 0; i < length; i++) - if (bitmap.get(i)) { - sb.append(" "); - sb.append(Type.string(i)); - } - return sb.toString(); -} - -/** Returns the next name */ -public Name -getNext() { - return next; -} - -/** Returns the set of types defined for this name */ -public BitSet -getBitmap() { - return bitmap; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - next.toWire(out, null, canonical); - int length = bitmap.length(); - for (int i = 0, t = 0; i < length; i++) { - t |= (bitmap.get(i) ? (1 << (7 - i % 8)) : 0); - if (i % 8 == 7 || i == length - 1) { - out.writeU8(t); - t = 0; - } - } -} - -} diff --git a/src/main/java/org/xbill/DNS/Name.java b/src/main/java/org/xbill/DNS/Name.java deleted file mode 100644 index 3d6f59774..000000000 --- a/src/main/java/org/xbill/DNS/Name.java +++ /dev/null @@ -1,823 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.io.Serializable; -import java.text.DecimalFormat; - -/** - * A representation of a domain name. It may either be absolute (fully - * qualified) or relative. - * - * @author Brian Wellington - */ - -public class Name implements Comparable, Serializable { - -private static final long serialVersionUID = -7257019940971525644L; - -private static final int LABEL_NORMAL = 0; -private static final int LABEL_COMPRESSION = 0xC0; -private static final int LABEL_MASK = 0xC0; - -/* The name data */ -private byte [] name; - -/* - * Effectively an 8 byte array, where the low order byte stores the number - * of labels and the 7 higher order bytes store per-label offsets. - */ -private long offsets; - -/* Precomputed hashcode. */ -private int hashcode; - -private static final byte [] emptyLabel = new byte[] {(byte)0}; -private static final byte [] wildLabel = new byte[] {(byte)1, (byte)'*'}; - -/** The root name */ -public static final Name root; - -/** The root name */ -public static final Name empty; - -/** The maximum length of a Name */ -private static final int MAXNAME = 255; - -/** The maximum length of a label a Name */ -private static final int MAXLABEL = 63; - -/** The maximum number of labels in a Name */ -private static final int MAXLABELS = 128; - -/** The maximum number of cached offsets */ -private static final int MAXOFFSETS = 7; - -/* Used for printing non-printable characters */ -private static final DecimalFormat byteFormat = new DecimalFormat(); - -/* Used to efficiently convert bytes to lowercase */ -private static final byte lowercase[] = new byte[256]; - -/* Used in wildcard names. */ -private static final Name wild; - -static { - byteFormat.setMinimumIntegerDigits(3); - for (int i = 0; i < lowercase.length; i++) { - if (i < 'A' || i > 'Z') - lowercase[i] = (byte)i; - else - lowercase[i] = (byte)(i - 'A' + 'a'); - } - root = new Name(); - root.appendSafe(emptyLabel, 0, 1); - empty = new Name(); - empty.name = new byte[0]; - wild = new Name(); - wild.appendSafe(wildLabel, 0, 1); -} - -private -Name() { -} - -private final void -setoffset(int n, int offset) { - if (n >= MAXOFFSETS) - return; - int shift = 8 * (7 - n); - offsets &= (~(0xFFL << shift)); - offsets |= ((long)offset << shift); -} - -private final int -offset(int n) { - if (n == 0 && getlabels() == 0) - return 0; - if (n < 0 || n >= getlabels()) - throw new IllegalArgumentException("label out of range"); - if (n < MAXOFFSETS) { - int shift = 8 * (7 - n); - return ((int)(offsets >>> shift) & 0xFF); - } else { - int pos = offset(MAXOFFSETS - 1); - for (int i = MAXOFFSETS - 1; i < n; i++) - pos += (name[pos] + 1); - return (pos); - } -} - -private final void -setlabels(int labels) { - offsets &= ~(0xFF); - offsets |= labels; -} - -private final int -getlabels() { - return (int)(offsets & 0xFF); -} - -private static final void -copy(Name src, Name dst) { - if (src.offset(0) == 0) { - dst.name = src.name; - dst.offsets = src.offsets; - } else { - int offset0 = src.offset(0); - int namelen = src.name.length - offset0; - int labels = src.labels(); - dst.name = new byte[namelen]; - System.arraycopy(src.name, offset0, dst.name, 0, namelen); - for (int i = 0; i < labels && i < MAXOFFSETS; i++) - dst.setoffset(i, src.offset(i) - offset0); - dst.setlabels(labels); - } -} - -private final void -append(byte [] array, int start, int n) throws NameTooLongException { - int length = (name == null ? 0 : (name.length - offset(0))); - int alength = 0; - for (int i = 0, pos = start; i < n; i++) { - int len = array[pos]; - if (len > MAXLABEL) - throw new IllegalStateException("invalid label"); - len++; - pos += len; - alength += len; - } - int newlength = length + alength; - if (newlength > MAXNAME) - throw new NameTooLongException(); - int labels = getlabels(); - int newlabels = labels + n; - if (newlabels > MAXLABELS) - throw new IllegalStateException("too many labels"); - byte [] newname = new byte[newlength]; - if (length != 0) - System.arraycopy(name, offset(0), newname, 0, length); - System.arraycopy(array, start, newname, length, alength); - name = newname; - for (int i = 0, pos = length; i < n; i++) { - setoffset(labels + i, pos); - pos += (newname[pos] + 1); - } - setlabels(newlabels); -} - -private static TextParseException -parseException(String str, String message) { - return new TextParseException("'" + str + "': " + message); -} - -private final void -appendFromString(String fullName, byte [] array, int start, int n) -throws TextParseException -{ - try { - append(array, start, n); - } - catch (NameTooLongException e) { - throw parseException(fullName, "Name too long"); - } -} - -private final void -appendSafe(byte [] array, int start, int n) { - try { - append(array, start, n); - } - catch (NameTooLongException e) { - } -} - -/** - * Create a new name from a string and an origin. This does not automatically - * make the name absolute; it will be absolute if it has a trailing dot or an - * absolute origin is appended. - * @param s The string to be converted - * @param origin If the name is not absolute, the origin to be appended. - * @throws TextParseException The name is invalid. - */ -public -Name(String s, Name origin) throws TextParseException { - if (s.equals("")) - throw parseException(s, "empty name"); - else if (s.equals("@")) { - if (origin == null) - copy(empty, this); - else - copy(origin, this); - return; - } else if (s.equals(".")) { - copy(root, this); - return; - } - int labelstart = -1; - int pos = 1; - byte [] label = new byte[MAXLABEL + 1]; - boolean escaped = false; - int digits = 0; - int intval = 0; - boolean absolute = false; - for (int i = 0; i < s.length(); i++) { - byte b = (byte) s.charAt(i); - if (escaped) { - if (b >= '0' && b <= '9' && digits < 3) { - digits++; - intval *= 10; - intval += (b - '0'); - if (intval > 255) - throw parseException(s, "bad escape"); - if (digits < 3) - continue; - b = (byte) intval; - } - else if (digits > 0 && digits < 3) - throw parseException(s, "bad escape"); - if (pos > MAXLABEL) - throw parseException(s, "label too long"); - labelstart = pos; - label[pos++] = b; - escaped = false; - } else if (b == '\\') { - escaped = true; - digits = 0; - intval = 0; - } else if (b == '.') { - if (labelstart == -1) - throw parseException(s, "invalid empty label"); - label[0] = (byte)(pos - 1); - appendFromString(s, label, 0, 1); - labelstart = -1; - pos = 1; - } else { - if (labelstart == -1) - labelstart = i; - if (pos > MAXLABEL) - throw parseException(s, "label too long"); - label[pos++] = b; - } - } - if (digits > 0 && digits < 3) - throw parseException(s, "bad escape"); - if (escaped) - throw parseException(s, "bad escape"); - if (labelstart == -1) { - appendFromString(s, emptyLabel, 0, 1); - absolute = true; - } else { - label[0] = (byte)(pos - 1); - appendFromString(s, label, 0, 1); - } - if (origin != null && !absolute) - appendFromString(s, origin.name, 0, origin.getlabels()); -} - -/** - * Create a new name from a string. This does not automatically make the name - * absolute; it will be absolute if it has a trailing dot. - * @param s The string to be converted - * @throws TextParseException The name is invalid. - */ -public -Name(String s) throws TextParseException { - this(s, null); -} - -/** - * Create a new name from a string and an origin. This does not automatically - * make the name absolute; it will be absolute if it has a trailing dot or an - * absolute origin is appended. This is identical to the constructor, except - * that it will avoid creating new objects in some cases. - * @param s The string to be converted - * @param origin If the name is not absolute, the origin to be appended. - * @throws TextParseException The name is invalid. - */ -public static Name -fromString(String s, Name origin) throws TextParseException { - if (s.equals("@") && origin != null) - return origin; - else if (s.equals(".")) - return (root); - - return new Name(s, origin); -} - -/** - * Create a new name from a string. This does not automatically make the name - * absolute; it will be absolute if it has a trailing dot. This is identical - * to the constructor, except that it will avoid creating new objects in some - * cases. - * @param s The string to be converted - * @throws TextParseException The name is invalid. - */ -public static Name -fromString(String s) throws TextParseException { - return fromString(s, null); -} - -/** - * Create a new name from a constant string. This should only be used when - the name is known to be good - that is, when it is constant. - * @param s The string to be converted - * @throws IllegalArgumentException The name is invalid. - */ -public static Name -fromConstantString(String s) { - try { - return fromString(s, null); - } - catch (TextParseException e) { - throw new IllegalArgumentException("Invalid name '" + s + "'"); - } -} - -/** - * Create a new name from DNS a wire format message - * @param in A stream containing the DNS message which is currently - * positioned at the start of the name to be read. - */ -public -Name(DNSInput in) throws WireParseException { - int len, pos; - boolean done = false; - byte [] label = new byte[MAXLABEL + 1]; - boolean savedState = false; - - while (!done) { - len = in.readU8(); - switch (len & LABEL_MASK) { - case LABEL_NORMAL: - if (getlabels() >= MAXLABELS) - throw new WireParseException("too many labels"); - if (len == 0) { - append(emptyLabel, 0, 1); - done = true; - } else { - label[0] = (byte)len; - in.readByteArray(label, 1, len); - append(label, 0, 1); - } - break; - case LABEL_COMPRESSION: - pos = in.readU8(); - pos += ((len & ~LABEL_MASK) << 8); - if (Options.check("verbosecompression")) - System.err.println("currently " + in.current() + - ", pointer to " + pos); - - if (pos >= in.current() - 2) - throw new WireParseException("bad compression"); - if (!savedState) { - in.save(); - savedState = true; - } - in.jump(pos); - if (Options.check("verbosecompression")) - System.err.println("current name '" + this + - "', seeking to " + pos); - break; - default: - throw new WireParseException("bad label type"); - } - } - if (savedState) { - in.restore(); - } -} - -/** - * Create a new name from DNS wire format - * @param b A byte array containing the wire format of the name. - */ -public -Name(byte [] b) throws IOException { - this(new DNSInput(b)); -} - -/** - * Create a new name by removing labels from the beginning of an existing Name - * @param src An existing Name - * @param n The number of labels to remove from the beginning in the copy - */ -public -Name(Name src, int n) { - int slabels = src.labels(); - if (n > slabels) - throw new IllegalArgumentException("attempted to remove too " + - "many labels"); - name = src.name; - setlabels(slabels - n); - for (int i = 0; i < MAXOFFSETS && i < slabels - n; i++) - setoffset(i, src.offset(i + n)); -} - -/** - * Creates a new name by concatenating two existing names. - * @param prefix The prefix name. - * @param suffix The suffix name. - * @return The concatenated name. - * @throws NameTooLongException The name is too long. - */ -public static Name -concatenate(Name prefix, Name suffix) throws NameTooLongException { - if (prefix.isAbsolute()) - return (prefix); - Name newname = new Name(); - copy(prefix, newname); - newname.append(suffix.name, suffix.offset(0), suffix.getlabels()); - return newname; -} - -/** - * If this name is a subdomain of origin, return a new name relative to - * origin with the same value. Otherwise, return the existing name. - * @param origin The origin to remove. - * @return The possibly relativized name. - */ -public Name -relativize(Name origin) { - if (origin == null || !subdomain(origin)) - return this; - Name newname = new Name(); - copy(this, newname); - int length = length() - origin.length(); - int labels = newname.labels() - origin.labels(); - newname.setlabels(labels); - newname.name = new byte[length]; - System.arraycopy(name, offset(0), newname.name, 0, length); - return newname; -} - -/** - * Generates a new Name with the first n labels replaced by a wildcard - * @return The wildcard name - */ -public Name -wild(int n) { - if (n < 1) - throw new IllegalArgumentException("must replace 1 or more " + - "labels"); - try { - Name newname = new Name(); - copy(wild, newname); - newname.append(name, offset(n), getlabels() - n); - return newname; - } - catch (NameTooLongException e) { - throw new IllegalStateException - ("Name.wild: concatenate failed"); - } -} - -/** - * Generates a new Name to be used when following a DNAME. - * @param dname The DNAME record to follow. - * @return The constructed name. - * @throws NameTooLongException The resulting name is too long. - */ -public Name -fromDNAME(DNAMERecord dname) throws NameTooLongException { - Name dnameowner = dname.getName(); - Name dnametarget = dname.getTarget(); - if (!subdomain(dnameowner)) - return null; - - int plabels = labels() - dnameowner.labels(); - int plength = length() - dnameowner.length(); - int pstart = offset(0); - - int dlabels = dnametarget.labels(); - int dlength = dnametarget.length(); - - if (plength + dlength > MAXNAME) - throw new NameTooLongException(); - - Name newname = new Name(); - newname.setlabels(plabels + dlabels); - newname.name = new byte[plength + dlength]; - System.arraycopy(name, pstart, newname.name, 0, plength); - System.arraycopy(dnametarget.name, 0, newname.name, plength, dlength); - - for (int i = 0, pos = 0; i < MAXOFFSETS && i < plabels + dlabels; i++) { - newname.setoffset(i, pos); - pos += (newname.name[pos] + 1); - } - return newname; -} - -/** - * Is this name a wildcard? - */ -public boolean -isWild() { - if (labels() == 0) - return false; - return (name[0] == (byte)1 && name[1] == (byte)'*'); -} - -/** - * Is this name absolute? - */ -public boolean -isAbsolute() { - if (labels() == 0) - return false; - return (name[name.length - 1] == 0); -} - -/** - * The length of the name. - */ -public short -length() { - if (getlabels() == 0) - return 0; - return (short)(name.length - offset(0)); -} - -/** - * The number of labels in the name. - */ -public int -labels() { - return getlabels(); -} - -/** - * Is the current Name a subdomain of the specified name? - */ -public boolean -subdomain(Name domain) { - int labels = labels(); - int dlabels = domain.labels(); - if (dlabels > labels) - return false; - if (dlabels == labels) - return equals(domain); - return domain.equals(name, offset(labels - dlabels)); -} - -private String -byteString(byte [] array, int pos) { - StringBuffer sb = new StringBuffer(); - int len = array[pos++]; - for (int i = pos; i < pos + len; i++) { - int b = array[i] & 0xFF; - if (b <= 0x20 || b >= 0x7f) { - sb.append('\\'); - sb.append(byteFormat.format(b)); - } - else if (b == '"' || b == '(' || b == ')' || b == '.' || - b == ';' || b == '\\' || b == '@' || b == '$') - { - sb.append('\\'); - sb.append((char)b); - } - else - sb.append((char)b); - } - return sb.toString(); -} - -/** - * Convert a Name to a String - * @return The representation of this name as a (printable) String. - */ -public String -toString() { - int labels = labels(); - if (labels == 0) - return "@"; - else if (labels == 1 && name[offset(0)] == 0) - return "."; - StringBuffer sb = new StringBuffer(); - for (int i = 0, pos = offset(0); i < labels; i++) { - int len = name[pos]; - if (len > MAXLABEL) - throw new IllegalStateException("invalid label"); - if (len == 0) - break; - sb.append(byteString(name, pos)); - sb.append('.'); - pos += (1 + len); - } - if (!isAbsolute()) - sb.deleteCharAt(sb.length() - 1); - return sb.toString(); -} - -/** - * Retrieve the nth label of a Name. This makes a copy of the label; changing - * this does not change the Name. - * @param n The label to be retrieved. The first label is 0. - */ -public byte [] -getLabel(int n) { - int pos = offset(n); - byte len = (byte)(name[pos] + 1); - byte [] label = new byte[len]; - System.arraycopy(name, pos, label, 0, len); - return label; -} - -/** - * Convert the nth label in a Name to a String - * @param n The label to be converted to a (printable) String. The first - * label is 0. - */ -public String -getLabelString(int n) { - int pos = offset(n); - return byteString(name, pos); -} - -/** - * Emit a Name in DNS wire format - * @param out The output stream containing the DNS message. - * @param c The compression context, or null of no compression is desired. - * @throws IllegalArgumentException The name is not absolute. - */ -public void -toWire(DNSOutput out, Compression c) { - if (!isAbsolute()) - throw new IllegalArgumentException("toWire() called on " + - "non-absolute name"); - - int labels = labels(); - for (int i = 0; i < labels - 1; i++) { - Name tname; - if (i == 0) - tname = this; - else - tname = new Name(this, i); - int pos = -1; - if (c != null) - pos = c.get(tname); - if (pos >= 0) { - pos |= (LABEL_MASK << 8); - out.writeU16(pos); - return; - } else { - if (c != null) - c.add(out.current(), tname); - int off = offset(i); - out.writeByteArray(name, off, name[off] + 1); - } - } - out.writeU8(0); -} - -/** - * Emit a Name in DNS wire format - * @throws IllegalArgumentException The name is not absolute. - */ -public byte [] -toWire() { - DNSOutput out = new DNSOutput(); - toWire(out, null); - return out.toByteArray(); -} - -/** - * Emit a Name in canonical DNS wire format (all lowercase) - * @param out The output stream to which the message is written. - */ -public void -toWireCanonical(DNSOutput out) { - byte [] b = toWireCanonical(); - out.writeByteArray(b); -} - -/** - * Emit a Name in canonical DNS wire format (all lowercase) - * @return The canonical form of the name. - */ -public byte [] -toWireCanonical() { - int labels = labels(); - if (labels == 0) - return (new byte[0]); - byte [] b = new byte[name.length - offset(0)]; - for (int i = 0, spos = offset(0), dpos = 0; i < labels; i++) { - int len = name[spos]; - if (len > MAXLABEL) - throw new IllegalStateException("invalid label"); - b[dpos++] = name[spos++]; - for (int j = 0; j < len; j++) - b[dpos++] = lowercase[(name[spos++] & 0xFF)]; - } - return b; -} - -/** - * Emit a Name in DNS wire format - * @param out The output stream containing the DNS message. - * @param c The compression context, or null of no compression is desired. - * @param canonical If true, emit the name in canonicalized form - * (all lowercase). - * @throws IllegalArgumentException The name is not absolute. - */ -public void -toWire(DNSOutput out, Compression c, boolean canonical) { - if (canonical) - toWireCanonical(out); - else - toWire(out, c); -} - -private final boolean -equals(byte [] b, int bpos) { - int labels = labels(); - for (int i = 0, pos = offset(0); i < labels; i++) { - if (name[pos] != b[bpos]) - return false; - int len = name[pos++]; - bpos++; - if (len > MAXLABEL) - throw new IllegalStateException("invalid label"); - for (int j = 0; j < len; j++) - if (lowercase[(name[pos++] & 0xFF)] != - lowercase[(b[bpos++] & 0xFF)]) - return false; - } - return true; -} - -/** - * Are these two Names equivalent? - */ -public boolean -equals(Object arg) { - if (arg == this) - return true; - if (arg == null || !(arg instanceof Name)) - return false; - Name d = (Name) arg; - if (d.hashcode == 0) - d.hashCode(); - if (hashcode == 0) - hashCode(); - if (d.hashcode != hashcode) - return false; - if (d.labels() != labels()) - return false; - return equals(d.name, d.offset(0)); -} - -/** - * Computes a hashcode based on the value - */ -public int -hashCode() { - if (hashcode != 0) - return (hashcode); - int code = 0; - for (int i = offset(0); i < name.length; i++) - code += ((code << 3) + lowercase[(name[i] & 0xFF)]); - hashcode = code; - return hashcode; -} - -/** - * Compares this Name to another Object. - * @param o The Object to be compared. - * @return The value 0 if the argument is a name equivalent to this name; - * a value less than 0 if the argument is less than this name in the canonical - * ordering, and a value greater than 0 if the argument is greater than this - * name in the canonical ordering. - * @throws ClassCastException if the argument is not a Name. - */ -public int -compareTo(Object o) { - Name arg = (Name) o; - - if (this == arg) - return (0); - - int labels = labels(); - int alabels = arg.labels(); - int compares = labels > alabels ? alabels : labels; - - for (int i = 1; i <= compares; i++) { - int start = offset(labels - i); - int astart = arg.offset(alabels - i); - int length = name[start]; - int alength = arg.name[astart]; - for (int j = 0; j < length && j < alength; j++) { - int n = lowercase[(name[j + start + 1]) & 0xFF] - - lowercase[(arg.name[j + astart + 1]) & 0xFF]; - if (n != 0) - return (n); - } - if (length != alength) - return (length - alength); - } - return (labels - alabels); -} - -} diff --git a/src/main/java/org/xbill/DNS/NameTooLongException.java b/src/main/java/org/xbill/DNS/NameTooLongException.java deleted file mode 100644 index 114be39ed..000000000 --- a/src/main/java/org/xbill/DNS/NameTooLongException.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An exception thrown when a name is longer than the maximum length of a DNS - * name. - * - * @author Brian Wellington - */ - -public class NameTooLongException extends WireParseException { - -public -NameTooLongException() { - super(); -} - -public -NameTooLongException(String s) { - super(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/OPTRecord.java b/src/main/java/org/xbill/DNS/OPTRecord.java deleted file mode 100644 index 07095b03f..000000000 --- a/src/main/java/org/xbill/DNS/OPTRecord.java +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * Options - describes Extended DNS (EDNS) properties of a Message. - * No specific options are defined other than those specified in the - * header. An OPT should be generated by Resolver. - * - * EDNS is a method to extend the DNS protocol while providing backwards - * compatibility and not significantly changing the protocol. This - * implementation of EDNS is mostly complete at level 0. - * - * @see Message - * @see Resolver - * - * @author Brian Wellington - */ - -public class OPTRecord extends Record { - -private static final long serialVersionUID = -6254521894809367938L; - -public static class Option { - public final int code; - public final byte [] data; - - /** - * Creates an option with the given option code and data. - */ - public - Option(int code, byte [] data) { - this.code = checkU8("option code", code); - this.data = data; - } - - public String - toString() { - return "{" + code + " <" + base16.toString(data) + ">}"; - } -} - -private List options; - -OPTRecord() {} - -Record -getObject() { - return new OPTRecord(); -} - -/** - * Creates an OPT Record. This is normally called by SimpleResolver, but can - * also be called by a server. - * @param payloadSize The size of a packet that can be reassembled on the - * sending host. - * @param xrcode The value of the extended rcode field. This is the upper - * 16 bits of the full rcode. - * @param flags Additional message flags. - * @param version The EDNS version that this DNS implementation supports. - * This should be 0 for dnsjava. - * @param options The list of options that comprise the data field. There - * are currently no defined options. - * @see ExtendedFlags - */ -public -OPTRecord(int payloadSize, int xrcode, int version, int flags, List options) { - super(Name.root, Type.OPT, payloadSize, 0); - checkU16("payloadSize", payloadSize); - checkU8("xrcode", xrcode); - checkU8("version", version); - checkU16("flags", flags); - ttl = ((long)xrcode << 24) + ((long)version << 16) + flags; - if (options != null) { - this.options = new ArrayList(options); - } -} - -/** - * Creates an OPT Record with no data. This is normally called by - * SimpleResolver, but can also be called by a server. - * @param payloadSize The size of a packet that can be reassembled on the - * sending host. - * @param xrcode The value of the extended rcode field. This is the upper - * 16 bits of the full rcode. - * @param flags Additional message flags. - * @param version The EDNS version that this DNS implementation supports. - * This should be 0 for dnsjava. - * @see ExtendedFlags - */ -public -OPTRecord(int payloadSize, int xrcode, int version, int flags) { - this(payloadSize, xrcode, version, flags, null); -} - -/** - * Creates an OPT Record with no data. This is normally called by - * SimpleResolver, but can also be called by a server. - */ -public -OPTRecord(int payloadSize, int xrcode, int version) { - this(payloadSize, xrcode, version, 0, null); -} - -void -rrFromWire(DNSInput in) throws IOException { - if (in.remaining() > 0) - options = new ArrayList(); - while (in.remaining() > 0) { - int code = in.readU16(); - int len = in.readU16(); - byte [] data = in.readByteArray(len); - options.add(new Option(code, data)); - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - throw st.exception("no text format defined for OPT"); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - if (options != null) { - sb.append(options); - sb.append(" "); - } - sb.append(" ; payload "); - sb.append(getPayloadSize()); - sb.append(", xrcode "); - sb.append(getExtendedRcode()); - sb.append(", version "); - sb.append(getVersion()); - sb.append(", flags "); - sb.append(getFlags()); - return sb.toString(); -} - -/** Returns the maximum allowed payload size. */ -public int -getPayloadSize() { - return dclass; -} - -/** - * Returns the extended Rcode - * @see Rcode - */ -public int -getExtendedRcode() { - return (int)(ttl >>> 24); -} - -/** Returns the highest supported EDNS version */ -public int -getVersion() { - return (int)((ttl >>> 16) & 0xFF); -} - -/** Returns the EDNS flags */ -public int -getFlags() { - return (int)(ttl & 0xFFFF); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - if (options == null) - return; - Iterator it = options.iterator(); - while (it.hasNext()) { - Option opt = (Option) it.next(); - out.writeU16(opt.code); - out.writeU16(opt.data.length); - out.writeByteArray(opt.data); - } -} - -/** - * Gets all options in the OPTRecord. This returns a list of Options. - */ -public List -getOptions() { - if (options == null) - return Collections.EMPTY_LIST; - return Collections.unmodifiableList(options); -} - -/** - * Gets all options in the OPTRecord with a specific code. This returns a - * list of byte arrays. - */ -public List -getOptions(int code) { - if (options == null) - return Collections.EMPTY_LIST; - List list = null; - for (Iterator it = options.iterator(); it.hasNext(); ) { - Option opt = (Option) it.next(); - if (opt.code == code) { - if (list == null) - list = new ArrayList(); - list.add(opt.data); - } - } - if (list == null) - return Collections.EMPTY_LIST; - return list; -} - -} diff --git a/src/main/java/org/xbill/DNS/Opcode.java b/src/main/java/org/xbill/DNS/Opcode.java deleted file mode 100644 index dadbca173..000000000 --- a/src/main/java/org/xbill/DNS/Opcode.java +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants and functions relating to DNS opcodes - * - * @author Brian Wellington - */ - -public final class Opcode { - -/** A standard query */ -public static final int QUERY = 0; - -/** An inverse query (deprecated) */ -public static final int IQUERY = 1; - -/** A server status request (not used) */ -public static final int STATUS = 2; - -/** - * A message from a primary to a secondary server to initiate a zone transfer - */ -public static final int NOTIFY = 4; - -/** A dynamic update message */ -public static final int UPDATE = 5; - -private static Mnemonic opcodes = new Mnemonic("DNS Opcode", - Mnemonic.CASE_UPPER); - -static { - opcodes.setMaximum(0xF); - opcodes.setPrefix("RESERVED"); - opcodes.setNumericAllowed(true); - - opcodes.add(QUERY, "QUERY"); - opcodes.add(IQUERY, "IQUERY"); - opcodes.add(STATUS, "STATUS"); - opcodes.add(NOTIFY, "NOTIFY"); - opcodes.add(UPDATE, "UPDATE"); -} - -private -Opcode() {} - -/** Converts a numeric Opcode into a String */ -public static String -string(int i) { - return opcodes.getText(i); -} - -/** Converts a String representation of an Opcode into its numeric value */ -public static int -value(String s) { - return opcodes.getValue(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/Options.java b/src/main/java/org/xbill/DNS/Options.java deleted file mode 100644 index cbbea17a8..000000000 --- a/src/main/java/org/xbill/DNS/Options.java +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.HashMap; -import java.util.Map; -import java.util.StringTokenizer; - -/** - * Boolean options:
    - * bindttl - Print TTLs in BIND format
    - * multiline - Print records in multiline format
    - * noprintin - Don't print the class of a record if it's IN
    - * verbose - Turn on general debugging statements
    - * verbosemsg - Print all messages sent or received by SimpleResolver
    - * verbosecompression - Print messages related to name compression
    - * verbosesec - Print messages related to signature verification
    - * verbosecache - Print messages related to cache lookups
    - *
    - * Valued options:
    - * tsigfudge=n - Sets the default TSIG fudge value (in seconds)
    - * sig0validity=n - Sets the default SIG(0) validity period (in seconds)
    - * - * @author Brian Wellington - */ - -public final class Options { - -private static Map table; - -static { - try { - refresh(); - } - catch (SecurityException e) { - } -} - -private -Options() {} - -public static void -refresh() { - String s = System.getProperty("dnsjava.options"); - if (s != null) { - StringTokenizer st = new StringTokenizer(s, ","); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - int index = token.indexOf('='); - if (index == -1) - set(token); - else { - String option = token.substring(0, index); - String value = token.substring(index + 1); - set(option, value); - } - } - } -} - -/** Clears all defined options */ -public static void -clear() { - table = null; -} - -/** Sets an option to "true" */ -public static void -set(String option) { - if (table == null) - table = new HashMap(); - table.put(option.toLowerCase(), "true"); -} - -/** Sets an option to the the supplied value */ -public static void -set(String option, String value) { - if (table == null) - table = new HashMap(); - table.put(option.toLowerCase(), value.toLowerCase()); -} - -/** Removes an option */ -public static void -unset(String option) { - if (table == null) - return; - table.remove(option.toLowerCase()); -} - -/** Checks if an option is defined */ -public static boolean -check(String option) { - if (table == null) - return false; - return (table.get(option.toLowerCase()) != null); -} - -/** Returns the value of an option */ -public static String -value(String option) { - if (table == null) - return null; - return ((String)table.get(option.toLowerCase())); -} - -/** - * Returns the value of an option as an integer, or -1 if not defined. - */ -public static int -intValue(String option) { - String s = value(option); - if (s != null) { - try { - int val = Integer.parseInt(s); - if (val > 0) - return (val); - } - catch (NumberFormatException e) { - } - } - return (-1); -} - -} diff --git a/src/main/java/org/xbill/DNS/PTRRecord.java b/src/main/java/org/xbill/DNS/PTRRecord.java deleted file mode 100644 index 89be5781e..000000000 --- a/src/main/java/org/xbill/DNS/PTRRecord.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Pointer Record - maps a domain name representing an Internet Address to - * a hostname. - * - * @author Brian Wellington - */ - -public class PTRRecord extends SingleCompressedNameBase { - -private static final long serialVersionUID = -8321636610425434192L; - -PTRRecord() {} - -Record -getObject() { - return new PTRRecord(); -} - -/** - * Creates a new PTR Record with the given data - * @param target The name of the machine with this address - */ -public -PTRRecord(Name name, int dclass, long ttl, Name target) { - super(name, Type.PTR, dclass, ttl, target, "target"); -} - -/** Gets the target of the PTR Record */ -public Name -getTarget() { - return getSingleName(); -} - -} diff --git a/src/main/java/org/xbill/DNS/PXRecord.java b/src/main/java/org/xbill/DNS/PXRecord.java deleted file mode 100644 index ce3582f37..000000000 --- a/src/main/java/org/xbill/DNS/PXRecord.java +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * X.400 mail mapping record. - * - * @author Brian Wellington - */ - -public class PXRecord extends Record { - -private static final long serialVersionUID = 1811540008806660667L; - -private int preference; -private Name map822; -private Name mapX400; - -PXRecord() {} - -Record -getObject() { - return new PXRecord(); -} - -/** - * Creates an PX Record from the given data - * @param preference The preference of this mail address. - * @param map822 The RFC 822 component of the mail address. - * @param mapX400 The X.400 component of the mail address. - */ -public -PXRecord(Name name, int dclass, long ttl, int preference, - Name map822, Name mapX400) -{ - super(name, Type.PX, dclass, ttl); - - this.preference = checkU16("preference", preference); - this.map822 = checkName("map822", map822); - this.mapX400 = checkName("mapX400", mapX400); -} - -void -rrFromWire(DNSInput in) throws IOException { - preference = in.readU16(); - map822 = new Name(in); - mapX400 = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - preference = st.getUInt16(); - map822 = st.getName(origin); - mapX400 = st.getName(origin); -} - -/** Converts the PX Record to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(preference); - sb.append(" "); - sb.append(map822); - sb.append(" "); - sb.append(mapX400); - return sb.toString(); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(preference); - map822.toWire(out, null, canonical); - mapX400.toWire(out, null, canonical); -} - -/** Gets the preference of the route. */ -public int -getPreference() { - return preference; -} - -/** Gets the RFC 822 component of the mail address. */ -public Name -getMap822() { - return map822; -} - -/** Gets the X.400 component of the mail address. */ -public Name -getMapX400() { - return mapX400; -} - -} diff --git a/src/main/java/org/xbill/DNS/RPRecord.java b/src/main/java/org/xbill/DNS/RPRecord.java deleted file mode 100644 index a8fd94879..000000000 --- a/src/main/java/org/xbill/DNS/RPRecord.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Responsible Person Record - lists the mail address of a responsible person - * and a domain where TXT records are available. - * - * @author Tom Scola - * @author Brian Wellington - */ - -public class RPRecord extends Record { - -private static final long serialVersionUID = 8124584364211337460L; - -private Name mailbox; -private Name textDomain; - -RPRecord() {} - -Record -getObject() { - return new RPRecord(); -} - -/** - * Creates an RP Record from the given data - * @param mailbox The responsible person - * @param textDomain The address where TXT records can be found - */ -public -RPRecord(Name name, int dclass, long ttl, Name mailbox, Name textDomain) { - super(name, Type.RP, dclass, ttl); - - this.mailbox = checkName("mailbox", mailbox); - this.textDomain = checkName("textDomain", textDomain); -} - -void -rrFromWire(DNSInput in) throws IOException { - mailbox = new Name(in); - textDomain = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - mailbox = st.getName(origin); - textDomain = st.getName(origin); -} - -/** Converts the RP Record to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(mailbox); - sb.append(" "); - sb.append(textDomain); - return sb.toString(); -} - -/** Gets the mailbox address of the RP Record */ -public Name -getMailbox() { - return mailbox; -} - -/** Gets the text domain info of the RP Record */ -public Name -getTextDomain() { - return textDomain; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - mailbox.toWire(out, null, canonical); - textDomain.toWire(out, null, canonical); -} - -} diff --git a/src/main/java/org/xbill/DNS/RRSIGRecord.java b/src/main/java/org/xbill/DNS/RRSIGRecord.java deleted file mode 100644 index 4b139f360..000000000 --- a/src/main/java/org/xbill/DNS/RRSIGRecord.java +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.Date; - -/** - * Recource Record Signature - An RRSIG provides the digital signature of an - * RRset, so that the data can be authenticated by a DNSSEC-capable resolver. - * The signature is generated by a key contained in a DNSKEY Record. - * @see RRset - * @see DNSSEC - * @see KEYRecord - * - * @author Brian Wellington - */ - -public class RRSIGRecord extends SIGBase { - -private static final long serialVersionUID = -2609150673537226317L; - -RRSIGRecord() {} - -Record -getObject() { - return new RRSIGRecord(); -} - -/** - * Creates an RRSIG Record from the given data - * @param covered The RRset type covered by this signature - * @param alg The cryptographic algorithm of the key that generated the - * signature - * @param origttl The original TTL of the RRset - * @param expire The time at which the signature expires - * @param timeSigned The time at which this signature was generated - * @param footprint The footprint/key id of the signing key. - * @param signer The owner of the signing key - * @param signature Binary data representing the signature - */ -public -RRSIGRecord(Name name, int dclass, long ttl, int covered, int alg, long origttl, - Date expire, Date timeSigned, int footprint, Name signer, - byte [] signature) -{ - super(name, Type.RRSIG, dclass, ttl, covered, alg, origttl, expire, - timeSigned, footprint, signer, signature); -} - -} diff --git a/src/main/java/org/xbill/DNS/RRset.java b/src/main/java/org/xbill/DNS/RRset.java deleted file mode 100644 index 02895f4ce..000000000 --- a/src/main/java/org/xbill/DNS/RRset.java +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * A set of Records with the same name, type, and class. Also included - * are all RRSIG records signing the data records. - * @see Record - * @see RRSIGRecord - * - * @author Brian Wellington - */ - -public class RRset implements Serializable { - -private static final long serialVersionUID = -3270249290171239695L; - -/* - * rrs contains both normal and RRSIG records, with the RRSIG records - * at the end. - */ -private List rrs; -private short nsigs; -private short position; - -/** Creates an empty RRset */ -public -RRset() { - rrs = new ArrayList(1); - nsigs = 0; - position = 0; -} - -/** Creates an RRset and sets its contents to the specified record */ -public -RRset(Record record) { - this(); - safeAddRR(record); -} - -/** Creates an RRset with the contents of an existing RRset */ -public -RRset(RRset rrset) { - synchronized (rrset) { - rrs = (List) ((ArrayList)rrset.rrs).clone(); - nsigs = rrset.nsigs; - position = rrset.position; - } -} - -private void -safeAddRR(Record r) { - if (!(r instanceof RRSIGRecord)) { - if (nsigs == 0) - rrs.add(r); - else - rrs.add(rrs.size() - nsigs, r); - } else { - rrs.add(r); - nsigs++; - } -} - -/** Adds a Record to an RRset */ -public synchronized void -addRR(Record r) { - if (rrs.size() == 0) { - safeAddRR(r); - return; - } - Record first = first(); - if (!r.sameRRset(first)) - throw new IllegalArgumentException("record does not match " + - "rrset"); - - if (r.getTTL() != first.getTTL()) { - if (r.getTTL() > first.getTTL()) { - r = r.cloneRecord(); - r.setTTL(first.getTTL()); - } else { - for (int i = 0; i < rrs.size(); i++) { - Record tmp = (Record) rrs.get(i); - tmp = tmp.cloneRecord(); - tmp.setTTL(r.getTTL()); - rrs.set(i, tmp); - } - } - } - - if (!rrs.contains(r)) - safeAddRR(r); -} - -/** Deletes a Record from an RRset */ -public synchronized void -deleteRR(Record r) { - if (rrs.remove(r) && (r instanceof RRSIGRecord)) - nsigs--; -} - -/** Deletes all Records from an RRset */ -public synchronized void -clear() { - rrs.clear(); - position = 0; - nsigs = 0; -} - -private synchronized Iterator -iterator(boolean data, boolean cycle) { - int size, start, total; - - total = rrs.size(); - - if (data) - size = total - nsigs; - else - size = nsigs; - if (size == 0) - return Collections.EMPTY_LIST.iterator(); - - if (data) { - if (!cycle) - start = 0; - else { - if (position >= size) - position = 0; - start = position++; - } - } else { - start = total - nsigs; - } - - List list = new ArrayList(size); - if (data) { - list.addAll(rrs.subList(start, size)); - if (start != 0) - list.addAll(rrs.subList(0, start)); - } else { - list.addAll(rrs.subList(start, total)); - } - - return list.iterator(); -} - -/** - * Returns an Iterator listing all (data) records. - * @param cycle If true, cycle through the records so that each Iterator will - * start with a different record. - */ -public synchronized Iterator -rrs(boolean cycle) { - return iterator(true, cycle); -} - -/** - * Returns an Iterator listing all (data) records. This cycles through - * the records, so each Iterator will start with a different record. - */ -public synchronized Iterator -rrs() { - return iterator(true, true); -} - -/** Returns an Iterator listing all signature records */ -public synchronized Iterator -sigs() { - return iterator(false, false); -} - -/** Returns the number of (data) records */ -public synchronized int -size() { - return rrs.size() - nsigs; -} - -/** - * Returns the name of the records - * @see Name - */ -public Name -getName() { - return first().getName(); -} - -/** - * Returns the type of the records - * @see Type - */ -public int -getType() { - return first().getRRsetType(); -} - -/** - * Returns the class of the records - * @see DClass - */ -public int -getDClass() { - return first().getDClass(); -} - -/** Returns the ttl of the records */ -public synchronized long -getTTL() { - return first().getTTL(); -} - -/** - * Returns the first record - * @throws IllegalStateException if the rrset is empty - */ -public synchronized Record -first() { - if (rrs.size() == 0) - throw new IllegalStateException("rrset is empty"); - return (Record) rrs.get(0); -} - -private String -iteratorToString(Iterator it) { - StringBuffer sb = new StringBuffer(); - while (it.hasNext()) { - Record rr = (Record) it.next(); - sb.append("["); - sb.append(rr.rdataToString()); - sb.append("]"); - if (it.hasNext()) - sb.append(" "); - } - return sb.toString(); -} - -/** Converts the RRset to a String */ -public String -toString() { - if (rrs == null) - return ("{empty}"); - StringBuffer sb = new StringBuffer(); - sb.append("{ "); - sb.append(getName() + " "); - sb.append(getTTL() + " "); - sb.append(DClass.string(getDClass()) + " "); - sb.append(Type.string(getType()) + " "); - sb.append(iteratorToString(iterator(true, false))); - if (nsigs > 0) { - sb.append(" sigs: "); - sb.append(iteratorToString(iterator(false, false))); - } - sb.append(" }"); - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/RTRecord.java b/src/main/java/org/xbill/DNS/RTRecord.java deleted file mode 100644 index 549731ec7..000000000 --- a/src/main/java/org/xbill/DNS/RTRecord.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Route Through Record - lists a route preference and intermediate host. - * - * @author Brian Wellington - */ - -public class RTRecord extends U16NameBase { - -private static final long serialVersionUID = -3206215651648278098L; - -RTRecord() {} - -Record -getObject() { - return new RTRecord(); -} - -/** - * Creates an RT Record from the given data - * @param preference The preference of the route. Smaller numbers indicate - * more preferred routes. - * @param intermediateHost The domain name of the host to use as a router. - */ -public -RTRecord(Name name, int dclass, long ttl, int preference, - Name intermediateHost) -{ - super(name, Type.RT, dclass, ttl, preference, "preference", - intermediateHost, "intermediateHost"); -} - -/** Gets the preference of the route. */ -public int -getPreference() { - return getU16Field(); -} - -/** Gets the host to use as a router. */ -public Name -getIntermediateHost() { - return getNameField(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Rcode.java b/src/main/java/org/xbill/DNS/Rcode.java deleted file mode 100644 index 7f0dd1f13..000000000 --- a/src/main/java/org/xbill/DNS/Rcode.java +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants and functions relating to DNS rcodes (error values) - * - * @author Brian Wellington - */ - -public final class Rcode { - -private static Mnemonic rcodes = new Mnemonic("DNS Rcode", - Mnemonic.CASE_UPPER); - -private static Mnemonic tsigrcodes = new Mnemonic("TSIG rcode", - Mnemonic.CASE_UPPER); - -/** No error */ -public static final int NOERROR = 0; - -/** Format error */ -public static final int FORMERR = 1; - -/** Server failure */ -public static final int SERVFAIL = 2; - -/** The name does not exist */ -public static final int NXDOMAIN = 3; - -/** The operation requested is not implemented */ -public static final int NOTIMP = 4; - -/** Deprecated synonym for NOTIMP. */ -public static final int NOTIMPL = 4; - -/** The operation was refused by the server */ -public static final int REFUSED = 5; - -/** The name exists */ -public static final int YXDOMAIN = 6; - -/** The RRset (name, type) exists */ -public static final int YXRRSET = 7; - -/** The RRset (name, type) does not exist */ -public static final int NXRRSET = 8; - -/** The requestor is not authorized to perform this operation */ -public static final int NOTAUTH = 9; - -/** The zone specified is not a zone */ -public static final int NOTZONE = 10; - -/* EDNS extended rcodes */ -/** Unsupported EDNS level */ -public static final int BADVERS = 16; - -/* TSIG/TKEY only rcodes */ -/** The signature is invalid (TSIG/TKEY extended error) */ -public static final int BADSIG = 16; - -/** The key is invalid (TSIG/TKEY extended error) */ -public static final int BADKEY = 17; - -/** The time is out of range (TSIG/TKEY extended error) */ -public static final int BADTIME = 18; - -/** The mode is invalid (TKEY extended error) */ -public static final int BADMODE = 19; - -static { - rcodes.setMaximum(0xFFF); - rcodes.setPrefix("RESERVED"); - rcodes.setNumericAllowed(true); - - rcodes.add(NOERROR, "NOERROR"); - rcodes.add(FORMERR, "FORMERR"); - rcodes.add(SERVFAIL, "SERVFAIL"); - rcodes.add(NXDOMAIN, "NXDOMAIN"); - rcodes.add(NOTIMP, "NOTIMP"); - rcodes.addAlias(NOTIMP, "NOTIMPL"); - rcodes.add(REFUSED, "REFUSED"); - rcodes.add(YXDOMAIN, "YXDOMAIN"); - rcodes.add(YXRRSET, "YXRRSET"); - rcodes.add(NXRRSET, "NXRRSET"); - rcodes.add(NOTAUTH, "NOTAUTH"); - rcodes.add(NOTZONE, "NOTZONE"); - rcodes.add(BADVERS, "BADVERS"); - - tsigrcodes.setMaximum(0xFFFF); - tsigrcodes.setPrefix("RESERVED"); - tsigrcodes.setNumericAllowed(true); - tsigrcodes.addAll(rcodes); - - tsigrcodes.add(BADSIG, "BADSIG"); - tsigrcodes.add(BADKEY, "BADKEY"); - tsigrcodes.add(BADTIME, "BADTIME"); - tsigrcodes.add(BADMODE, "BADMODE"); -} - -private -Rcode() {} - -/** Converts a numeric Rcode into a String */ -public static String -string(int i) { - return rcodes.getText(i); -} - -/** Converts a numeric TSIG extended Rcode into a String */ -public static String -TSIGstring(int i) { - return tsigrcodes.getText(i); -} - -/** Converts a String representation of an Rcode into its numeric value */ -public static int -value(String s) { - return rcodes.getValue(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/Record.java b/src/main/java/org/xbill/DNS/Record.java deleted file mode 100644 index 7393275b9..000000000 --- a/src/main/java/org/xbill/DNS/Record.java +++ /dev/null @@ -1,733 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.Serializable; -import java.text.DecimalFormat; -import java.util.Arrays; - -/** - * A generic DNS resource record. The specific record types extend this class. - * A record contains a name, type, class, ttl, and rdata. - * - * @author Brian Wellington - */ - -public abstract class Record implements Cloneable, Comparable, Serializable { - -private static final long serialVersionUID = 2694906050116005466L; - -protected Name name; -protected int type, dclass; -protected long ttl; - -private static final DecimalFormat byteFormat = new DecimalFormat(); - -static { - byteFormat.setMinimumIntegerDigits(3); -} - -protected -Record() {} - -Record(Name name, int type, int dclass, long ttl) { - if (!name.isAbsolute()) - throw new RelativeNameException(name); - Type.check(type); - DClass.check(dclass); - TTL.check(ttl); - this.name = name; - this.type = type; - this.dclass = dclass; - this.ttl = ttl; -} - -/** - * Creates an empty record of the correct type; must be overriden - */ -abstract Record -getObject(); - -private static final Record -getEmptyRecord(Name name, int type, int dclass, long ttl, boolean hasData) { - Record proto, rec; - - if (hasData) { - proto = Type.getProto(type); - if (proto != null) - rec = proto.getObject(); - else - rec = new UNKRecord(); - } else - rec = new EmptyRecord(); - rec.name = name; - rec.type = type; - rec.dclass = dclass; - rec.ttl = ttl; - return rec; -} - -/** - * Converts the type-specific RR to wire format - must be overriden - */ -abstract void -rrFromWire(DNSInput in) throws IOException; - -private static Record -newRecord(Name name, int type, int dclass, long ttl, int length, DNSInput in) -throws IOException -{ - Record rec; - rec = getEmptyRecord(name, type, dclass, ttl, in != null); - if (in != null) { - if (in.remaining() < length) - throw new WireParseException("truncated record"); - in.setActive(length); - - rec.rrFromWire(in); - - if (in.remaining() > 0) - throw new WireParseException("invalid record length"); - in.clearActive(); - } - return rec; -} - -/** - * Creates a new record, with the given parameters. - * @param name The owner name of the record. - * @param type The record's type. - * @param dclass The record's class. - * @param ttl The record's time to live. - * @param length The length of the record's data. - * @param data The rdata of the record, in uncompressed DNS wire format. Only - * the first length bytes are used. - */ -public static Record -newRecord(Name name, int type, int dclass, long ttl, int length, byte [] data) { - if (!name.isAbsolute()) - throw new RelativeNameException(name); - Type.check(type); - DClass.check(dclass); - TTL.check(ttl); - - DNSInput in; - if (data != null) - in = new DNSInput(data); - else - in = null; - try { - return newRecord(name, type, dclass, ttl, length, in); - } - catch (IOException e) { - return null; - } -} - -/** - * Creates a new record, with the given parameters. - * @param name The owner name of the record. - * @param type The record's type. - * @param dclass The record's class. - * @param ttl The record's time to live. - * @param data The complete rdata of the record, in uncompressed DNS wire - * format. - */ -public static Record -newRecord(Name name, int type, int dclass, long ttl, byte [] data) { - return newRecord(name, type, dclass, ttl, data.length, data); -} - -/** - * Creates a new empty record, with the given parameters. - * @param name The owner name of the record. - * @param type The record's type. - * @param dclass The record's class. - * @param ttl The record's time to live. - * @return An object of a subclass of Record - */ -public static Record -newRecord(Name name, int type, int dclass, long ttl) { - if (!name.isAbsolute()) - throw new RelativeNameException(name); - Type.check(type); - DClass.check(dclass); - TTL.check(ttl); - - return getEmptyRecord(name, type, dclass, ttl, false); -} - -/** - * Creates a new empty record, with the given parameters. This method is - * designed to create records that will be added to the QUERY section - * of a message. - * @param name The owner name of the record. - * @param type The record's type. - * @param dclass The record's class. - * @return An object of a subclass of Record - */ -public static Record -newRecord(Name name, int type, int dclass) { - return newRecord(name, type, dclass, 0); -} - -static Record -fromWire(DNSInput in, int section, boolean isUpdate) throws IOException { - int type, dclass; - long ttl; - int length; - Name name; - Record rec; - - name = new Name(in); - type = in.readU16(); - dclass = in.readU16(); - - if (section == Section.QUESTION) - return newRecord(name, type, dclass); - - ttl = in.readU32(); - length = in.readU16(); - if (length == 0 && isUpdate) - return newRecord(name, type, dclass, ttl); - rec = newRecord(name, type, dclass, ttl, length, in); - return rec; -} - -static Record -fromWire(DNSInput in, int section) throws IOException { - return fromWire(in, section, false); -} - -/** - * Builds a Record from DNS uncompressed wire format. - */ -public static Record -fromWire(byte [] b, int section) throws IOException { - return fromWire(new DNSInput(b), section, false); -} - -void -toWire(DNSOutput out, int section, Compression c) { - name.toWire(out, c); - out.writeU16(type); - out.writeU16(dclass); - if (section == Section.QUESTION) - return; - out.writeU32(ttl); - int lengthPosition = out.current(); - out.writeU16(0); /* until we know better */ - rrToWire(out, c, false); - int rrlength = out.current() - lengthPosition - 2; - out.save(); - out.jump(lengthPosition); - out.writeU16(rrlength); - out.restore(); -} - -/** - * Converts a Record into DNS uncompressed wire format. - */ -public byte [] -toWire(int section) { - DNSOutput out = new DNSOutput(); - toWire(out, section, null); - return out.toByteArray(); -} - -private void -toWireCanonical(DNSOutput out, boolean noTTL) { - name.toWireCanonical(out); - out.writeU16(type); - out.writeU16(dclass); - if (noTTL) { - out.writeU32(0); - } else { - out.writeU32(ttl); - } - int lengthPosition = out.current(); - out.writeU16(0); /* until we know better */ - rrToWire(out, null, true); - int rrlength = out.current() - lengthPosition - 2; - out.save(); - out.jump(lengthPosition); - out.writeU16(rrlength); - out.restore(); -} - -/* - * Converts a Record into canonical DNS uncompressed wire format (all names are - * converted to lowercase), optionally ignoring the TTL. - */ -private byte [] -toWireCanonical(boolean noTTL) { - DNSOutput out = new DNSOutput(); - toWireCanonical(out, noTTL); - return out.toByteArray(); -} - -/** - * Converts a Record into canonical DNS uncompressed wire format (all names are - * converted to lowercase). - */ -public byte [] -toWireCanonical() { - return toWireCanonical(false); -} - -/** - * Converts the rdata in a Record into canonical DNS uncompressed wire format - * (all names are converted to lowercase). - */ -public byte [] -rdataToWireCanonical() { - DNSOutput out = new DNSOutput(); - rrToWire(out, null, true); - return out.toByteArray(); -} - -/** - * Converts the type-specific RR to text format - must be overriden - */ -abstract String rrToString(); - -/** - * Converts the rdata portion of a Record into a String representation - */ -public String -rdataToString() { - return rrToString(); -} - -/** - * Converts a Record into a String representation - */ -public String -toString() { - StringBuffer sb = new StringBuffer(); - sb.append(name); - if (sb.length() < 8) - sb.append("\t"); - if (sb.length() < 16) - sb.append("\t"); - sb.append("\t"); - if (Options.check("BINDTTL")) - sb.append(TTL.format(ttl)); - else - sb.append(ttl); - sb.append("\t"); - if (dclass != DClass.IN || !Options.check("noPrintIN")) { - sb.append(DClass.string(dclass)); - sb.append("\t"); - } - sb.append(Type.string(type)); - String rdata = rrToString(); - if (!rdata.equals("")) { - sb.append("\t"); - sb.append(rdata); - } - return sb.toString(); -} - -/** - * Converts the text format of an RR to the internal format - must be overriden - */ -abstract void -rdataFromString(Tokenizer st, Name origin) throws IOException; - -/** - * Converts a String into a byte array. - */ -protected static byte [] -byteArrayFromString(String s) throws TextParseException { - byte [] array = s.getBytes(); - boolean escaped = false; - boolean hasEscapes = false; - - for (int i = 0; i < array.length; i++) { - if (array[i] == '\\') { - hasEscapes = true; - break; - } - } - if (!hasEscapes) { - if (array.length > 255) { - throw new TextParseException("text string too long"); - } - return array; - } - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - int digits = 0; - int intval = 0; - for (int i = 0; i < array.length; i++) { - byte b = array[i]; - if (escaped) { - if (b >= '0' && b <= '9' && digits < 3) { - digits++; - intval *= 10; - intval += (b - '0'); - if (intval > 255) - throw new TextParseException - ("bad escape"); - if (digits < 3) - continue; - b = (byte) intval; - } - else if (digits > 0 && digits < 3) - throw new TextParseException("bad escape"); - os.write(b); - escaped = false; - } - else if (array[i] == '\\') { - escaped = true; - digits = 0; - intval = 0; - } - else - os.write(array[i]); - } - if (digits > 0 && digits < 3) - throw new TextParseException("bad escape"); - array = os.toByteArray(); - if (array.length > 255) { - throw new TextParseException("text string too long"); - } - - return os.toByteArray(); -} - -/** - * Converts a byte array into a String. - */ -protected static String -byteArrayToString(byte [] array, boolean quote) { - StringBuffer sb = new StringBuffer(); - if (quote) - sb.append('"'); - for (int i = 0; i < array.length; i++) { - int b = array[i] & 0xFF; - if (b < 0x20 || b >= 0x7f) { - sb.append('\\'); - sb.append(byteFormat.format(b)); - } else if (b == '"' || b == '\\') { - sb.append('\\'); - sb.append((char)b); - } else - sb.append((char)b); - } - if (quote) - sb.append('"'); - return sb.toString(); -} - -/** - * Converts a byte array into the unknown RR format. - */ -protected static String -unknownToString(byte [] data) { - StringBuffer sb = new StringBuffer(); - sb.append("\\# "); - sb.append(data.length); - sb.append(" "); - sb.append(base16.toString(data)); - return sb.toString(); -} - -/** - * Builds a new Record from its textual representation - * @param name The owner name of the record. - * @param type The record's type. - * @param dclass The record's class. - * @param ttl The record's time to live. - * @param st A tokenizer containing the textual representation of the rdata. - * @param origin The default origin to be appended to relative domain names. - * @return The new record - * @throws IOException The text format was invalid. - */ -public static Record -fromString(Name name, int type, int dclass, long ttl, Tokenizer st, Name origin) -throws IOException -{ - Record rec; - - if (!name.isAbsolute()) - throw new RelativeNameException(name); - Type.check(type); - DClass.check(dclass); - TTL.check(ttl); - - Tokenizer.Token t = st.get(); - if (t.type == Tokenizer.IDENTIFIER && t.value.equals("\\#")) { - int length = st.getUInt16(); - byte [] data = st.getHex(); - if (data == null) { - data = new byte[0]; - } - if (length != data.length) - throw st.exception("invalid unknown RR encoding: " + - "length mismatch"); - DNSInput in = new DNSInput(data); - return newRecord(name, type, dclass, ttl, length, in); - } - st.unget(); - rec = getEmptyRecord(name, type, dclass, ttl, true); - rec.rdataFromString(st, origin); - t = st.get(); - if (t.type != Tokenizer.EOL && t.type != Tokenizer.EOF) { - throw st.exception("unexpected tokens at end of record"); - } - return rec; -} - -/** - * Builds a new Record from its textual representation - * @param name The owner name of the record. - * @param type The record's type. - * @param dclass The record's class. - * @param ttl The record's time to live. - * @param s The textual representation of the rdata. - * @param origin The default origin to be appended to relative domain names. - * @return The new record - * @throws IOException The text format was invalid. - */ -public static Record -fromString(Name name, int type, int dclass, long ttl, String s, Name origin) -throws IOException -{ - return fromString(name, type, dclass, ttl, new Tokenizer(s), origin); -} - -/** - * Returns the record's name - * @see Name - */ -public Name -getName() { - return name; -} - -/** - * Returns the record's type - * @see Type - */ -public int -getType() { - return type; -} - -/** - * Returns the type of RRset that this record would belong to. For all types - * except RRSIGRecord, this is equivalent to getType(). - * @return The type of record, if not SIGRecord. If the type is RRSIGRecord, - * the type covered is returned. - * @see Type - * @see RRset - * @see SIGRecord - */ -public int -getRRsetType() { - if (type == Type.RRSIG) { - RRSIGRecord sig = (RRSIGRecord) this; - return sig.getTypeCovered(); - } - return type; -} - -/** - * Returns the record's class - */ -public int -getDClass() { - return dclass; -} - -/** - * Returns the record's TTL - */ -public long -getTTL() { - return ttl; -} - -/** - * Converts the type-specific RR to wire format - must be overriden - */ -abstract void -rrToWire(DNSOutput out, Compression c, boolean canonical); - -/** - * Determines if two Records could be part of the same RRset. - * This compares the name, type, and class of the Records; the ttl and - * rdata are not compared. - */ -public boolean -sameRRset(Record rec) { - return (getRRsetType() == rec.getRRsetType() && - dclass == rec.dclass && - name.equals(rec.name)); -} - -/** - * Determines if two Records are identical. This compares the name, type, - * class, and rdata (with names canonicalized). The TTLs are not compared. - * @param arg The record to compare to - * @return true if the records are equal, false otherwise. - */ -public boolean -equals(Object arg) { - if (arg == null || !(arg instanceof Record)) - return false; - Record r = (Record) arg; - if (type != r.type || dclass != r.dclass || !name.equals(r.name)) - return false; - byte [] array1 = rdataToWireCanonical(); - byte [] array2 = r.rdataToWireCanonical(); - return Arrays.equals(array1, array2); -} - -/** - * Generates a hash code based on the Record's data. - */ -public int -hashCode() { - byte [] array = toWireCanonical(true); - int code = 0; - for (int i = 0; i < array.length; i++) - code += ((code << 3) + (array[i] & 0xFF)); - return code; -} - -Record -cloneRecord() { - try { - return (Record) clone(); - } - catch (CloneNotSupportedException e) { - throw new IllegalStateException(); - } -} - -/** - * Creates a new record identical to the current record, but with a different - * name. This is most useful for replacing the name of a wildcard record. - */ -public Record -withName(Name name) { - if (!name.isAbsolute()) - throw new RelativeNameException(name); - Record rec = cloneRecord(); - rec.name = name; - return rec; -} - -/** - * Creates a new record identical to the current record, but with a different - * class and ttl. This is most useful for dynamic update. - */ -Record -withDClass(int dclass, long ttl) { - Record rec = cloneRecord(); - rec.dclass = dclass; - rec.ttl = ttl; - return rec; -} - -/* Sets the TTL to the specified value. This is intentionally not public. */ -void -setTTL(long ttl) { - this.ttl = ttl; -} - -/** - * Compares this Record to another Object. - * @param o The Object to be compared. - * @return The value 0 if the argument is a record equivalent to this record; - * a value less than 0 if the argument is less than this record in the - * canonical ordering, and a value greater than 0 if the argument is greater - * than this record in the canonical ordering. The canonical ordering - * is defined to compare by name, class, type, and rdata. - * @throws ClassCastException if the argument is not a Record. - */ -public int -compareTo(Object o) { - Record arg = (Record) o; - - if (this == arg) - return (0); - - int n = name.compareTo(arg.name); - if (n != 0) - return (n); - n = dclass - arg.dclass; - if (n != 0) - return (n); - n = type - arg.type; - if (n != 0) - return (n); - byte [] rdata1 = rdataToWireCanonical(); - byte [] rdata2 = arg.rdataToWireCanonical(); - for (int i = 0; i < rdata1.length && i < rdata2.length; i++) { - n = (rdata1[i] & 0xFF) - (rdata2[i] & 0xFF); - if (n != 0) - return (n); - } - return (rdata1.length - rdata2.length); -} - -/** - * Returns the name for which additional data processing should be done - * for this record. This can be used both for building responses and - * parsing responses. - * @return The name to used for additional data processing, or null if this - * record type does not require additional data processing. - */ -public Name -getAdditionalName() { - return null; -} - -/* Checks that an int contains an unsigned 8 bit value */ -static int -checkU8(String field, int val) { - if (val < 0 || val > 0xFF) - throw new IllegalArgumentException("\"" + field + "\" " + val + - " must be an unsigned 8 " + - "bit value"); - return val; -} - -/* Checks that an int contains an unsigned 16 bit value */ -static int -checkU16(String field, int val) { - if (val < 0 || val > 0xFFFF) - throw new IllegalArgumentException("\"" + field + "\" " + val + - " must be an unsigned 16 " + - "bit value"); - return val; -} - -/* Checks that a long contains an unsigned 32 bit value */ -static long -checkU32(String field, long val) { - if (val < 0 || val > 0xFFFFFFFFL) - throw new IllegalArgumentException("\"" + field + "\" " + val + - " must be an unsigned 32 " + - "bit value"); - return val; -} - -/* Checks that a name is absolute */ -static Name -checkName(String field, Name name) { - if (!name.isAbsolute()) - throw new RelativeNameException(name); - return name; -} - -} diff --git a/src/main/java/org/xbill/DNS/RelativeNameException.java b/src/main/java/org/xbill/DNS/RelativeNameException.java deleted file mode 100644 index 869fd39bb..000000000 --- a/src/main/java/org/xbill/DNS/RelativeNameException.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An exception thrown when a relative name is passed as an argument to - * a method requiring an absolute name. - * - * @author Brian Wellington - */ - -public class RelativeNameException extends IllegalArgumentException { - -public -RelativeNameException(Name name) { - super("'" + name + "' is not an absolute name"); -} - -public -RelativeNameException(String s) { - super(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/ResolveThread.java b/src/main/java/org/xbill/DNS/ResolveThread.java deleted file mode 100644 index 3087cdbf0..000000000 --- a/src/main/java/org/xbill/DNS/ResolveThread.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * A special-purpose thread used by Resolvers (both SimpleResolver and - * ExtendedResolver) to perform asynchronous queries. - * - * @author Brian Wellington - */ - -class ResolveThread extends Thread { - -private Message query; -private Object id; -private ResolverListener listener; -private Resolver res; - -/** Creates a new ResolveThread */ -public -ResolveThread(Resolver res, Message query, Object id, - ResolverListener listener) -{ - this.res = res; - this.query = query; - this.id = id; - this.listener = listener; -} - - -/** - * Performs the query, and executes the callback. - */ -public void -run() { - try { - Message response = res.send(query); - listener.receiveMessage(id, response); - } - catch (Exception e) { - listener.handleException(id, e); - } -} - -} diff --git a/src/main/java/org/xbill/DNS/Resolver.java b/src/main/java/org/xbill/DNS/Resolver.java deleted file mode 100644 index dd614d543..000000000 --- a/src/main/java/org/xbill/DNS/Resolver.java +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.List; - -/** - * Interface describing a resolver. - * - * @author Brian Wellington - */ - -public interface Resolver { - -/** - * Sets the port to communicate with on the server - * @param port The port to send messages to - */ -void setPort(int port); - -/** - * Sets whether TCP connections will be sent by default - * @param flag Indicates whether TCP connections are made - */ -void setTCP(boolean flag); - -/** - * Sets whether truncated responses will be ignored. If not, a truncated - * response over UDP will cause a retransmission over TCP. - * @param flag Indicates whether truncated responses should be ignored. - */ -void setIgnoreTruncation(boolean flag); - -/** - * Sets the EDNS version used on outgoing messages. - * @param level The EDNS level to use. 0 indicates EDNS0 and -1 indicates no - * EDNS. - * @throws IllegalArgumentException An invalid level was indicated. - */ -void setEDNS(int level); - -/** - * Sets the EDNS information on outgoing messages. - * @param level The EDNS level to use. 0 indicates EDNS0 and -1 indicates no - * EDNS. - * @param payloadSize The maximum DNS packet size that this host is capable - * of receiving over UDP. If 0 is specified, the default (1280) is used. - * @param flags EDNS extended flags to be set in the OPT record. - * @param options EDNS options to be set in the OPT record, specified as a - * List of OPTRecord.Option elements. - * @throws IllegalArgumentException An invalid field was specified. - * @see OPTRecord - */ -void setEDNS(int level, int payloadSize, int flags, List options); - -/** - * Specifies the TSIG key that messages will be signed with - * @param key The key - */ -void setTSIGKey(TSIG key); - -/** - * Sets the amount of time to wait for a response before giving up. - * @param secs The number of seconds to wait. - * @param msecs The number of milliseconds to wait. - */ -void setTimeout(int secs, int msecs); - -/** - * Sets the amount of time to wait for a response before giving up. - * @param secs The number of seconds to wait. - */ -void setTimeout(int secs); - -/** - * Sends a message and waits for a response. - * @param query The query to send. - * @return The response - * @throws IOException An error occurred while sending or receiving. - */ -Message send(Message query) throws IOException; - -/** - * Asynchronously sends a message registering a listener to receive a callback - * on success or exception. Multiple asynchronous lookups can be performed - * in parallel. Since the callback may be invoked before the function returns, - * external synchronization is necessary. - * @param query The query to send - * @param listener The object containing the callbacks. - * @return An identifier, which is also a parameter in the callback - */ -Object sendAsync(final Message query, final ResolverListener listener); - -} diff --git a/src/main/java/org/xbill/DNS/ResolverConfig.java b/src/main/java/org/xbill/DNS/ResolverConfig.java deleted file mode 100644 index c0bbdf44e..000000000 --- a/src/main/java/org/xbill/DNS/ResolverConfig.java +++ /dev/null @@ -1,440 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.*; -import java.lang.reflect.Method; -import java.util.*; - -/** - * A class that tries to locate name servers and the search path to - * be appended to unqualified names. - * - * The following are attempted, in order, until one succeeds. - *

      - *
    • The properties 'dns.server' and 'dns.search' (comma delimited lists) - * are checked. The servers can either be IP addresses or hostnames - * (which are resolved using Java's built in DNS support). - *
    • The sun.net.dns.ResolverConfiguration class is queried. - *
    • On Unix, /etc/resolv.conf is parsed. - *
    • On Windows, ipconfig/winipcfg is called and its output parsed. This - * may fail for non-English versions on Windows. - *
    • "localhost" is used as the nameserver, and the search path is empty. - *
    - * - * These routines will be called internally when creating Resolvers/Lookups - * without explicitly specifying server names, and can also be called - * directly if desired. - * - * @author Brian Wellington - * @author Yannick Meudal - * @author Arnt Gulbrandsen - */ - -public class ResolverConfig { - -private String [] servers = null; -private Name [] searchlist = null; - -private static ResolverConfig currentConfig; - -static { - refresh(); -} - -public -ResolverConfig() { - if (findProperty()) - return; - if (findSunJVM()) - return; - if (servers == null || searchlist == null) { - String OS = System.getProperty("os.name"); - String vendor = System.getProperty("java.vendor"); - if (OS.indexOf("Windows") != -1) { - if (OS.indexOf("95") != -1 || - OS.indexOf("98") != -1 || - OS.indexOf("ME") != -1) - find95(); - else - findNT(); - } else if (OS.indexOf("NetWare") != -1) { - findNetware(); - } else if (vendor.indexOf("Android") != -1) { - findAndroid(); - } else { - findUnix(); - } - } -} - -private void -addServer(String server, List list) { - if (list.contains(server)) - return; - if (Options.check("verbose")) - System.out.println("adding server " + server); - list.add(server); -} - -private void -addSearch(String search, List list) { - Name name; - if (Options.check("verbose")) - System.out.println("adding search " + search); - try { - name = Name.fromString(search, Name.root); - } - catch (TextParseException e) { - return; - } - if (list.contains(name)) - return; - list.add(name); -} - -private void -configureFromLists(List lserver, List lsearch) { - if (servers == null && lserver.size() > 0) - servers = (String []) lserver.toArray(new String[0]); - if (searchlist == null && lsearch.size() > 0) - searchlist = (Name []) lsearch.toArray(new Name[0]); -} - -/** - * Looks in the system properties to find servers and a search path. - * Servers are defined by dns.server=server1,server2... - * The search path is defined by dns.search=domain1,domain2... - */ -private boolean -findProperty() { - String prop; - List lserver = new ArrayList(0); - List lsearch = new ArrayList(0); - StringTokenizer st; - - prop = System.getProperty("dns.server"); - if (prop != null) { - st = new StringTokenizer(prop, ","); - while (st.hasMoreTokens()) - addServer(st.nextToken(), lserver); - } - - prop = System.getProperty("dns.search"); - if (prop != null) { - st = new StringTokenizer(prop, ","); - while (st.hasMoreTokens()) - addSearch(st.nextToken(), lsearch); - } - configureFromLists(lserver, lsearch); - return (servers != null && searchlist != null); -} - -/** - * Uses the undocumented Sun DNS implementation to determine the configuration. - * This doesn't work or even compile with all JVMs (gcj, for example). - */ -private boolean -findSunJVM() { - List lserver = new ArrayList(0); - List lserver_tmp; - List lsearch = new ArrayList(0); - List lsearch_tmp; - - try { - Class [] noClasses = new Class[0]; - Object [] noObjects = new Object[0]; - String resConfName = "sun.net.dns.ResolverConfiguration"; - Class resConfClass = Class.forName(resConfName); - Object resConf; - - // ResolverConfiguration resConf = ResolverConfiguration.open(); - Method open = resConfClass.getDeclaredMethod("open", noClasses); - resConf = open.invoke(null, noObjects); - - // lserver_tmp = resConf.nameservers(); - Method nameservers = resConfClass.getMethod("nameservers", - noClasses); - lserver_tmp = (List) nameservers.invoke(resConf, noObjects); - - // lsearch_tmp = resConf.searchlist(); - Method searchlist = resConfClass.getMethod("searchlist", - noClasses); - lsearch_tmp = (List) searchlist.invoke(resConf, noObjects); - } - catch (Exception e) { - return false; - } - - if (lserver_tmp.size() == 0) - return false; - - if (lserver_tmp.size() > 0) { - Iterator it = lserver_tmp.iterator(); - while (it.hasNext()) - addServer((String) it.next(), lserver); - } - - if (lsearch_tmp.size() > 0) { - Iterator it = lsearch_tmp.iterator(); - while (it.hasNext()) - addSearch((String) it.next(), lsearch); - } - configureFromLists(lserver, lsearch); - return true; -} - -/** - * Looks in /etc/resolv.conf to find servers and a search path. - * "nameserver" lines specify servers. "domain" and "search" lines - * define the search path. - */ -private void -findResolvConf(String file) { - InputStream in = null; - try { - in = new FileInputStream(file); - } - catch (FileNotFoundException e) { - return; - } - InputStreamReader isr = new InputStreamReader(in); - BufferedReader br = new BufferedReader(isr); - List lserver = new ArrayList(0); - List lsearch = new ArrayList(0); - try { - String line; - while ((line = br.readLine()) != null) { - if (line.startsWith("nameserver")) { - StringTokenizer st = new StringTokenizer(line); - st.nextToken(); /* skip nameserver */ - addServer(st.nextToken(), lserver); - } - else if (line.startsWith("domain")) { - StringTokenizer st = new StringTokenizer(line); - st.nextToken(); /* skip domain */ - if (!st.hasMoreTokens()) - continue; - if (lsearch.isEmpty()) - addSearch(st.nextToken(), lsearch); - } - else if (line.startsWith("search")) { - if (!lsearch.isEmpty()) - lsearch.clear(); - StringTokenizer st = new StringTokenizer(line); - st.nextToken(); /* skip search */ - while (st.hasMoreTokens()) - addSearch(st.nextToken(), lsearch); - } - } - br.close(); - } - catch (IOException e) { - } - - configureFromLists(lserver, lsearch); -} - -private void -findUnix() { - findResolvConf("/etc/resolv.conf"); -} - -private void -findNetware() { - findResolvConf("sys:/etc/resolv.cfg"); -} - -/** - * Parses the output of winipcfg or ipconfig. - */ -private void -findWin(InputStream in) { - String packageName = ResolverConfig.class.getPackage().getName(); - String resPackageName = packageName + ".windows.DNSServer"; - ResourceBundle res = ResourceBundle.getBundle(resPackageName); - - String host_name = res.getString("host_name"); - String primary_dns_suffix = res.getString("primary_dns_suffix"); - String dns_suffix = res.getString("dns_suffix"); - String dns_servers = res.getString("dns_servers"); - - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - try { - List lserver = new ArrayList(); - List lsearch = new ArrayList(); - String line = null; - boolean readingServers = false; - boolean readingSearches = false; - while ((line = br.readLine()) != null) { - StringTokenizer st = new StringTokenizer(line); - if (!st.hasMoreTokens()) { - readingServers = false; - readingSearches = false; - continue; - } - String s = st.nextToken(); - if (line.indexOf(":") != -1) { - readingServers = false; - readingSearches = false; - } - - if (line.indexOf(host_name) != -1) { - while (st.hasMoreTokens()) - s = st.nextToken(); - Name name; - try { - name = Name.fromString(s, null); - } - catch (TextParseException e) { - continue; - } - if (name.labels() == 1) - continue; - addSearch(s, lsearch); - } else if (line.indexOf(primary_dns_suffix) != -1) { - while (st.hasMoreTokens()) - s = st.nextToken(); - if (s.equals(":")) - continue; - addSearch(s, lsearch); - readingSearches = true; - } else if (readingSearches || - line.indexOf(dns_suffix) != -1) - { - while (st.hasMoreTokens()) - s = st.nextToken(); - if (s.equals(":")) - continue; - addSearch(s, lsearch); - readingSearches = true; - } else if (readingServers || - line.indexOf(dns_servers) != -1) - { - while (st.hasMoreTokens()) - s = st.nextToken(); - if (s.equals(":")) - continue; - addServer(s, lserver); - readingServers = true; - } - } - - configureFromLists(lserver, lsearch); - } - catch (IOException e) { - } - finally { - try { - br.close(); - } - catch (IOException e) { - } - } - return; -} - -/** - * Calls winipcfg and parses the result to find servers and a search path. - */ -private void -find95() { - String s = "winipcfg.out"; - try { - Process p; - p = Runtime.getRuntime().exec("winipcfg /all /batch " + s); - p.waitFor(); - File f = new File(s); - findWin(new FileInputStream(f)); - new File(s).delete(); - } - catch (Exception e) { - return; - } -} - -/** - * Calls ipconfig and parses the result to find servers and a search path. - */ -private void -findNT() { - try { - Process p; - p = Runtime.getRuntime().exec("ipconfig /all"); - findWin(p.getInputStream()); - p.destroy(); - } - catch (Exception e) { - return; - } -} - -/** - * Parses the output of getprop, which is the only way to get DNS - * info on Android. getprop might disappear in future releases, so - * this code comes with a use-by date. - */ -private void -findAndroid() { - String re1 = "^\\d+(\\.\\d+){3}$"; - String re2 = "^[0-9a-f]+(:[0-9a-f]*)+:[0-9a-f]+$"; - try { - ArrayList maybe = new ArrayList(); - String line; - Process p = Runtime.getRuntime().exec("getprop"); - InputStream in = p.getInputStream(); - InputStreamReader isr = new InputStreamReader(in); - BufferedReader br = new BufferedReader(isr); - while ((line = br.readLine()) != null ) { - StringTokenizer t = new StringTokenizer( line, ":" ); - String name = t.nextToken(); - if (name.indexOf( ".dns" ) > -1) { - String v = t.nextToken(); - v = v.replaceAll( "[ \\[\\]]", "" ); - if ((v.matches(re1) || v.matches(re2)) && - !maybe.contains(v)) - maybe.add(v); - } - } - configureFromLists(maybe, null); - } catch ( Exception e ) { - // ignore resolutely - } -} - -/** Returns all located servers */ -public String [] -servers() { - return servers; -} - -/** Returns the first located server */ -public String -server() { - if (servers == null) - return null; - return servers[0]; -} - -/** Returns all entries in the located search path */ -public Name [] -searchPath() { - return searchlist; -} - -/** Gets the current configuration */ -public static synchronized ResolverConfig -getCurrentConfig() { - return currentConfig; -} - -/** Gets the current configuration */ -public static void -refresh() { - ResolverConfig newConfig = new ResolverConfig(); - synchronized (ResolverConfig.class) { - currentConfig = newConfig; - } -} - -} diff --git a/src/main/java/org/xbill/DNS/ResolverListener.java b/src/main/java/org/xbill/DNS/ResolverListener.java deleted file mode 100644 index accf82c59..000000000 --- a/src/main/java/org/xbill/DNS/ResolverListener.java +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.EventListener; - -/** - * An interface to the asynchronous resolver. - * @see Resolver - * - * @author Brian Wellington - */ - -public interface ResolverListener extends EventListener { - -/** - * The callback used by an asynchronous resolver - * @param id The identifier returned by Resolver.sendAsync() - * @param m The response message as returned by the Resolver - */ -void receiveMessage(Object id, Message m); - -/** - * The callback used by an asynchronous resolver when an exception is thrown - * @param id The identifier returned by Resolver.sendAsync() - * @param e The thrown exception - */ -void handleException(Object id, Exception e); - -} diff --git a/src/main/java/org/xbill/DNS/ReverseMap.java b/src/main/java/org/xbill/DNS/ReverseMap.java deleted file mode 100644 index 0068eb28c..000000000 --- a/src/main/java/org/xbill/DNS/ReverseMap.java +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * A set functions designed to deal with DNS names used in reverse mappings. - * For the IPv4 address a.b.c.d, the reverse map name is d.c.b.a.in-addr.arpa. - * For an IPv6 address, the reverse map name is ...ip6.arpa. - * - * @author Brian Wellington - */ - -public final class ReverseMap { - -private static Name inaddr4 = Name.fromConstantString("in-addr.arpa."); -private static Name inaddr6 = Name.fromConstantString("ip6.arpa."); - -/* Otherwise the class could be instantiated */ -private -ReverseMap() {} - -/** - * Creates a reverse map name corresponding to an address contained in - * an array of 4 bytes (for an IPv4 address) or 16 bytes (for an IPv6 address). - * @param addr The address from which to build a name. - * @return The name corresponding to the address in the reverse map. - */ -public static Name -fromAddress(byte [] addr) { - if (addr.length != 4 && addr.length != 16) - throw new IllegalArgumentException("array must contain " + - "4 or 16 elements"); - - StringBuffer sb = new StringBuffer(); - if (addr.length == 4) { - for (int i = addr.length - 1; i >= 0; i--) { - sb.append(addr[i] & 0xFF); - if (i > 0) - sb.append("."); - } - } else { - int [] nibbles = new int[2]; - for (int i = addr.length - 1; i >= 0; i--) { - nibbles[0] = (addr[i] & 0xFF) >> 4; - nibbles[1] = (addr[i] & 0xFF) & 0xF; - for (int j = nibbles.length - 1; j >= 0; j--) { - sb.append(Integer.toHexString(nibbles[j])); - if (i > 0 || j > 0) - sb.append("."); - } - } - } - - try { - if (addr.length == 4) - return Name.fromString(sb.toString(), inaddr4); - else - return Name.fromString(sb.toString(), inaddr6); - } - catch (TextParseException e) { - throw new IllegalStateException("name cannot be invalid"); - } -} - -/** - * Creates a reverse map name corresponding to an address contained in - * an array of 4 integers between 0 and 255 (for an IPv4 address) or 16 - * integers between 0 and 255 (for an IPv6 address). - * @param addr The address from which to build a name. - * @return The name corresponding to the address in the reverse map. - */ -public static Name -fromAddress(int [] addr) { - byte [] bytes = new byte[addr.length]; - for (int i = 0; i < addr.length; i++) { - if (addr[i] < 0 || addr[i] > 0xFF) - throw new IllegalArgumentException("array must " + - "contain values " + - "between 0 and 255"); - bytes[i] = (byte) addr[i]; - } - return fromAddress(bytes); -} - -/** - * Creates a reverse map name corresponding to an address contained in - * an InetAddress. - * @param addr The address from which to build a name. - * @return The name corresponding to the address in the reverse map. - */ -public static Name -fromAddress(InetAddress addr) { - return fromAddress(addr.getAddress()); -} - -/** - * Creates a reverse map name corresponding to an address contained in - * a String. - * @param addr The address from which to build a name. - * @return The name corresponding to the address in the reverse map. - * @throws UnknownHostException The string does not contain a valid address. - */ -public static Name -fromAddress(String addr, int family) throws UnknownHostException { - byte [] array = Address.toByteArray(addr, family); - if (array == null) - throw new UnknownHostException("Invalid IP address"); - return fromAddress(array); -} - -/** - * Creates a reverse map name corresponding to an address contained in - * a String. - * @param addr The address from which to build a name. - * @return The name corresponding to the address in the reverse map. - * @throws UnknownHostException The string does not contain a valid address. - */ -public static Name -fromAddress(String addr) throws UnknownHostException { - byte [] array = Address.toByteArray(addr, Address.IPv4); - if (array == null) - array = Address.toByteArray(addr, Address.IPv6); - if (array == null) - throw new UnknownHostException("Invalid IP address"); - return fromAddress(array); -} - -} diff --git a/src/main/java/org/xbill/DNS/SIGBase.java b/src/main/java/org/xbill/DNS/SIGBase.java deleted file mode 100644 index 7585c7887..000000000 --- a/src/main/java/org/xbill/DNS/SIGBase.java +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; -import java.util.Date; - -/** - * The base class for SIG/RRSIG records, which have identical formats - * - * @author Brian Wellington - */ - -abstract class SIGBase extends Record { - -private static final long serialVersionUID = -3738444391533812369L; - -protected int covered; -protected int alg, labels; -protected long origttl; -protected Date expire, timeSigned; -protected int footprint; -protected Name signer; -protected byte [] signature; - -protected -SIGBase() {} - -public -SIGBase(Name name, int type, int dclass, long ttl, int covered, int alg, - long origttl, Date expire, Date timeSigned, int footprint, Name signer, - byte [] signature) -{ - super(name, type, dclass, ttl); - Type.check(covered); - TTL.check(origttl); - this.covered = covered; - this.alg = checkU8("alg", alg); - this.labels = name.labels() - 1; - if (name.isWild()) - this.labels--; - this.origttl = origttl; - this.expire = expire; - this.timeSigned = timeSigned; - this.footprint = checkU16("footprint", footprint); - this.signer = checkName("signer", signer); - this.signature = signature; -} - -void -rrFromWire(DNSInput in) throws IOException { - covered = in.readU16(); - alg = in.readU8(); - labels = in.readU8(); - origttl = in.readU32(); - expire = new Date(1000 * in.readU32()); - timeSigned = new Date(1000 * in.readU32()); - footprint = in.readU16(); - signer = new Name(in); - signature = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - String typeString = st.getString(); - covered = Type.value(typeString); - if (covered < 0) - throw st.exception("Invalid type: " + typeString); - String algString = st.getString(); - alg = DNSSEC.Algorithm.value(algString); - if (alg < 0) - throw st.exception("Invalid algorithm: " + algString); - labels = st.getUInt8(); - origttl = st.getTTL(); - expire = FormattedTime.parse(st.getString()); - timeSigned = FormattedTime.parse(st.getString()); - footprint = st.getUInt16(); - signer = st.getName(origin); - signature = st.getBase64(); -} - -/** Converts the RRSIG/SIG Record to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append (Type.string(covered)); - sb.append (" "); - sb.append (alg); - sb.append (" "); - sb.append (labels); - sb.append (" "); - sb.append (origttl); - sb.append (" "); - if (Options.check("multiline")) - sb.append ("(\n\t"); - sb.append (FormattedTime.format(expire)); - sb.append (" "); - sb.append (FormattedTime.format(timeSigned)); - sb.append (" "); - sb.append (footprint); - sb.append (" "); - sb.append (signer); - if (Options.check("multiline")) { - sb.append("\n"); - sb.append(base64.formatString(signature, 64, "\t", - true)); - } else { - sb.append (" "); - sb.append(base64.toString(signature)); - } - return sb.toString(); -} - -/** Returns the RRset type covered by this signature */ -public int -getTypeCovered() { - return covered; -} - -/** - * Returns the cryptographic algorithm of the key that generated the signature - */ -public int -getAlgorithm() { - return alg; -} - -/** - * Returns the number of labels in the signed domain name. This may be - * different than the record's domain name if the record is a wildcard - * record. - */ -public int -getLabels() { - return labels; -} - -/** Returns the original TTL of the RRset */ -public long -getOrigTTL() { - return origttl; -} - -/** Returns the time at which the signature expires */ -public Date -getExpire() { - return expire; -} - -/** Returns the time at which this signature was generated */ -public Date -getTimeSigned() { - return timeSigned; -} - -/** Returns The footprint/key id of the signing key. */ -public int -getFootprint() { - return footprint; -} - -/** Returns the owner of the signing key */ -public Name -getSigner() { - return signer; -} - -/** Returns the binary data representing the signature */ -public byte [] -getSignature() { - return signature; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(covered); - out.writeU8(alg); - out.writeU8(labels); - out.writeU32(origttl); - out.writeU32(expire.getTime() / 1000); - out.writeU32(timeSigned.getTime() / 1000); - out.writeU16(footprint); - signer.toWire(out, null, canonical); - out.writeByteArray(signature); -} - -} diff --git a/src/main/java/org/xbill/DNS/SIGRecord.java b/src/main/java/org/xbill/DNS/SIGRecord.java deleted file mode 100644 index d18ea3edb..000000000 --- a/src/main/java/org/xbill/DNS/SIGRecord.java +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.Date; - -/** - * Signature - A SIG provides the digital signature of an RRset, so that - * the data can be authenticated by a DNSSEC-capable resolver. The - * signature is usually generated by a key contained in a KEYRecord - * @see RRset - * @see DNSSEC - * @see KEYRecord - * - * @author Brian Wellington - */ - -public class SIGRecord extends SIGBase { - -private static final long serialVersionUID = 4963556060953589058L; - -SIGRecord() {} - -Record -getObject() { - return new SIGRecord(); -} - -/** - * Creates an SIG Record from the given data - * @param covered The RRset type covered by this signature - * @param alg The cryptographic algorithm of the key that generated the - * signature - * @param origttl The original TTL of the RRset - * @param expire The time at which the signature expires - * @param timeSigned The time at which this signature was generated - * @param footprint The footprint/key id of the signing key. - * @param signer The owner of the signing key - * @param signature Binary data representing the signature - */ -public -SIGRecord(Name name, int dclass, long ttl, int covered, int alg, long origttl, - Date expire, Date timeSigned, int footprint, Name signer, - byte [] signature) -{ - super(name, Type.SIG, dclass, ttl, covered, alg, origttl, expire, - timeSigned, footprint, signer, signature); -} - -} diff --git a/src/main/java/org/xbill/DNS/SOARecord.java b/src/main/java/org/xbill/DNS/SOARecord.java deleted file mode 100644 index 96e3369fc..000000000 --- a/src/main/java/org/xbill/DNS/SOARecord.java +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Start of Authority - describes properties of a zone. - * - * @author Brian Wellington - */ - -public class SOARecord extends Record { - -private static final long serialVersionUID = 1049740098229303931L; - -private Name host, admin; -private long serial, refresh, retry, expire, minimum; - -SOARecord() {} - -Record -getObject() { - return new SOARecord(); -} - -/** - * Creates an SOA Record from the given data - * @param host The primary name server for the zone - * @param admin The zone administrator's address - * @param serial The zone's serial number - * @param refresh The amount of time until a secondary checks for a new serial - * number - * @param retry The amount of time between a secondary's checks for a new - * serial number - * @param expire The amount of time until a secondary expires a zone - * @param minimum The minimum TTL for records in the zone -*/ -public -SOARecord(Name name, int dclass, long ttl, Name host, Name admin, - long serial, long refresh, long retry, long expire, long minimum) -{ - super(name, Type.SOA, dclass, ttl); - this.host = checkName("host", host); - this.admin = checkName("admin", admin); - this.serial = checkU32("serial", serial); - this.refresh = checkU32("refresh", refresh); - this.retry = checkU32("retry", retry); - this.expire = checkU32("expire", expire); - this.minimum = checkU32("minimum", minimum); -} - -void -rrFromWire(DNSInput in) throws IOException { - host = new Name(in); - admin = new Name(in); - serial = in.readU32(); - refresh = in.readU32(); - retry = in.readU32(); - expire = in.readU32(); - minimum = in.readU32(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - host = st.getName(origin); - admin = st.getName(origin); - serial = st.getUInt32(); - refresh = st.getTTLLike(); - retry = st.getTTLLike(); - expire = st.getTTLLike(); - minimum = st.getTTLLike(); -} - -/** Convert to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(host); - sb.append(" "); - sb.append(admin); - if (Options.check("multiline")) { - sb.append(" (\n\t\t\t\t\t"); - sb.append(serial); - sb.append("\t; serial\n\t\t\t\t\t"); - sb.append(refresh); - sb.append("\t; refresh\n\t\t\t\t\t"); - sb.append(retry); - sb.append("\t; retry\n\t\t\t\t\t"); - sb.append(expire); - sb.append("\t; expire\n\t\t\t\t\t"); - sb.append(minimum); - sb.append(" )\t; minimum"); - } else { - sb.append(" "); - sb.append(serial); - sb.append(" "); - sb.append(refresh); - sb.append(" "); - sb.append(retry); - sb.append(" "); - sb.append(expire); - sb.append(" "); - sb.append(minimum); - } - return sb.toString(); -} - -/** Returns the primary name server */ -public Name -getHost() { - return host; -} - -/** Returns the zone administrator's address */ -public Name -getAdmin() { - return admin; -} - -/** Returns the zone's serial number */ -public long -getSerial() { - return serial; -} - -/** Returns the zone refresh interval */ -public long -getRefresh() { - return refresh; -} - -/** Returns the zone retry interval */ -public long -getRetry() { - return retry; -} - -/** Returns the time until a secondary expires a zone */ -public long -getExpire() { - return expire; -} - -/** Returns the minimum TTL for records in the zone */ -public long -getMinimum() { - return minimum; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - host.toWire(out, c, canonical); - admin.toWire(out, c, canonical); - out.writeU32(serial); - out.writeU32(refresh); - out.writeU32(retry); - out.writeU32(expire); - out.writeU32(minimum); -} - -} diff --git a/src/main/java/org/xbill/DNS/SPFRecord.java b/src/main/java/org/xbill/DNS/SPFRecord.java deleted file mode 100644 index 6e3d0ac9d..000000000 --- a/src/main/java/org/xbill/DNS/SPFRecord.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.List; - -/** - * Sender Policy Framework (RFC 4408, experimental) - * - * @author Brian Wellington - */ - -public class SPFRecord extends TXTBase { - -private static final long serialVersionUID = -2100754352801658722L; - -SPFRecord() {} - -Record -getObject() { - return new SPFRecord(); -} - -/** - * Creates a SPF Record from the given data - * @param strings The text strings - * @throws IllegalArgumentException One of the strings has invalid escapes - */ -public -SPFRecord(Name name, int dclass, long ttl, List strings) { - super(name, Type.SPF, dclass, ttl, strings); -} - -/** - * Creates a SPF Record from the given data - * @param string One text string - * @throws IllegalArgumentException The string has invalid escapes - */ -public -SPFRecord(Name name, int dclass, long ttl, String string) { - super(name, Type.SPF, dclass, ttl, string); -} - -} diff --git a/src/main/java/org/xbill/DNS/SRVRecord.java b/src/main/java/org/xbill/DNS/SRVRecord.java deleted file mode 100644 index 09613d736..000000000 --- a/src/main/java/org/xbill/DNS/SRVRecord.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Server Selection Record - finds hosts running services in a domain. An - * SRV record will normally be named ..domain - an - * example would be http.tcp.example.com (if HTTP used SRV records) - * - * @author Brian Wellington - */ - -public class SRVRecord extends Record { - -private static final long serialVersionUID = -3886460132387522052L; - -private int priority, weight, port; -private Name target; - -SRVRecord() {} - -Record -getObject() { - return new SRVRecord(); -} - -/** - * Creates an SRV Record from the given data - * @param priority The priority of this SRV. Records with lower priority - * are preferred. - * @param weight The weight, used to select between records at the same - * priority. - * @param port The TCP/UDP port that the service uses - * @param target The host running the service - */ -public -SRVRecord(Name name, int dclass, long ttl, int priority, - int weight, int port, Name target) -{ - super(name, Type.SRV, dclass, ttl); - this.priority = checkU16("priority", priority); - this.weight = checkU16("weight", weight); - this.port = checkU16("port", port); - this.target = checkName("target", target); -} - -void -rrFromWire(DNSInput in) throws IOException { - priority = in.readU16(); - weight = in.readU16(); - port = in.readU16(); - target = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - priority = st.getUInt16(); - weight = st.getUInt16(); - port = st.getUInt16(); - target = st.getName(origin); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(priority + " "); - sb.append(weight + " "); - sb.append(port + " "); - sb.append(target); - return sb.toString(); -} - -/** Returns the priority */ -public int -getPriority() { - return priority; -} - -/** Returns the weight */ -public int -getWeight() { - return weight; -} - -/** Returns the port that the service runs on */ -public int -getPort() { - return port; -} - -/** Returns the host running that the service */ -public Name -getTarget() { - return target; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(priority); - out.writeU16(weight); - out.writeU16(port); - target.toWire(out, null, canonical); -} - -public Name -getAdditionalName() { - return target; -} - -} diff --git a/src/main/java/org/xbill/DNS/SSHFPRecord.java b/src/main/java/org/xbill/DNS/SSHFPRecord.java deleted file mode 100644 index 86989818b..000000000 --- a/src/main/java/org/xbill/DNS/SSHFPRecord.java +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; - -import java.io.IOException; - -/** - * SSH Fingerprint - stores the fingerprint of an SSH host key. - * - * @author Brian Wellington - */ - -public class SSHFPRecord extends Record { - -private static final long serialVersionUID = -8104701402654687025L; - -public static class Algorithm { - private Algorithm() {} - - public static final int RSA = 1; - public static final int DSS = 2; -} - -public static class Digest { - private Digest() {} - - public static final int SHA1 = 1; -} - -private int alg; -private int digestType; -private byte [] fingerprint; - -SSHFPRecord() {} - -Record -getObject() { - return new SSHFPRecord(); -} - -/** - * Creates an SSHFP Record from the given data. - * @param alg The public key's algorithm. - * @param digestType The public key's digest type. - * @param fingerprint The public key's fingerprint. - */ -public -SSHFPRecord(Name name, int dclass, long ttl, int alg, int digestType, - byte [] fingerprint) -{ - super(name, Type.SSHFP, dclass, ttl); - this.alg = checkU8("alg", alg); - this.digestType = checkU8("digestType", digestType); - this.fingerprint = fingerprint; -} - -void -rrFromWire(DNSInput in) throws IOException { - alg = in.readU8(); - digestType = in.readU8(); - fingerprint = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - alg = st.getUInt8(); - digestType = st.getUInt8(); - fingerprint = st.getHex(true); -} - -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(alg); - sb.append(" "); - sb.append(digestType); - sb.append(" "); - sb.append(base16.toString(fingerprint)); - return sb.toString(); -} - -/** Returns the public key's algorithm. */ -public int -getAlgorithm() { - return alg; -} - -/** Returns the public key's digest type. */ -public int -getDigestType() { - return digestType; -} - -/** Returns the fingerprint */ -public byte [] -getFingerPrint() { - return fingerprint; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU8(alg); - out.writeU8(digestType); - out.writeByteArray(fingerprint); -} - -} diff --git a/src/main/java/org/xbill/DNS/Section.java b/src/main/java/org/xbill/DNS/Section.java deleted file mode 100644 index e0c8caab1..000000000 --- a/src/main/java/org/xbill/DNS/Section.java +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Constants and functions relating to DNS message sections - * - * @author Brian Wellington - */ - -public final class Section { - -/** The question (first) section */ -public static final int QUESTION = 0; - -/** The answer (second) section */ -public static final int ANSWER = 1; - -/** The authority (third) section */ -public static final int AUTHORITY = 2; - -/** The additional (fourth) section */ -public static final int ADDITIONAL = 3; - -/* Aliases for dynamic update */ -/** The zone (first) section of a dynamic update message */ -public static final int ZONE = 0; - -/** The prerequisite (second) section of a dynamic update message */ -public static final int PREREQ = 1; - -/** The update (third) section of a dynamic update message */ -public static final int UPDATE = 2; - -private static Mnemonic sections = new Mnemonic("Message Section", - Mnemonic.CASE_LOWER); -private static String [] longSections = new String[4]; -private static String [] updateSections = new String[4]; - -static { - sections.setMaximum(3); - sections.setNumericAllowed(true); - - sections.add(QUESTION, "qd"); - sections.add(ANSWER, "an"); - sections.add(AUTHORITY, "au"); - sections.add(ADDITIONAL, "ad"); - - longSections[QUESTION] = "QUESTIONS"; - longSections[ANSWER] = "ANSWERS"; - longSections[AUTHORITY] = "AUTHORITY RECORDS"; - longSections[ADDITIONAL] = "ADDITIONAL RECORDS"; - - updateSections[ZONE] = "ZONE"; - updateSections[PREREQ] = "PREREQUISITES"; - updateSections[UPDATE] = "UPDATE RECORDS"; - updateSections[ADDITIONAL] = "ADDITIONAL RECORDS"; -} - -private -Section() {} - -/** Converts a numeric Section into an abbreviation String */ -public static String -string(int i) { - return sections.getText(i); -} - -/** Converts a numeric Section into a full description String */ -public static String -longString(int i) { - sections.check(i); - return longSections[i]; -} - -/** - * Converts a numeric Section into a full description String for an update - * Message. - */ -public static String -updString(int i) { - sections.check(i); - return updateSections[i]; -} - -/** Converts a String representation of a Section into its numeric value */ -public static int -value(String s) { - return sections.getValue(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/Serial.java b/src/main/java/org/xbill/DNS/Serial.java deleted file mode 100644 index 3a146c65f..000000000 --- a/src/main/java/org/xbill/DNS/Serial.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Helper functions for doing serial arithmetic. These should be used when - * setting/checking SOA serial numbers. SOA serial number arithmetic is - * defined in RFC 1982. - * - * @author Brian Wellington - */ - -public final class Serial { - -private static final long MAX32 = 0xFFFFFFFFL; - -private -Serial() { -} - -/** - * Compares two numbers using serial arithmetic. The numbers are assumed - * to be 32 bit unsigned integers stored in longs. - * @param serial1 The first integer - * @param serial2 The second integer - * @return 0 if the 2 numbers are equal, a positive number if serial1 is greater - * than serial2, and a negative number if serial2 is greater than serial1. - * @throws IllegalArgumentException serial1 or serial2 is out of range - */ -public static int -compare(long serial1, long serial2) { - if (serial1 < 0 || serial1 > MAX32) - throw new IllegalArgumentException(serial1 + " out of range"); - if (serial2 < 0 || serial2 > MAX32) - throw new IllegalArgumentException(serial2 + " out of range"); - long diff = serial1 - serial2; - if (diff >= MAX32) - diff -= (MAX32 + 1); - else if (diff < -MAX32) - diff += (MAX32 + 1); - return (int)diff; -} - -/** - * Increments a serial number. The number is assumed to be a 32 bit unsigned - * integer stored in a long. This basically adds 1 and resets the value to - * 0 if it is 2^32. - * @param serial The serial number - * @return The incremented serial number - * @throws IllegalArgumentException serial is out of range - */ -public static long -increment(long serial) { - if (serial < 0 || serial > MAX32) - throw new IllegalArgumentException(serial + " out of range"); - if (serial == MAX32) - return 0; - return serial + 1; -} - -} diff --git a/src/main/java/org/xbill/DNS/SetResponse.java b/src/main/java/org/xbill/DNS/SetResponse.java deleted file mode 100644 index 24bdab521..000000000 --- a/src/main/java/org/xbill/DNS/SetResponse.java +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.ArrayList; -import java.util.List; - -/** - * The Response from a query to Cache.lookupRecords() or Zone.findRecords() - * @see Cache - * @see Zone - * - * @author Brian Wellington - */ - -public class SetResponse { - -/** - * The Cache contains no information about the requested name/type - */ -static final int UNKNOWN = 0; - -/** - * The Zone does not contain the requested name, or the Cache has - * determined that the name does not exist. - */ -static final int NXDOMAIN = 1; - -/** - * The Zone contains the name, but no data of the requested type, - * or the Cache has determined that the name exists and has no data - * of the requested type. - */ -static final int NXRRSET = 2; - -/** - * A delegation enclosing the requested name was found. - */ -static final int DELEGATION = 3; - -/** - * The Cache/Zone found a CNAME when looking for the name. - * @see CNAMERecord - */ -static final int CNAME = 4; - -/** - * The Cache/Zone found a DNAME when looking for the name. - * @see DNAMERecord - */ -static final int DNAME = 5; - -/** - * The Cache/Zone has successfully answered the question for the - * requested name/type/class. - */ -static final int SUCCESSFUL = 6; - -private static final SetResponse unknown = new SetResponse(UNKNOWN); -private static final SetResponse nxdomain = new SetResponse(NXDOMAIN); -private static final SetResponse nxrrset = new SetResponse(NXRRSET); - -private int type; -private Object data; - -private -SetResponse() {} - -SetResponse(int type, RRset rrset) { - if (type < 0 || type > 6) - throw new IllegalArgumentException("invalid type"); - this.type = type; - this.data = rrset; -} - -SetResponse(int type) { - if (type < 0 || type > 6) - throw new IllegalArgumentException("invalid type"); - this.type = type; - this.data = null; -} - -static SetResponse -ofType(int type) { - switch (type) { - case UNKNOWN: - return unknown; - case NXDOMAIN: - return nxdomain; - case NXRRSET: - return nxrrset; - case DELEGATION: - case CNAME: - case DNAME: - case SUCCESSFUL: - SetResponse sr = new SetResponse(); - sr.type = type; - sr.data = null; - return sr; - default: - throw new IllegalArgumentException("invalid type"); - } -} - -void -addRRset(RRset rrset) { - if (data == null) - data = new ArrayList(); - List l = (List) data; - l.add(rrset); -} - -/** Is the answer to the query unknown? */ -public boolean -isUnknown() { - return (type == UNKNOWN); -} - -/** Is the answer to the query that the name does not exist? */ -public boolean -isNXDOMAIN() { - return (type == NXDOMAIN); -} - -/** Is the answer to the query that the name exists, but the type does not? */ -public boolean -isNXRRSET() { - return (type == NXRRSET); -} - -/** Is the result of the lookup that the name is below a delegation? */ -public boolean -isDelegation() { - return (type == DELEGATION); -} - -/** Is the result of the lookup a CNAME? */ -public boolean -isCNAME() { - return (type == CNAME); -} - -/** Is the result of the lookup a DNAME? */ -public boolean -isDNAME() { - return (type == DNAME); -} - -/** Was the query successful? */ -public boolean -isSuccessful() { - return (type == SUCCESSFUL); -} - -/** If the query was successful, return the answers */ -public RRset [] -answers() { - if (type != SUCCESSFUL) - return null; - List l = (List) data; - return (RRset []) l.toArray(new RRset[l.size()]); -} - -/** - * If the query encountered a CNAME, return it. - */ -public CNAMERecord -getCNAME() { - return (CNAMERecord)((RRset)data).first(); -} - -/** - * If the query encountered a DNAME, return it. - */ -public DNAMERecord -getDNAME() { - return (DNAMERecord)((RRset)data).first(); -} - -/** - * If the query hit a delegation point, return the NS set. - */ -public RRset -getNS() { - return (RRset)data; -} - -/** Prints the value of the SetResponse */ -public String -toString() { - switch (type) { - case UNKNOWN: return "unknown"; - case NXDOMAIN: return "NXDOMAIN"; - case NXRRSET: return "NXRRSET"; - case DELEGATION: return "delegation: " + data; - case CNAME: return "CNAME: " + data; - case DNAME: return "DNAME: " + data; - case SUCCESSFUL: return "successful"; - default: throw new IllegalStateException(); - } -} - -} diff --git a/src/main/java/org/xbill/DNS/SimpleResolver.java b/src/main/java/org/xbill/DNS/SimpleResolver.java deleted file mode 100644 index 6d6a928db..000000000 --- a/src/main/java/org/xbill/DNS/SimpleResolver.java +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.Iterator; -import java.util.List; - -/** - * An implementation of Resolver that sends one query to one server. - * SimpleResolver handles TCP retries, transaction security (TSIG), and - * EDNS 0. - * @see Resolver - * @see TSIG - * @see OPTRecord - * - * @author Brian Wellington - */ - - -public class SimpleResolver implements Resolver { - -/** The default port to send queries to */ -public static final int DEFAULT_PORT = 53; - -/** The default EDNS payload size */ -public static final int DEFAULT_EDNS_PAYLOADSIZE = 1280; - -private InetSocketAddress address; -private InetSocketAddress localAddress; -private boolean useTCP, ignoreTruncation; -private OPTRecord queryOPT; -private TSIG tsig; -private long timeoutValue = 10 * 1000; - -private static final short DEFAULT_UDPSIZE = 512; - -private static String defaultResolver = "localhost"; -private static int uniqueID = 0; - -/** - * Creates a SimpleResolver that will query the specified host - * @exception UnknownHostException Failure occurred while finding the host - */ -public -SimpleResolver(String hostname) throws UnknownHostException { - if (hostname == null) { - hostname = ResolverConfig.getCurrentConfig().server(); - if (hostname == null) - hostname = defaultResolver; - } - InetAddress addr; - if (hostname.equals("0")) - addr = InetAddress.getLocalHost(); - else - addr = InetAddress.getByName(hostname); - address = new InetSocketAddress(addr, DEFAULT_PORT); -} - -/** - * Creates a SimpleResolver. The host to query is either found by using - * ResolverConfig, or the default host is used. - * @see ResolverConfig - * @exception UnknownHostException Failure occurred while finding the host - */ -public -SimpleResolver() throws UnknownHostException { - this(null); -} - -InetSocketAddress -getAddress() { - return address; -} - -/** Sets the default host (initially localhost) to query */ -public static void -setDefaultResolver(String hostname) { - defaultResolver = hostname; -} - -public void -setPort(int port) { - address = new InetSocketAddress(address.getAddress(), port); -} - -/** - * Sets the address of the server to communicate with. - * @param addr The address of the DNS server - */ -public void -setAddress(InetSocketAddress addr) { - address = addr; -} - -/** - * Sets the address of the server to communicate with (on the default - * DNS port) - * @param addr The address of the DNS server - */ -public void -setAddress(InetAddress addr) { - address = new InetSocketAddress(addr, address.getPort()); -} - -/** - * Sets the local address to bind to when sending messages. - * @param addr The local address to send messages from. - */ -public void -setLocalAddress(InetSocketAddress addr) { - localAddress = addr; -} - -/** - * Sets the local address to bind to when sending messages. A random port - * will be used. - * @param addr The local address to send messages from. - */ -public void -setLocalAddress(InetAddress addr) { - localAddress = new InetSocketAddress(addr, 0); -} - -public void -setTCP(boolean flag) { - this.useTCP = flag; -} - -public void -setIgnoreTruncation(boolean flag) { - this.ignoreTruncation = flag; -} - -public void -setEDNS(int level, int payloadSize, int flags, List options) { - if (level != 0 && level != -1) - throw new IllegalArgumentException("invalid EDNS level - " + - "must be 0 or -1"); - if (payloadSize == 0) - payloadSize = DEFAULT_EDNS_PAYLOADSIZE; - queryOPT = new OPTRecord(payloadSize, 0, level, flags, options); -} - -public void -setEDNS(int level) { - setEDNS(level, 0, 0, null); -} - -public void -setTSIGKey(TSIG key) { - tsig = key; -} - -TSIG -getTSIGKey() { - return tsig; -} - -public void -setTimeout(int secs, int msecs) { - timeoutValue = (long)secs * 1000 + msecs; -} - -public void -setTimeout(int secs) { - setTimeout(secs, 0); -} - -long -getTimeout() { - return timeoutValue; -} - -private Message -parseMessage(byte [] b) throws WireParseException { - try { - return (new Message(b)); - } - catch (IOException e) { - if (Options.check("verbose")) - e.printStackTrace(); - if (!(e instanceof WireParseException)) - e = new WireParseException("Error parsing message"); - throw (WireParseException) e; - } -} - -private void -verifyTSIG(Message query, Message response, byte [] b, TSIG tsig) { - if (tsig == null) - return; - int error = tsig.verify(response, b, query.getTSIG()); - if (Options.check("verbose")) - System.err.println("TSIG verify: " + Rcode.string(error)); -} - -private void -applyEDNS(Message query) { - if (queryOPT == null || query.getOPT() != null) - return; - query.addRecord(queryOPT, Section.ADDITIONAL); -} - -private int -maxUDPSize(Message query) { - OPTRecord opt = query.getOPT(); - if (opt == null) - return DEFAULT_UDPSIZE; - else - return opt.getPayloadSize(); -} - -/** - * Sends a message to a single server and waits for a response. No checking - * is done to ensure that the response is associated with the query. - * @param query The query to send. - * @return The response. - * @throws IOException An error occurred while sending or receiving. - */ -public Message -send(Message query) throws IOException { - if (Options.check("verbose")) - System.err.println("Sending to " + - address.getAddress().getHostAddress() + - ":" + address.getPort()); - - if (query.getHeader().getOpcode() == Opcode.QUERY) { - Record question = query.getQuestion(); - if (question != null && question.getType() == Type.AXFR) - return sendAXFR(query); - } - - query = (Message) query.clone(); - applyEDNS(query); - if (tsig != null) - tsig.apply(query, null); - - byte [] out = query.toWire(Message.MAXLENGTH); - int udpSize = maxUDPSize(query); - boolean tcp = false; - long endTime = System.currentTimeMillis() + timeoutValue; - do { - byte [] in; - - if (useTCP || out.length > udpSize) - tcp = true; - if (tcp) - in = TCPClient.sendrecv(localAddress, address, out, - endTime); - else - in = UDPClient.sendrecv(localAddress, address, out, - udpSize, endTime); - - /* - * Check that the response is long enough. - */ - if (in.length < Header.LENGTH) { - throw new WireParseException("invalid DNS header - " + - "too short"); - } - /* - * Check that the response ID matches the query ID. We want - * to check this before actually parsing the message, so that - * if there's a malformed response that's not ours, it - * doesn't confuse us. - */ - int id = ((in[0] & 0xFF) << 8) + (in[1] & 0xFF); - int qid = query.getHeader().getID(); - if (id != qid) { - String error = "invalid message id: expected " + qid + - "; got id " + id; - if (tcp) { - throw new WireParseException(error); - } else { - if (Options.check("verbose")) { - System.err.println(error); - } - continue; - } - } - Message response = parseMessage(in); - verifyTSIG(query, response, in, tsig); - if (!tcp && !ignoreTruncation && - response.getHeader().getFlag(Flags.TC)) - { - tcp = true; - continue; - } - return response; - } while (true); -} - -/** - * Asynchronously sends a message to a single server, registering a listener - * to receive a callback on success or exception. Multiple asynchronous - * lookups can be performed in parallel. Since the callback may be invoked - * before the function returns, external synchronization is necessary. - * @param query The query to send - * @param listener The object containing the callbacks. - * @return An identifier, which is also a parameter in the callback - */ -public Object -sendAsync(final Message query, final ResolverListener listener) { - final Object id; - synchronized (this) { - id = new Integer(uniqueID++); - } - Record question = query.getQuestion(); - String qname; - if (question != null) - qname = question.getName().toString(); - else - qname = "(none)"; - String name = this.getClass() + ": " + qname; - Thread thread = new ResolveThread(this, query, id, listener); - thread.setName(name); - thread.setDaemon(true); - thread.start(); - return id; -} - -private Message -sendAXFR(Message query) throws IOException { - Name qname = query.getQuestion().getName(); - ZoneTransferIn xfrin = ZoneTransferIn.newAXFR(qname, address, tsig); - xfrin.setTimeout((int)(getTimeout() / 1000)); - xfrin.setLocalAddress(localAddress); - try { - xfrin.run(); - } - catch (ZoneTransferException e) { - throw new WireParseException(e.getMessage()); - } - List records = xfrin.getAXFR(); - Message response = new Message(query.getHeader().getID()); - response.getHeader().setFlag(Flags.AA); - response.getHeader().setFlag(Flags.QR); - response.addRecord(query.getQuestion(), Section.QUESTION); - Iterator it = records.iterator(); - while (it.hasNext()) - response.addRecord((Record)it.next(), Section.ANSWER); - return response; -} - -} diff --git a/src/main/java/org/xbill/DNS/SingleCompressedNameBase.java b/src/main/java/org/xbill/DNS/SingleCompressedNameBase.java deleted file mode 100644 index 790ca1f10..000000000 --- a/src/main/java/org/xbill/DNS/SingleCompressedNameBase.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Implements common functionality for the many record types whose format - * is a single compressed name. - * - * @author Brian Wellington - */ - -abstract class SingleCompressedNameBase extends SingleNameBase { - -private static final long serialVersionUID = -236435396815460677L; - -protected -SingleCompressedNameBase() {} - -protected -SingleCompressedNameBase(Name name, int type, int dclass, long ttl, - Name singleName, String description) -{ - super(name, type, dclass, ttl, singleName, description); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - singleName.toWire(out, c, canonical); -} - -} diff --git a/src/main/java/org/xbill/DNS/SingleNameBase.java b/src/main/java/org/xbill/DNS/SingleNameBase.java deleted file mode 100644 index f9948c4c2..000000000 --- a/src/main/java/org/xbill/DNS/SingleNameBase.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Implements common functionality for the many record types whose format - * is a single name. - * - * @author Brian Wellington - */ - -abstract class SingleNameBase extends Record { - -private static final long serialVersionUID = -18595042501413L; - -protected Name singleName; - -protected -SingleNameBase() {} - -protected -SingleNameBase(Name name, int type, int dclass, long ttl) { - super(name, type, dclass, ttl); -} - -protected -SingleNameBase(Name name, int type, int dclass, long ttl, Name singleName, - String description) -{ - super(name, type, dclass, ttl); - this.singleName = checkName(description, singleName); -} - -void -rrFromWire(DNSInput in) throws IOException { - singleName = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - singleName = st.getName(origin); -} - -String -rrToString() { - return singleName.toString(); -} - -protected Name -getSingleName() { - return singleName; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - singleName.toWire(out, null, canonical); -} - -} diff --git a/src/main/java/org/xbill/DNS/TCPClient.java b/src/main/java/org/xbill/DNS/TCPClient.java deleted file mode 100644 index 09cc16351..000000000 --- a/src/main/java/org/xbill/DNS/TCPClient.java +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.EOFException; -import java.io.IOException; -import java.net.SocketAddress; -import java.net.SocketTimeoutException; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; - -final class TCPClient extends Client { - -public -TCPClient(long endTime) throws IOException { - super(SocketChannel.open(), endTime); -} - -void -bind(SocketAddress addr) throws IOException { - SocketChannel channel = (SocketChannel) key.channel(); - channel.socket().bind(addr); -} - -void -connect(SocketAddress addr) throws IOException { - SocketChannel channel = (SocketChannel) key.channel(); - if (channel.connect(addr)) - return; - key.interestOps(SelectionKey.OP_CONNECT); - try { - while (!channel.finishConnect()) { - if (!key.isConnectable()) - blockUntil(key, endTime); - } - } - finally { - if (key.isValid()) - key.interestOps(0); - } -} - -void -send(byte [] data) throws IOException { - SocketChannel channel = (SocketChannel) key.channel(); - verboseLog("TCP write", data); - byte [] lengthArray = new byte[2]; - lengthArray[0] = (byte)(data.length >>> 8); - lengthArray[1] = (byte)(data.length & 0xFF); - ByteBuffer [] buffers = new ByteBuffer[2]; - buffers[0] = ByteBuffer.wrap(lengthArray); - buffers[1] = ByteBuffer.wrap(data); - int nsent = 0; - key.interestOps(SelectionKey.OP_WRITE); - try { - while (nsent < data.length + 2) { - if (key.isWritable()) { - long n = channel.write(buffers); - if (n < 0) - throw new EOFException(); - nsent += (int) n; - if (nsent < data.length + 2 && - System.currentTimeMillis() > endTime) - throw new SocketTimeoutException(); - } else - blockUntil(key, endTime); - } - } - finally { - if (key.isValid()) - key.interestOps(0); - } -} - -private byte [] -_recv(int length) throws IOException { - SocketChannel channel = (SocketChannel) key.channel(); - int nrecvd = 0; - byte [] data = new byte[length]; - ByteBuffer buffer = ByteBuffer.wrap(data); - key.interestOps(SelectionKey.OP_READ); - try { - while (nrecvd < length) { - if (key.isReadable()) { - long n = channel.read(buffer); - if (n < 0) - throw new EOFException(); - nrecvd += (int) n; - if (nrecvd < length && - System.currentTimeMillis() > endTime) - throw new SocketTimeoutException(); - } else - blockUntil(key, endTime); - } - } - finally { - if (key.isValid()) - key.interestOps(0); - } - return data; -} - -byte [] -recv() throws IOException { - byte [] buf = _recv(2); - int length = ((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF); - byte [] data = _recv(length); - verboseLog("TCP read", data); - return data; -} - -static byte [] -sendrecv(SocketAddress local, SocketAddress remote, byte [] data, long endTime) -throws IOException -{ - TCPClient client = new TCPClient(endTime); - try { - if (local != null) - client.bind(local); - client.connect(remote); - client.send(data); - return client.recv(); - } - finally { - client.cleanup(); - } -} - -static byte [] -sendrecv(SocketAddress addr, byte [] data, long endTime) throws IOException { - return sendrecv(null, addr, data, endTime); -} - -} diff --git a/src/main/java/org/xbill/DNS/TKEYRecord.java b/src/main/java/org/xbill/DNS/TKEYRecord.java deleted file mode 100644 index c55267f92..000000000 --- a/src/main/java/org/xbill/DNS/TKEYRecord.java +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; -import java.util.Date; - -/** - * Transaction Key - used to compute and/or securely transport a shared - * secret to be used with TSIG. - * @see TSIG - * - * @author Brian Wellington - */ - -public class TKEYRecord extends Record { - -private static final long serialVersionUID = 8828458121926391756L; - -private Name alg; -private Date timeInception; -private Date timeExpire; -private int mode, error; -private byte [] key; -private byte [] other; - -/** The key is assigned by the server (unimplemented) */ -public static final int SERVERASSIGNED = 1; - -/** The key is computed using a Diffie-Hellman key exchange */ -public static final int DIFFIEHELLMAN = 2; - -/** The key is computed using GSS_API (unimplemented) */ -public static final int GSSAPI = 3; - -/** The key is assigned by the resolver (unimplemented) */ -public static final int RESOLVERASSIGNED = 4; - -/** The key should be deleted */ -public static final int DELETE = 5; - -TKEYRecord() {} - -Record -getObject() { - return new TKEYRecord(); -} - -/** - * Creates a TKEY Record from the given data. - * @param alg The shared key's algorithm - * @param timeInception The beginning of the validity period of the shared - * secret or keying material - * @param timeExpire The end of the validity period of the shared - * secret or keying material - * @param mode The mode of key agreement - * @param error The extended error field. Should be 0 in queries - * @param key The shared secret - * @param other The other data field. Currently unused - * responses. - */ -public -TKEYRecord(Name name, int dclass, long ttl, Name alg, - Date timeInception, Date timeExpire, int mode, int error, - byte [] key, byte other[]) -{ - super(name, Type.TKEY, dclass, ttl); - this.alg = checkName("alg", alg); - this.timeInception = timeInception; - this.timeExpire = timeExpire; - this.mode = checkU16("mode", mode); - this.error = checkU16("error", error); - this.key = key; - this.other = other; -} - -void -rrFromWire(DNSInput in) throws IOException { - alg = new Name(in); - timeInception = new Date(1000 * in.readU32()); - timeExpire = new Date(1000 * in.readU32()); - mode = in.readU16(); - error = in.readU16(); - - int keylen = in.readU16(); - if (keylen > 0) - key = in.readByteArray(keylen); - else - key = null; - - int otherlen = in.readU16(); - if (otherlen > 0) - other = in.readByteArray(otherlen); - else - other = null; -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - throw st.exception("no text format defined for TKEY"); -} - -protected String -modeString() { - switch (mode) { - case SERVERASSIGNED: return "SERVERASSIGNED"; - case DIFFIEHELLMAN: return "DIFFIEHELLMAN"; - case GSSAPI: return "GSSAPI"; - case RESOLVERASSIGNED: return "RESOLVERASSIGNED"; - case DELETE: return "DELETE"; - default: return Integer.toString(mode); - } -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(alg); - sb.append(" "); - if (Options.check("multiline")) - sb.append("(\n\t"); - sb.append(FormattedTime.format(timeInception)); - sb.append(" "); - sb.append(FormattedTime.format(timeExpire)); - sb.append(" "); - sb.append(modeString()); - sb.append(" "); - sb.append(Rcode.TSIGstring(error)); - if (Options.check("multiline")) { - sb.append("\n"); - if (key != null) { - sb.append(base64.formatString(key, 64, "\t", false)); - sb.append("\n"); - } - if (other != null) - sb.append(base64.formatString(other, 64, "\t", false)); - sb.append(" )"); - } else { - sb.append(" "); - if (key != null) { - sb.append(base64.toString(key)); - sb.append(" "); - } - if (other != null) - sb.append(base64.toString(other)); - } - return sb.toString(); -} - -/** Returns the shared key's algorithm */ -public Name -getAlgorithm() { - return alg; -} - -/** - * Returns the beginning of the validity period of the shared secret or - * keying material - */ -public Date -getTimeInception() { - return timeInception; -} - -/** - * Returns the end of the validity period of the shared secret or - * keying material - */ -public Date -getTimeExpire() { - return timeExpire; -} - -/** Returns the key agreement mode */ -public int -getMode() { - return mode; -} - -/** Returns the extended error */ -public int -getError() { - return error; -} - -/** Returns the shared secret or keying material */ -public byte [] -getKey() { - return key; -} - -/** Returns the other data */ -public byte [] -getOther() { - return other; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - alg.toWire(out, null, canonical); - - out.writeU32(timeInception.getTime() / 1000); - out.writeU32(timeExpire.getTime() / 1000); - - out.writeU16(mode); - out.writeU16(error); - - if (key != null) { - out.writeU16(key.length); - out.writeByteArray(key); - } - else - out.writeU16(0); - - if (other != null) { - out.writeU16(other.length); - out.writeByteArray(other); - } - else - out.writeU16(0); -} - -} diff --git a/src/main/java/org/xbill/DNS/TSIG.java b/src/main/java/org/xbill/DNS/TSIG.java deleted file mode 100644 index b898bce2c..000000000 --- a/src/main/java/org/xbill/DNS/TSIG.java +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.HMAC; -import org.xbill.DNS.utils.base64; - -import java.util.Date; - -/** - * Transaction signature handling. This class generates and verifies - * TSIG records on messages, which provide transaction security. - * @see TSIGRecord - * - * @author Brian Wellington - */ - -public class TSIG { - -private static final String HMAC_MD5_STR = "HMAC-MD5.SIG-ALG.REG.INT."; -private static final String HMAC_SHA1_STR = "hmac-sha1."; -private static final String HMAC_SHA256_STR = "hmac-sha256."; - -/** The domain name representing the HMAC-MD5 algorithm. */ -public static final Name HMAC_MD5 = Name.fromConstantString(HMAC_MD5_STR); - -/** The domain name representing the HMAC-MD5 algorithm (deprecated). */ -public static final Name HMAC = HMAC_MD5; - -/** The domain name representing the HMAC-SHA1 algorithm. */ -public static final Name HMAC_SHA1 = Name.fromConstantString(HMAC_SHA1_STR); - -/** The domain name representing the HMAC-SHA256 algorithm. */ -public static final Name HMAC_SHA256 = Name.fromConstantString(HMAC_SHA256_STR); - -/** - * The default fudge value for outgoing packets. Can be overriden by the - * tsigfudge option. - */ -public static final short FUDGE = 300; - -private Name name, alg; -private String digest; -private byte [] key; - -private void -getDigest() { - if (alg.equals(HMAC_MD5)) - digest = "md5"; - else if (alg.equals(HMAC_SHA1)) - digest = "sha-1"; - else if (alg.equals(HMAC_SHA256)) - digest = "sha-256"; - else - throw new IllegalArgumentException("Invalid algorithm"); -} - -/** - * Creates a new TSIG key, which can be used to sign or verify a message. - * @param algorithm The algorithm of the shared key. - * @param name The name of the shared key. - * @param key The shared key's data. - */ -public -TSIG(Name algorithm, Name name, byte [] key) { - this.name = name; - this.alg = algorithm; - this.key = key; - getDigest(); -} - -/** - * Creates a new TSIG key with the hmac-md5 algorithm, which can be used to - * sign or verify a message. - * @param name The name of the shared key. - * @param key The shared key's data. - */ -public -TSIG(Name name, byte [] key) { - this(HMAC_MD5, name, key); -} - -/** - * Creates a new TSIG object, which can be used to sign or verify a message. - * @param name The name of the shared key. - * @param key The shared key's data represented as a base64 encoded string. - * @throws IllegalArgumentException The key name is an invalid name - * @throws IllegalArgumentException The key data is improperly encoded - */ -public -TSIG(Name algorithm, String name, String key) { - this.key = base64.fromString(key); - if (this.key == null) - throw new IllegalArgumentException("Invalid TSIG key string"); - try { - this.name = Name.fromString(name, Name.root); - } - catch (TextParseException e) { - throw new IllegalArgumentException("Invalid TSIG key name"); - } - this.alg = algorithm; - getDigest(); -} - -/** - * Creates a new TSIG object, which can be used to sign or verify a message. - * @param name The name of the shared key. The legal values are "hmac-md5", - * "hmac-sha1", and "hmac-sha256". - * @param key The shared key's data represented as a base64 encoded string. - * @throws IllegalArgumentException The key name is an invalid name - * @throws IllegalArgumentException The key data is improperly encoded - */ -public -TSIG(String algorithm, String name, String key) { - this(HMAC_MD5, name, key); - if (algorithm.equalsIgnoreCase("hmac-md5")) - this.alg = HMAC_MD5; - else if (algorithm.equalsIgnoreCase("hmac-sha1")) - this.alg = HMAC_SHA1; - else if (algorithm.equalsIgnoreCase("hmac-sha256")) - this.alg = HMAC_SHA256; - else - throw new IllegalArgumentException("Invalid TSIG algorithm"); - getDigest(); -} - -/** - * Creates a new TSIG object with the hmac-md5 algorithm, which can be used to - * sign or verify a message. - * @param name The name of the shared key - * @param key The shared key's data, represented as a base64 encoded string. - * @throws IllegalArgumentException The key name is an invalid name - * @throws IllegalArgumentException The key data is improperly encoded - */ -public -TSIG(String name, String key) { - this(HMAC_MD5, name, key); -} - -/** - * Creates a new TSIG object with the hmac-md5 algorithm, which can be used to - * sign or verify a message. - * @param str The TSIG key, in the form name:secret, name/secret, - * alg:name:secret, or alg/name/secret. If an algorithm is specified, it must - * be "hmac-md5", "hmac-sha1", or "hmac-sha256". - * @throws IllegalArgumentException The string does not contain both a name - * and secret. - * @throws IllegalArgumentException The key name is an invalid name - * @throws IllegalArgumentException The key data is improperly encoded - */ -static public TSIG -fromString(String str) { - String [] parts = str.split("[:/]"); - if (parts.length < 2 || parts.length > 3) - throw new IllegalArgumentException("Invalid TSIG key " + - "specification"); - if (parts.length == 3) - return new TSIG(parts[0], parts[1], parts[2]); - else - return new TSIG(HMAC_MD5, parts[0], parts[1]); -} - -/** - * Generates a TSIG record with a specific error for a message that has - * been rendered. - * @param m The message - * @param b The rendered message - * @param error The error - * @param old If this message is a response, the TSIG from the request - * @return The TSIG record to be added to the message - */ -public TSIGRecord -generate(Message m, byte [] b, int error, TSIGRecord old) { - Date timeSigned; - if (error != Rcode.BADTIME) - timeSigned = new Date(); - else - timeSigned = old.getTimeSigned(); - int fudge; - HMAC hmac = null; - if (error == Rcode.NOERROR || error == Rcode.BADTIME) - hmac = new HMAC(digest, key); - - fudge = Options.intValue("tsigfudge"); - if (fudge < 0 || fudge > 0x7FFF) - fudge = FUDGE; - - if (old != null) { - DNSOutput out = new DNSOutput(); - out.writeU16(old.getSignature().length); - if (hmac != null) { - hmac.update(out.toByteArray()); - hmac.update(old.getSignature()); - } - } - - /* Digest the message */ - if (hmac != null) - hmac.update(b); - - DNSOutput out = new DNSOutput(); - name.toWireCanonical(out); - out.writeU16(DClass.ANY); /* class */ - out.writeU32(0); /* ttl */ - alg.toWireCanonical(out); - long time = timeSigned.getTime() / 1000; - int timeHigh = (int) (time >> 32); - long timeLow = (time & 0xFFFFFFFFL); - out.writeU16(timeHigh); - out.writeU32(timeLow); - out.writeU16(fudge); - - out.writeU16(error); - out.writeU16(0); /* No other data */ - - if (hmac != null) - hmac.update(out.toByteArray()); - - byte [] signature; - if (hmac != null) - signature = hmac.sign(); - else - signature = new byte[0]; - - byte [] other = null; - if (error == Rcode.BADTIME) { - out = new DNSOutput(); - time = new Date().getTime() / 1000; - timeHigh = (int) (time >> 32); - timeLow = (time & 0xFFFFFFFFL); - out.writeU16(timeHigh); - out.writeU32(timeLow); - other = out.toByteArray(); - } - - return (new TSIGRecord(name, DClass.ANY, 0, alg, timeSigned, fudge, - signature, m.getHeader().getID(), error, other)); -} - -/** - * Generates a TSIG record with a specific error for a message and adds it - * to the message. - * @param m The message - * @param error The error - * @param old If this message is a response, the TSIG from the request - */ -public void -apply(Message m, int error, TSIGRecord old) { - Record r = generate(m, m.toWire(), error, old); - m.addRecord(r, Section.ADDITIONAL); - m.tsigState = Message.TSIG_SIGNED; -} - -/** - * Generates a TSIG record for a message and adds it to the message - * @param m The message - * @param old If this message is a response, the TSIG from the request - */ -public void -apply(Message m, TSIGRecord old) { - apply(m, Rcode.NOERROR, old); -} - -/** - * Generates a TSIG record for a message and adds it to the message - * @param m The message - * @param old If this message is a response, the TSIG from the request - */ -public void -applyStream(Message m, TSIGRecord old, boolean first) { - if (first) { - apply(m, old); - return; - } - Date timeSigned = new Date(); - int fudge; - HMAC hmac = new HMAC(digest, key); - - fudge = Options.intValue("tsigfudge"); - if (fudge < 0 || fudge > 0x7FFF) - fudge = FUDGE; - - DNSOutput out = new DNSOutput(); - out.writeU16(old.getSignature().length); - hmac.update(out.toByteArray()); - hmac.update(old.getSignature()); - - /* Digest the message */ - hmac.update(m.toWire()); - - out = new DNSOutput(); - long time = timeSigned.getTime() / 1000; - int timeHigh = (int) (time >> 32); - long timeLow = (time & 0xFFFFFFFFL); - out.writeU16(timeHigh); - out.writeU32(timeLow); - out.writeU16(fudge); - - hmac.update(out.toByteArray()); - - byte [] signature = hmac.sign(); - byte [] other = null; - - Record r = new TSIGRecord(name, DClass.ANY, 0, alg, timeSigned, fudge, - signature, m.getHeader().getID(), - Rcode.NOERROR, other); - m.addRecord(r, Section.ADDITIONAL); - m.tsigState = Message.TSIG_SIGNED; -} - -/** - * Verifies a TSIG record on an incoming message. Since this is only called - * in the context where a TSIG is expected to be present, it is an error - * if one is not present. After calling this routine, Message.isVerified() may - * be called on this message. - * @param m The message - * @param b An array containing the message in unparsed form. This is - * necessary since TSIG signs the message in wire format, and we can't - * recreate the exact wire format (with the same name compression). - * @param length The length of the message in the array. - * @param old If this message is a response, the TSIG from the request - * @return The result of the verification (as an Rcode) - * @see Rcode - */ -public byte -verify(Message m, byte [] b, int length, TSIGRecord old) { - m.tsigState = Message.TSIG_FAILED; - TSIGRecord tsig = m.getTSIG(); - HMAC hmac = new HMAC(digest, key); - if (tsig == null) - return Rcode.FORMERR; - - if (!tsig.getName().equals(name) || !tsig.getAlgorithm().equals(alg)) { - if (Options.check("verbose")) - System.err.println("BADKEY failure"); - return Rcode.BADKEY; - } - long now = System.currentTimeMillis(); - long then = tsig.getTimeSigned().getTime(); - long fudge = tsig.getFudge(); - if (Math.abs(now - then) > fudge * 1000) { - if (Options.check("verbose")) - System.err.println("BADTIME failure"); - return Rcode.BADTIME; - } - - if (old != null && tsig.getError() != Rcode.BADKEY && - tsig.getError() != Rcode.BADSIG) - { - DNSOutput out = new DNSOutput(); - out.writeU16(old.getSignature().length); - hmac.update(out.toByteArray()); - hmac.update(old.getSignature()); - } - m.getHeader().decCount(Section.ADDITIONAL); - byte [] header = m.getHeader().toWire(); - m.getHeader().incCount(Section.ADDITIONAL); - hmac.update(header); - - int len = m.tsigstart - header.length; - hmac.update(b, header.length, len); - - DNSOutput out = new DNSOutput(); - tsig.getName().toWireCanonical(out); - out.writeU16(tsig.dclass); - out.writeU32(tsig.ttl); - tsig.getAlgorithm().toWireCanonical(out); - long time = tsig.getTimeSigned().getTime() / 1000; - int timeHigh = (int) (time >> 32); - long timeLow = (time & 0xFFFFFFFFL); - out.writeU16(timeHigh); - out.writeU32(timeLow); - out.writeU16(tsig.getFudge()); - out.writeU16(tsig.getError()); - if (tsig.getOther() != null) { - out.writeU16(tsig.getOther().length); - out.writeByteArray(tsig.getOther()); - } else { - out.writeU16(0); - } - - hmac.update(out.toByteArray()); - - if (hmac.verify(tsig.getSignature())) { - m.tsigState = Message.TSIG_VERIFIED; - return Rcode.NOERROR; - } else { - if (Options.check("verbose")) - System.err.println("BADSIG failure"); - return Rcode.BADSIG; - } -} - -/** - * Verifies a TSIG record on an incoming message. Since this is only called - * in the context where a TSIG is expected to be present, it is an error - * if one is not present. After calling this routine, Message.isVerified() may - * be called on this message. - * @param m The message - * @param b The message in unparsed form. This is necessary since TSIG - * signs the message in wire format, and we can't recreate the exact wire - * format (with the same name compression). - * @param old If this message is a response, the TSIG from the request - * @return The result of the verification (as an Rcode) - * @see Rcode - */ -public int -verify(Message m, byte [] b, TSIGRecord old) { - return verify(m, b, b.length, old); -} - -/** - * Returns the maximum length of a TSIG record generated by this key. - * @see TSIGRecord - */ -public int -recordLength() { - return (name.length() + 10 + - alg.length() + - 8 + // time signed, fudge - 18 + // 2 byte MAC length, 16 byte MAC - 4 + // original id, error - 8); // 2 byte error length, 6 byte max error field. -} - -public static class StreamVerifier { - /** - * A helper class for verifying multiple message responses. - */ - - private TSIG key; - private HMAC verifier; - private int nresponses; - private int lastsigned; - private TSIGRecord lastTSIG; - - /** Creates an object to verify a multiple message response */ - public - StreamVerifier(TSIG tsig, TSIGRecord old) { - key = tsig; - verifier = new HMAC(key.digest, key.key); - nresponses = 0; - lastTSIG = old; - } - - /** - * Verifies a TSIG record on an incoming message that is part of a - * multiple message response. - * TSIG records must be present on the first and last messages, and - * at least every 100 records in between. - * After calling this routine, Message.isVerified() may be called on - * this message. - * @param m The message - * @param b The message in unparsed form - * @return The result of the verification (as an Rcode) - * @see Rcode - */ - public int - verify(Message m, byte [] b) { - TSIGRecord tsig = m.getTSIG(); - - nresponses++; - - if (nresponses == 1) { - int result = key.verify(m, b, lastTSIG); - if (result == Rcode.NOERROR) { - byte [] signature = tsig.getSignature(); - DNSOutput out = new DNSOutput(); - out.writeU16(signature.length); - verifier.update(out.toByteArray()); - verifier.update(signature); - } - lastTSIG = tsig; - return result; - } - - if (tsig != null) - m.getHeader().decCount(Section.ADDITIONAL); - byte [] header = m.getHeader().toWire(); - if (tsig != null) - m.getHeader().incCount(Section.ADDITIONAL); - verifier.update(header); - - int len; - if (tsig == null) - len = b.length - header.length; - else - len = m.tsigstart - header.length; - verifier.update(b, header.length, len); - - if (tsig != null) { - lastsigned = nresponses; - lastTSIG = tsig; - } - else { - boolean required = (nresponses - lastsigned >= 100); - if (required) { - m.tsigState = Message.TSIG_FAILED; - return Rcode.FORMERR; - } else { - m.tsigState = Message.TSIG_INTERMEDIATE; - return Rcode.NOERROR; - } - } - - if (!tsig.getName().equals(key.name) || - !tsig.getAlgorithm().equals(key.alg)) - { - if (Options.check("verbose")) - System.err.println("BADKEY failure"); - m.tsigState = Message.TSIG_FAILED; - return Rcode.BADKEY; - } - - DNSOutput out = new DNSOutput(); - long time = tsig.getTimeSigned().getTime() / 1000; - int timeHigh = (int) (time >> 32); - long timeLow = (time & 0xFFFFFFFFL); - out.writeU16(timeHigh); - out.writeU32(timeLow); - out.writeU16(tsig.getFudge()); - verifier.update(out.toByteArray()); - - if (verifier.verify(tsig.getSignature()) == false) { - if (Options.check("verbose")) - System.err.println("BADSIG failure"); - return Rcode.BADSIG; - } - - verifier.clear(); - out = new DNSOutput(); - out.writeU16(tsig.getSignature().length); - verifier.update(out.toByteArray()); - verifier.update(tsig.getSignature()); - - return Rcode.NOERROR; - } -} - -} diff --git a/src/main/java/org/xbill/DNS/TSIGRecord.java b/src/main/java/org/xbill/DNS/TSIGRecord.java deleted file mode 100644 index f9723c0b4..000000000 --- a/src/main/java/org/xbill/DNS/TSIGRecord.java +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base64; - -import java.io.IOException; -import java.util.Date; - -/** - * Transaction Signature - this record is automatically generated by the - * resolver. TSIG records provide transaction security between the - * sender and receiver of a message, using a shared key. - * @see Resolver - * @see TSIG - * - * @author Brian Wellington - */ - -public class TSIGRecord extends Record { - -private static final long serialVersionUID = -88820909016649306L; - -private Name alg; -private Date timeSigned; -private int fudge; -private byte [] signature; -private int originalID; -private int error; -private byte [] other; - -TSIGRecord() {} - -Record -getObject() { - return new TSIGRecord(); -} - -/** - * Creates a TSIG Record from the given data. This is normally called by - * the TSIG class - * @param alg The shared key's algorithm - * @param timeSigned The time that this record was generated - * @param fudge The fudge factor for time - if the time that the message is - * received is not in the range [now - fudge, now + fudge], the signature - * fails - * @param signature The signature - * @param originalID The message ID at the time of its generation - * @param error The extended error field. Should be 0 in queries. - * @param other The other data field. Currently used only in BADTIME - * responses. - * @see TSIG - */ -public -TSIGRecord(Name name, int dclass, long ttl, Name alg, Date timeSigned, - int fudge, byte [] signature, int originalID, int error, - byte other[]) -{ - super(name, Type.TSIG, dclass, ttl); - this.alg = checkName("alg", alg); - this.timeSigned = timeSigned; - this.fudge = checkU16("fudge", fudge); - this.signature = signature; - this.originalID = checkU16("originalID", originalID); - this.error = checkU16("error", error); - this.other = other; -} - -void -rrFromWire(DNSInput in) throws IOException { - alg = new Name(in); - - long timeHigh = in.readU16(); - long timeLow = in.readU32(); - long time = (timeHigh << 32) + timeLow; - timeSigned = new Date(time * 1000); - fudge = in.readU16(); - - int sigLen = in.readU16(); - signature = in.readByteArray(sigLen); - - originalID = in.readU16(); - error = in.readU16(); - - int otherLen = in.readU16(); - if (otherLen > 0) - other = in.readByteArray(otherLen); - else - other = null; -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - throw st.exception("no text format defined for TSIG"); -} - -/** Converts rdata to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(alg); - sb.append(" "); - if (Options.check("multiline")) - sb.append("(\n\t"); - - sb.append (timeSigned.getTime() / 1000); - sb.append (" "); - sb.append (fudge); - sb.append (" "); - sb.append (signature.length); - if (Options.check("multiline")) { - sb.append ("\n"); - sb.append (base64.formatString(signature, 64, "\t", false)); - } else { - sb.append (" "); - sb.append (base64.toString(signature)); - } - sb.append (" "); - sb.append (Rcode.TSIGstring(error)); - sb.append (" "); - if (other == null) - sb.append (0); - else { - sb.append (other.length); - if (Options.check("multiline")) - sb.append("\n\n\n\t"); - else - sb.append(" "); - if (error == Rcode.BADTIME) { - if (other.length != 6) { - sb.append(""); - } else { - long time = ((long)(other[0] & 0xFF) << 40) + - ((long)(other[1] & 0xFF) << 32) + - ((other[2] & 0xFF) << 24) + - ((other[3] & 0xFF) << 16) + - ((other[4] & 0xFF) << 8) + - ((other[5] & 0xFF) ); - sb.append(""); - } - } else { - sb.append("<"); - sb.append(base64.toString(other)); - sb.append(">"); - } - } - if (Options.check("multiline")) - sb.append(" )"); - return sb.toString(); -} - -/** Returns the shared key's algorithm */ -public Name -getAlgorithm() { - return alg; -} - -/** Returns the time that this record was generated */ -public Date -getTimeSigned() { - return timeSigned; -} - -/** Returns the time fudge factor */ -public int -getFudge() { - return fudge; -} - -/** Returns the signature */ -public byte [] -getSignature() { - return signature; -} - -/** Returns the original message ID */ -public int -getOriginalID() { - return originalID; -} - -/** Returns the extended error */ -public int -getError() { - return error; -} - -/** Returns the other data */ -public byte [] -getOther() { - return other; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - alg.toWire(out, null, canonical); - - long time = timeSigned.getTime() / 1000; - int timeHigh = (int) (time >> 32); - long timeLow = (time & 0xFFFFFFFFL); - out.writeU16(timeHigh); - out.writeU32(timeLow); - out.writeU16(fudge); - - out.writeU16(signature.length); - out.writeByteArray(signature); - - out.writeU16(originalID); - out.writeU16(error); - - if (other != null) { - out.writeU16(other.length); - out.writeByteArray(other); - } - else - out.writeU16(0); -} - -} diff --git a/src/main/java/org/xbill/DNS/TTL.java b/src/main/java/org/xbill/DNS/TTL.java deleted file mode 100644 index 01bf41681..000000000 --- a/src/main/java/org/xbill/DNS/TTL.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Routines for parsing BIND-style TTL values. These values consist of - * numbers followed by 1 letter units of time (W - week, D - day, H - hour, - * M - minute, S - second). - * - * @author Brian Wellington - */ - -public final class TTL { - -public static final long MAX_VALUE = 0x7FFFFFFFL; - -private -TTL() {} - -static void -check(long i) { - if (i < 0 || i > MAX_VALUE) - throw new InvalidTTLException(i); -} - -/** - * Parses a TTL-like value, which can either be expressed as a number or a - * BIND-style string with numbers and units. - * @param s The string representing the numeric value. - * @param clamp Whether to clamp values in the range [MAX_VALUE + 1, 2^32 -1] - * to MAX_VALUE. This should be donw for TTLs, but not other values which - * can be expressed in this format. - * @return The value as a number of seconds - * @throws NumberFormatException The string was not in a valid TTL format. - */ -public static long -parse(String s, boolean clamp) { - if (s == null || s.length() == 0 || !Character.isDigit(s.charAt(0))) - throw new NumberFormatException(); - long value = 0; - long ttl = 0; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - long oldvalue = value; - if (Character.isDigit(c)) { - value = (value * 10) + Character.getNumericValue(c); - if (value < oldvalue) - throw new NumberFormatException(); - } else { - switch (Character.toUpperCase(c)) { - case 'W': value *= 7; - case 'D': value *= 24; - case 'H': value *= 60; - case 'M': value *= 60; - case 'S': break; - default: throw new NumberFormatException(); - } - ttl += value; - value = 0; - if (ttl > 0xFFFFFFFFL) - throw new NumberFormatException(); - } - } - if (ttl == 0) - ttl = value; - - if (ttl > 0xFFFFFFFFL) - throw new NumberFormatException(); - else if (ttl > MAX_VALUE && clamp) - ttl = MAX_VALUE; - return ttl; -} - -/** - * Parses a TTL, which can either be expressed as a number or a BIND-style - * string with numbers and units. - * @param s The string representing the TTL - * @return The TTL as a number of seconds - * @throws NumberFormatException The string was not in a valid TTL format. - */ -public static long -parseTTL(String s) { - return parse(s, true); -} - -public static String -format(long ttl) { - TTL.check(ttl); - StringBuffer sb = new StringBuffer(); - long secs, mins, hours, days, weeks; - secs = ttl % 60; - ttl /= 60; - mins = ttl % 60; - ttl /= 60; - hours = ttl % 24; - ttl /= 24; - days = ttl % 7; - ttl /= 7; - weeks = ttl; - if (weeks > 0) - sb.append(weeks + "W"); - if (days > 0) - sb.append(days + "D"); - if (hours > 0) - sb.append(hours + "H"); - if (mins > 0) - sb.append(mins + "M"); - if (secs > 0 || (weeks == 0 && days == 0 && hours == 0 && mins == 0)) - sb.append(secs + "S"); - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/TXTBase.java b/src/main/java/org/xbill/DNS/TXTBase.java deleted file mode 100644 index 30315ae9c..000000000 --- a/src/main/java/org/xbill/DNS/TXTBase.java +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * Implements common functionality for the many record types whose format - * is a list of strings. - * - * @author Brian Wellington - */ - -abstract class TXTBase extends Record { - -private static final long serialVersionUID = -4319510507246305931L; - -protected List strings; - -protected -TXTBase() {} - -protected -TXTBase(Name name, int type, int dclass, long ttl) { - super(name, type, dclass, ttl); -} - -protected -TXTBase(Name name, int type, int dclass, long ttl, List strings) { - super(name, type, dclass, ttl); - if (strings == null) - throw new IllegalArgumentException("strings must not be null"); - this.strings = new ArrayList(strings.size()); - Iterator it = strings.iterator(); - try { - while (it.hasNext()) { - String s = (String) it.next(); - this.strings.add(byteArrayFromString(s)); - } - } - catch (TextParseException e) { - throw new IllegalArgumentException(e.getMessage()); - } -} - -protected -TXTBase(Name name, int type, int dclass, long ttl, String string) { - this(name, type, dclass, ttl, Collections.singletonList(string)); -} - -void -rrFromWire(DNSInput in) throws IOException { - strings = new ArrayList(2); - while (in.remaining() > 0) { - byte [] b = in.readCountedString(); - strings.add(b); - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - strings = new ArrayList(2); - while (true) { - Tokenizer.Token t = st.get(); - if (!t.isString()) - break; - try { - strings.add(byteArrayFromString(t.value)); - } - catch (TextParseException e) { - throw st.exception(e.getMessage()); - } - - } - st.unget(); -} - -/** converts to a String */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - Iterator it = strings.iterator(); - while (it.hasNext()) { - byte [] array = (byte []) it.next(); - sb.append(byteArrayToString(array, true)); - if (it.hasNext()) - sb.append(" "); - } - return sb.toString(); -} - -/** - * Returns the text strings - * @return A list of Strings corresponding to the text strings. - */ -public List -getStrings() { - List list = new ArrayList(strings.size()); - for (int i = 0; i < strings.size(); i++) - list.add(byteArrayToString((byte []) strings.get(i), false)); - return list; -} - -/** - * Returns the text strings - * @return A list of byte arrays corresponding to the text strings. - */ -public List -getStringsAsByteArrays() { - return strings; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - Iterator it = strings.iterator(); - while (it.hasNext()) { - byte [] b = (byte []) it.next(); - out.writeCountedString(b); - } -} - -} diff --git a/src/main/java/org/xbill/DNS/TXTRecord.java b/src/main/java/org/xbill/DNS/TXTRecord.java deleted file mode 100644 index d0a4ac208..000000000 --- a/src/main/java/org/xbill/DNS/TXTRecord.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.List; - -/** - * Text - stores text strings - * - * @author Brian Wellington - */ - -public class TXTRecord extends TXTBase { - -private static final long serialVersionUID = -5780785764284221342L; - -TXTRecord() {} - -Record -getObject() { - return new TXTRecord(); -} - -/** - * Creates a TXT Record from the given data - * @param strings The text strings - * @throws IllegalArgumentException One of the strings has invalid escapes - */ -public -TXTRecord(Name name, int dclass, long ttl, List strings) { - super(name, Type.TXT, dclass, ttl, strings); -} - -/** - * Creates a TXT Record from the given data - * @param string One text string - * @throws IllegalArgumentException The string has invalid escapes - */ -public -TXTRecord(Name name, int dclass, long ttl, String string) { - super(name, Type.TXT, dclass, ttl, string); -} - -} diff --git a/src/main/java/org/xbill/DNS/TextParseException.java b/src/main/java/org/xbill/DNS/TextParseException.java deleted file mode 100644 index 0abb13e5e..000000000 --- a/src/main/java/org/xbill/DNS/TextParseException.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2002-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * An exception thrown when unable to parse text. - * - * @author Brian Wellington - */ - -public class TextParseException extends IOException { - -public -TextParseException() { - super(); -} - -public -TextParseException(String s) { - super(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/Tokenizer.java b/src/main/java/org/xbill/DNS/Tokenizer.java deleted file mode 100644 index 7ec1d0b8d..000000000 --- a/src/main/java/org/xbill/DNS/Tokenizer.java +++ /dev/null @@ -1,716 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) -// -// Copyright (C) 2003-2004 Nominum, Inc. -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR ANY -// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -// OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// - -package org.xbill.DNS; - -import org.xbill.DNS.utils.base16; -import org.xbill.DNS.utils.base32; -import org.xbill.DNS.utils.base64; - -import java.io.*; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * Tokenizer is used to parse DNS records and zones from text format, - * - * @author Brian Wellington - * @author Bob Halley - */ - -public class Tokenizer { - -private static String delim = " \t\n;()\""; -private static String quotes = "\""; - -/** End of file */ -public static final int EOF = 0; - -/** End of line */ -public static final int EOL = 1; - -/** Whitespace; only returned when wantWhitespace is set */ -public static final int WHITESPACE = 2; - -/** An identifier (unquoted string) */ -public static final int IDENTIFIER = 3; - -/** A quoted string */ -public static final int QUOTED_STRING = 4; - -/** A comment; only returned when wantComment is set */ -public static final int COMMENT = 5; - -private PushbackInputStream is; -private boolean ungottenToken; -private int multiline; -private boolean quoting; -private String delimiters; -private Token current; -private StringBuffer sb; -private boolean wantClose; - -private String filename; -private int line; - -public static class Token { - /** The type of token. */ - public int type; - - /** The value of the token, or null for tokens without values. */ - public String value; - - private - Token() { - type = -1; - value = null; - } - - private Token - set(int type, StringBuffer value) { - if (type < 0) - throw new IllegalArgumentException(); - this.type = type; - this.value = value == null ? null : value.toString(); - return this; - } - - /** - * Converts the token to a string containing a representation useful - * for debugging. - */ - public String - toString() { - switch (type) { - case EOF: - return ""; - case EOL: - return ""; - case WHITESPACE: - return ""; - case IDENTIFIER: - return ""; - case QUOTED_STRING: - return ""; - case COMMENT: - return ""; - default: - return ""; - } - } - - /** Indicates whether this token contains a string. */ - public boolean - isString() { - return (type == IDENTIFIER || type == QUOTED_STRING); - } - - /** Indicates whether this token contains an EOL or EOF. */ - public boolean - isEOL() { - return (type == EOL || type == EOF); - } -} - -static class TokenizerException extends TextParseException { - String message; - - public - TokenizerException(String filename, int line, String message) { - super(filename + ":" + line + ": " + message); - this.message = message; - } - - public String - getBaseMessage() { - return message; - } -} - -/** - * Creates a Tokenizer from an arbitrary input stream. - * @param is The InputStream to tokenize. - */ -public -Tokenizer(InputStream is) { - if (!(is instanceof BufferedInputStream)) - is = new BufferedInputStream(is); - this.is = new PushbackInputStream(is, 2); - ungottenToken = false; - multiline = 0; - quoting = false; - delimiters = delim; - current = new Token(); - sb = new StringBuffer(); - filename = ""; - line = 1; -} - -/** - * Creates a Tokenizer from a string. - * @param s The String to tokenize. - */ -public -Tokenizer(String s) { - this(new ByteArrayInputStream(s.getBytes())); -} - -/** - * Creates a Tokenizer from a file. - * @param f The File to tokenize. - */ -public -Tokenizer(File f) throws FileNotFoundException { - this(new FileInputStream(f)); - wantClose = true; - filename = f.getName(); -} - -private int -getChar() throws IOException { - int c = is.read(); - if (c == '\r') { - int next = is.read(); - if (next != '\n') - is.unread(next); - c = '\n'; - } - if (c == '\n') - line++; - return c; -} - -private void -ungetChar(int c) throws IOException { - if (c == -1) - return; - is.unread(c); - if (c == '\n') - line--; -} - -private int -skipWhitespace() throws IOException { - int skipped = 0; - while (true) { - int c = getChar(); - if (c != ' ' && c != '\t') { - if (!(c == '\n' && multiline > 0)) { - ungetChar(c); - return skipped; - } - } - skipped++; - } -} - -private void -checkUnbalancedParens() throws TextParseException { - if (multiline > 0) - throw exception("unbalanced parentheses"); -} - -/** - * Gets the next token from a tokenizer. - * @param wantWhitespace If true, leading whitespace will be returned as a - * token. - * @param wantComment If true, comments are returned as tokens. - * @return The next token in the stream. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public Token -get(boolean wantWhitespace, boolean wantComment) throws IOException { - int type; - int c; - - if (ungottenToken) { - ungottenToken = false; - if (current.type == WHITESPACE) { - if (wantWhitespace) - return current; - } else if (current.type == COMMENT) { - if (wantComment) - return current; - } else { - if (current.type == EOL) - line++; - return current; - } - } - int skipped = skipWhitespace(); - if (skipped > 0 && wantWhitespace) - return current.set(WHITESPACE, null); - type = IDENTIFIER; - sb.setLength(0); - while (true) { - c = getChar(); - if (c == -1 || delimiters.indexOf(c) != -1) { - if (c == -1) { - if (quoting) - throw exception("EOF in " + - "quoted string"); - else if (sb.length() == 0) - return current.set(EOF, null); - else - return current.set(type, sb); - } - if (sb.length() == 0 && type != QUOTED_STRING) { - if (c == '(') { - multiline++; - skipWhitespace(); - continue; - } else if (c == ')') { - if (multiline <= 0) - throw exception("invalid " + - "close " + - "parenthesis"); - multiline--; - skipWhitespace(); - continue; - } else if (c == '"') { - if (!quoting) { - quoting = true; - delimiters = quotes; - type = QUOTED_STRING; - } else { - quoting = false; - delimiters = delim; - skipWhitespace(); - } - continue; - } else if (c == '\n') { - return current.set(EOL, null); - } else if (c == ';') { - while (true) { - c = getChar(); - if (c == '\n' || c == -1) - break; - sb.append((char)c); - } - if (wantComment) { - ungetChar(c); - return current.set(COMMENT, sb); - } else if (c == -1 && - type != QUOTED_STRING) - { - checkUnbalancedParens(); - return current.set(EOF, null); - } else if (multiline > 0) { - skipWhitespace(); - sb.setLength(0); - continue; - } else - return current.set(EOL, null); - } else - throw new IllegalStateException(); - } else - ungetChar(c); - break; - } else if (c == '\\') { - c = getChar(); - if (c == -1) - throw exception("unterminated escape sequence"); - sb.append('\\'); - } else if (quoting && c == '\n') { - throw exception("newline in quoted string"); - } - sb.append((char)c); - } - if (sb.length() == 0 && type != QUOTED_STRING) { - checkUnbalancedParens(); - return current.set(EOF, null); - } - return current.set(type, sb); -} - -/** - * Gets the next token from a tokenizer, ignoring whitespace and comments. - * @return The next token in the stream. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public Token -get() throws IOException { - return get(false, false); -} - -/** - * Returns a token to the stream, so that it will be returned by the next call - * to get(). - * @throws IllegalStateException There are already ungotten tokens. - */ -public void -unget() { - if (ungottenToken) - throw new IllegalStateException - ("Cannot unget multiple tokens"); - if (current.type == EOL) - line--; - ungottenToken = true; -} - -/** - * Gets the next token from a tokenizer and converts it to a string. - * @return The next token in the stream, as a string. - * @throws TextParseException The input was invalid or not a string. - * @throws IOException An I/O error occurred. - */ -public String -getString() throws IOException { - Token next = get(); - if (!next.isString()) { - throw exception("expected a string"); - } - return next.value; -} - -private String -_getIdentifier(String expected) throws IOException { - Token next = get(); - if (next.type != IDENTIFIER) - throw exception("expected " + expected); - return next.value; -} - -/** - * Gets the next token from a tokenizer, ensures it is an unquoted string, - * and converts it to a string. - * @return The next token in the stream, as a string. - * @throws TextParseException The input was invalid or not an unquoted string. - * @throws IOException An I/O error occurred. - */ -public String -getIdentifier() throws IOException { - return _getIdentifier("an identifier"); -} - -/** - * Gets the next token from a tokenizer and converts it to a long. - * @return The next token in the stream, as a long. - * @throws TextParseException The input was invalid or not a long. - * @throws IOException An I/O error occurred. - */ -public long -getLong() throws IOException { - String next = _getIdentifier("an integer"); - if (!Character.isDigit(next.charAt(0))) - throw exception("expected an integer"); - try { - return Long.parseLong(next); - } catch (NumberFormatException e) { - throw exception("expected an integer"); - } -} - -/** - * Gets the next token from a tokenizer and converts it to an unsigned 32 bit - * integer. - * @return The next token in the stream, as an unsigned 32 bit integer. - * @throws TextParseException The input was invalid or not an unsigned 32 - * bit integer. - * @throws IOException An I/O error occurred. - */ -public long -getUInt32() throws IOException { - long l = getLong(); - if (l < 0 || l > 0xFFFFFFFFL) - throw exception("expected an 32 bit unsigned integer"); - return l; -} - -/** - * Gets the next token from a tokenizer and converts it to an unsigned 16 bit - * integer. - * @return The next token in the stream, as an unsigned 16 bit integer. - * @throws TextParseException The input was invalid or not an unsigned 16 - * bit integer. - * @throws IOException An I/O error occurred. - */ -public int -getUInt16() throws IOException { - long l = getLong(); - if (l < 0 || l > 0xFFFFL) - throw exception("expected an 16 bit unsigned integer"); - return (int) l; -} - -/** - * Gets the next token from a tokenizer and converts it to an unsigned 8 bit - * integer. - * @return The next token in the stream, as an unsigned 8 bit integer. - * @throws TextParseException The input was invalid or not an unsigned 8 - * bit integer. - * @throws IOException An I/O error occurred. - */ -public int -getUInt8() throws IOException { - long l = getLong(); - if (l < 0 || l > 0xFFL) - throw exception("expected an 8 bit unsigned integer"); - return (int) l; -} - -/** - * Gets the next token from a tokenizer and parses it as a TTL. - * @return The next token in the stream, as an unsigned 32 bit integer. - * @throws TextParseException The input was not valid. - * @throws IOException An I/O error occurred. - * @see TTL - */ -public long -getTTL() throws IOException { - String next = _getIdentifier("a TTL value"); - try { - return TTL.parseTTL(next); - } - catch (NumberFormatException e) { - throw exception("expected a TTL value"); - } -} - -/** - * Gets the next token from a tokenizer and parses it as if it were a TTL. - * @return The next token in the stream, as an unsigned 32 bit integer. - * @throws TextParseException The input was not valid. - * @throws IOException An I/O error occurred. - * @see TTL - */ -public long -getTTLLike() throws IOException { - String next = _getIdentifier("a TTL-like value"); - try { - return TTL.parse(next, false); - } - catch (NumberFormatException e) { - throw exception("expected a TTL-like value"); - } -} - -/** - * Gets the next token from a tokenizer and converts it to a name. - * @param origin The origin to append to relative names. - * @return The next token in the stream, as a name. - * @throws TextParseException The input was invalid or not a valid name. - * @throws IOException An I/O error occurred. - * @throws RelativeNameException The parsed name was relative, even with the - * origin. - * @see Name - */ -public Name -getName(Name origin) throws IOException { - String next = _getIdentifier("a name"); - try { - Name name = Name.fromString(next, origin); - if (!name.isAbsolute()) - throw new RelativeNameException(name); - return name; - } - catch (TextParseException e) { - throw exception(e.getMessage()); - } -} - -/** - * Gets the next token from a tokenizer and converts it to an IP Address. - * @param family The address family. - * @return The next token in the stream, as an InetAddress - * @throws TextParseException The input was invalid or not a valid address. - * @throws IOException An I/O error occurred. - * @see Address - */ -public InetAddress -getAddress(int family) throws IOException { - String next = _getIdentifier("an address"); - try { - return Address.getByAddress(next, family); - } - catch (UnknownHostException e) { - throw exception(e.getMessage()); - } -} - -/** - * Gets the next token from a tokenizer, which must be an EOL or EOF. - * @throws TextParseException The input was invalid or not an EOL or EOF token. - * @throws IOException An I/O error occurred. - */ -public void -getEOL() throws IOException { - Token next = get(); - if (next.type != EOL && next.type != EOF) { - throw exception("expected EOL or EOF"); - } -} - -/** - * Returns a concatenation of the remaining strings from a Tokenizer. - */ -private String -remainingStrings() throws IOException { - StringBuffer buffer = null; - while (true) { - Tokenizer.Token t = get(); - if (!t.isString()) - break; - if (buffer == null) - buffer = new StringBuffer(); - buffer.append(t.value); - } - unget(); - if (buffer == null) - return null; - return buffer.toString(); -} - -/** - * Gets the remaining string tokens until an EOL/EOF is seen, concatenates - * them together, and converts the base64 encoded data to a byte array. - * @param required If true, an exception will be thrown if no strings remain; - * otherwise null be be returned. - * @return The byte array containing the decoded strings, or null if there - * were no strings to decode. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public byte [] -getBase64(boolean required) throws IOException { - String s = remainingStrings(); - if (s == null) { - if (required) - throw exception("expected base64 encoded string"); - else - return null; - } - byte [] array = base64.fromString(s); - if (array == null) - throw exception("invalid base64 encoding"); - return array; -} - -/** - * Gets the remaining string tokens until an EOL/EOF is seen, concatenates - * them together, and converts the base64 encoded data to a byte array. - * @return The byte array containing the decoded strings, or null if there - * were no strings to decode. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public byte [] -getBase64() throws IOException { - return getBase64(false); -} - -/** - * Gets the remaining string tokens until an EOL/EOF is seen, concatenates - * them together, and converts the hex encoded data to a byte array. - * @param required If true, an exception will be thrown if no strings remain; - * otherwise null be be returned. - * @return The byte array containing the decoded strings, or null if there - * were no strings to decode. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public byte [] -getHex(boolean required) throws IOException { - String s = remainingStrings(); - if (s == null) { - if (required) - throw exception("expected hex encoded string"); - else - return null; - } - byte [] array = base16.fromString(s); - if (array == null) - throw exception("invalid hex encoding"); - return array; -} - -/** - * Gets the remaining string tokens until an EOL/EOF is seen, concatenates - * them together, and converts the hex encoded data to a byte array. - * @return The byte array containing the decoded strings, or null if there - * were no strings to decode. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public byte [] -getHex() throws IOException { - return getHex(false); -} - -/** - * Gets the next token from a tokenizer and decodes it as hex. - * @return The byte array containing the decoded string. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public byte [] -getHexString() throws IOException { - String next = _getIdentifier("a hex string"); - byte [] array = base16.fromString(next); - if (array == null) - throw exception("invalid hex encoding"); - return array; -} - -/** - * Gets the next token from a tokenizer and decodes it as base32. - * @param b32 The base32 context to decode with. - * @return The byte array containing the decoded string. - * @throws TextParseException The input was invalid. - * @throws IOException An I/O error occurred. - */ -public byte [] -getBase32String(base32 b32) throws IOException { - String next = _getIdentifier("a base32 string"); - byte [] array = b32.fromString(next); - if (array == null) - throw exception("invalid base32 encoding"); - return array; -} - -/** - * Creates an exception which includes the current state in the error message - * @param s The error message to include. - * @return The exception to be thrown - */ -public TextParseException -exception(String s) { - return new TokenizerException(filename, line, s); -} - -/** - * Closes any files opened by this tokenizer. - */ -public void -close() { - if (wantClose) { - try { - is.close(); - } - catch (IOException e) { - } - } -} - -protected void -finalize() { - close(); -} - -} diff --git a/src/main/java/org/xbill/DNS/Type.java b/src/main/java/org/xbill/DNS/Type.java deleted file mode 100644 index a5d479ada..000000000 --- a/src/main/java/org/xbill/DNS/Type.java +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.util.HashMap; - -/** - * Constants and functions relating to DNS Types - * - * @author Brian Wellington - */ - -public final class Type { - -/** Address */ -public static final int A = 1; - -/** Name server */ -public static final int NS = 2; - -/** Mail destination */ -public static final int MD = 3; - -/** Mail forwarder */ -public static final int MF = 4; - -/** Canonical name (alias) */ -public static final int CNAME = 5; - -/** Start of authority */ -public static final int SOA = 6; - -/** Mailbox domain name */ -public static final int MB = 7; - -/** Mail group member */ -public static final int MG = 8; - -/** Mail rename name */ -public static final int MR = 9; - -/** Null record */ -public static final int NULL = 10; - -/** Well known services */ -public static final int WKS = 11; - -/** Domain name pointer */ -public static final int PTR = 12; - -/** Host information */ -public static final int HINFO = 13; - -/** Mailbox information */ -public static final int MINFO = 14; - -/** Mail routing information */ -public static final int MX = 15; - -/** Text strings */ -public static final int TXT = 16; - -/** Responsible person */ -public static final int RP = 17; - -/** AFS cell database */ -public static final int AFSDB = 18; - -/** X.25 calling address */ -public static final int X25 = 19; - -/** ISDN calling address */ -public static final int ISDN = 20; - -/** Router */ -public static final int RT = 21; - -/** NSAP address */ -public static final int NSAP = 22; - -/** Reverse NSAP address (deprecated) */ -public static final int NSAP_PTR = 23; - -/** Signature */ -public static final int SIG = 24; - -/** Key */ -public static final int KEY = 25; - -/** X.400 mail mapping */ -public static final int PX = 26; - -/** Geographical position (withdrawn) */ -public static final int GPOS = 27; - -/** IPv6 address */ -public static final int AAAA = 28; - -/** Location */ -public static final int LOC = 29; - -/** Next valid name in zone */ -public static final int NXT = 30; - -/** Endpoint identifier */ -public static final int EID = 31; - -/** Nimrod locator */ -public static final int NIMLOC = 32; - -/** Server selection */ -public static final int SRV = 33; - -/** ATM address */ -public static final int ATMA = 34; - -/** Naming authority pointer */ -public static final int NAPTR = 35; - -/** Key exchange */ -public static final int KX = 36; - -/** Certificate */ -public static final int CERT = 37; - -/** IPv6 address (experimental) */ -public static final int A6 = 38; - -/** Non-terminal name redirection */ -public static final int DNAME = 39; - -/** Options - contains EDNS metadata */ -public static final int OPT = 41; - -/** Address Prefix List */ -public static final int APL = 42; - -/** Delegation Signer */ -public static final int DS = 43; - -/** SSH Key Fingerprint */ -public static final int SSHFP = 44; - -/** IPSEC key */ -public static final int IPSECKEY = 45; - -/** Resource Record Signature */ -public static final int RRSIG = 46; - -/** Next Secure Name */ -public static final int NSEC = 47; - -/** DNSSEC Key */ -public static final int DNSKEY = 48; - -/** Dynamic Host Configuration Protocol (DHCP) ID */ -public static final int DHCID = 49; - -/** Next SECure, 3rd edition, RFC 5155 */ -public static final int NSEC3 = 50; - -public static final int NSEC3PARAM = 51; - -/** Sender Policy Framework (experimental) */ -public static final int SPF = 99; - -/** Transaction key - used to compute a shared secret or exchange a key */ -public static final int TKEY = 249; - -/** Transaction signature */ -public static final int TSIG = 250; - -/** Incremental zone transfer */ -public static final int IXFR = 251; - -/** Zone transfer */ -public static final int AXFR = 252; - -/** Transfer mailbox records */ -public static final int MAILB = 253; - -/** Transfer mail agent records */ -public static final int MAILA = 254; - -/** Matches any type */ -public static final int ANY = 255; - -/** DNSSEC Lookaside Validation, RFC 4431 . */ -public static final int DLV = 32769; - - -private static class TypeMnemonic extends Mnemonic { - private HashMap objects; - - public - TypeMnemonic() { - super("Type", CASE_UPPER); - setPrefix("TYPE"); - objects = new HashMap(); - } - - public void - add(int val, String str, Record proto) { - super.add(val, str); - objects.put(Mnemonic.toInteger(val), proto); - } - - public void - check(int val) { - Type.check(val); - } - - public Record - getProto(int val) { - check(val); - return (Record) objects.get(toInteger(val)); - } -} - -private static TypeMnemonic types = new TypeMnemonic(); - -static { - types.add(A, "A", new ARecord()); - types.add(NS, "NS", new NSRecord()); - types.add(MD, "MD", new MDRecord()); - types.add(MF, "MF", new MFRecord()); - types.add(CNAME, "CNAME", new CNAMERecord()); - types.add(SOA, "SOA", new SOARecord()); - types.add(MB, "MB", new MBRecord()); - types.add(MG, "MG", new MGRecord()); - types.add(MR, "MR", new MRRecord()); - types.add(NULL, "NULL", new NULLRecord()); - types.add(WKS, "WKS", new WKSRecord()); - types.add(PTR, "PTR", new PTRRecord()); - types.add(HINFO, "HINFO", new HINFORecord()); - types.add(MINFO, "MINFO", new MINFORecord()); - types.add(MX, "MX", new MXRecord()); - types.add(TXT, "TXT", new TXTRecord()); - types.add(RP, "RP", new RPRecord()); - types.add(AFSDB, "AFSDB", new AFSDBRecord()); - types.add(X25, "X25", new X25Record()); - types.add(ISDN, "ISDN", new ISDNRecord()); - types.add(RT, "RT", new RTRecord()); - types.add(NSAP, "NSAP", new NSAPRecord()); - types.add(NSAP_PTR, "NSAP-PTR", new NSAP_PTRRecord()); - types.add(SIG, "SIG", new SIGRecord()); - types.add(KEY, "KEY", new KEYRecord()); - types.add(PX, "PX", new PXRecord()); - types.add(GPOS, "GPOS", new GPOSRecord()); - types.add(AAAA, "AAAA", new AAAARecord()); - types.add(LOC, "LOC", new LOCRecord()); - types.add(NXT, "NXT", new NXTRecord()); - types.add(EID, "EID"); - types.add(NIMLOC, "NIMLOC"); - types.add(SRV, "SRV", new SRVRecord()); - types.add(ATMA, "ATMA"); - types.add(NAPTR, "NAPTR", new NAPTRRecord()); - types.add(KX, "KX", new KXRecord()); - types.add(CERT, "CERT", new CERTRecord()); - types.add(A6, "A6", new A6Record()); - types.add(DNAME, "DNAME", new DNAMERecord()); - types.add(OPT, "OPT", new OPTRecord()); - types.add(APL, "APL", new APLRecord()); - types.add(DS, "DS", new DSRecord()); - types.add(SSHFP, "SSHFP", new SSHFPRecord()); - types.add(IPSECKEY, "IPSECKEY", new IPSECKEYRecord()); - types.add(RRSIG, "RRSIG", new RRSIGRecord()); - types.add(NSEC, "NSEC", new NSECRecord()); - types.add(DNSKEY, "DNSKEY", new DNSKEYRecord()); - types.add(DHCID, "DHCID", new DHCIDRecord()); - types.add(NSEC3, "NSEC3", new NSEC3Record()); - types.add(NSEC3PARAM, "NSEC3PARAM", new NSEC3PARAMRecord()); - types.add(SPF, "SPF", new SPFRecord()); - types.add(TKEY, "TKEY", new TKEYRecord()); - types.add(TSIG, "TSIG", new TSIGRecord()); - types.add(IXFR, "IXFR"); - types.add(AXFR, "AXFR"); - types.add(MAILB, "MAILB"); - types.add(MAILA, "MAILA"); - types.add(ANY, "ANY"); - types.add(DLV, "DLV", new DLVRecord()); -} - -private -Type() { -} - -/** - * Checks that a numeric Type is valid. - * @throws InvalidTypeException The type is out of range. - */ -public static void -check(int val) { - if (val < 0 || val > 0xFFFF) - throw new InvalidTypeException(val); -} - -/** - * Converts a numeric Type into a String - * @param val The type value. - * @return The canonical string representation of the type - * @throws InvalidTypeException The type is out of range. - */ -public static String -string(int val) { - return types.getText(val); -} - -/** - * Converts a String representation of an Type into its numeric value. - * @param s The string representation of the type - * @param numberok Whether a number will be accepted or not. - * @return The type code, or -1 on error. - */ -public static int -value(String s, boolean numberok) { - int val = types.getValue(s); - if (val == -1 && numberok) { - val = types.getValue("TYPE" + s); - } - return val; -} - -/** - * Converts a String representation of an Type into its numeric value - * @return The type code, or -1 on error. - */ -public static int -value(String s) { - return value(s, false); -} - -static Record -getProto(int val) { - return types.getProto(val); -} - -/** Is this type valid for a record (a non-meta type)? */ -public static boolean -isRR(int type) { - switch (type) { - case OPT: - case TKEY: - case TSIG: - case IXFR: - case AXFR: - case MAILB: - case MAILA: - case ANY: - return false; - default: - return true; - } -} - -} diff --git a/src/main/java/org/xbill/DNS/TypeBitmap.java b/src/main/java/org/xbill/DNS/TypeBitmap.java deleted file mode 100644 index 6bb8e676f..000000000 --- a/src/main/java/org/xbill/DNS/TypeBitmap.java +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2004-2009 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * Routines for deal with the lists of types found in NSEC/NSEC3 records. - * - * @author Brian Wellington - */ - -import java.io.IOException; -import java.io.Serializable; -import java.util.Iterator; -import java.util.TreeSet; - -final class TypeBitmap implements Serializable { - -private static final long serialVersionUID = -125354057735389003L; - -private TreeSet types; - -private -TypeBitmap() { - types = new TreeSet(); -} - -public -TypeBitmap(int [] array) { - this(); - for (int i = 0; i < array.length; i++) { - Type.check(array[i]); - types.add(new Integer(array[i])); - } -} - -public -TypeBitmap(DNSInput in) throws WireParseException { - this(); - int lastbase = -1; - while (in.remaining() > 0) { - if (in.remaining() < 2) - throw new WireParseException - ("invalid bitmap descriptor"); - int mapbase = in.readU8(); - if (mapbase < lastbase) - throw new WireParseException("invalid ordering"); - int maplength = in.readU8(); - if (maplength > in.remaining()) - throw new WireParseException("invalid bitmap"); - for (int i = 0; i < maplength; i++) { - int current = in.readU8(); - if (current == 0) - continue; - for (int j = 0; j < 8; j++) { - if ((current & (1 << (7 - j))) == 0) - continue; - int typecode = mapbase * 256 + + i * 8 + j; - types.add(Mnemonic.toInteger(typecode)); - } - } - } -} - -public -TypeBitmap(Tokenizer st) throws IOException { - this(); - while (true) { - Tokenizer.Token t = st.get(); - if (!t.isString()) - break; - int typecode = Type.value(t.value); - if (typecode < 0) { - throw st.exception("Invalid type: " + t.value); - } - types.add(Mnemonic.toInteger(typecode)); - } - st.unget(); -} - -public int [] -toArray() { - int [] array = new int[types.size()]; - int n = 0; - for (Iterator it = types.iterator(); it.hasNext(); ) - array[n++] = ((Integer)it.next()).intValue(); - return array; -} - -public String -toString() { - StringBuffer sb = new StringBuffer(); - for (Iterator it = types.iterator(); it.hasNext(); ) { - int t = ((Integer)it.next()).intValue(); - sb.append(Type.string(t)); - sb.append(' '); - } - return sb.substring(0, sb.length() - 1); -} - -private static void -mapToWire(DNSOutput out, TreeSet map, int mapbase) { - int arraymax = (((Integer)map.last()).intValue()) & 0xFF; - int arraylength = (arraymax / 8) + 1; - int [] array = new int[arraylength]; - out.writeU8(mapbase); - out.writeU8(arraylength); - for (Iterator it = map.iterator(); it.hasNext(); ) { - int typecode = ((Integer)it.next()).intValue(); - array[(typecode & 0xFF) / 8] |= (1 << ( 7 - typecode % 8)); - } - for (int j = 0; j < arraylength; j++) - out.writeU8(array[j]); -} - -public void -toWire(DNSOutput out) { - if (types.size() == 0) - return; - - int mapbase = -1; - TreeSet map = new TreeSet(); - - for (Iterator it = types.iterator(); it.hasNext(); ) { - int t = ((Integer)it.next()).intValue(); - int base = t >> 8; - if (base == mapbase) - map.add(new Integer(t)); - else { - if (map.size() > 0) - mapToWire(out, map, mapbase); - map.clear(); - mapbase = base; - } - } - mapToWire(out, map, mapbase); -} - -public boolean -empty() { - return types.isEmpty(); -} - -public boolean -contains(int typecode) { - return types.contains(Mnemonic.toInteger(typecode)); -} - -} diff --git a/src/main/java/org/xbill/DNS/U16NameBase.java b/src/main/java/org/xbill/DNS/U16NameBase.java deleted file mode 100644 index de423fdd3..000000000 --- a/src/main/java/org/xbill/DNS/U16NameBase.java +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * Implements common functionality for the many record types whose format - * is an unsigned 16 bit integer followed by a name. - * - * @author Brian Wellington - */ - -abstract class U16NameBase extends Record { - -private static final long serialVersionUID = -8315884183112502995L; - -protected int u16Field; -protected Name nameField; - -protected -U16NameBase() {} - -protected -U16NameBase(Name name, int type, int dclass, long ttl) { - super(name, type, dclass, ttl); -} - -protected -U16NameBase(Name name, int type, int dclass, long ttl, int u16Field, - String u16Description, Name nameField, String nameDescription) -{ - super(name, type, dclass, ttl); - this.u16Field = checkU16(u16Description, u16Field); - this.nameField = checkName(nameDescription, nameField); -} - -void -rrFromWire(DNSInput in) throws IOException { - u16Field = in.readU16(); - nameField = new Name(in); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - u16Field = st.getUInt16(); - nameField = st.getName(origin); -} - -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(u16Field); - sb.append(" "); - sb.append(nameField); - return sb.toString(); -} - -protected int -getU16Field() { - return u16Field; -} - -protected Name -getNameField() { - return nameField; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeU16(u16Field); - nameField.toWire(out, null, canonical); -} - -} diff --git a/src/main/java/org/xbill/DNS/UDPClient.java b/src/main/java/org/xbill/DNS/UDPClient.java deleted file mode 100644 index 2268f8f70..000000000 --- a/src/main/java/org/xbill/DNS/UDPClient.java +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.EOFException; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.nio.channels.SelectionKey; -import java.security.SecureRandom; - -final class UDPClient extends Client { - -private static final int EPHEMERAL_START = 1024; -private static final int EPHEMERAL_STOP = 65535; -private static final int EPHEMERAL_RANGE = EPHEMERAL_STOP - EPHEMERAL_START; - -private static SecureRandom prng = new SecureRandom(); - -private boolean bound = false; - -public -UDPClient(long endTime) throws IOException { - super(DatagramChannel.open(), endTime); -} - -private void -bind_random(InetSocketAddress addr) throws IOException -{ - DatagramChannel channel = (DatagramChannel) key.channel(); - InetSocketAddress temp; - - for (int i = 0; i < 1024; i++) { - try { - int port = prng.nextInt(EPHEMERAL_RANGE) + - EPHEMERAL_START; - if (addr != null) - temp = new InetSocketAddress(addr.getAddress(), - port); - else - temp = new InetSocketAddress(port); - channel.socket().bind(temp); - bound = true; - return; - } - catch (SocketException e) { - } - } -} - -void -bind(SocketAddress addr) throws IOException { - if (addr == null || - (addr instanceof InetSocketAddress && - ((InetSocketAddress)addr).getPort() == 0)) - { - bind_random((InetSocketAddress) addr); - if (bound) - return; - } - - if (addr != null) { - DatagramChannel channel = (DatagramChannel) key.channel(); - channel.socket().bind(addr); - bound = true; - } -} - -void -connect(SocketAddress addr) throws IOException { - if (!bound) - bind(null); - DatagramChannel channel = (DatagramChannel) key.channel(); - channel.connect(addr); -} - -void -send(byte [] data) throws IOException { - DatagramChannel channel = (DatagramChannel) key.channel(); - verboseLog("UDP write", data); - channel.write(ByteBuffer.wrap(data)); -} - -byte [] -recv(int max) throws IOException { - DatagramChannel channel = (DatagramChannel) key.channel(); - byte [] temp = new byte[max]; - key.interestOps(SelectionKey.OP_READ); - try { - while (!key.isReadable()) - blockUntil(key, endTime); - } - finally { - if (key.isValid()) - key.interestOps(0); - } - long ret = channel.read(ByteBuffer.wrap(temp)); - if (ret <= 0) - throw new EOFException(); - int len = (int) ret; - byte [] data = new byte[len]; - System.arraycopy(temp, 0, data, 0, len); - verboseLog("UDP read", data); - return data; -} - -static byte [] -sendrecv(SocketAddress local, SocketAddress remote, byte [] data, int max, - long endTime) -throws IOException -{ - UDPClient client = new UDPClient(endTime); - try { - client.bind(local); - client.connect(remote); - client.send(data); - return client.recv(max); - } - finally { - client.cleanup(); - } -} - -static byte [] -sendrecv(SocketAddress addr, byte [] data, int max, long endTime) -throws IOException -{ - return sendrecv(null, addr, data, max, endTime); -} - -} diff --git a/src/main/java/org/xbill/DNS/UNKRecord.java b/src/main/java/org/xbill/DNS/UNKRecord.java deleted file mode 100644 index 218ae38ba..000000000 --- a/src/main/java/org/xbill/DNS/UNKRecord.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * A class implementing Records of unknown and/or unimplemented types. This - * class can only be initialized using static Record initializers. - * - * @author Brian Wellington - */ - -public class UNKRecord extends Record { - -private static final long serialVersionUID = -4193583311594626915L; - -private byte [] data; - -UNKRecord() {} - -Record -getObject() { - return new UNKRecord(); -} - -void -rrFromWire(DNSInput in) throws IOException { - data = in.readByteArray(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - throw st.exception("invalid unknown RR encoding"); -} - -/** Converts this Record to the String "unknown format" */ -String -rrToString() { - return unknownToString(data); -} - -/** Returns the contents of this record. */ -public byte [] -getData() { - return data; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeByteArray(data); -} - -} diff --git a/src/main/java/org/xbill/DNS/Update.java b/src/main/java/org/xbill/DNS/Update.java deleted file mode 100644 index 4298b3df6..000000000 --- a/src/main/java/org/xbill/DNS/Update.java +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.util.Iterator; - -/** - * A helper class for constructing dynamic DNS (DDNS) update messages. - * - * @author Brian Wellington - */ - -public class Update extends Message { - -private Name origin; -private int dclass; - -/** - * Creates an update message. - * @param zone The name of the zone being updated. - * @param dclass The class of the zone being updated. - */ -public -Update(Name zone, int dclass) { - super(); - if (!zone.isAbsolute()) - throw new RelativeNameException(zone); - DClass.check(dclass); - getHeader().setOpcode(Opcode.UPDATE); - Record soa = Record.newRecord(zone, Type.SOA, DClass.IN); - addRecord(soa, Section.QUESTION); - this.origin = zone; - this.dclass = dclass; -} - -/** - * Creates an update message. The class is assumed to be IN. - * @param zone The name of the zone being updated. - */ -public -Update(Name zone) { - this(zone, DClass.IN); -} - -private void -newPrereq(Record rec) { - addRecord(rec, Section.PREREQ); -} - -private void -newUpdate(Record rec) { - addRecord(rec, Section.UPDATE); -} - -/** - * Inserts a prerequisite that the specified name exists; that is, there - * exist records with the given name in the zone. - */ -public void -present(Name name) { - newPrereq(Record.newRecord(name, Type.ANY, DClass.ANY, 0)); -} - -/** - * Inserts a prerequisite that the specified rrset exists; that is, there - * exist records with the given name and type in the zone. - */ -public void -present(Name name, int type) { - newPrereq(Record.newRecord(name, type, DClass.ANY, 0)); -} - -/** - * Parses a record from the string, and inserts a prerequisite that the - * record exists. Due to the way value-dependent prequisites work, the - * condition that must be met is that the set of all records with the same - * and type in the update message must be identical to the set of all records - * with that name and type on the server. - * @throws IOException The record could not be parsed. - */ -public void -present(Name name, int type, String record) throws IOException { - newPrereq(Record.fromString(name, type, dclass, 0, record, origin)); -} - -/** - * Parses a record from the tokenizer, and inserts a prerequisite that the - * record exists. Due to the way value-dependent prequisites work, the - * condition that must be met is that the set of all records with the same - * and type in the update message must be identical to the set of all records - * with that name and type on the server. - * @throws IOException The record could not be parsed. - */ -public void -present(Name name, int type, Tokenizer tokenizer) throws IOException { - newPrereq(Record.fromString(name, type, dclass, 0, tokenizer, origin)); -} - -/** - * Inserts a prerequisite that the specified record exists. Due to the way - * value-dependent prequisites work, the condition that must be met is that - * the set of all records with the same and type in the update message must - * be identical to the set of all records with that name and type on the server. - */ -public void -present(Record record) { - newPrereq(record); -} - -/** - * Inserts a prerequisite that the specified name does not exist; that is, - * there are no records with the given name in the zone. - */ -public void -absent(Name name) { - newPrereq(Record.newRecord(name, Type.ANY, DClass.NONE, 0)); -} - -/** - * Inserts a prerequisite that the specified rrset does not exist; that is, - * there are no records with the given name and type in the zone. - */ -public void -absent(Name name, int type) { - newPrereq(Record.newRecord(name, type, DClass.NONE, 0)); -} - -/** - * Parses a record from the string, and indicates that the record - * should be inserted into the zone. - * @throws IOException The record could not be parsed. - */ -public void -add(Name name, int type, long ttl, String record) throws IOException { - newUpdate(Record.fromString(name, type, dclass, ttl, record, origin)); -} - -/** - * Parses a record from the tokenizer, and indicates that the record - * should be inserted into the zone. - * @throws IOException The record could not be parsed. - */ -public void -add(Name name, int type, long ttl, Tokenizer tokenizer) throws IOException { - newUpdate(Record.fromString(name, type, dclass, ttl, tokenizer, - origin)); -} - -/** - * Indicates that the record should be inserted into the zone. - */ -public void -add(Record record) { - newUpdate(record); -} - -/** - * Indicates that the records should be inserted into the zone. - */ -public void -add(Record [] records) { - for (int i = 0; i < records.length; i++) - add(records[i]); -} - -/** - * Indicates that all of the records in the rrset should be inserted into the - * zone. - */ -public void -add(RRset rrset) { - for (Iterator it = rrset.rrs(); it.hasNext(); ) - add((Record) it.next()); -} - -/** - * Indicates that all records with the given name should be deleted from - * the zone. - */ -public void -delete(Name name) { - newUpdate(Record.newRecord(name, Type.ANY, DClass.ANY, 0)); -} - -/** - * Indicates that all records with the given name and type should be deleted - * from the zone. - */ -public void -delete(Name name, int type) { - newUpdate(Record.newRecord(name, type, DClass.ANY, 0)); -} - -/** - * Parses a record from the string, and indicates that the record - * should be deleted from the zone. - * @throws IOException The record could not be parsed. - */ -public void -delete(Name name, int type, String record) throws IOException { - newUpdate(Record.fromString(name, type, DClass.NONE, 0, record, - origin)); -} - -/** - * Parses a record from the tokenizer, and indicates that the record - * should be deleted from the zone. - * @throws IOException The record could not be parsed. - */ -public void -delete(Name name, int type, Tokenizer tokenizer) throws IOException { - newUpdate(Record.fromString(name, type, DClass.NONE, 0, tokenizer, - origin)); -} - -/** - * Indicates that the specified record should be deleted from the zone. - */ -public void -delete(Record record) { - newUpdate(record.withDClass(DClass.NONE, 0)); -} - -/** - * Indicates that the records should be deleted from the zone. - */ -public void -delete(Record [] records) { - for (int i = 0; i < records.length; i++) - delete(records[i]); -} - -/** - * Indicates that all of the records in the rrset should be deleted from the - * zone. - */ -public void -delete(RRset rrset) { - for (Iterator it = rrset.rrs(); it.hasNext(); ) - delete((Record) it.next()); -} - -/** - * Parses a record from the string, and indicates that the record - * should be inserted into the zone replacing any other records with the - * same name and type. - * @throws IOException The record could not be parsed. - */ -public void -replace(Name name, int type, long ttl, String record) throws IOException { - delete(name, type); - add(name, type, ttl, record); -} - -/** - * Parses a record from the tokenizer, and indicates that the record - * should be inserted into the zone replacing any other records with the - * same name and type. - * @throws IOException The record could not be parsed. - */ -public void -replace(Name name, int type, long ttl, Tokenizer tokenizer) throws IOException -{ - delete(name, type); - add(name, type, ttl, tokenizer); -} - -/** - * Indicates that the record should be inserted into the zone replacing any - * other records with the same name and type. - */ -public void -replace(Record record) { - delete(record.getName(), record.getType()); - add(record); -} - -/** - * Indicates that the records should be inserted into the zone replacing any - * other records with the same name and type as each one. - */ -public void -replace(Record [] records) { - for (int i = 0; i < records.length; i++) - replace(records[i]); -} - -/** - * Indicates that all of the records in the rrset should be inserted into the - * zone replacing any other records with the same name and type. - */ -public void -replace(RRset rrset) { - delete(rrset.getName(), rrset.getType()); - for (Iterator it = rrset.rrs(); it.hasNext(); ) - add((Record) it.next()); -} - -} diff --git a/src/main/java/org/xbill/DNS/Verifier.java b/src/main/java/org/xbill/DNS/Verifier.java deleted file mode 100644 index 7ed19e292..000000000 --- a/src/main/java/org/xbill/DNS/Verifier.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An interface to a DNSSEC Verifier. This is used to verify the validity - * of data received by dnsjava. The specific implementation of the verifier - * is expected to store trusted keys in some way. The Verifier will use - * these trusted keys as well as secure cached keys to verify data. - * @see org.xbill.DNS.security.DNSSECVerifier - * - * @author Brian Wellington - */ - -public interface Verifier { - -/** - * Verifies this RRset, using secure keys found in this Cache if necessary. - * @see RRset - * @see Cache - */ -int verify(RRset set, Cache cache); - -} diff --git a/src/main/java/org/xbill/DNS/WKSRecord.java b/src/main/java/org/xbill/DNS/WKSRecord.java deleted file mode 100644 index 08738b5c3..000000000 --- a/src/main/java/org/xbill/DNS/WKSRecord.java +++ /dev/null @@ -1,722 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Well Known Services - Lists services offered by this host. - * - * @author Brian Wellington - */ - -public class WKSRecord extends Record { - -private static final long serialVersionUID = -9104259763909119805L; - -public static class Protocol { - /** - * IP protocol identifiers. This is basically copied out of RFC 1010. - */ - - private Protocol() {} - - /** Internet Control Message */ - public static final int ICMP = 1; - - /** Internet Group Management */ - public static final int IGMP = 2; - - /** Gateway-to-Gateway */ - public static final int GGP = 3; - - /** Stream */ - public static final int ST = 5; - - /** Transmission Control */ - public static final int TCP = 6; - - /** UCL */ - public static final int UCL = 7; - - /** Exterior Gateway Protocol */ - public static final int EGP = 8; - - /** any private interior gateway */ - public static final int IGP = 9; - - /** BBN RCC Monitoring */ - public static final int BBN_RCC_MON = 10; - - /** Network Voice Protocol */ - public static final int NVP_II = 11; - - /** PUP */ - public static final int PUP = 12; - - /** ARGUS */ - public static final int ARGUS = 13; - - /** EMCON */ - public static final int EMCON = 14; - - /** Cross Net Debugger */ - public static final int XNET = 15; - - /** Chaos */ - public static final int CHAOS = 16; - - /** User Datagram */ - public static final int UDP = 17; - - /** Multiplexing */ - public static final int MUX = 18; - - /** DCN Measurement Subsystems */ - public static final int DCN_MEAS = 19; - - /** Host Monitoring */ - public static final int HMP = 20; - - /** Packet Radio Measurement */ - public static final int PRM = 21; - - /** XEROX NS IDP */ - public static final int XNS_IDP = 22; - - /** Trunk-1 */ - public static final int TRUNK_1 = 23; - - /** Trunk-2 */ - public static final int TRUNK_2 = 24; - - /** Leaf-1 */ - public static final int LEAF_1 = 25; - - /** Leaf-2 */ - public static final int LEAF_2 = 26; - - /** Reliable Data Protocol */ - public static final int RDP = 27; - - /** Internet Reliable Transaction */ - public static final int IRTP = 28; - - /** ISO Transport Protocol Class 4 */ - public static final int ISO_TP4 = 29; - - /** Bulk Data Transfer Protocol */ - public static final int NETBLT = 30; - - /** MFE Network Services Protocol */ - public static final int MFE_NSP = 31; - - /** MERIT Internodal Protocol */ - public static final int MERIT_INP = 32; - - /** Sequential Exchange Protocol */ - public static final int SEP = 33; - - /** CFTP */ - public static final int CFTP = 62; - - /** SATNET and Backroom EXPAK */ - public static final int SAT_EXPAK = 64; - - /** MIT Subnet Support */ - public static final int MIT_SUBNET = 65; - - /** MIT Remote Virtual Disk Protocol */ - public static final int RVD = 66; - - /** Internet Pluribus Packet Core */ - public static final int IPPC = 67; - - /** SATNET Monitoring */ - public static final int SAT_MON = 69; - - /** Internet Packet Core Utility */ - public static final int IPCV = 71; - - /** Backroom SATNET Monitoring */ - public static final int BR_SAT_MON = 76; - - /** WIDEBAND Monitoring */ - public static final int WB_MON = 78; - - /** WIDEBAND EXPAK */ - public static final int WB_EXPAK = 79; - - private static Mnemonic protocols = new Mnemonic("IP protocol", - Mnemonic.CASE_LOWER); - - static { - protocols.setMaximum(0xFF); - protocols.setNumericAllowed(true); - - protocols.add(ICMP, "icmp"); - protocols.add(IGMP, "igmp"); - protocols.add(GGP, "ggp"); - protocols.add(ST, "st"); - protocols.add(TCP, "tcp"); - protocols.add(UCL, "ucl"); - protocols.add(EGP, "egp"); - protocols.add(IGP, "igp"); - protocols.add(BBN_RCC_MON, "bbn-rcc-mon"); - protocols.add(NVP_II, "nvp-ii"); - protocols.add(PUP, "pup"); - protocols.add(ARGUS, "argus"); - protocols.add(EMCON, "emcon"); - protocols.add(XNET, "xnet"); - protocols.add(CHAOS, "chaos"); - protocols.add(UDP, "udp"); - protocols.add(MUX, "mux"); - protocols.add(DCN_MEAS, "dcn-meas"); - protocols.add(HMP, "hmp"); - protocols.add(PRM, "prm"); - protocols.add(XNS_IDP, "xns-idp"); - protocols.add(TRUNK_1, "trunk-1"); - protocols.add(TRUNK_2, "trunk-2"); - protocols.add(LEAF_1, "leaf-1"); - protocols.add(LEAF_2, "leaf-2"); - protocols.add(RDP, "rdp"); - protocols.add(IRTP, "irtp"); - protocols.add(ISO_TP4, "iso-tp4"); - protocols.add(NETBLT, "netblt"); - protocols.add(MFE_NSP, "mfe-nsp"); - protocols.add(MERIT_INP, "merit-inp"); - protocols.add(SEP, "sep"); - protocols.add(CFTP, "cftp"); - protocols.add(SAT_EXPAK, "sat-expak"); - protocols.add(MIT_SUBNET, "mit-subnet"); - protocols.add(RVD, "rvd"); - protocols.add(IPPC, "ippc"); - protocols.add(SAT_MON, "sat-mon"); - protocols.add(IPCV, "ipcv"); - protocols.add(BR_SAT_MON, "br-sat-mon"); - protocols.add(WB_MON, "wb-mon"); - protocols.add(WB_EXPAK, "wb-expak"); - } - - /** - * Converts an IP protocol value into its textual representation - */ - public static String - string(int type) { - return protocols.getText(type); - } - - /** - * Converts a textual representation of an IP protocol into its - * numeric code. Integers in the range 0..255 are also accepted. - * @param s The textual representation of the protocol - * @return The protocol code, or -1 on error. - */ - public static int - value(String s) { - return protocols.getValue(s); - } -} - -public static class Service { - /** - * TCP/UDP services. This is basically copied out of RFC 1010, - * with MIT-ML-DEV removed, as it is not unique, and the description - * of SWIFT-RVF fixed. - */ - - private Service() {} - - /** Remote Job Entry */ - public static final int RJE = 5; - - /** Echo */ - public static final int ECHO = 7; - - /** Discard */ - public static final int DISCARD = 9; - - /** Active Users */ - public static final int USERS = 11; - - /** Daytime */ - public static final int DAYTIME = 13; - - /** Quote of the Day */ - public static final int QUOTE = 17; - - /** Character Generator */ - public static final int CHARGEN = 19; - - /** File Transfer [Default Data] */ - public static final int FTP_DATA = 20; - - /** File Transfer [Control] */ - public static final int FTP = 21; - - /** Telnet */ - public static final int TELNET = 23; - - /** Simple Mail Transfer */ - public static final int SMTP = 25; - - /** NSW User System FE */ - public static final int NSW_FE = 27; - - /** MSG ICP */ - public static final int MSG_ICP = 29; - - /** MSG Authentication */ - public static final int MSG_AUTH = 31; - - /** Display Support Protocol */ - public static final int DSP = 33; - - /** Time */ - public static final int TIME = 37; - - /** Resource Location Protocol */ - public static final int RLP = 39; - - /** Graphics */ - public static final int GRAPHICS = 41; - - /** Host Name Server */ - public static final int NAMESERVER = 42; - - /** Who Is */ - public static final int NICNAME = 43; - - /** MPM FLAGS Protocol */ - public static final int MPM_FLAGS = 44; - - /** Message Processing Module [recv] */ - public static final int MPM = 45; - - /** MPM [default send] */ - public static final int MPM_SND = 46; - - /** NI FTP */ - public static final int NI_FTP = 47; - - /** Login Host Protocol */ - public static final int LOGIN = 49; - - /** IMP Logical Address Maintenance */ - public static final int LA_MAINT = 51; - - /** Domain Name Server */ - public static final int DOMAIN = 53; - - /** ISI Graphics Language */ - public static final int ISI_GL = 55; - - /** NI MAIL */ - public static final int NI_MAIL = 61; - - /** VIA Systems - FTP */ - public static final int VIA_FTP = 63; - - /** TACACS-Database Service */ - public static final int TACACS_DS = 65; - - /** Bootstrap Protocol Server */ - public static final int BOOTPS = 67; - - /** Bootstrap Protocol Client */ - public static final int BOOTPC = 68; - - /** Trivial File Transfer */ - public static final int TFTP = 69; - - /** Remote Job Service */ - public static final int NETRJS_1 = 71; - - /** Remote Job Service */ - public static final int NETRJS_2 = 72; - - /** Remote Job Service */ - public static final int NETRJS_3 = 73; - - /** Remote Job Service */ - public static final int NETRJS_4 = 74; - - /** Finger */ - public static final int FINGER = 79; - - /** HOSTS2 Name Server */ - public static final int HOSTS2_NS = 81; - - /** SU/MIT Telnet Gateway */ - public static final int SU_MIT_TG = 89; - - /** MIT Dover Spooler */ - public static final int MIT_DOV = 91; - - /** Device Control Protocol */ - public static final int DCP = 93; - - /** SUPDUP */ - public static final int SUPDUP = 95; - - /** Swift Remote Virtual File Protocol */ - public static final int SWIFT_RVF = 97; - - /** TAC News */ - public static final int TACNEWS = 98; - - /** Metagram Relay */ - public static final int METAGRAM = 99; - - /** NIC Host Name Server */ - public static final int HOSTNAME = 101; - - /** ISO-TSAP */ - public static final int ISO_TSAP = 102; - - /** X400 */ - public static final int X400 = 103; - - /** X400-SND */ - public static final int X400_SND = 104; - - /** Mailbox Name Nameserver */ - public static final int CSNET_NS = 105; - - /** Remote Telnet Service */ - public static final int RTELNET = 107; - - /** Post Office Protocol - Version 2 */ - public static final int POP_2 = 109; - - /** SUN Remote Procedure Call */ - public static final int SUNRPC = 111; - - /** Authentication Service */ - public static final int AUTH = 113; - - /** Simple File Transfer Protocol */ - public static final int SFTP = 115; - - /** UUCP Path Service */ - public static final int UUCP_PATH = 117; - - /** Network News Transfer Protocol */ - public static final int NNTP = 119; - - /** HYDRA Expedited Remote Procedure */ - public static final int ERPC = 121; - - /** Network Time Protocol */ - public static final int NTP = 123; - - /** Locus PC-Interface Net Map Server */ - public static final int LOCUS_MAP = 125; - - /** Locus PC-Interface Conn Server */ - public static final int LOCUS_CON = 127; - - /** Password Generator Protocol */ - public static final int PWDGEN = 129; - - /** CISCO FNATIVE */ - public static final int CISCO_FNA = 130; - - /** CISCO TNATIVE */ - public static final int CISCO_TNA = 131; - - /** CISCO SYSMAINT */ - public static final int CISCO_SYS = 132; - - /** Statistics Service */ - public static final int STATSRV = 133; - - /** INGRES-NET Service */ - public static final int INGRES_NET = 134; - - /** Location Service */ - public static final int LOC_SRV = 135; - - /** PROFILE Naming System */ - public static final int PROFILE = 136; - - /** NETBIOS Name Service */ - public static final int NETBIOS_NS = 137; - - /** NETBIOS Datagram Service */ - public static final int NETBIOS_DGM = 138; - - /** NETBIOS Session Service */ - public static final int NETBIOS_SSN = 139; - - /** EMFIS Data Service */ - public static final int EMFIS_DATA = 140; - - /** EMFIS Control Service */ - public static final int EMFIS_CNTL = 141; - - /** Britton-Lee IDM */ - public static final int BL_IDM = 142; - - /** Survey Measurement */ - public static final int SUR_MEAS = 243; - - /** LINK */ - public static final int LINK = 245; - - private static Mnemonic services = new Mnemonic("TCP/UDP service", - Mnemonic.CASE_LOWER); - - static { - services.setMaximum(0xFFFF); - services.setNumericAllowed(true); - - services.add(RJE, "rje"); - services.add(ECHO, "echo"); - services.add(DISCARD, "discard"); - services.add(USERS, "users"); - services.add(DAYTIME, "daytime"); - services.add(QUOTE, "quote"); - services.add(CHARGEN, "chargen"); - services.add(FTP_DATA, "ftp-data"); - services.add(FTP, "ftp"); - services.add(TELNET, "telnet"); - services.add(SMTP, "smtp"); - services.add(NSW_FE, "nsw-fe"); - services.add(MSG_ICP, "msg-icp"); - services.add(MSG_AUTH, "msg-auth"); - services.add(DSP, "dsp"); - services.add(TIME, "time"); - services.add(RLP, "rlp"); - services.add(GRAPHICS, "graphics"); - services.add(NAMESERVER, "nameserver"); - services.add(NICNAME, "nicname"); - services.add(MPM_FLAGS, "mpm-flags"); - services.add(MPM, "mpm"); - services.add(MPM_SND, "mpm-snd"); - services.add(NI_FTP, "ni-ftp"); - services.add(LOGIN, "login"); - services.add(LA_MAINT, "la-maint"); - services.add(DOMAIN, "domain"); - services.add(ISI_GL, "isi-gl"); - services.add(NI_MAIL, "ni-mail"); - services.add(VIA_FTP, "via-ftp"); - services.add(TACACS_DS, "tacacs-ds"); - services.add(BOOTPS, "bootps"); - services.add(BOOTPC, "bootpc"); - services.add(TFTP, "tftp"); - services.add(NETRJS_1, "netrjs-1"); - services.add(NETRJS_2, "netrjs-2"); - services.add(NETRJS_3, "netrjs-3"); - services.add(NETRJS_4, "netrjs-4"); - services.add(FINGER, "finger"); - services.add(HOSTS2_NS, "hosts2-ns"); - services.add(SU_MIT_TG, "su-mit-tg"); - services.add(MIT_DOV, "mit-dov"); - services.add(DCP, "dcp"); - services.add(SUPDUP, "supdup"); - services.add(SWIFT_RVF, "swift-rvf"); - services.add(TACNEWS, "tacnews"); - services.add(METAGRAM, "metagram"); - services.add(HOSTNAME, "hostname"); - services.add(ISO_TSAP, "iso-tsap"); - services.add(X400, "x400"); - services.add(X400_SND, "x400-snd"); - services.add(CSNET_NS, "csnet-ns"); - services.add(RTELNET, "rtelnet"); - services.add(POP_2, "pop-2"); - services.add(SUNRPC, "sunrpc"); - services.add(AUTH, "auth"); - services.add(SFTP, "sftp"); - services.add(UUCP_PATH, "uucp-path"); - services.add(NNTP, "nntp"); - services.add(ERPC, "erpc"); - services.add(NTP, "ntp"); - services.add(LOCUS_MAP, "locus-map"); - services.add(LOCUS_CON, "locus-con"); - services.add(PWDGEN, "pwdgen"); - services.add(CISCO_FNA, "cisco-fna"); - services.add(CISCO_TNA, "cisco-tna"); - services.add(CISCO_SYS, "cisco-sys"); - services.add(STATSRV, "statsrv"); - services.add(INGRES_NET, "ingres-net"); - services.add(LOC_SRV, "loc-srv"); - services.add(PROFILE, "profile"); - services.add(NETBIOS_NS, "netbios-ns"); - services.add(NETBIOS_DGM, "netbios-dgm"); - services.add(NETBIOS_SSN, "netbios-ssn"); - services.add(EMFIS_DATA, "emfis-data"); - services.add(EMFIS_CNTL, "emfis-cntl"); - services.add(BL_IDM, "bl-idm"); - services.add(SUR_MEAS, "sur-meas"); - services.add(LINK, "link"); - } - - /** - * Converts a TCP/UDP service port number into its textual - * representation. - */ - public static String - string(int type) { - return services.getText(type); - } - - /** - * Converts a textual representation of a TCP/UDP service into its - * port number. Integers in the range 0..65535 are also accepted. - * @param s The textual representation of the service. - * @return The port number, or -1 on error. - */ - public static int - value(String s) { - return services.getValue(s); - } -} -private byte [] address; -private int protocol; -private int [] services; - -WKSRecord() {} - -Record -getObject() { - return new WKSRecord(); -} - -/** - * Creates a WKS Record from the given data - * @param address The IP address - * @param protocol The IP protocol number - * @param services An array of supported services, represented by port number. - */ -public -WKSRecord(Name name, int dclass, long ttl, InetAddress address, int protocol, - int [] services) -{ - super(name, Type.WKS, dclass, ttl); - if (Address.familyOf(address) != Address.IPv4) - throw new IllegalArgumentException("invalid IPv4 address"); - this.address = address.getAddress(); - this.protocol = checkU8("protocol", protocol); - for (int i = 0; i < services.length; i++) { - checkU16("service", services[i]); - } - this.services = new int[services.length]; - System.arraycopy(services, 0, this.services, 0, services.length); - Arrays.sort(this.services); -} - -void -rrFromWire(DNSInput in) throws IOException { - address = in.readByteArray(4); - protocol = in.readU8(); - byte [] array = in.readByteArray(); - List list = new ArrayList(); - for (int i = 0; i < array.length; i++) { - for (int j = 0; j < 8; j++) { - int octet = array[i] & 0xFF; - if ((octet & (1 << (7 - j))) != 0) { - list.add(new Integer(i * 8 + j)); - } - } - } - services = new int[list.size()]; - for (int i = 0; i < list.size(); i++) { - services[i] = ((Integer) list.get(i)).intValue(); - } -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - String s = st.getString(); - address = Address.toByteArray(s, Address.IPv4); - if (address == null) - throw st.exception("invalid address"); - - s = st.getString(); - protocol = Protocol.value(s); - if (protocol < 0) { - throw st.exception("Invalid IP protocol: " + s); - } - - List list = new ArrayList(); - while (true) { - Tokenizer.Token t = st.get(); - if (!t.isString()) - break; - int service = Service.value(t.value); - if (service < 0) { - throw st.exception("Invalid TCP/UDP service: " + - t.value); - } - list.add(new Integer(service)); - } - st.unget(); - services = new int[list.size()]; - for (int i = 0; i < list.size(); i++) { - services[i] = ((Integer) list.get(i)).intValue(); - } -} - -/** - * Converts rdata to a String - */ -String -rrToString() { - StringBuffer sb = new StringBuffer(); - sb.append(Address.toDottedQuad(address)); - sb.append(" "); - sb.append(protocol); - for (int i = 0; i < services.length; i++) { - sb.append(" " + services[i]); - } - return sb.toString(); -} - -/** - * Returns the IP address. - */ -public InetAddress -getAddress() { - try { - return InetAddress.getByAddress(address); - } catch (UnknownHostException e) { - return null; - } -} - -/** - * Returns the IP protocol. - */ -public int -getProtocol() { - return protocol; -} - -/** - * Returns the services provided by the host on the specified address. - */ -public int [] -getServices() { - return services; -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeByteArray(address); - out.writeU8(protocol); - int highestPort = services[services.length - 1]; - byte [] array = new byte[highestPort / 8 + 1]; - for (int i = 0; i < services.length; i++) { - int port = services[i]; - array[port / 8] |= (1 << (7 - port % 8)); - } - out.writeByteArray(array); -} - -} diff --git a/src/main/java/org/xbill/DNS/WireParseException.java b/src/main/java/org/xbill/DNS/WireParseException.java deleted file mode 100644 index 66803dd9b..000000000 --- a/src/main/java/org/xbill/DNS/WireParseException.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * An exception thrown when a DNS message is invalid. - * - * @author Brian Wellington - */ - -public class WireParseException extends IOException { - -public -WireParseException() { - super(); -} - -public -WireParseException(String s) { - super(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/X25Record.java b/src/main/java/org/xbill/DNS/X25Record.java deleted file mode 100644 index 06fd69361..000000000 --- a/src/main/java/org/xbill/DNS/X25Record.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; - -/** - * X25 - identifies the PSDN (Public Switched Data Network) address in the - * X.121 numbering plan associated with a name. - * - * @author Brian Wellington - */ - -public class X25Record extends Record { - -private static final long serialVersionUID = 4267576252335579764L; - -private byte [] address; - -X25Record() {} - -Record -getObject() { - return new X25Record(); -} - -private static final byte [] -checkAndConvertAddress(String address) { - int length = address.length(); - byte [] out = new byte [length]; - for (int i = 0; i < length; i++) { - char c = address.charAt(i); - if (!Character.isDigit(c)) - return null; - out[i] = (byte) c; - } - return out; -} - -/** - * Creates an X25 Record from the given data - * @param address The X.25 PSDN address. - * @throws IllegalArgumentException The address is not a valid PSDN address. - */ -public -X25Record(Name name, int dclass, long ttl, String address) { - super(name, Type.X25, dclass, ttl); - this.address = checkAndConvertAddress(address); - if (this.address == null) { - throw new IllegalArgumentException("invalid PSDN address " + - address); - } -} - -void -rrFromWire(DNSInput in) throws IOException { - address = in.readCountedString(); -} - -void -rdataFromString(Tokenizer st, Name origin) throws IOException { - String addr = st.getString(); - this.address = checkAndConvertAddress(addr); - if (this.address == null) - throw st.exception("invalid PSDN address " + addr); -} - -/** - * Returns the X.25 PSDN address. - */ -public String -getAddress() { - return byteArrayToString(address, false); -} - -void -rrToWire(DNSOutput out, Compression c, boolean canonical) { - out.writeCountedString(address); -} - -String -rrToString() { - return byteArrayToString(address, true); -} - -} diff --git a/src/main/java/org/xbill/DNS/Zone.java b/src/main/java/org/xbill/DNS/Zone.java deleted file mode 100644 index 730e477d0..000000000 --- a/src/main/java/org/xbill/DNS/Zone.java +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -import java.io.IOException; -import java.io.Serializable; -import java.util.*; - -/** - * A DNS Zone. This encapsulates all data related to a Zone, and provides - * convenient lookup methods. - * - * @author Brian Wellington - */ - -public class Zone implements Serializable { - -private static final long serialVersionUID = -9220510891189510942L; - -/** A primary zone */ -public static final int PRIMARY = 1; - -/** A secondary zone */ -public static final int SECONDARY = 2; - -private Map data; -private Name origin; -private Object originNode; -private int dclass = DClass.IN; -private RRset NS; -private SOARecord SOA; -private boolean hasWild; - -class ZoneIterator implements Iterator { - private Iterator zentries; - private RRset [] current; - private int count; - private boolean wantLastSOA; - - ZoneIterator(boolean axfr) { - synchronized (Zone.this) { - zentries = data.entrySet().iterator(); - } - wantLastSOA = axfr; - RRset [] sets = allRRsets(originNode); - current = new RRset[sets.length]; - for (int i = 0, j = 2; i < sets.length; i++) { - int type = sets[i].getType(); - if (type == Type.SOA) - current[0] = sets[i]; - else if (type == Type.NS) - current[1] = sets[i]; - else - current[j++] = sets[i]; - } - } - - public boolean - hasNext() { - return (current != null || wantLastSOA); - } - - public Object - next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - if (current == null) { - wantLastSOA = false; - return oneRRset(originNode, Type.SOA); - } - Object set = current[count++]; - if (count == current.length) { - current = null; - while (zentries.hasNext()) { - Map.Entry entry = (Map.Entry) zentries.next(); - if (entry.getKey().equals(origin)) - continue; - RRset [] sets = allRRsets(entry.getValue()); - if (sets.length == 0) - continue; - current = sets; - count = 0; - break; - } - } - return set; - } - - public void - remove() { - throw new UnsupportedOperationException(); - } -} - -private void -validate() throws IOException { - originNode = exactName(origin); - if (originNode == null) - throw new IOException(origin + ": no data specified"); - - RRset rrset = oneRRset(originNode, Type.SOA); - if (rrset == null || rrset.size() != 1) - throw new IOException(origin + - ": exactly 1 SOA must be specified"); - Iterator it = rrset.rrs(); - SOA = (SOARecord) it.next(); - - NS = oneRRset(originNode, Type.NS); - if (NS == null) - throw new IOException(origin + ": no NS set specified"); -} - -private final void -maybeAddRecord(Record record) throws IOException { - int rtype = record.getType(); - Name name = record.getName(); - - if (rtype == Type.SOA && !name.equals(origin)) { - throw new IOException("SOA owner " + name + - " does not match zone origin " + - origin); - } - if (name.subdomain(origin)) - addRecord(record); -} - -/** - * Creates a Zone from the records in the specified master file. - * @param zone The name of the zone. - * @param file The master file to read from. - * @see Master - */ -public -Zone(Name zone, String file) throws IOException { - data = new HashMap(); - - if (zone == null) - throw new IllegalArgumentException("no zone name specified"); - Master m = new Master(file, zone); - Record record; - - origin = zone; - while ((record = m.nextRecord()) != null) - maybeAddRecord(record); - validate(); -} - -/** - * Creates a Zone from an array of records. - * @param zone The name of the zone. - * @param records The records to add to the zone. - * @see Master - */ -public -Zone(Name zone, Record [] records) throws IOException { - data = new HashMap(); - - if (zone == null) - throw new IllegalArgumentException("no zone name specified"); - origin = zone; - for (int i = 0; i < records.length; i++) - maybeAddRecord(records[i]); - validate(); -} - -private void -fromXFR(ZoneTransferIn xfrin) throws IOException, ZoneTransferException { - data = new HashMap(); - - origin = xfrin.getName(); - List records = xfrin.run(); - for (Iterator it = records.iterator(); it.hasNext(); ) { - Record record = (Record) it.next(); - maybeAddRecord(record); - } - if (!xfrin.isAXFR()) - throw new IllegalArgumentException("zones can only be " + - "created from AXFRs"); - validate(); -} - -/** - * Creates a Zone by doing the specified zone transfer. - * @param xfrin The incoming zone transfer to execute. - * @see ZoneTransferIn - */ -public -Zone(ZoneTransferIn xfrin) throws IOException, ZoneTransferException { - fromXFR(xfrin); -} - -/** - * Creates a Zone by performing a zone transfer to the specified host. - * @see ZoneTransferIn - */ -public -Zone(Name zone, int dclass, String remote) -throws IOException, ZoneTransferException -{ - ZoneTransferIn xfrin = ZoneTransferIn.newAXFR(zone, remote, null); - xfrin.setDClass(dclass); - fromXFR(xfrin); -} - -/** Returns the Zone's origin */ -public Name -getOrigin() { - return origin; -} - -/** Returns the Zone origin's NS records */ -public RRset -getNS() { - return NS; -} - -/** Returns the Zone's SOA record */ -public SOARecord -getSOA() { - return SOA; -} - -/** Returns the Zone's class */ -public int -getDClass() { - return dclass; -} - -private synchronized Object -exactName(Name name) { - return data.get(name); -} - -private synchronized RRset [] -allRRsets(Object types) { - if (types instanceof List) { - List typelist = (List) types; - return (RRset []) typelist.toArray(new RRset[typelist.size()]); - } else { - RRset set = (RRset) types; - return new RRset [] {set}; - } -} - -private synchronized RRset -oneRRset(Object types, int type) { - if (type == Type.ANY) - throw new IllegalArgumentException("oneRRset(ANY)"); - if (types instanceof List) { - List list = (List) types; - for (int i = 0; i < list.size(); i++) { - RRset set = (RRset) list.get(i); - if (set.getType() == type) - return set; - } - } else { - RRset set = (RRset) types; - if (set.getType() == type) - return set; - } - return null; -} - -private synchronized RRset -findRRset(Name name, int type) { - Object types = exactName(name); - if (types == null) - return null; - return oneRRset(types, type); -} - -private synchronized void -addRRset(Name name, RRset rrset) { - if (!hasWild && name.isWild()) - hasWild = true; - Object types = data.get(name); - if (types == null) { - data.put(name, rrset); - return; - } - int rtype = rrset.getType(); - if (types instanceof List) { - List list = (List) types; - for (int i = 0; i < list.size(); i++) { - RRset set = (RRset) list.get(i); - if (set.getType() == rtype) { - list.set(i, rrset); - return; - } - } - list.add(rrset); - } else { - RRset set = (RRset) types; - if (set.getType() == rtype) - data.put(name, rrset); - else { - LinkedList list = new LinkedList(); - list.add(set); - list.add(rrset); - data.put(name, list); - } - } -} - -private synchronized void -removeRRset(Name name, int type) { - Object types = data.get(name); - if (types == null) { - return; - } - if (types instanceof List) { - List list = (List) types; - for (int i = 0; i < list.size(); i++) { - RRset set = (RRset) list.get(i); - if (set.getType() == type) { - list.remove(i); - if (list.size() == 0) - data.remove(name); - return; - } - } - } else { - RRset set = (RRset) types; - if (set.getType() != type) - return; - data.remove(name); - } -} - -private synchronized SetResponse -lookup(Name name, int type) { - int labels; - int olabels; - int tlabels; - RRset rrset; - Name tname; - Object types; - SetResponse sr; - - if (!name.subdomain(origin)) - return SetResponse.ofType(SetResponse.NXDOMAIN); - - labels = name.labels(); - olabels = origin.labels(); - - for (tlabels = olabels; tlabels <= labels; tlabels++) { - boolean isOrigin = (tlabels == olabels); - boolean isExact = (tlabels == labels); - - if (isOrigin) - tname = origin; - else if (isExact) - tname = name; - else - tname = new Name(name, labels - tlabels); - - types = exactName(tname); - if (types == null) - continue; - - /* If this is a delegation, return that. */ - if (!isOrigin) { - RRset ns = oneRRset(types, Type.NS); - if (ns != null) - return new SetResponse(SetResponse.DELEGATION, - ns); - } - - /* If this is an ANY lookup, return everything. */ - if (isExact && type == Type.ANY) { - sr = new SetResponse(SetResponse.SUCCESSFUL); - RRset [] sets = allRRsets(types); - for (int i = 0; i < sets.length; i++) - sr.addRRset(sets[i]); - return sr; - } - - /* - * If this is the name, look for the actual type or a CNAME. - * Otherwise, look for a DNAME. - */ - if (isExact) { - rrset = oneRRset(types, type); - if (rrset != null) { - sr = new SetResponse(SetResponse.SUCCESSFUL); - sr.addRRset(rrset); - return sr; - } - rrset = oneRRset(types, Type.CNAME); - if (rrset != null) - return new SetResponse(SetResponse.CNAME, - rrset); - } else { - rrset = oneRRset(types, Type.DNAME); - if (rrset != null) - return new SetResponse(SetResponse.DNAME, - rrset); - } - - /* We found the name, but not the type. */ - if (isExact) - return SetResponse.ofType(SetResponse.NXRRSET); - } - - if (hasWild) { - for (int i = 0; i < labels - olabels; i++) { - tname = name.wild(i + 1); - - types = exactName(tname); - if (types == null) - continue; - - rrset = oneRRset(types, type); - if (rrset != null) { - sr = new SetResponse(SetResponse.SUCCESSFUL); - sr.addRRset(rrset); - return sr; - } - } - } - - return SetResponse.ofType(SetResponse.NXDOMAIN); -} - -/** - * Looks up Records in the Zone. This follows CNAMEs and wildcards. - * @param name The name to look up - * @param type The type to look up - * @return A SetResponse object - * @see SetResponse - */ -public SetResponse -findRecords(Name name, int type) { - return lookup(name, type); -} - -/** - * Looks up Records in the zone, finding exact matches only. - * @param name The name to look up - * @param type The type to look up - * @return The matching RRset - * @see RRset - */ -public RRset -findExactMatch(Name name, int type) { - Object types = exactName(name); - if (types == null) - return null; - return oneRRset(types, type); -} - -/** - * Adds an RRset to the Zone - * @param rrset The RRset to be added - * @see RRset - */ -public void -addRRset(RRset rrset) { - Name name = rrset.getName(); - addRRset(name, rrset); -} - -/** - * Adds a Record to the Zone - * @param r The record to be added - * @see Record - */ -public void -addRecord(Record r) { - Name name = r.getName(); - int rtype = r.getRRsetType(); - synchronized (this) { - RRset rrset = findRRset(name, rtype); - if (rrset == null) { - rrset = new RRset(r); - addRRset(name, rrset); - } else { - rrset.addRR(r); - } - } -} - -/** - * Removes a record from the Zone - * @param r The record to be removed - * @see Record - */ -public void -removeRecord(Record r) { - Name name = r.getName(); - int rtype = r.getRRsetType(); - synchronized (this) { - RRset rrset = findRRset(name, rtype); - if (rrset == null) - return; - if (rrset.size() == 1 && rrset.first().equals(r)) - removeRRset(name, rtype); - else - rrset.deleteRR(r); - } -} - -/** - * Returns an Iterator over the RRsets in the zone. - */ -public Iterator -iterator() { - return new ZoneIterator(false); -} - -/** - * Returns an Iterator over the RRsets in the zone that can be used to - * construct an AXFR response. This is identical to {@link #iterator} except - * that the SOA is returned at the end as well as the beginning. - */ -public Iterator -AXFR() { - return new ZoneIterator(true); -} - -private void -nodeToString(StringBuffer sb, Object node) { - RRset [] sets = allRRsets(node); - for (int i = 0; i < sets.length; i++) { - RRset rrset = sets[i]; - Iterator it = rrset.rrs(); - while (it.hasNext()) - sb.append(it.next() + "\n"); - it = rrset.sigs(); - while (it.hasNext()) - sb.append(it.next() + "\n"); - } -} - -/** - * Returns the contents of the Zone in master file format. - */ -public synchronized String -toMasterFile() { - Iterator zentries = data.entrySet().iterator(); - StringBuffer sb = new StringBuffer(); - nodeToString(sb, originNode); - while (zentries.hasNext()) { - Map.Entry entry = (Map.Entry) zentries.next(); - if (!origin.equals(entry.getKey())) - nodeToString(sb, entry.getValue()); - } - return sb.toString(); -} - -/** - * Returns the contents of the Zone as a string (in master file format). - */ -public String -toString() { - return toMasterFile(); -} - -} diff --git a/src/main/java/org/xbill/DNS/ZoneTransferException.java b/src/main/java/org/xbill/DNS/ZoneTransferException.java deleted file mode 100644 index 3ba487b4f..000000000 --- a/src/main/java/org/xbill/DNS/ZoneTransferException.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS; - -/** - * An exception thrown when a zone transfer fails. - * - * @author Brian Wellington - */ - -public class ZoneTransferException extends Exception { - -public -ZoneTransferException() { - super(); -} - -public -ZoneTransferException(String s) { - super(s); -} - -} diff --git a/src/main/java/org/xbill/DNS/ZoneTransferIn.java b/src/main/java/org/xbill/DNS/ZoneTransferIn.java deleted file mode 100644 index 7866a5452..000000000 --- a/src/main/java/org/xbill/DNS/ZoneTransferIn.java +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright (c) 2003-2004 Brian Wellington (bwelling@xbill.org) -// Parts of this are derived from lib/dns/xfrin.c from BIND 9; its copyright -// notice follows. - -/* - * Copyright (C) 1999-2001 Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM - * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package org.xbill.DNS; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -/** - * An incoming DNS Zone Transfer. To use this class, first initialize an - * object, then call the run() method. If run() doesn't throw an exception - * the result will either be an IXFR-style response, an AXFR-style response, - * or an indication that the zone is up to date. - * - * @author Brian Wellington - */ - -public class ZoneTransferIn { - -private static final int INITIALSOA = 0; -private static final int FIRSTDATA = 1; -private static final int IXFR_DELSOA = 2; -private static final int IXFR_DEL = 3; -private static final int IXFR_ADDSOA = 4; -private static final int IXFR_ADD = 5; -private static final int AXFR = 6; -private static final int END = 7; - -private Name zname; -private int qtype; -private int dclass; -private long ixfr_serial; -private boolean want_fallback; - -private SocketAddress localAddress; -private SocketAddress address; -private TCPClient client; -private TSIG tsig; -private TSIG.StreamVerifier verifier; -private long timeout = 900 * 1000; - -private int state; -private long end_serial; -private long current_serial; -private Record initialsoa; - -private int rtype; - -private List axfr; -private List ixfr; - -public static class Delta { - /** - * All changes between two versions of a zone in an IXFR response. - */ - - /** The starting serial number of this delta. */ - public long start; - - /** The ending serial number of this delta. */ - public long end; - - /** A list of records added between the start and end versions */ - public List adds; - - /** A list of records deleted between the start and end versions */ - public List deletes; - - private - Delta() { - adds = new ArrayList(); - deletes = new ArrayList(); - } -} - -private -ZoneTransferIn() {} - -private -ZoneTransferIn(Name zone, int xfrtype, long serial, boolean fallback, - SocketAddress address, TSIG key) -{ - this.address = address; - this.tsig = key; - if (zone.isAbsolute()) - zname = zone; - else { - try { - zname = Name.concatenate(zone, Name.root); - } - catch (NameTooLongException e) { - throw new IllegalArgumentException("ZoneTransferIn: " + - "name too long"); - } - } - qtype = xfrtype; - dclass = DClass.IN; - ixfr_serial = serial; - want_fallback = fallback; - state = INITIALSOA; -} - -/** - * Instantiates a ZoneTransferIn object to do an AXFR (full zone transfer). - * @param zone The zone to transfer. - * @param address The host/port from which to transfer the zone. - * @param key The TSIG key used to authenticate the transfer, or null. - * @return The ZoneTransferIn object. - * @throws UnknownHostException The host does not exist. - */ -public static ZoneTransferIn -newAXFR(Name zone, SocketAddress address, TSIG key) { - return new ZoneTransferIn(zone, Type.AXFR, 0, false, address, key); -} - -/** - * Instantiates a ZoneTransferIn object to do an AXFR (full zone transfer). - * @param zone The zone to transfer. - * @param host The host from which to transfer the zone. - * @param port The port to connect to on the server, or 0 for the default. - * @param key The TSIG key used to authenticate the transfer, or null. - * @return The ZoneTransferIn object. - * @throws UnknownHostException The host does not exist. - */ -public static ZoneTransferIn -newAXFR(Name zone, String host, int port, TSIG key) -throws UnknownHostException -{ - if (port == 0) - port = SimpleResolver.DEFAULT_PORT; - return newAXFR(zone, new InetSocketAddress(host, port), key); -} - -/** - * Instantiates a ZoneTransferIn object to do an AXFR (full zone transfer). - * @param zone The zone to transfer. - * @param host The host from which to transfer the zone. - * @param key The TSIG key used to authenticate the transfer, or null. - * @return The ZoneTransferIn object. - * @throws UnknownHostException The host does not exist. - */ -public static ZoneTransferIn -newAXFR(Name zone, String host, TSIG key) -throws UnknownHostException -{ - return newAXFR(zone, host, 0, key); -} - -/** - * Instantiates a ZoneTransferIn object to do an IXFR (incremental zone - * transfer). - * @param zone The zone to transfer. - * @param serial The existing serial number. - * @param fallback If true, fall back to AXFR if IXFR is not supported. - * @param address The host/port from which to transfer the zone. - * @param key The TSIG key used to authenticate the transfer, or null. - * @return The ZoneTransferIn object. - * @throws UnknownHostException The host does not exist. - */ -public static ZoneTransferIn -newIXFR(Name zone, long serial, boolean fallback, SocketAddress address, - TSIG key) -{ - return new ZoneTransferIn(zone, Type.IXFR, serial, fallback, address, - key); -} - -/** - * Instantiates a ZoneTransferIn object to do an IXFR (incremental zone - * transfer). - * @param zone The zone to transfer. - * @param serial The existing serial number. - * @param fallback If true, fall back to AXFR if IXFR is not supported. - * @param host The host from which to transfer the zone. - * @param port The port to connect to on the server, or 0 for the default. - * @param key The TSIG key used to authenticate the transfer, or null. - * @return The ZoneTransferIn object. - * @throws UnknownHostException The host does not exist. - */ -public static ZoneTransferIn -newIXFR(Name zone, long serial, boolean fallback, String host, int port, - TSIG key) -throws UnknownHostException -{ - if (port == 0) - port = SimpleResolver.DEFAULT_PORT; - return newIXFR(zone, serial, fallback, - new InetSocketAddress(host, port), key); -} - -/** - * Instantiates a ZoneTransferIn object to do an IXFR (incremental zone - * transfer). - * @param zone The zone to transfer. - * @param serial The existing serial number. - * @param fallback If true, fall back to AXFR if IXFR is not supported. - * @param host The host from which to transfer the zone. - * @param key The TSIG key used to authenticate the transfer, or null. - * @return The ZoneTransferIn object. - * @throws UnknownHostException The host does not exist. - */ -public static ZoneTransferIn -newIXFR(Name zone, long serial, boolean fallback, String host, TSIG key) -throws UnknownHostException -{ - return newIXFR(zone, serial, fallback, host, 0, key); -} - -/** - * Gets the name of the zone being transferred. - */ -public Name -getName() { - return zname; -} - -/** - * Gets the type of zone transfer (either AXFR or IXFR). - */ -public int -getType() { - return qtype; -} - -/** - * Sets a timeout on this zone transfer. The default is 900 seconds (15 - * minutes). - * @param secs The maximum amount of time that this zone transfer can take. - */ -public void -setTimeout(int secs) { - if (secs < 0) - throw new IllegalArgumentException("timeout cannot be " + - "negative"); - timeout = 1000L * secs; -} - -/** - * Sets an alternate DNS class for this zone transfer. - * @param dclass The class to use instead of class IN. - */ -public void -setDClass(int dclass) { - DClass.check(dclass); - this.dclass = dclass; -} - -/** - * Sets the local address to bind to when sending messages. - * @param addr The local address to send messages from. - */ -public void -setLocalAddress(SocketAddress addr) { - this.localAddress = addr; -} - -private void -openConnection() throws IOException { - long endTime = System.currentTimeMillis() + timeout; - client = new TCPClient(endTime); - if (localAddress != null) - client.bind(localAddress); - client.connect(address); -} - -private void -sendQuery() throws IOException { - Record question = Record.newRecord(zname, qtype, dclass); - - Message query = new Message(); - query.getHeader().setOpcode(Opcode.QUERY); - query.addRecord(question, Section.QUESTION); - if (qtype == Type.IXFR) { - Record soa = new SOARecord(zname, dclass, 0, Name.root, - Name.root, ixfr_serial, - 0, 0, 0, 0); - query.addRecord(soa, Section.AUTHORITY); - } - if (tsig != null) { - tsig.apply(query, null); - verifier = new TSIG.StreamVerifier(tsig, query.getTSIG()); - } - byte [] out = query.toWire(Message.MAXLENGTH); - client.send(out); -} - -private long -getSOASerial(Record rec) { - SOARecord soa = (SOARecord) rec; - return soa.getSerial(); -} - -private void -logxfr(String s) { - if (Options.check("verbose")) - System.out.println(zname + ": " + s); -} - -private void -fail(String s) throws ZoneTransferException { - throw new ZoneTransferException(s); -} - -private void -fallback() throws ZoneTransferException { - if (!want_fallback) - fail("server doesn't support IXFR"); - - logxfr("falling back to AXFR"); - qtype = Type.AXFR; - state = INITIALSOA; -} - -private void -parseRR(Record rec) throws ZoneTransferException { - int type = rec.getType(); - Delta delta; - - switch (state) { - case INITIALSOA: - if (type != Type.SOA) - fail("missing initial SOA"); - initialsoa = rec; - // Remember the serial number in the initial SOA; we need it - // to recognize the end of an IXFR. - end_serial = getSOASerial(rec); - if (qtype == Type.IXFR && end_serial <= ixfr_serial) { - logxfr("up to date"); - state = END; - break; - } - state = FIRSTDATA; - break; - - case FIRSTDATA: - // If the transfer begins with 1 SOA, it's an AXFR. - // If it begins with 2 SOAs, it's an IXFR. - if (qtype == Type.IXFR && type == Type.SOA && - getSOASerial(rec) == ixfr_serial) - { - rtype = Type.IXFR; - ixfr = new ArrayList(); - logxfr("got incremental response"); - state = IXFR_DELSOA; - } else { - rtype = Type.AXFR; - axfr = new ArrayList(); - axfr.add(initialsoa); - logxfr("got nonincremental response"); - state = AXFR; - } - parseRR(rec); // Restart... - return; - - case IXFR_DELSOA: - delta = new Delta(); - ixfr.add(delta); - delta.start = getSOASerial(rec); - delta.deletes.add(rec); - state = IXFR_DEL; - break; - - case IXFR_DEL: - if (type == Type.SOA) { - current_serial = getSOASerial(rec); - state = IXFR_ADDSOA; - parseRR(rec); // Restart... - return; - } - delta = (Delta) ixfr.get(ixfr.size() - 1); - delta.deletes.add(rec); - break; - - case IXFR_ADDSOA: - delta = (Delta) ixfr.get(ixfr.size() - 1); - delta.end = getSOASerial(rec); - delta.adds.add(rec); - state = IXFR_ADD; - break; - - case IXFR_ADD: - if (type == Type.SOA) { - long soa_serial = getSOASerial(rec); - if (soa_serial == end_serial) { - state = END; - break; - } else if (soa_serial != current_serial) { - fail("IXFR out of sync: expected serial " + - current_serial + " , got " + soa_serial); - } else { - state = IXFR_DELSOA; - parseRR(rec); // Restart... - return; - } - } - delta = (Delta) ixfr.get(ixfr.size() - 1); - delta.adds.add(rec); - break; - - case AXFR: - // Old BINDs sent cross class A records for non IN classes. - if (type == Type.A && rec.getDClass() != dclass) - break; - axfr.add(rec); - if (type == Type.SOA) { - state = END; - } - break; - - case END: - fail("extra data"); - break; - - default: - fail("invalid state"); - break; - } -} - -private void -closeConnection() { - try { - if (client != null) - client.cleanup(); - } - catch (IOException e) { - } -} - -private Message -parseMessage(byte [] b) throws WireParseException { - try { - return new Message(b); - } - catch (IOException e) { - if (e instanceof WireParseException) - throw (WireParseException) e; - throw new WireParseException("Error parsing message"); - } -} - -private void -doxfr() throws IOException, ZoneTransferException { - sendQuery(); - while (state != END) { - byte [] in = client.recv(); - Message response = parseMessage(in); - if (response.getHeader().getRcode() == Rcode.NOERROR && - verifier != null) - { - TSIGRecord tsigrec = response.getTSIG(); - - int error = verifier.verify(response, in); - if (error != Rcode.NOERROR) - fail("TSIG failure"); - } - - Record [] answers = response.getSectionArray(Section.ANSWER); - - if (state == INITIALSOA) { - int rcode = response.getRcode(); - if (rcode != Rcode.NOERROR) { - if (qtype == Type.IXFR && - rcode == Rcode.NOTIMP) - { - fallback(); - doxfr(); - return; - } - fail(Rcode.string(rcode)); - } - - Record question = response.getQuestion(); - if (question != null && question.getType() != qtype) { - fail("invalid question section"); - } - - if (answers.length == 0 && qtype == Type.IXFR) { - fallback(); - doxfr(); - return; - } - } - - for (int i = 0; i < answers.length; i++) { - parseRR(answers[i]); - } - - if (state == END && verifier != null && - !response.isVerified()) - fail("last message must be signed"); - } -} - -/** - * Does the zone transfer. - * @return A list, which is either an AXFR-style response (List of Records), - * and IXFR-style response (List of Deltas), or null, which indicates that - * an IXFR was performed and the zone is up to date. - * @throws IOException The zone transfer failed to due an IO problem. - * @throws ZoneTransferException The zone transfer failed to due a problem - * with the zone transfer itself. - */ -public List -run() throws IOException, ZoneTransferException { - try { - openConnection(); - doxfr(); - } - finally { - closeConnection(); - } - if (axfr != null) - return axfr; - return ixfr; -} - -/** - * Returns true if the response is an AXFR-style response (List of Records). - * This will be true if either an IXFR was performed, an IXFR was performed - * and the server provided a full zone transfer, or an IXFR failed and - * fallback to AXFR occurred. - */ -public boolean -isAXFR() { - return (rtype == Type.AXFR); -} - -/** - * Gets the AXFR-style response. - */ -public List -getAXFR() { - return axfr; -} - -/** - * Returns true if the response is an IXFR-style response (List of Deltas). - * This will be true only if an IXFR was performed and the server provided - * an incremental zone transfer. - */ -public boolean -isIXFR() { - return (rtype == Type.IXFR); -} - -/** - * Gets the IXFR-style response. - */ -public List -getIXFR() { - return ixfr; -} - -/** - * Returns true if the response indicates that the zone is up to date. - * This will be true only if an IXFR was performed. - */ -public boolean -isCurrent() { - return (axfr == null && ixfr == null); -} - -} diff --git a/src/main/java/org/xbill/DNS/security/CERTConverter.java b/src/main/java/org/xbill/DNS/security/CERTConverter.java deleted file mode 100644 index bfe9aa9a3..000000000 --- a/src/main/java/org/xbill/DNS/security/CERTConverter.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import org.xbill.DNS.CERTRecord; -import org.xbill.DNS.Name; -import org.xbill.DNS.Options; - -import java.io.ByteArrayInputStream; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -/** - * Routines to convert between a DNS CERT record and a Java Certificate. - * @see CERTRecord - * @see java.security.cert.Certificate - * - * @author Brian Wellington - */ - - -public class CERTConverter { - -/** Converts a CERT record into a Certificate */ -public static Certificate -parseRecord(CERTRecord r) { - int type = r.getCertType(); - byte [] data = r.getCert(); - Certificate cert; - try { - switch (type) { - case CERTRecord.PKIX: { - CertificateFactory cf; - ByteArrayInputStream bs; - - cf = CertificateFactory.getInstance("X.509"); - bs = new ByteArrayInputStream(data); - cert = cf.generateCertificate(bs); - break; - } - default: - return null; - } - return cert; - } - catch (CertificateException e) { - if (Options.check("verboseexceptions")) - System.err.println("Cert parse exception:" + e); - return null; - } -} - -/** Builds a CERT record from a Certificate associated with a key also in DNS */ -public static CERTRecord -buildRecord(Name name, int dclass, long ttl, Certificate cert, int tag, - int alg) -{ - int type; - byte [] data; - - try { - if (cert instanceof X509Certificate) { - type = CERTRecord.PKIX; - data = cert.getEncoded(); - } - else - return null; - - return new CERTRecord(name, dclass, ttl, type, tag, alg, data); - } - catch (CertificateException e) { - if (Options.check("verboseexceptions")) - System.err.println("Cert build exception:" + e); - return null; - } -} - -/** Builds a CERT record from a Certificate */ -public static CERTRecord -buildRecord(Name name, int dclass, long ttl, Certificate cert) { - return buildRecord(name, dclass, ttl, cert, 0, 0); -} - -} diff --git a/src/main/java/org/xbill/DNS/security/DHPubKey.java b/src/main/java/org/xbill/DNS/security/DHPubKey.java deleted file mode 100644 index a48ae98a1..000000000 --- a/src/main/java/org/xbill/DNS/security/DHPubKey.java +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import javax.crypto.interfaces.DHPublicKey; -import javax.crypto.spec.DHParameterSpec; -import java.math.BigInteger; - -/** - * A stub implementation of a Diffie-Hellman public key - * - * @author Brian Wellington - */ - -class DHPubKey implements DHPublicKey { - -private DHParameterSpec params; -private BigInteger Y; - -/** Create a Diffie-Hellman public key from its parts */ -public -DHPubKey(BigInteger p, BigInteger g, BigInteger y) { - params = new DHParameterSpec(p, g); - Y = y; -} - -/** Obtain the public value of a Diffie-Hellman public key */ -public BigInteger -getY() { - return Y; -} - -/** Obtain the parameters of a Diffie-Hellman public key */ -public DHParameterSpec -getParams() { - return params; -} - -/** Obtain the algorithm of a Diffie-Hellman public key */ -public String -getAlgorithm() { - return "DH"; -} - -/** Obtain the format of a Diffie-Hellman public key (unimplemented) */ -public String -getFormat() { - return null; -} - -/** - * Obtain the encoded representation of a Diffie-Hellman public key - * (unimplemented) - */ -public byte [] -getEncoded() { - return null; -} - -public String -toString() { - StringBuffer sb = new StringBuffer(); - sb.append("P = "); - sb.append(params.getP()); - sb.append("\nG = "); - sb.append(params.getG()); - sb.append("\nY = "); - sb.append(Y); - return sb.toString(); -} - -} diff --git a/src/main/java/org/xbill/DNS/security/DNSSECVerifier.java b/src/main/java/org/xbill/DNS/security/DNSSECVerifier.java deleted file mode 100644 index 843ebf349..000000000 --- a/src/main/java/org/xbill/DNS/security/DNSSECVerifier.java +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import org.xbill.DNS.*; - -import java.security.GeneralSecurityException; -import java.security.PublicKey; -import java.security.Signature; -import java.util.*; - -/** - * A class that verifies DNS data using digital signatures contained in DNSSEC - * SIG records. DNSSECVerifier stores a set of trusted keys. Each specific - * verification references a cache where additional secure keys may be found. - * @see Verifier - * @see DNSSEC - * - * @author Brian Wellington - */ - -public class DNSSECVerifier implements Verifier { - -private Map trustedKeys; - -/** Creates a new DNSSECVerifier */ -public -DNSSECVerifier() { - trustedKeys = new HashMap(); -} - -/** Adds the specified key to the set of trusted keys */ -public synchronized void -addTrustedKey(DNSKEYRecord key) { - Name name = key.getName(); - List list = (List) trustedKeys.get(name); - if (list == null) - trustedKeys.put(name, list = new LinkedList()); - list.add(key); -} - -/** Adds the specified key to the set of trusted keys */ -public void -addTrustedKey(Name name, int alg, PublicKey key) { - Record rec; - rec = KEYConverter.buildRecord(name, Type.DNSKEY, DClass.IN, 0, 0, - DNSKEYRecord.Protocol.DNSSEC, alg, key); - if (rec != null) - addTrustedKey((DNSKEYRecord) rec); -} - -private PublicKey -findMatchingKey(Iterator it, int algorithm, int footprint) { - while (it.hasNext()) { - DNSKEYRecord keyrec = (DNSKEYRecord) it.next(); - if (keyrec.getAlgorithm() == algorithm && - keyrec.getFootprint() == footprint) - return KEYConverter.parseRecord(keyrec); - } - return null; -} - -private synchronized PublicKey -findTrustedKey(Name name, int algorithm, int footprint) { - List list = (List) trustedKeys.get(name); - if (list == null) - return null; - return findMatchingKey(list.iterator(), algorithm, footprint); -} - -private PublicKey -findCachedKey(Cache cache, Name name, int algorithm, int footprint) { - RRset [] keysets = cache.findAnyRecords(name, Type.DNSKEY); - if (keysets == null) - return null; - RRset keys = keysets[0]; - return findMatchingKey(keys.rrs(), algorithm, footprint); -} - -private PublicKey -findKey(Cache cache, Name name, int algorithm, int footprint) { - PublicKey key = findTrustedKey(name, algorithm, footprint); - if (key == null && cache != null) - return findCachedKey(cache, name, algorithm, footprint); - return key; -} - -private int -verifySIG(RRset set, RRSIGRecord sigrec, Cache cache) { - PublicKey key = findKey(cache, sigrec.getSigner(), - sigrec.getAlgorithm(), sigrec.getFootprint()); - if (key == null) - return DNSSEC.Insecure; - - Date now = new Date(); - if (now.compareTo(sigrec.getExpire()) > 0 || - now.compareTo(sigrec.getTimeSigned()) < 0) - { - System.err.println("Outside of validity period"); - return DNSSEC.Failed; - } - byte [] data = DNSSEC.digestRRset(sigrec, set); - - byte [] sig; - String algString; - - switch (sigrec.getAlgorithm()) { - case DNSSEC.RSAMD5: - sig = sigrec.getSignature(); - algString = "MD5withRSA"; - break; - case DNSSEC.DSA: - case DNSSEC.DSA_NSEC3_SHA1: - sig = DSASignature.fromDNS(sigrec.getSignature()); - algString = "SHA1withDSA"; - break; - case DNSSEC.RSASHA1: - case DNSSEC.RSA_NSEC3_SHA1: - sig = sigrec.getSignature(); - algString = "SHA1withRSA"; - break; - default: - return DNSSEC.Failed; - } - - try { - Signature s = Signature.getInstance(algString); - s.initVerify(key); - s.update(data); - return s.verify(sig) ? DNSSEC.Secure : DNSSEC.Failed; - } - catch (GeneralSecurityException e) { - if (Options.check("verboseexceptions")) - System.err.println("Signing data: " + e); - return DNSSEC.Failed; - } -} - -/** - * Attempts to verify an RRset. This does not modify the set. - * @param set The RRset to verify - * @param cache The Cache where obtained secure keys are found (may be null) - * @return The new security status of the set - * @see RRset - */ -public int -verify(RRset set, Cache cache) { - Iterator sigs = set.sigs(); - if (Options.check("verbosesec")) - System.out.print("Verifying " + set.getName() + "/" + - Type.string(set.getType()) + ": "); - if (!sigs.hasNext()) { - if (Options.check("verbosesec")) - System.out.println("Insecure"); - return DNSSEC.Insecure; - } - while (sigs.hasNext()) { - RRSIGRecord sigrec = (RRSIGRecord) sigs.next(); - if (verifySIG(set, sigrec, cache) == DNSSEC.Secure) { - if (Options.check("verbosesec")) - System.out.println("Secure"); - return DNSSEC.Secure; - } - } - if (Options.check("verbosesec")) - System.out.println("Failed"); - return DNSSEC.Failed; -} - -} diff --git a/src/main/java/org/xbill/DNS/security/DSAPubKey.java b/src/main/java/org/xbill/DNS/security/DSAPubKey.java deleted file mode 100644 index 9f6f1191c..000000000 --- a/src/main/java/org/xbill/DNS/security/DSAPubKey.java +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import java.math.BigInteger; -import java.security.interfaces.DSAParams; -import java.security.interfaces.DSAPublicKey; - -/** - * A stub implementation of a DSA (Digital Signature Algorithm) public key - * - * @author Brian Wellington - */ - -class DSAPubKey implements DSAPublicKey { - -static class SimpleDSAParams implements DSAParams { - private BigInteger P, Q, G; - - public - SimpleDSAParams(BigInteger p, BigInteger q, BigInteger g) { - P = p; - Q = q; - G = g; - } - - public BigInteger - getP() { - return P; - } - - public BigInteger - getQ() { - return Q; - } - - public BigInteger - getG() { - return G; - } -} - -private DSAParams params; -private BigInteger Y; - -/** Create a DSA public key from its parts */ -public -DSAPubKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y) { - params = (DSAParams) new SimpleDSAParams(p, q, g); - Y = y; -} - -/** Obtain the public value of a DSA public key */ -public BigInteger -getY() { - return Y; -} - -/** Obtain the parameters of a DSA public key */ -public DSAParams -getParams() { - return params; -} - -/** Obtain the algorithm of a DSA public key */ -public String -getAlgorithm() { - return "DSA"; -} - -/** Obtain the format of a DSA public key (unimplemented) */ -public String -getFormat() { - return null; -} - -/** Obtain the encoded representation of a DSA public key (unimplemented) */ -public byte [] -getEncoded() { - return null; -} - -} diff --git a/src/main/java/org/xbill/DNS/security/DSASignature.java b/src/main/java/org/xbill/DNS/security/DSASignature.java deleted file mode 100644 index d7ee07a3e..000000000 --- a/src/main/java/org/xbill/DNS/security/DSASignature.java +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import java.security.SignatureException; -import java.security.interfaces.DSAParams; -import java.util.Arrays; - -/** - * Converts DSA signatures between the RRSIG/SIG record format (as specified - * in RFC 2536) and the format used by Java DSA routines (DER-encoded). - * - * @author Brian Wellington - */ - -public class DSASignature { - -static final int ASN1_SEQ = 0x30; -static final int ASN1_INT = 0x2; - -private DSASignature() {} - -/** - * Converts the signature field in a SIG record to the - * format expected by the DSA verification routines. - */ -public static byte [] -fromDNS(byte [] sig) { - final int len = 20; - int n = 0; - byte rlen, slen, seqlen; - - rlen = len; - if (sig[1] < 0) - rlen++; - - slen = len; - if (sig[len + 1] < 0) - slen++; - - /* 4 = 2 * (INT, value) */ - seqlen = (byte) (rlen + slen + 4); - - /* 2 = 1 * (SEQ, value) */ - byte [] array = new byte[seqlen + 2]; - - array[n++] = ASN1_SEQ; - array[n++] = (byte) seqlen; - array[n++] = ASN1_INT; - array[n++] = rlen; - if (rlen > len) - array[n++] = 0; - for (int i = 0; i < len; i++, n++) - array[n] = sig[1 + i]; - array[n++] = ASN1_INT; - array[n++] = slen; - if (slen > len) - array[n++] = 0; - for (int i = 0; i < len; i++, n++) - array[n] = sig[1 + len + i]; - return array; -} - -/** - * Converts the signature generated by DSA signature routines to - * the one expected inside an RRSIG/SIG record. - */ -public static byte [] -toDNS(DSAParams params, byte [] sig) -throws SignatureException -{ - int rLength, sLength; - int rOffset, sOffset; - if ((sig[0] != ASN1_SEQ) || (sig[2] != ASN1_INT)) - throw new SignatureException("Expected SEQ, INT"); - rLength = sig[3]; - rOffset = 4; - if (sig[rOffset] == 0) { - rLength--; - rOffset++; - } - if (sig[rOffset+rLength] != ASN1_INT) - throw new SignatureException("Expected INT"); - sLength = sig[rOffset + rLength + 1]; - sOffset = rOffset + rLength + 2; - if (sig[sOffset] == 0) { - sLength--; - sOffset++; - } - - if ((rLength > 20) || (sLength > 20)) - throw new SignatureException("DSA R/S too long"); - - byte[] newSig = new byte[41]; - Arrays.fill(newSig, (byte) 0); - newSig[0] = (byte) ((params.getP().bitLength() - 512)/64); - System.arraycopy(sig, rOffset, newSig, 1 + (20 - rLength), rLength); - System.arraycopy(sig, sOffset, newSig, 21 + (20 - sLength), sLength); - return newSig; -} - -} - diff --git a/src/main/java/org/xbill/DNS/security/KEYConverter.java b/src/main/java/org/xbill/DNS/security/KEYConverter.java deleted file mode 100644 index 5140f14ee..000000000 --- a/src/main/java/org/xbill/DNS/security/KEYConverter.java +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import org.xbill.DNS.*; - -import javax.crypto.interfaces.DHPublicKey; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.security.PublicKey; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.RSAPublicKey; - -/** - * Routines to convert between a DNS KEY record and a Java PublicKey. - * - * @author Brian Wellington - */ - -public class KEYConverter { - -private static final BigInteger DHPRIME768 = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16); -private static final BigInteger DHPRIME1024 = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16); -private static final BigInteger TWO = new BigInteger("2", 16); - -static int -BigIntegerLength(BigInteger i) { - byte [] b = i.toByteArray(); - return (b[0] == 0 ? b.length - 1 : b.length); -} - -static BigInteger -readBigInteger(DataInputStream in, int len) throws IOException { - byte [] b = new byte[len]; - int n = in.read(b); - if (n < len) - throw new IOException("end of input"); - return new BigInteger(1, b); -} - -static void -writeBigInteger(ByteArrayOutputStream out, BigInteger val) { - byte [] b = val.toByteArray(); - if (b[0] == 0) - out.write(b, 1, b.length - 1); - else - out.write(b, 0, b.length); -} - -static void -writeShort(ByteArrayOutputStream out, int i) { - out.write((i >> 8) & 0xFF); - out.write(i & 0xFF); -} - -static RSAPublicKey -parseRSA(DataInputStream in) throws IOException { - int exponentLength = in.readUnsignedByte(); - if (exponentLength == 0) - exponentLength = in.readUnsignedShort(); - BigInteger exponent = readBigInteger(in, exponentLength); - - int modulusLength = in.available(); - BigInteger modulus = readBigInteger(in, modulusLength); - - RSAPublicKey rsa = new RSAPubKey(modulus, exponent); - return rsa; -} - -static DHPublicKey -parseDH(DataInputStream in) throws IOException { - int special = 0; - int pLength = in.readUnsignedShort(); - if (pLength < 16 && pLength != 1 && pLength != 2) - return null; - BigInteger p; - if (pLength == 1 || pLength == 2) { - if (pLength == 1) - special = in.readUnsignedByte(); - else - special = in.readUnsignedShort(); - if (special != 1 && special != 2) - return null; - if (special == 1) - p = DHPRIME768; - else - p = DHPRIME1024; - } - else - p = readBigInteger(in, pLength); - - int gLength = in.readUnsignedShort(); - BigInteger g; - if (gLength == 0) { - if (special != 0) - g = TWO; - else - return null; - } - else - g = readBigInteger(in, gLength); - - int yLength = in.readUnsignedShort(); - BigInteger y = readBigInteger(in, yLength); - - return new DHPubKey(p, g, y); -} - -static DSAPublicKey -parseDSA(DataInputStream in) throws IOException { - byte t = in.readByte(); - - BigInteger q = readBigInteger(in, 20); - BigInteger p = readBigInteger(in, 64 + t*8); - BigInteger g = readBigInteger(in, 64 + t*8); - BigInteger y = readBigInteger(in, 64 + t*8); - - DSAPublicKey dsa = new DSAPubKey(p, q, g, y); - return dsa; -} - -/** Converts a KEY/DNSKEY record into a PublicKey */ -static PublicKey -parseRecord(int alg, byte [] data) { - ByteArrayInputStream bytes = new ByteArrayInputStream(data); - DataInputStream in = new DataInputStream(bytes); - try { - switch (alg) { - case DNSSEC.RSAMD5: - case DNSSEC.RSASHA1: - case DNSSEC.RSA_NSEC3_SHA1: - return parseRSA(in); - case DNSSEC.DH: - return parseDH(in); - case DNSSEC.DSA: - case DNSSEC.DSA_NSEC3_SHA1: - return parseDSA(in); - default: - return null; - } - } - catch (IOException e) { - if (Options.check("verboseexceptions")) - System.err.println(e); - return null; - } -} - -/** Converts a DNSKEY record into a PublicKey */ -public static PublicKey -parseRecord(DNSKEYRecord r) { - int alg = r.getAlgorithm(); - byte [] data = r.getKey(); - return parseRecord(alg, data); -} - -/** Converts a KEY record into a PublicKey */ -public static PublicKey -parseRecord(KEYRecord r) { - int alg = r.getAlgorithm(); - byte [] data = r.getKey(); - return parseRecord(alg, data); -} - -static byte [] -buildRSA(RSAPublicKey key) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - BigInteger exponent = key.getPublicExponent(); - BigInteger modulus = key.getModulus(); - int exponentLength = BigIntegerLength(exponent); - - if (exponentLength < 256) - out.write(exponentLength); - else { - out.write(0); - writeShort(out, exponentLength); - } - writeBigInteger(out, exponent); - writeBigInteger(out, modulus); - - return out.toByteArray(); -} - -static byte [] -buildDH(DHPublicKey key) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - BigInteger p = key.getParams().getP(); - BigInteger g = key.getParams().getG(); - BigInteger y = key.getY(); - - int pLength, gLength, yLength; - if (g.equals(TWO) && (p.equals(DHPRIME768) || p.equals(DHPRIME1024))) { - pLength = 1; - gLength = 0; - } - else { - pLength = BigIntegerLength(p); - gLength = BigIntegerLength(g); - } - yLength = BigIntegerLength(y); - - writeShort(out, pLength); - if (pLength == 1) { - if (p.bitLength() == 768) - out.write(1); - else - out.write(2); - } - else - writeBigInteger(out, p); - writeShort(out, gLength); - if (gLength > 0) - writeBigInteger(out, g); - writeShort(out, yLength); - writeBigInteger(out, y); - - return out.toByteArray(); -} - -static byte [] -buildDSA(DSAPublicKey key) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - BigInteger q = key.getParams().getQ(); - BigInteger p = key.getParams().getP(); - BigInteger g = key.getParams().getG(); - BigInteger y = key.getY(); - int t = (p.toByteArray().length - 64) / 8; - - out.write(t); - writeBigInteger(out, q); - writeBigInteger(out, p); - writeBigInteger(out, g); - writeBigInteger(out, y); - - return out.toByteArray(); -} - -/** Builds a KEY record from a PublicKey */ -public static KEYRecord -buildRecord(Name name, int dclass, long ttl, int flags, int proto, - PublicKey key) -{ - byte alg; - - if (key instanceof RSAPublicKey) { - alg = DNSSEC.RSAMD5; - } - else if (key instanceof DHPublicKey) { - alg = DNSSEC.DH; - } - else if (key instanceof DSAPublicKey) { - alg = DNSSEC.DSA; - } - else - return null; - - return (KEYRecord) buildRecord(name, Type.KEY, dclass, ttl, flags, - proto, alg, key); -} - -/** Builds a DNSKEY or KEY record from a PublicKey */ -public static Record -buildRecord(Name name, int type, int dclass, long ttl, int flags, int proto, - int alg, PublicKey key) -{ - byte [] data; - - if (type != Type.KEY && type != Type.DNSKEY) - throw new IllegalArgumentException("type must be KEY " + - "or DNSKEY"); - - if (key instanceof RSAPublicKey) { - data = buildRSA((RSAPublicKey) key); - } - else if (key instanceof DHPublicKey) { - data = buildDH((DHPublicKey) key); - } - else if (key instanceof DSAPublicKey) { - data = buildDSA((DSAPublicKey) key); - } - else - return null; - - if (data == null) - return null; - - if (type == Type.DNSKEY) - return new DNSKEYRecord(name, dclass, ttl, flags, proto, alg, - data); - else - return new KEYRecord(name, dclass, ttl, flags, proto, alg, - data); -} - -} diff --git a/src/main/java/org/xbill/DNS/security/RSAPubKey.java b/src/main/java/org/xbill/DNS/security/RSAPubKey.java deleted file mode 100644 index 48c64dd40..000000000 --- a/src/main/java/org/xbill/DNS/security/RSAPubKey.java +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import java.math.BigInteger; -import java.security.interfaces.RSAPublicKey; - -/** - * A stub implementation of an RSA public key - * - * @author Brian Wellington - */ - -class RSAPubKey implements RSAPublicKey { - -private BigInteger Modulus, Exponent; - -/** Create an RSA public key from its parts */ -public -RSAPubKey(BigInteger modulus, BigInteger exponent) { - Modulus = modulus; - Exponent = exponent; -} - -/** Obtain the modulus of an RSA public key */ -public BigInteger -getModulus() { - return Modulus; -} - -/** Obtain the exponent of an RSA public key */ -public BigInteger -getPublicExponent() { - return Exponent; -} - -/** Obtain the algorithm of an RSA public key */ -public String -getAlgorithm() { - return "RSA"; -} - -/** Obtain the format of an RSA public key (unimplemented) */ -public String -getFormat() { - return null; -} - -/** Obtain the encoded representation of an RSA public key (unimplemented) */ -public byte [] -getEncoded() { - return null; -} - -} diff --git a/src/main/java/org/xbill/DNS/security/SIG0Signer.java b/src/main/java/org/xbill/DNS/security/SIG0Signer.java deleted file mode 100644 index ecd16b53f..000000000 --- a/src/main/java/org/xbill/DNS/security/SIG0Signer.java +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2001-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.security; - -import org.xbill.DNS.*; - -import java.io.IOException; -import java.security.*; -import java.security.interfaces.DSAKey; -import java.util.Date; - -/** - * Creates SIG(0) transaction signatures. - * - * @author Pasi Eronen - * @author Brian Wellington - */ - -public class SIG0Signer { - -/** - * The default validity period for outgoing SIG(0) signed messages. - * Can be overriden by the sig0validity option. - */ -private static final short VALIDITY = 300; - -private int algorithm; -private PrivateKey privateKey; -private Name name; -private int footprint; - -/** - * Creates a new SIG(0) signer object. - * @param algorithm usually DNSSEC.RSAMD5, DNSSEC.DSA, or DNSSEC.RSASHA1 - * @param privateKey signing key (must match algorithm) - * @param name the name of the key - * @param keyFootprint the key tag - */ -public -SIG0Signer(int algorithm, PrivateKey privateKey, Name name, int keyFootprint) { - this.algorithm = (byte) algorithm; - this.privateKey = privateKey; - this.name = name; - this.footprint = keyFootprint; -} - -/** - * Creates a new SIG(0) signer object. This is the same as the - * other constructor, except that the key tag is calculated automatically - * from the given public key. - */ -public -SIG0Signer(int algorithm, PrivateKey privateKey, Name name, - PublicKey publicKey) -{ - this.algorithm = (byte) algorithm; - this.privateKey = privateKey; - this.name = name; - Record rec = KEYConverter.buildRecord(name, Type.KEY, DClass.IN, 0, - KEYRecord.OWNER_USER, - KEYRecord.PROTOCOL_ANY, - algorithm, publicKey); - KEYRecord keyRecord = (KEYRecord) rec; - this.footprint = keyRecord.getFootprint(); -} - -/** - * Appends a SIG(0) signature to the message. - * @param m the message - * @param old if this message is a response, the original message - */ -public void apply(Message m, byte [] old) -throws IOException, SignatureException, InvalidKeyException, - NoSuchAlgorithmException -{ - - int validity = Options.intValue("sig0validity"); - if (validity < 0) - validity = VALIDITY; - - long now = System.currentTimeMillis(); - Date timeSigned = new Date(now); - Date timeExpires = new Date(now + validity * 1000); - - String algorithmName; - if (algorithm == DNSSEC.DSA) { - algorithmName = "SHA1withDSA"; - } else if (algorithm == DNSSEC.RSAMD5) { - algorithmName = "MD5withRSA"; - } else if (algorithm == DNSSEC.RSASHA1) { - algorithmName = "SHA1withRSA"; - } else { - throw new NoSuchAlgorithmException("Unknown algorithm"); - } - - SIGRecord tmpsig = new SIGRecord(Name.root, DClass.ANY, 0, 0, - algorithm, 0, timeExpires, timeSigned, - footprint, name, null); - - byte [] outBytes = DNSSEC.digestMessage(tmpsig, m, old); - - Signature signer = Signature.getInstance(algorithmName); - signer.initSign(privateKey); - signer.update(outBytes); - byte [] signature = signer.sign(); - - /* - * RSA signatures are already in correct format, but Java DSA - * routines use ASN.1; convert this to SIG format. - */ - if (algorithm == DNSSEC.DSA) { - DSAKey dsakey = (DSAKey) privateKey; - signature = DSASignature.toDNS(dsakey.getParams(), signature); - } - - SIGRecord sig = new SIGRecord(Name.root, DClass.ANY, 0, 0, algorithm, - 0, timeExpires, timeSigned, footprint, - name, signature); - m.addRecord(sig, Section.ADDITIONAL); -} - -} diff --git a/src/main/java/org/xbill/DNS/spi/DNSJavaNameService.java b/src/main/java/org/xbill/DNS/spi/DNSJavaNameService.java deleted file mode 100644 index d473058eb..000000000 --- a/src/main/java/org/xbill/DNS/spi/DNSJavaNameService.java +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.spi; - -import org.xbill.DNS.*; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.StringTokenizer; - -/** - * This class implements a Name Service Provider, which Java can use - * (starting with version 1.4), to perform DNS resolutions instead of using - * the standard calls. - *

    - * This Name Service Provider uses dnsjava. - *

    - * To use this provider, you must set the following system propery: - * sun.net.spi.nameservice.provider.1=dns,dnsjava - * - * @author Brian Wellington - * @author Paul Cowan (pwc21@yahoo.com) - */ - -public class DNSJavaNameService implements InvocationHandler { - -private static final String nsProperty = "sun.net.spi.nameservice.nameservers"; -private static final String domainProperty = "sun.net.spi.nameservice.domain"; -private static final String v6Property = "java.net.preferIPv6Addresses"; - -private boolean preferV6 = false; - -/** - * Creates a DNSJavaNameService instance. - *

    - * Uses the - * sun.net.spi.nameservice.nameservers, - * sun.net.spi.nameservice.domain, and - * java.net.preferIPv6Addresses properties for configuration. - */ -protected DNSJavaNameService() { - String nameServers = System.getProperty(nsProperty); - String domain = System.getProperty(domainProperty); - String v6 = System.getProperty(v6Property); - - if (nameServers != null) { - StringTokenizer st = new StringTokenizer(nameServers, ","); - String [] servers = new String[st.countTokens()]; - int n = 0; - while (st.hasMoreTokens()) - servers[n++] = st.nextToken(); - try { - Resolver res = new ExtendedResolver(servers); - Lookup.setDefaultResolver(res); - } - catch (UnknownHostException e) { - System.err.println("DNSJavaNameService: invalid " + - nsProperty); - } - } - - if (domain != null) { - try { - Lookup.setDefaultSearchPath(new String[] {domain}); - } - catch (TextParseException e) { - System.err.println("DNSJavaNameService: invalid " + - domainProperty); - } - } - - if (v6 != null && v6.equalsIgnoreCase("true")) - preferV6 = true; -} - - -public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - try { - if (method.getName().equals("getHostByAddr")) { - return this.getHostByAddr((byte[]) args[0]); - } else if (method.getName().equals("lookupAllHostAddr")) { - InetAddress[] addresses = this.lookupAllHostAddr((String) args[0]); - if (method.getReturnType().equals(InetAddress[].class)) { - // method for Java >= 1.6 - return addresses; - } else if (method.getReturnType().equals(byte[][].class)) { - // method for Java <= 1.5 - byte[][] byteAddresses = new byte[addresses.length][]; - for (int i=0; i < addresses.length; i++) { - byteAddresses[i] = addresses[i].getAddress(); - } - return byteAddresses; - } - } - } catch (Throwable e) { - System.err.println("DNSJavaNameService: Unexpected error."); - e.printStackTrace(); - throw e; - } - throw new IllegalArgumentException("Unknown function name or arguments."); -} - -/** - * Performs a forward DNS lookup for the host name. - * @param host The host name to resolve. - * @return All the ip addresses found for the host name. - */ -public InetAddress [] -lookupAllHostAddr(String host) throws UnknownHostException { - Name name = null; - - try { - name = new Name(host); - } - catch (TextParseException e) { - throw new UnknownHostException(host); - } - - Record [] records = null; - if (preferV6) - records = new Lookup(name, Type.AAAA).run(); - if (records == null) - records = new Lookup(name, Type.A).run(); - if (records == null && !preferV6) - records = new Lookup(name, Type.AAAA).run(); - if (records == null) - throw new UnknownHostException(host); - - InetAddress[] array = new InetAddress[records.length]; - for (int i = 0; i < records.length; i++) { - Record record = records[i]; - if (records[i] instanceof ARecord) { - ARecord a = (ARecord) records[i]; - array[i] = a.getAddress(); - } else { - AAAARecord aaaa = (AAAARecord) records[i]; - array[i] = aaaa.getAddress(); - } - } - return array; -} - -/** - * Performs a reverse DNS lookup. - * @param addr The ip address to lookup. - * @return The host name found for the ip address. - */ -public String getHostByAddr(byte [] addr) throws UnknownHostException { - Name name = ReverseMap.fromAddress(InetAddress.getByAddress(addr)); - Record [] records = new Lookup(name, Type.PTR).run(); - if (records == null) - throw new UnknownHostException(); - return ((PTRRecord) records[0]).getTarget().toString(); -} -} diff --git a/src/main/java/org/xbill/DNS/spi/DNSJavaNameServiceDescriptor.java b/src/main/java/org/xbill/DNS/spi/DNSJavaNameServiceDescriptor.java deleted file mode 100644 index c49d1a46c..000000000 --- a/src/main/java/org/xbill/DNS/spi/DNSJavaNameServiceDescriptor.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2005 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.spi; - -import sun.net.spi.nameservice.NameService; -import sun.net.spi.nameservice.NameServiceDescriptor; - -import java.lang.reflect.Proxy; - -/** - * The descriptor class for the dnsjava name service provider. - * - * @author Brian Wellington - * @author Paul Cowan (pwc21@yahoo.com) - */ - -public class DNSJavaNameServiceDescriptor implements NameServiceDescriptor { - -private static NameService nameService; - -static { - nameService = (NameService) Proxy.newProxyInstance(NameService.class.getClassLoader(), - new Class[] { NameService.class }, - new DNSJavaNameService()); -} - -/** - * Returns a reference to a dnsjava name server provider. - */ -public NameService -createNameService() { - return nameService; -} - -public String -getType() { - return "dns"; -} - -public String -getProviderName() { - return "dnsjava"; -} - -} diff --git a/src/main/java/org/xbill/DNS/spi/services/sun.net.spi.nameservice.NameServiceDescriptor b/src/main/java/org/xbill/DNS/spi/services/sun.net.spi.nameservice.NameServiceDescriptor deleted file mode 100644 index 1ca895c42..000000000 --- a/src/main/java/org/xbill/DNS/spi/services/sun.net.spi.nameservice.NameServiceDescriptor +++ /dev/null @@ -1 +0,0 @@ -org.xbill.DNS.spi.DNSJavaNameServiceDescriptor diff --git a/src/main/java/org/xbill/DNS/utils/HMAC.java b/src/main/java/org/xbill/DNS/utils/HMAC.java deleted file mode 100644 index bf97ee6ad..000000000 --- a/src/main/java/org/xbill/DNS/utils/HMAC.java +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.utils; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -/** - * A pure java implementation of the HMAC-MD5 secure hash algorithm - * - * @author Brian Wellington - */ - -public class HMAC { - -MessageDigest digest; -private byte [] ipad, opad; - -private static final byte IPAD = 0x36; -private static final byte OPAD = 0x5c; -private static final byte PADLEN = 64; - -private void -init(byte [] key) { - int i; - - if (key.length > PADLEN) { - key = digest.digest(key); - digest.reset(); - } - ipad = new byte[PADLEN]; - opad = new byte[PADLEN]; - for (i = 0; i < key.length; i++) { - ipad[i] = (byte) (key[i] ^ IPAD); - opad[i] = (byte) (key[i] ^ OPAD); - } - for (; i < PADLEN; i++) { - ipad[i] = IPAD; - opad[i] = OPAD; - } - digest.update(ipad); -} - -/** - * Creates a new HMAC instance - * @param digest The message digest object. - * @param key The secret key - */ -public -HMAC(MessageDigest digest, byte [] key) { - digest.reset(); - this.digest = digest; - init(key); -} - -/** - * Creates a new HMAC instance - * @param digestName The name of the message digest function. - * @param key The secret key. - */ -public -HMAC(String digestName, byte [] key) { - try { - digest = MessageDigest.getInstance(digestName); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException("unknown digest algorithm " - + digestName); - } - init(key); -} - -/** - * Adds data to the current hash - * @param b The data - * @param offset The index at which to start adding to the hash - * @param length The number of bytes to hash - */ -public void -update(byte [] b, int offset, int length) { - digest.update(b, offset, length); -} - -/** - * Adds data to the current hash - * @param b The data - */ -public void -update(byte [] b) { - digest.update(b); -} - -/** - * Signs the data (computes the secure hash) - * @return An array with the signature - */ -public byte [] -sign() { - byte [] output = digest.digest(); - digest.reset(); - digest.update(opad); - return digest.digest(output); -} - -/** - * Verifies the data (computes the secure hash and compares it to the input) - * @param signature The signature to compare against - * @return true if the signature matched, false otherwise - */ -public boolean -verify(byte [] signature) { - return Arrays.equals(signature, sign()); -} - -/** - * Resets the HMAC object for further use - */ -public void -clear() { - digest.reset(); - digest.update(ipad); -} - -} diff --git a/src/main/java/org/xbill/DNS/utils/base16.java b/src/main/java/org/xbill/DNS/utils/base16.java deleted file mode 100644 index f97bb90ae..000000000 --- a/src/main/java/org/xbill/DNS/utils/base16.java +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.utils; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Routines for converting between Strings of hex-encoded data and arrays of - * binary data. This is not actually used by DNS. - * - * @author Brian Wellington - */ - -public class base16 { - -private static final String Base16 = "0123456789ABCDEF"; - -private -base16() {} - -/** - * Convert binary data to a hex-encoded String - * @param b An array containing binary data - * @return A String containing the encoded data - */ -public static String -toString(byte [] b) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - for (int i = 0; i < b.length; i++) { - short value = (short) (b[i] & 0xFF); - byte high = (byte) (value >> 4); - byte low = (byte) (value & 0xF); - os.write(Base16.charAt(high)); - os.write(Base16.charAt(low)); - } - return new String(os.toByteArray()); -} - -/** - * Convert a hex-encoded String to binary data - * @param str A String containing the encoded data - * @return An array containing the binary data, or null if the string is invalid - */ -public static byte [] -fromString(String str) { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - byte [] raw = str.getBytes(); - for (int i = 0; i < raw.length; i++) { - if (!Character.isWhitespace((char)raw[i])) - bs.write(raw[i]); - } - byte [] in = bs.toByteArray(); - if (in.length % 2 != 0) { - return null; - } - - bs.reset(); - DataOutputStream ds = new DataOutputStream(bs); - - for (int i = 0; i < in.length; i += 2) { - byte high = (byte) Base16.indexOf(Character.toUpperCase((char)in[i])); - byte low = (byte) Base16.indexOf(Character.toUpperCase((char)in[i+1])); - try { - ds.writeByte((high << 4) + low); - } - catch (IOException e) { - } - } - return bs.toByteArray(); -} - -} diff --git a/src/main/java/org/xbill/DNS/utils/base32.java b/src/main/java/org/xbill/DNS/utils/base32.java deleted file mode 100644 index 29d68c1eb..000000000 --- a/src/main/java/org/xbill/DNS/utils/base32.java +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.utils; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Routines for converting between Strings of base32-encoded data and arrays - * of binary data. This currently supports the base32 and base32hex alphabets - * specified in RFC 4648, sections 6 and 7. - * - * @author Brian Wellington - */ - -public class base32 { - -public static class Alphabet { - private Alphabet() {} - - public static final String BASE32 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="; - public static final String BASE32HEX = - "0123456789ABCDEFGHIJKLMNOPQRSTUV="; -}; - -private String alphabet; -private boolean padding, lowercase; - -/** - * Creates an object that can be used to do base32 conversions. - * @param alphabet Which alphabet should be used - * @param padding Whether padding should be used - * @param lowercase Whether lowercase characters should be used. - * default parameters (The standard base32 alphabet, no padding, uppercase) - */ -public -base32(String alphabet, boolean padding, boolean lowercase) { - this.alphabet = alphabet; - this.padding = padding; - this.lowercase = lowercase; -} - -static private int -blockLenToPadding(int blocklen) { - switch (blocklen) { - case 1: - return 6; - case 2: - return 4; - case 3: - return 3; - case 4: - return 1; - case 5: - return 0; - default: - return -1; - } -} - -static private int -paddingToBlockLen(int padlen) { - switch (padlen) { - case 6: - return 1; - case 4: - return 2; - case 3: - return 3; - case 1: - return 4; - case 0: - return 5; - default : - return -1; - } -} - -/** - * Convert binary data to a base32-encoded String - * - * @param b An array containing binary data - * @return A String containing the encoded data - */ -public String -toString(byte [] b) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - for (int i = 0; i < (b.length + 4) / 5; i++) { - short s[] = new short[5]; - int t[] = new int[8]; - - int blocklen = 5; - for (int j = 0; j < 5; j++) { - if ((i * 5 + j) < b.length) - s[j] = (short) (b[i * 5 + j] & 0xFF); - else { - s[j] = 0; - blocklen--; - } - } - int padlen = blockLenToPadding(blocklen); - - // convert the 5 byte block into 8 characters (values 0-31). - - // upper 5 bits from first byte - t[0] = (byte) ((s[0] >> 3) & 0x1F); - // lower 3 bits from 1st byte, upper 2 bits from 2nd. - t[1] = (byte) (((s[0] & 0x07) << 2) | ((s[1] >> 6) & 0x03)); - // bits 5-1 from 2nd. - t[2] = (byte) ((s[1] >> 1) & 0x1F); - // lower 1 bit from 2nd, upper 4 from 3rd - t[3] = (byte) (((s[1] & 0x01) << 4) | ((s[2] >> 4) & 0x0F)); - // lower 4 from 3rd, upper 1 from 4th. - t[4] = (byte) (((s[2] & 0x0F) << 1) | ((s[3] >> 7) & 0x01)); - // bits 6-2 from 4th - t[5] = (byte) ((s[3] >> 2) & 0x1F); - // lower 2 from 4th, upper 3 from 5th; - t[6] = (byte) (((s[3] & 0x03) << 3) | ((s[4] >> 5) & 0x07)); - // lower 5 from 5th; - t[7] = (byte) (s[4] & 0x1F); - - // write out the actual characters. - for (int j = 0; j < t.length - padlen; j++) { - char c = alphabet.charAt(t[j]); - if (lowercase) - c = Character.toLowerCase(c); - os.write(c); - } - - // write out the padding (if any) - if (padding) { - for (int j = t.length - padlen; j < t.length; j++) - os.write('='); - } - } - - return new String(os.toByteArray()); -} - -/** - * Convert a base32-encoded String to binary data - * - * @param str A String containing the encoded data - * @return An array containing the binary data, or null if the string is invalid - */ -public byte[] -fromString(String str) { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - byte [] raw = str.getBytes(); - for (int i = 0; i < raw.length; i++) - { - char c = (char) raw[i]; - if (!Character.isWhitespace(c)) { - c = Character.toUpperCase(c); - bs.write((byte) c); - } - } - - if (padding) { - if (bs.size() % 8 != 0) - return null; - } else { - while (bs.size() % 8 != 0) - bs.write('='); - } - - byte [] in = bs.toByteArray(); - - bs.reset(); - DataOutputStream ds = new DataOutputStream(bs); - - for (int i = 0; i < in.length / 8; i++) { - short[] s = new short[8]; - int[] t = new int[5]; - - int padlen = 8; - for (int j = 0; j < 8; j++) { - char c = (char) in[i * 8 + j]; - if (c == '=') - break; - s[j] = (short) alphabet.indexOf(in[i * 8 + j]); - if (s[j] < 0) - return null; - padlen--; - } - int blocklen = paddingToBlockLen(padlen); - if (blocklen < 0) - return null; - - // all 5 bits of 1st, high 3 (of 5) of 2nd - t[0] = (s[0] << 3) | s[1] >> 2; - // lower 2 of 2nd, all 5 of 3rd, high 1 of 4th - t[1] = ((s[1] & 0x03) << 6) | (s[2] << 1) | (s[3] >> 4); - // lower 4 of 4th, high 4 of 5th - t[2] = ((s[3] & 0x0F) << 4) | ((s[4] >> 1) & 0x0F); - // lower 1 of 5th, all 5 of 6th, high 2 of 7th - t[3] = (s[4] << 7) | (s[5] << 2) | (s[6] >> 3); - // lower 3 of 7th, all of 8th - t[4] = ((s[6] & 0x07) << 5) | s[7]; - - try { - for (int j = 0; j < blocklen; j++) - ds.writeByte((byte) (t[j] & 0xFF)); - } - catch (IOException e) { - } - } - - return bs.toByteArray(); -} - -} diff --git a/src/main/java/org/xbill/DNS/utils/base64.java b/src/main/java/org/xbill/DNS/utils/base64.java deleted file mode 100644 index 9f239cf7b..000000000 --- a/src/main/java/org/xbill/DNS/utils/base64.java +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.utils; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Routines for converting between Strings of base64-encoded data and arrays of - * binary data. - * - * @author Brian Wellington - */ - -public class base64 { - -private static final String Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - -private -base64() {} - -/** - * Convert binary data to a base64-encoded String - * @param b An array containing binary data - * @return A String containing the encoded data - */ -public static String -toString(byte [] b) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - for (int i = 0; i < (b.length + 2) / 3; i++) { - short [] s = new short[3]; - short [] t = new short[4]; - for (int j = 0; j < 3; j++) { - if ((i * 3 + j) < b.length) - s[j] = (short) (b[i*3+j] & 0xFF); - else - s[j] = -1; - } - - t[0] = (short) (s[0] >> 2); - if (s[1] == -1) - t[1] = (short) (((s[0] & 0x3) << 4)); - else - t[1] = (short) (((s[0] & 0x3) << 4) + (s[1] >> 4)); - if (s[1] == -1) - t[2] = t[3] = 64; - else if (s[2] == -1) { - t[2] = (short) (((s[1] & 0xF) << 2)); - t[3] = 64; - } - else { - t[2] = (short) (((s[1] & 0xF) << 2) + (s[2] >> 6)); - t[3] = (short) (s[2] & 0x3F); - } - for (int j = 0; j < 4; j++) - os.write(Base64.charAt(t[j])); - } - return new String(os.toByteArray()); -} - -/** - * Formats data into a nicely formatted base64 encoded String - * @param b An array containing binary data - * @param lineLength The number of characters per line - * @param prefix A string prefixing the characters on each line - * @param addClose Whether to add a close parenthesis or not - * @return A String representing the formatted output - */ -public static String -formatString(byte [] b, int lineLength, String prefix, boolean addClose) { - String s = toString(b); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < s.length(); i += lineLength) { - sb.append (prefix); - if (i + lineLength >= s.length()) { - sb.append(s.substring(i)); - if (addClose) - sb.append(" )"); - } - else { - sb.append(s.substring(i, i + lineLength)); - sb.append("\n"); - } - } - return sb.toString(); -} - - -/** - * Convert a base64-encoded String to binary data - * @param str A String containing the encoded data - * @return An array containing the binary data, or null if the string is invalid - */ -public static byte [] -fromString(String str) { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - byte [] raw = str.getBytes(); - for (int i = 0; i < raw.length; i++) { - if (!Character.isWhitespace((char)raw[i])) - bs.write(raw[i]); - } - byte [] in = bs.toByteArray(); - if (in.length % 4 != 0) { - return null; - } - - bs.reset(); - DataOutputStream ds = new DataOutputStream(bs); - - for (int i = 0; i < (in.length + 3) / 4; i++) { - short [] s = new short[4]; - short [] t = new short[3]; - - for (int j = 0; j < 4; j++) - s[j] = (short) Base64.indexOf(in[i*4+j]); - - t[0] = (short) ((s[0] << 2) + (s[1] >> 4)); - if (s[2] == 64) { - t[1] = t[2] = (short) (-1); - if ((s[1] & 0xF) != 0) - return null; - } - else if (s[3] == 64) { - t[1] = (short) (((s[1] << 4) + (s[2] >> 2)) & 0xFF); - t[2] = (short) (-1); - if ((s[2] & 0x3) != 0) - return null; - } - else { - t[1] = (short) (((s[1] << 4) + (s[2] >> 2)) & 0xFF); - t[2] = (short) (((s[2] << 6) + s[3]) & 0xFF); - } - - try { - for (int j = 0; j < 3; j++) - if (t[j] >= 0) - ds.writeByte(t[j]); - } - catch (IOException e) { - } - } - return bs.toByteArray(); -} - -} diff --git a/src/main/java/org/xbill/DNS/utils/hexdump.java b/src/main/java/org/xbill/DNS/utils/hexdump.java deleted file mode 100644 index 1a79a4026..000000000 --- a/src/main/java/org/xbill/DNS/utils/hexdump.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) - -package org.xbill.DNS.utils; - -/** - * A routine to produce a nice looking hex dump - * - * @author Brian Wellington - */ - -public class hexdump { - -private static final char [] hex = "0123456789ABCDEF".toCharArray(); - -/** - * Dumps a byte array into hex format. - * @param description If not null, a description of the data. - * @param b The data to be printed. - * @param offset The start of the data in the array. - * @param length The length of the data in the array. - */ -public static String -dump(String description, byte [] b, int offset, int length) { - StringBuffer sb = new StringBuffer(); - - sb.append(length + "b"); - if (description != null) - sb.append(" (" + description + ")"); - sb.append(':'); - - int prefixlen = sb.toString().length(); - prefixlen = (prefixlen + 8) & ~ 7; - sb.append('\t'); - - int perline = (80 - prefixlen) / 3; - for (int i = 0; i < length; i++) { - if (i != 0 && i % perline == 0) { - sb.append('\n'); - for (int j = 0; j < prefixlen / 8 ; j++) - sb.append('\t'); - } - int value = (int)(b[i + offset]) & 0xFF; - sb.append(hex[(value >> 4)]); - sb.append(hex[(value & 0xF)]); - sb.append(' '); - } - sb.append('\n'); - return sb.toString(); -} - -public static String -dump(String s, byte [] b) { - return dump(s, b, 0, b.length); -} - -} diff --git a/src/main/java/org/xbill/DNS/windows/DNSServer.properties b/src/main/java/org/xbill/DNS/windows/DNSServer.properties deleted file mode 100644 index 25342f97b..000000000 --- a/src/main/java/org/xbill/DNS/windows/DNSServer.properties +++ /dev/null @@ -1,4 +0,0 @@ -host_name=Host Name -primary_dns_suffix=Primary Dns Suffix -dns_suffix=DNS Suffix -dns_servers=DNS Servers diff --git a/src/main/java/org/xbill/DNS/windows/DNSServer_de.properties b/src/main/java/org/xbill/DNS/windows/DNSServer_de.properties deleted file mode 100644 index aa3f4a690..000000000 --- a/src/main/java/org/xbill/DNS/windows/DNSServer_de.properties +++ /dev/null @@ -1,4 +0,0 @@ -host_name=Hostname -primary_dns_suffix=Prim\u00E4res DNS-Suffix -dns_suffix=DNS-Suffixsuchliste -dns_servers=DNS-Server diff --git a/src/main/java/org/xbill/DNS/windows/DNSServer_fr.properties b/src/main/java/org/xbill/DNS/windows/DNSServer_fr.properties deleted file mode 100644 index 7c87a25b3..000000000 --- a/src/main/java/org/xbill/DNS/windows/DNSServer_fr.properties +++ /dev/null @@ -1,4 +0,0 @@ -host_name=Nom de l'h\u00F4te -primary_dns_suffix=Suffixe DNS principal -dns_suffix=Suffixe DNS propre \u00E0 la connexion -dns_servers=Serveurs DNS diff --git a/src/main/java/org/xbill/DNS/windows/DNSServer_pl.properties b/src/main/java/org/xbill/DNS/windows/DNSServer_pl.properties deleted file mode 100644 index eab57743b..000000000 --- a/src/main/java/org/xbill/DNS/windows/DNSServer_pl.properties +++ /dev/null @@ -1,4 +0,0 @@ -host_name=Nazwa hosta -primary_dns_suffix=Sufiks podstawowej domeny DNS -dns_suffix=Sufiks DNS konkretnego po\u0142\u0105czenia -dns_servers=Serwery DNS diff --git a/src/main/resources/sslSupport/blank_crl.dec b/src/main/resources/sslSupport/blank_crl.dec deleted file mode 100644 index 4485d7662..000000000 Binary files a/src/main/resources/sslSupport/blank_crl.dec and /dev/null differ diff --git a/src/main/resources/sslSupport/blank_crl.pem b/src/main/resources/sslSupport/blank_crl.pem deleted file mode 100644 index ba8593074..000000000 --- a/src/main/resources/sslSupport/blank_crl.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN X509 CRL----- -MIIBuDCCASECAQEwDQYJKoZIhvcNAQEFBQAwWTEaMBgGA1UECgwRQ3liZXJWaWxs -aWFucy5jb20xLjAsBgNVBAsMJUN5YmVyVmlsbGlhbnMgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkxCzAJBgNVBAYTAlVTFw0xMjAyMDUwMzAwMTBaFw0zMTEwMjMwMzAw -MTBaoIGTMIGQMIGBBgNVHSMEejB4gBQKvBeVNGu8hxtbTP31Y4UttI/1bKFdpFsw -WTEaMBgGA1UECgwRQ3liZXJWaWxsaWFucy5jb20xLjAsBgNVBAsMJUN5YmVyVmls -bGlhbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxCzAJBgNVBAYTAlVTggEBMAoG -A1UdFAQDAgEBMA0GCSqGSIb3DQEBBQUAA4GBAEtCmbwTrP7xgkGH3uWH7QU7i52j -aqraBnfheTv6jMFvXq/Nc9hvcmhkkw/+UezjZ/tK1a8a1+vJDPYXUzeAV/eWTPWf -AgWAwBlUTi5ALnRY07TCyQYN2rOb52ChO8cNIUGfEvs41I5N5Vrtvg6m10Eb4XF6 -M2dSJ5eLlMm40kYx ------END X509 CRL----- diff --git a/src/main/resources/sslSupport/cybervillainsCA.cer b/src/main/resources/sslSupport/cybervillainsCA.cer deleted file mode 100644 index 945a93de0..000000000 Binary files a/src/main/resources/sslSupport/cybervillainsCA.cer and /dev/null differ diff --git a/src/main/resources/sslSupport/cybervillainsCA.jks b/src/main/resources/sslSupport/cybervillainsCA.jks deleted file mode 100644 index ed1a64601..000000000 Binary files a/src/main/resources/sslSupport/cybervillainsCA.jks and /dev/null differ diff --git a/src/test/java/org/browsermob/proxy/CookieTest.java b/src/test/java/org/browsermob/proxy/CookieTest.java deleted file mode 100644 index 49cce2e4a..000000000 --- a/src/test/java/org/browsermob/proxy/CookieTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.browsermob.proxy; - -import junit.framework.Assert; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.cookie.BasicClientCookie; -import org.browsermob.core.har.Har; -import org.browsermob.core.har.HarCookie; -import org.browsermob.core.har.HarEntry; -import org.browsermob.proxy.util.IOUtils; -import org.junit.Test; - -import java.io.IOException; - -public class CookieTest extends DummyServerTest { - @Test - public void testNoDoubleCookies() throws IOException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - // set the cookie on the server side - IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/cookie/")).getEntity().getContent()); - - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/echo/")).getEntity().getContent()); - int first = body.indexOf("foo=bar"); - int last = body.lastIndexOf("foo=bar"); - Assert.assertTrue("foo=bar cookie not found", first != -1); - Assert.assertEquals("Multiple foo=bar cookies found", first, last); - } - - @Test - public void testCookiesAreCapturedWhenSet() throws IOException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - // set the cookie on the server side - IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/cookie/")).getEntity().getContent()); - - Har har = proxy.getHar(); - HarEntry entry = har.getLog().getEntries().get(0); - HarCookie cookie = entry.getResponse().getCookies().get(0); - Assert.assertEquals("foo", cookie.getName()); - Assert.assertEquals("bar", cookie.getValue()); - } - - @Test - public void testCookiesAreCapturedWhenRequested() throws IOException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - BasicClientCookie cookie = new BasicClientCookie("foo", "bar"); - cookie.setDomain("127.0.0.1"); - cookie.setPath("/"); - client.getCookieStore().addCookie(cookie); - - // set the cookie on the server side - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/echo/")).getEntity().getContent()); - System.out.println(body); - - Har har = proxy.getHar(); - HarEntry entry = har.getLog().getEntries().get(0); - HarCookie harCookie = entry.getRequest().getCookies().get(0); - Assert.assertEquals("foo", harCookie.getName()); - Assert.assertEquals("bar", harCookie.getValue()); - } - -} diff --git a/src/test/java/org/browsermob/proxy/DummyServer.java b/src/test/java/org/browsermob/proxy/DummyServer.java deleted file mode 100644 index c26c79c4f..000000000 --- a/src/test/java/org/browsermob/proxy/DummyServer.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.browsermob.proxy; - -import org.browsermob.proxy.jetty.http.HttpContext; -import org.browsermob.proxy.jetty.http.HttpListener; -import org.browsermob.proxy.jetty.http.SocketListener; -import org.browsermob.proxy.jetty.http.handler.ResourceHandler; -import org.browsermob.proxy.jetty.jetty.Server; -import org.browsermob.proxy.jetty.jetty.servlet.ServletHttpContext; -import org.browsermob.proxy.jetty.util.InetAddrPort; -import org.browsermob.proxy.jetty.util.Resource; - -import javax.servlet.http.HttpServlet; - -public class DummyServer { - private int port; - private Server server = new Server(); - private ResourceHandler handler; - - public DummyServer(int port) { - this.port = port; - } - - public void start() throws Exception { - HttpListener listener = new SocketListener(new InetAddrPort(port)); - - server.addListener(listener); - addServlet("/jsonrpc/", JsonServlet.class); - addServlet("/cookie/", SetCookieServlet.class); - addServlet("/echo/", EchoServlet.class); - - HttpContext context = new HttpContext(); - context.setContextPath("/"); - context.setBaseResource(Resource.newResource("src/test/dummy-server")); - server.addContext(context); - handler = new ResourceHandler(); - context.addHandler(handler); - - server.start(); - } - - private void addServlet(String path, Class servletClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException { - ServletHttpContext servletContext = new ServletHttpContext(); - servletContext.setContextPath(path); - servletContext.addServlet("/", servletClass.getName()); - server.addContext(servletContext); - } - - public ResourceHandler getHandler() { - return handler; - } - - public void stop() throws InterruptedException { - server.stop(); - } - -} diff --git a/src/test/java/org/browsermob/proxy/DummyServerTest.java b/src/test/java/org/browsermob/proxy/DummyServerTest.java deleted file mode 100644 index 8bf822db9..000000000 --- a/src/test/java/org/browsermob/proxy/DummyServerTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.browsermob.proxy; - -import org.apache.http.HttpHost; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.impl.client.DefaultHttpClient; -import org.junit.After; -import org.junit.Before; - -public class DummyServerTest { - protected DummyServer dummy = new DummyServer(8080); - protected ProxyServer proxy = new ProxyServer(8081); - protected DefaultHttpClient client = new DefaultHttpClient(); - - @Before - public void startServer() throws Exception { - dummy.start(); - proxy.start(); - - HttpHost proxyHost = new HttpHost("127.0.0.1", 8081, "http"); - client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost); - } - - @After - public void stopServer() throws Exception { - proxy.stop(); - dummy.stop(); - } - -} diff --git a/src/test/java/org/browsermob/proxy/MailingListIssuesTest.java b/src/test/java/org/browsermob/proxy/MailingListIssuesTest.java deleted file mode 100644 index 872e10f84..000000000 --- a/src/test/java/org/browsermob/proxy/MailingListIssuesTest.java +++ /dev/null @@ -1,277 +0,0 @@ -package org.browsermob.proxy; - -import junit.framework.Assert; -import org.apache.http.HttpEntity; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.message.BasicNameValuePair; -import org.browsermob.core.har.*; -import org.browsermob.proxy.http.BrowserMobHttpRequest; -import org.browsermob.proxy.http.RequestInterceptor; -import org.browsermob.proxy.util.IOUtils; -import org.junit.Test; - -import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.zip.GZIPInputStream; - -public class MailingListIssuesTest extends DummyServerTest { - @Test - public void testThatInterceptorIsCalled() throws IOException, InterruptedException { - final boolean[] interceptorHit = {false}; - proxy.addRequestInterceptor(new RequestInterceptor() { - @Override - public void process(BrowserMobHttpRequest request) { - interceptorHit[0] = true; - } - }); - - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/a.txt")).getEntity().getContent()); - - Assert.assertTrue(body.contains("this is a.txt")); - Assert.assertTrue(interceptorHit[0]); - } - - @Test - public void testThatInterceptorCanCaptureCallingIpAddress() throws IOException, InterruptedException { - final String[] remoteHost = {null}; - proxy.addRequestInterceptor(new RequestInterceptor() { - @Override - public void process(BrowserMobHttpRequest request) { - remoteHost[0] = request.getProxyRequest().getRemoteHost(); - } - }); - - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/a.txt")).getEntity().getContent()); - - Assert.assertTrue(body.contains("this is a.txt")); - Assert.assertEquals("Remote host incorrect", "127.0.0.1", remoteHost[0]); - } - - @Test - public void testThatWeCanChangeTheUserAgent() throws IOException, InterruptedException { - proxy.addRequestInterceptor(new RequestInterceptor() { - @Override - public void process(BrowserMobHttpRequest request) { - request.getMethod().removeHeaders("User-Agent"); - request.getMethod().addHeader("User-Agent", "Bananabot/1.0"); - } - }); - - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/a.txt")).getEntity().getContent()); - - Assert.assertTrue(body.contains("this is a.txt")); - } - - @Test - public void testThatInterceptorsCanRewriteUrls() throws IOException, InterruptedException { - proxy.addRequestInterceptor(new RequestInterceptor() { - @Override - public void process(BrowserMobHttpRequest request) { - try { - request.getMethod().setURI(new URI("http://127.0.0.1:8080/b.txt")); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - } - }); - - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/a.txt")).getEntity().getContent()); - - Assert.assertTrue(body.contains("this is b.txt")); - } - - @Test - public void testThatProxyCanCaptureBodyInHar() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - String body = IOUtils.readFully(client.execute(new HttpGet("http://127.0.0.1:8080/a.txt")).getEntity().getContent()); - System.out.println("Done with request"); - - Assert.assertTrue(body.contains("this is a.txt")); - - Har har = proxy.getHar(); - Assert.assertNotNull("Har is null", har); - HarLog log = har.getLog(); - Assert.assertNotNull("Log is null", log); - List entries = log.getEntries(); - Assert.assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - Assert.assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - Assert.assertNotNull("Response is null", response); - HarContent content = response.getContent(); - Assert.assertNotNull("Content is null", content); - String mime = content.getMimeType(); - Assert.assertEquals("Mime not matched", "text/plain", mime); - String text = content.getText(); - Assert.assertEquals("Text not matched", "this is a.txt", text); - } - - @Test - public void testThatProxyCanCaptureJsonRpc() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - HttpPost post = new HttpPost("http://127.0.0.1:8080/jsonrpc/"); - HttpEntity entity = new StringEntity("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}"); - post.setEntity(entity); - post.addHeader("Accept", "application/json-rpc"); - post.addHeader("Content-Type", "application/json; charset=UTF-8"); - - String body = IOUtils.readFully(client.execute(post).getEntity().getContent()); - - Assert.assertTrue(body.contains("{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{}}")); - - Har har = proxy.getHar(); - Assert.assertNotNull("Har is null", har); - HarLog log = har.getLog(); - Assert.assertNotNull("Log is null", log); - List entries = log.getEntries(); - Assert.assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - Assert.assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - Assert.assertNotNull("Response is null", response); - HarRequest request = entry.getRequest(); - Assert.assertNotNull("Request is null", request); - HarPostData postdata = request.getPostData(); - Assert.assertNotNull("PostData is null", postdata); - Assert.assertTrue(postdata.getText().contains("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"test\",\"params\":{}}")); - } - - @Test - public void testThatTraditionalPostParamsAreCaptured() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - HttpPost post = new HttpPost("http://127.0.0.1:8080/jsonrpc/"); - post.setEntity(new UrlEncodedFormEntity(Collections.singletonList(new BasicNameValuePair("foo", "bar")))); - - IOUtils.readFully(client.execute(post).getEntity().getContent()); - - Har har = proxy.getHar(); - Assert.assertNotNull("Har is null", har); - HarLog log = har.getLog(); - Assert.assertNotNull("Log is null", log); - List entries = log.getEntries(); - Assert.assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - Assert.assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - Assert.assertNotNull("Response is null", response); - HarRequest request = entry.getRequest(); - Assert.assertNotNull("Request is null", request); - HarPostData postdata = request.getPostData(); - Assert.assertNotNull("PostData is null", postdata); - Assert.assertEquals("application/x-www-form-urlencoded", postdata.getMimeType()); - Assert.assertEquals(1, postdata.getParams().size()); - Assert.assertEquals("foo", postdata.getParams().get(0).getName()); - Assert.assertEquals("bar", postdata.getParams().get(0).getValue()); - } - - @Test - public void testThatImagesAreCapturedAsBase64EncodedContent() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - InputStream is1 = client.execute(new HttpGet("http://127.0.0.1:8080/c.png")).getEntity().getContent(); - ByteArrayOutputStream o1 = new ByteArrayOutputStream(); - IOUtils.copy(is1, o1); - ByteArrayOutputStream o2 = new ByteArrayOutputStream(); - IOUtils.copy(new FileInputStream("src/test/dummy-server/c.png"), o2); - - Assert.assertTrue("Image does not match file system", Arrays.equals(o1.toByteArray(), o2.toByteArray())); - - Har har = proxy.getHar(); - Assert.assertNotNull("Har is null", har); - HarLog log = har.getLog(); - Assert.assertNotNull("Log is null", log); - List entries = log.getEntries(); - Assert.assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - Assert.assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - Assert.assertNotNull("Response is null", response); - HarContent content = response.getContent(); - Assert.assertNotNull("Content is null", content); - String mime = content.getMimeType(); - Assert.assertEquals("Mime not matched", "image/png", mime); - String text = content.getText(); - String base64 = "iVBORw0KGgoAAAANSUhEUgAAATAAAAA5CAIAAAA+4eDYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAIUBJREFUeNrsPQdYFMf3u8cdXaoICigi2LFhARsqKooajd1oYosaY0zU2KImamIsSdSfJjH2JJbE2KPGXmNXLIgFQelFpXeOK/t/dw/X5W53bg+B5B/vfXz3zc3NvJl58/rMLjTDMJQJTGCCfwfQJoE0wZsMidEPbp45mJed6eTq3ia4/18H/vj75GEra+ue/Ye//c54WiIxCaQJTFBF8Cjs4i/LpqtVKvyaklWYkVvA/jp07IfjPp5nEkgTmKAqQKko+W7qoIxnSfg1I68oJTOf28DSyvqPs+EWllZVOSupaWNM8P8dFPLihKiIEnmRnaOLWx0fM6lMTK+7F0+w0qhmmBc5hToNiosKUxLj6vo2gvK5Ywe3//QdAdvc5evqN25mEkgTvOlw5+LxA+uXFhXklZo1a5umAcGd3xpV08uX3PFJxA22nFMgV6rUPA4kXRpDFuTngnD6NWC6tefHZpf3dVGUs4XndImVr0kgTfDvgiePIq5dOEVu0yG4Nxoffdi7bX1xYSGhr5tH7e59B0Mh+emjXavnq9WvZKm4sCDs7KFb5w4HhAx6a9wsqbm5EJK05Hi2nJlfrN/AzExa06N2mXFdqA6tBUI81UVFOmXu9i5VGQJ5+9rfipISg50lZmbm5uBjW1d3renk7FL1KSkT/Dvh8rnjv29a4+RA1fUQzFDYy+57Vh9hZtOMNnfl1r94lrx59RIoNKnPWApI03MX1y4d7GmZ85Vjh7nSyALDMFeP702Nix67YI21rT0vktzMtFKPV6kulCv0GzRu7l/FAaSgQH674JOsjDSjEMnMzes3bu7nHwCqy8Ornokp32RIjImGz85tmXf6E1KGxwsfHbfwmGZReza39uals/BpY0V9PpUR1vDPCh+NlljXz05vTJhGXGT4+vkTJn653tbeSf/X4sJSLzenUM7bvWf/YVVPugqzaWBRH9y9uWvL9xMGdln4yZj056kmvnxjISH2CXy6uxluqcy5qFNz89I5+GzWiDHob6mLYr0aNAFjmJ5b+CQ160FC+v2EtMikjLgXOVn5xXh6kBofvX7BhILcLF6OxUJeEY8zWKde/a6hA/8fCyTXW7j+9+nJw3rcC7tqYs03EFQqZUpCLBQ8aho+UVPlhzOqvFfyqVCE37wMhZZNxLCaol3Xdkk5JalZBUUlSjWjOcJTqNQgYEkZeVEpmWj6nifGbPh8UmF+jh6janxd6FUg1xVI8FQ/W7ZOKpX+FwSyVOvkZC+aNu55SqKJQd80SEmMUyo1IZm7qxihUqpyr7HfIm5fKyosoGmqRWNRx+N//rE1OyeX96cSpSohLTfmebZcoQI7uXnxR/LiMokiicQMPguKFTon8TRNz1i00su34T9CvUpMwxQW5P28drmJQd84f/WpJoB0dqQsLUS1V2a/8lpvXtb4q14elIOdqL7XLl0nNwB5i07NfJFTmBB1/+evpykVr4whHldCA50u702eGRTy1j9FvcrNi146e7QgP8/Eo29YAKkRSA83sTfAlDmXXgnkRU1Gp1UTsX2zMnJExFDU8+yCJ8+y7t+6unPlZ2xW1tzCEj7zi0t0EjkjJnzyD1KvcgUSQoLH9++YePSNgsRSgRTbXl0YySg0KX0IcBLjNNmgFqIFEuywSCguUT5NzTpz/PC+dUtKA0VrGwggoZ5t065z908+X/HPClR5wtZO3fsMem+SSqVKe55yYMemx/fvEho/T0ky8egbBfHaMw93NyO6gJGUVX8bDzxsbShfL7Ede3ZSRMeIbQxSDr7rb9u3qijJ8I8WWFrbFsmVrOg3adFm3oqfzMxeK5FDy5xyszPPH/8z4vb1jBfPzC0s3Gt7t+nYtW2nYAxZK0UgHZxdGvq1wrJ/QNCI7i0xiOcFRUmZQ57szHTCdXbwImxsq2ndDOZheFhkxO2MtOclcvnYqXOxXsAnYUCzgl7ISk/Ly82GOKGavYOdvaOXT8P6TZqjZ8IL+Xm5OtPTAUsraytrm7I+Es/xrKOzSxllXFRYVFhAQCszt7CtxhMkpSYlPHl0LynuKUxMpVLC0HYOTu6163rW9dW5MkKG5IRYoEZqUlx+bg54aNY2ti6utbx8G/o08jM3tzDk4DGwRySaWFpZ2dhqLJtaFXHretSD8Mz05wqFYsqcr2iJBLrD/ClxKVZuGKkRSG0A2bwhI/6CSVBbRYnDJzs2bSUTnAtFJcpft26IurNFSltnF5b6q3Xqei5es+X1rwEc3HPo13U/yeXFNE351KF8fBgn6lL0uW33Trt0HLC0sX9opQgkF4D1be3sCVto7+jM/ToqpA1Benv0G/Lpl6sf3L255svZGIogjBg/lVcgn0beP7z710tnjgLn8S9PKmvaqm3ooFHtu/XWz2If3btj69qlhNV17T1gztIfuAIzth/PXcZfjlxxc38lMD99s/DEwd8JaMd9PG/o2A/Zr/LioiN7tp3884/4p1FCXWq4ubcK7AwLARUjyGqFBcf27QRUKYlxvA2A4dp06Bo6aGSrwCDBLEheLmhYwuTxoSQQnh+WzuNm0SfPXmxGSV6kJsFyjLWQqpxLipKSuze0Bx5NjePA/m+1CAw+sWPDqqvnTxYV5IsylQxz5TYw4SueyctMXDWzrX9g526DvrSy8yhngKaSbFy9Ggp+DZgxQxjPmtwfXzDF78df8K8duIk2d6tEgYyLjszJyiA0aOjXUjw2e6fqsNOLp40jCC1C+vPU75d+dv3v0wZopFTANsNfjZoeHy9Y3rp9l7IR/NBtP30Lga5Qd1D/3K8Rt/hPVkGDcAXy0b1bJIrLZCEDXl0BCb95ZcW8jzLTX5AX8uJZ8vEDv8Pf1PnL+gx+V7/BqcN7Nny3SEgxsZIPygv+GjdvDXiErpIa2CNH5zN/7fvu82m8nk6C1l+1r0ZVszECp1qeFHn7T5geGJbmjYx7HlBVGFXLM2T2krXgU8REPYx5/DA5ISYm6tGDOzfkxQXWVmDJKXkJ9fKZR61jSVMgLT5eDMwTPGRYR14BlZldtOf3k1s2nQrp2/W9aT/peEZiIDtHTdOS0YOY0K48S4BBHWW3cm51qeb3u5lty4oUyNTEONhUcMwSY58c2/8bwQX18w9wreUpHjP438vmfGhQGkEOV8ybWlhgRP4WNPeCKaP6Dh2NnlWp7+1UvUO33hdOHBLqBaYGvEfWvYy4dY1fIO/cCO4zqNTC5Oclcmw7bwTOeg0XTx1ZNncK+H5iI36pLCCop57SUa5aNOPsX/vFUwPCgakjQ6fM/br3wHeM3f2UhLifv18utOno1xhlHktjgfjD8OntqRFmo0BdVOpWQPjn26gZ/J0+snf/9k2eNRXzpqjBK9r8O335Fo1tQA57dmY6tmFs+J1TJiuHOn/tzNzRLcbO2tKibWejZlJUTE0YznTvSFIoEia38NF7Ns2OSSw8Kkwgw66chz/D3COTTfp0oVGYwW0z2ObGxTNfzZxAMGsEOLL7V3CNpn3xDU2X7hBYG4JAAttFPwxv2a4Tfr0nJJB3b7LlyHu3yc98g1LAAsRaKxfOEC+NAB2DQ51dXHVmaKw0loqxQrHmq9mgVd8e+b5RHf/au53wa0JpRsfop94t1RpnpHF9ozuqC8v4+RdP/7Xyi+luLsznU9W0hFqyVvI0QYvfgho5gOnZiXm58/zgaE+9HcL0Cy448Oc7JcVb2nYOET8Tz1rwZ3j+jCKjKGqyjd/hSsnSCgHE/fO/We/TyK9i0SbERC2ZNbF80ogA0d0fW1+Fhc1aB5KvwrNeKziNQhePgAvzcrJL/dUIkr/q5duwSYs2WP5t8xqQB+PipXfG6dTs2vJ9OaSRhY0rF4vRrcZskOEzj1y+QM/DJQNEpX5dQeMjbCGfaBOoGoANWrXoU4mEmT5ebWVJLfuxVBpBzFbMVYd0NiCNHE+EGtJHTcWPe3D7QmVIhyrvliKjCgWyfbdem/adC+wSUuGYv186r0Quf00kOzas4opW6KBRJIF8GE72V9mc8MsA8jbJPA55j+1y7fwpYpw5vP/wsW06dLV4mSWu37h5o2b+3Gawip0bVxP823cnf7pozc/9ho2hBTgRpvG/L2cZqxcIIOYQ8q+zPJOxtVbXcae8+B7XUiipK7cFJYlRF6mLS3dz69plRQX53TswXh7UwVN0VKw272hLLfxEXbOG0WtpWI+RR40pkedXhoyUJK+vOoG8fuH0ljVfV/hFVvAMCVJhlLe2d9sGbmrX3MLCoIW8F3aNOLcbyN+PIwQF0sraJvjlAwRZGWmEGLhrrwHTF343ec5XX/2wfdfZ8ImfLqxm7/DWiLE6zWAVBGdh/LT5IydOD+jcY8rcJVAgpMfIXqh4yEx/ASE3ZejM4wl/Dpjyb8rUcOapj0mgMrLIYeRj+Ex7lvL3qSOgefp0YwqKqD9PlsowxHW1XMu5Ip/a8sdnJ1aKkcy/oy6OqyKBVKmU544dnDQ4+NKZoxWIVoxv5l67LlgSqaG3qpw/fpB5eYUKeL1Tj75CLWGb8exRKMX6Mq9zE+0DciQvdAsdiCd42pwbyXm6fPb4vu0boh/dK5EXgxgPHDVh0/7zQT3LXLCE4PPcsQPk7BFbHkAMFI/u21mB/ir4io72wtpQJXkcQ6v4AuchffhdynuRdE4uOYzUjAvMBnsKttHNhbocRhdrHSkfLyqgJY92SC3scD9teFhkXfZJD4aSJRQOfpAz/XJk+yxOrtrd5gKjeCGeCLSZbXaxe54Is6rMvlAxSR2RAI7Q0tmTF6/9BfyuCkEYHnaFnIGc/+169JNBij6f+m7ck8dCjSHki4l6WK9hUza1c+bIPoKRrN+kudDhHuvZKkpKyJeW+g59jy07ODqDpAkdZ4Px3LTqK0r7RELter6t2nUK7jPIwal6GTsT+YB8yDFjzABKXMyUHB+TnBALuqwK/FUVY1uiyI9J5LmOI3Qf4FYE7exIMrkqbaL1/h3NRfNGPpqWd+6X/gTuq77EZNhtatC+Fyq1X9fM7dZwp6ODLK/G3qb1SsP73VtX+RV/h0+rSKVM1tMtIJhiKGBmF2Dd8Bdbie0Hgzv37RQb1I447fzwirGQECJ+t2Xft5v3Lly9ZdQHM5xrCO4ALHjVohnib1Eg9Ow/bO2Ov/ZdfLTz5K1PF69CRlEqlSkJJJHoN2w0G7W6uNWavnAleRS84YXQuHlrwuM2IJB4+4QAII0gk3gVkxdgCO65Hy2RdAw2fG8DCBgXHbl/x6YpI3otnj6ee+Qb/ySS3PfFs+QXqUnsH7mxzomrASMgkQx4Z/y6P07uv/Ro27HrH332NRCcJSnZX5VZa9L9dx7QIsdipG7gsubm0QYTrcnahzBrai9NxSaVttd/jCuTertuk14vVYDZyA+X7DvplEEN9XgpjQCDx3xy6MIrCZSnnxaby/ReRkvtgD61fZpu3kVnEw07I0+oGIF0dnFr2qqdn38ACMCoSTPW7Tqhcx2HC1npaWePGpEGHPH+xzMWrQSLZGNbzdnFtcdbQzfsPWvvVD0/N5t8QqCTQ2rQtIVjdRdSri87k/u1j3BqJ+rB3aR4wzcmwWslNGPTOSyMmTpXx+iR4er5E7PGD2a1W1ZGegW6MyC94htPnrX4g1mLves3trapVsPNve/Q0ZsPXDAzkyaKOIQ0t/UA3XczXKxAypw1aovM2eoiGJdBf8HGWnPQjz4nlJ0cdBvLZW3LzMfCokjSXC5tVtZWmymkr14OYk4liNJTUnuJdQMsgz8FzvCjJ6RlMsrsSokhQRrZkzpeCLt8XqyCsbEdPn6qfspRKpXyvstIJ3DVrVGSBFhdNo4J7jvI0spawB29lyxGIO/ewCfl9cHOwUk/TAV1s3LrfqNOhhJio9kzG/31vg6IvHSG3ke/l0epLOAt0AQR18ol5m6BQT3jk6m0TFHDWdToD1yRQ7wDwqgK1PIUifaVjZqXBigpPAnmvWMgL9S9WJafmyHP1/UgcjNfaSiZpIA9WSFNQy3HZjcunnkaqXGaydeVeM+rKyaGzMvNJvyakhgrNqnVsKnQBV9bO3uaJr1n/ezRA1y9cPvqBR0bqAPVHMo8ugPKPiikP+8dVHAU/9y1VTcDMebDPb+s49bcvHSOEdg2cMJlfO8jdK/jvXbHkavnTp49tj/8xmVCQoiFc8cOjvlojkb92xq40tIhuLfIJwwAxF+jA9+b9/WCYKAw+0V+EpKW1QjsEvz75rVh9+jeXQxwOTQ2q+YP8XZqUj4YHAtzgpGMAsMAdh5EVybVhKOgwHkjaKuC34oKxrPZtYhb12pYRzgokjPTxztVr8E6I3WcH5WZiQiBpNTFT26ui4iy2KZ9pbJnLc2L80i6SeZYKQJ54cSh29f+JjSQFxeLRFXN3kHQ1TG3cHOvnZoUL9Tg1KHd0H3gyAnAqdcvnVm3bAF5rDre9XVq+gx5V+hSuP5LMd8aNubEwV1cmRfyqEGP9Bk8ijc+3Pvrhv4jxoLkwB/6jfFPHh/YuZlAz+cpiSUlcqCGRx1v8gI/+fwbO3tHqqKhmp2DQEyuCeTMZVQNZwMW0rdxM/AOwu49693FoL8KZJGAnMC+g6QRMEMY6d2gcfSje2B7QQ6h5bM0/hsIrvaJl/cMtved7VrTIzzs6uXDy6ePZawsMyKO9S2o9rGzqyeIaOK99R+OfCVLKsqWEedLStO+vnqEVivoBt7Uh++qyTk1iaVXxQjkvbAra5fMUSqVhfl5MVEPyelHrU/rJBKzhCYtu1VAZ/KJ2f7tG+FPzEDAqT6NdB8rqN9YcxMSNtVgd9danuC5+fm3u3zmmMHGrQKDanrU0a+/duHU1rVLD/3x8zsTpvXoNwRMKMRj8Ofk4np7+N9k9xIEsmGzVlKpjHDvNyUhzs7Pkc2jRtwWfNsFqAyw4bTIaywCzRK1b5qr5Wogs0ub14CB2gX1OHFgR0GR0J3Sl9zprDm5cXDWRNo5uSSBVBVFte7QDVRk+CONG9WwHvMsjc7LpzKyKWc9BdK2/r2MrFEPb9MudsxnH5RO2K9ekrxkdtxTqkNtyrVV2WnYin0CBULWBVMZMf6tJiVbrXXFCGT80yjCg0L6AFxeIbo55O0RFXWE3f2tIbzuXOjgUWu+mm1YxgI0vnEz/0AxAtlv6Hu89Ud2b6O05/Kg3X75YUXH4N5NWwVYWlod+uMXEo/KZOC9o4/dtnPwlbPHhVoe2Llp7rIfgfvBFP+wbP6d6xeFWjZrHRgyYPhrkvTltXJDXqj24aPALiFH9+64+4AWfBG4JkfiILUL0Aiko0YgszWJVoZgIQODloMtzUx/cfMeoKXOa+9xgGMc0pmnl7Mj1d5ftx5c4gZ8boeFSyj1uKI9DdpM6tC1UpI6BiGoV/8KwQMWrH3XXq+PB3zaIaMn8/7UtfcAYHQxRo/SPssiJgXStlOwfn1yQixXQsD1Pbpv5zfzp3756ft3b1wiIGzQtCX7VPvw8R8TzBqEElNHhq5ePPODIT0I0qjBM+6j16dqglZHe9Q05ASZa+K0Fm06WFnb3CT6IlKnXiCU1Mvnvw3eDQBVNXqKRpnuOkw38WW8tc/DHT9PG8oGGgoMaVuZy9AKFwqpQxedV7ZXkUD6t+/i16pdRWGbOn8Z+TBDLBJnfiSWVtbd+hh4PS4tkbRs21GTCPFpiMaKAKEDR/KaYjD15ftHgNzjGdBQ5Gc1njyKAC8uIYbkzoQMGEF4XtkYC6lxWT1cDZgFWqYxd+Cf+wcG3XnAf2WHE0BSr1xWA4nWXKbkGdj5jsGhSanUjoP0xBGaZ6+Sn1NHztKvsy4b70W01K6ixYK28JjGr7AqVRpreXrN/HJ1BSIEQVqxcTfhKoKh8If+aN7SLr0GkDh+8CiDhhrlECSzacu2JC0olfXie+CwRF586tDucsw/IKhH19C3uTXjp80Hq15+dRkYNGXuV6+/L8VFhWnak0x3AynW6izLBXYNKSqmHkTTApJrK7UvVROOTi5al9XAHFTa6wHgpbfv1uvoOfrqHXrKe4yZhPr9EA1ObDntmOs4mavYR0bjk0UfrtYYZlbNv6oFsnX7Lmu2HxGyReWG2nV9V27d36BpC6PTg/YOny1fp39ArwN1fRvpPFTB668ikL1W4Dne5V86fZR9XEs8wFizlqzV8VHBfZ29ZO3AdyeW4z8d9Xp7xKL//Ux455B4SIx7AgYfuJ/8UIWE46S17ah575OQ1yp1DIbWZSykoSMhfFIZHNcF324YPHrykTOyO/cpkEmpGbVqE33uqnEyyTC0zH2Gdb0l4rvY1x135ILhtDaIopX3MkESVYYoAkMvWvPzkh93EI4xXgfc3Gv/79dDU+YuYQ+ODOg5maxn/2GbD1zo3LOfmPahRCPZKqAzR0gCCS2FhL9L7/6L1/wSENRTKu5fi0K4NWrSjOUbdvGePYIoTpzxxZpth8l6pIyRb9J86U+/TfviW5nwf2szjn3VTI9+QwYODTEjHnzSMleufhzx/sdWNfiv9WN+FaGmRx1A7utn4EY0+6QyyPn70+Z//9vRfEnQtv2SIaFM80bMTzvopT/SqeJuiReqvGybH7OqM5Ot8ahTD+ZAprCrq/3gT29HZw9QKAXFSmrf3rrRdkoi+HQR/1H7rPGDcnOyxPuBllbAKnbudby96zdq3b4rwaX8cFhPwhUT4PVJMxeJ5wNAdf3C6fMn/nwYHqb/730gIARD2qZjNyAl4XIfr0s5c9wgubyIT7bNweyz78tSq1Ufj+rL++o6Zxc3YHryQAX5eTcvnblx6ezj+3dTEmJ19gLE1aeRX8fuoeLnHxP18PThPXdvXI5/GqVPZ+Dsth27derRt6lwVF9YkDd9NCkJ1yWkv9CrhJU5l4tjSce/Muc+Fp4zdUeMHKMu1jleltj4HabNylycgjbQkmR5bFtY+ejGR6lJ8acO7Ym+f9FSdTcnTxUdS/l6MUHtqIY+jIuTjkmkMnIs1Zat3JvPtHLmd3wU6YfkSYIhmMxlsIX7FO1UE4oTVyszDoOSeLUkKx+LWh/IXIeTrSBdvtTCvxAy0p7DX152lkJZUs3Owd7BqVZtL/FXVf5xAOFMSYzNzswA8QaT6OBU3dPLB2x7+bCBTklOiM3PzSnIz5XJLCDo9axbT0wC+b8KoJ4yXjzLycqMjbzyLOlpQV62lC62MFc6ONpY2zq7ejap27ibrYNHRQ6pLlEVPmRKXoDvLbH0lliKepHnf0cgTWCC/wCYBNIEJvgXQRl39tatWyu0AIVyY8zKyjp9+nTVryQmJoYwbfKv/ywAuYBolbTwf2RKxs62koaryrEqXiAnTZo0dOjQLC1AAb6WDyOQoEePHlW/kj179sydO7d8v1YZAGH1tRWQ63UkindpqFiNZd/WrVtXyJTEzBboANSo1OF4KVN5S6tIgUSrGBYWtlwLUEBraXIhKpxFqszqvo49gUCme/fulTGxOXPmnDp1ChX3v9lS/fMCCZRydCw91oQCfEWBRIu3ceNGJy0I2RlojA24PAd9QenSNF2vXj3AwKooKEMN1AM2xIxllmuxFwAabZ1ebCV8giVH/OLVHosHRsFeXK0JyIE1CdoUVqrTHX0KqOHSB+mGlUhJ/Ak+habKSy6oROQ6IxIWDn2hcqMWhNACkXEV8InSi9ND74ZduBDZcUpQP1cL3NFxT1lasfyA9QBIAZwk2xfmoDMKlyywWCQmYOOlNhIEGYm7TCG1qMPMuAqoQR/BWA7R32sC88NAyO28vTS6EDUWowdQeUoLUACzCTW7d2sufD19+lSn5YYNG0CGwa5mZmb6+/sjNihDJXaEn6AM3RHtkCFD4FfEzC1DM0AOBUAILaHs7e2NGNiWUAmosMHEiRNhOKhE/KDUGQEAJPgrzAFawnBYCWVACD+BAmLnyV2sDh5udxgdpgcFmAOUcW5QRlRDtMAlGiBHSkJLfTrjunBo+MqSC4YA5EhPFid54TgNaAMFoV1gK2G2uAr4ijNhpyREduhCGB3wwK9YBsw4YWiMOHEj4CsuATDgKIiQOwoLLAdiA+iO1EZmQ2qzBGG3lbvpOtTmTh7HgmYwVawsB4fo7zWZ+bFSvxeUxd7UgQUjLvRt9B0kpAgMjC1RD8FXqMQwGpbEKkvAgxuJmNkyNAO6wFyhEhUPdxTsBQ0AJypRQIiGHWpwbmKcRmiJw2FfqIGvaCXgE37FMoyuj5PbHcgN24bzRFQoPKgO4SsQCsrQGFaE00ZuYD0RHczomEAZusAoSC5kYvQnMcI3uHAYCycDILQLUAkIEQ9yPE5P31M1luyAAacKnzguEpbdZRwdJ4mDsnvBHUXf10W6ASocAjcIqQ0kQrbGZZKdYWQ51Fmsb89WloND9PeawPwoIMgGOr1KXVZcp84a8CvXidVxinq8BGzMNkC8WAmw4iWgsPFi0/e40HXRibj0e/GOazAJzG2JZSArEBeog6wGBWiG24B+BQLyGXcVUEZCsZVsDRoKdIRgRWLiJZ2JsdwPPg84P9yQXvzChXYB2ReIDMjJka0+2YE+hNFh1ciRSEDULMjHRo0iFOgih+ByAC2KNG4T+n76BkOIzjCo/r4YyyG8e01gfhZ4OUTKpSBr3JAPUF3xpgfQxHOpyVKBu0I0/SwpxcgMUhktPnxl835Cu8g7rsgu7AazxgTLsDogEPwEBdZqsZaH2x1mi1vCro5lVpgPUBw8IlgOhDeAkMVD5jYu02OUBeoT2Y6NP41auNAuoEzCxGB6RqVwuHzMOzoqMlg4eumntWBw+SKHxogGV8GODppljhaQdckqhu3F1Szl5hDevRbD/LwcImGFlZtsQD7AoEJIx3R/Cag/YK5oQFhFDpXsV5gQKDBj07YGT0TRP0TviBvHE86aWIWN+KEZam50fnA5SG7UU+hoIUBZpzsAqi1EhR4I6jUM31kVy248YWIsuQA/Ok46cQG7RqGF67Md7y7gCQd2FDk9HbLjeoVGRwfPUQvo4MGI+hayHFlWfy0AYdEE4VmdjlIzyGbs5PWJXA4O4d1rMczPzyE6iRlcMBuP6qd82HBfP2uCG4DsiJWsakc7o49Bv4ypC5Y0GMHrtIRKnB7mBnBJ2Fhoktz4ntWvbFKBXSb2wgwESwH9DJZOd3bO3GXyrh1nq5O0YMdluwB+tg0bO2HMgwkYoYVzJ4mSIzQTdhXsWGxCDvNqOtkdHbLjeoVGx44sAQEt24bdCEx14HJ4R9FJ6rBfdTgEEznc5aCdhFUIJXUwXOQyuc6gxnIIL4UNMj9vA92rc6gYKukMqsoAPUk2YWCCigXQ66xzhMYK5d8Erw9SfWfjv7EwkzRWHoBGB0cRTTR4bmykZILXB9PlchOUB8CTwvAenEaR+W0TmATSBCYwCaQJTGCC8sL/CTAAKdXwRQT6S1AAAAAASUVORK5CYII="; - Assert.assertEquals("Base64 not correct", base64, text); - } - - @Test - public void testThatUrlEncodedQueryStringIsParsedCorrecty() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - HttpGet get = new HttpGet("http://127.0.0.1:8080/a.txt?foo=bar&a=1%262"); - client.execute(get); - - Har har = proxy.getHar(); - Assert.assertNotNull("Har is null", har); - HarLog log = har.getLog(); - Assert.assertNotNull("Log is null", log); - List entries = log.getEntries(); - Assert.assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - Assert.assertNotNull("No entry found", entry); - HarRequest req = entry.getRequest(); - Assert.assertNotNull("No request found", req); - HarNameValuePair queryStringParam = req.getQueryString().get(1); - Assert.assertNotNull("No request query string param found", queryStringParam); - Assert.assertEquals("foo", queryStringParam.getName()); - Assert.assertEquals("bar", queryStringParam.getValue()); - queryStringParam = req.getQueryString().get(0); - Assert.assertNotNull("No request query string param found", queryStringParam); - Assert.assertEquals("a", queryStringParam.getName()); - Assert.assertEquals("1&2", queryStringParam.getValue()); - } - - @Test - public void testThatGzippedContentIsProperlyCapturedInHar() throws IOException, InterruptedException { - proxy.setCaptureContent(true); - proxy.newHar("Test"); - - // gzip all requests - dummy.getHandler().setMinGzipLength(1); - - - HttpGet get = new HttpGet("http://127.0.0.1:8080/a.txt"); - get.addHeader("Accept-Encoding", "gzip"); - String body = IOUtils.readFully(new GZIPInputStream(client.execute(get).getEntity().getContent())); - System.out.println("Done with request"); - - Assert.assertTrue(body.contains("this is a.txt")); - - Har har = proxy.getHar(); - Assert.assertNotNull("Har is null", har); - HarLog log = har.getLog(); - Assert.assertNotNull("Log is null", log); - List entries = log.getEntries(); - Assert.assertNotNull("Entries are null", entries); - HarEntry entry = entries.get(0); - Assert.assertNotNull("No entry found", entry); - HarResponse response = entry.getResponse(); - Assert.assertNotNull("Response is null", response); - HarContent content = response.getContent(); - Assert.assertNotNull("Content is null", content); - String mime = content.getMimeType(); - Assert.assertEquals("Mime not matched", "text/plain", mime); - String text = content.getText(); - Assert.assertEquals("Text not matched", "this is a.txt", text); - } - -} diff --git a/src/test/java/org/browsermob/proxy/ProxyServerTest.java b/src/test/java/org/browsermob/proxy/ProxyServerTest.java deleted file mode 100644 index 3faced5be..000000000 --- a/src/test/java/org/browsermob/proxy/ProxyServerTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.browsermob.proxy; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExternalResource; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; - -public class ProxyServerTest { - - private ProxyServer server = new ProxyServer(0); - - @Before - public void startServer() throws Exception { - server.start(); - } - - @After - public void stopServer() throws Exception { - server.stop(); - } - - @Test - public void portAllocation() throws Exception { - assertThat(server.getPort(), not(equalTo(0))); - } -}